Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POC feat[lang]: add EIP-3074 support #3958

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

charles-cooper
Copy link
Member

@charles-cooper charles-cooper commented Apr 15, 2024

POC, add EIP-3074 support to the language via new authcall call type and authorize() builtin.

What I did

How I did it

How to verify it

example usage:

from ethereum.ercs import IERC20

@external
def do_transfer(token: IERC20, receiver: address, amount: uint256, sig: Bytes[97]):
    authorize(addr, sig)
    authcall token.approve(receiver, amount)
    authcall token.transfer(receiver, amount)

Commit message

Commit message for the final, squashed PR. (Optional, but reviewers will appreciate it! Please see our commit message style guide for what we would ideally like to see in a commit message.)

Description for the changelog

Cute Animal Picture

Put a link to a cute animal picture inside the parenthesis-->

POC, add EIP-3074 support to the language via new `authcall` call type
and `authorize()` builtin.
@@ -32,7 +32,7 @@
)
from vyper.semantics.data_locations import DataLocation
from vyper.semantics.types.base import KwargSettings, VyperType
from vyper.semantics.types.primitives import BoolT
from vyper.semantics.types.primitives import BoolT, AddressT

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
vyper.semantics.types.primitives
begins an import cycle.
@fubuloubu
Copy link
Member

fubuloubu commented Apr 15, 2024

from ethereum.ercs import IERC20

@external
def do_transfer(token: IERC20, receiver: address, amount: uint256, sig: Bytes[97]):
    authorize(addr, sig)
    authcall token.approve(receiver, amount)
    authcall token.transfer(receiver, amount)

I feel like while it is not necessary to use a context manager based on how this EIP works, we might be able to ensure a little better level of safety by using one:

from ethereum.ercs import IERC20

@external
def do_transfer(token: IERC20, receiver: address, amount: uint256, sig: Bytes[97]):
    commit: bytes32 = abi_encode(...)  # could be an empty bytes32 literal
    with authorize(msg.sender, sig, commit=commit):  # Calls `AUTH`
        # NOTE: EIP-3074 doesn't require `token.approve` workflow
        authcall token.transfer(receiver, amount)  # happens within `authorize` context
    # authcall outside of context raises compile-time exception

@charles-cooper
Copy link
Member Author

charles-cooper commented Apr 15, 2024

from ethereum.ercs import IERC20

@external
def do_transfer(token: IERC20, receiver: address, amount: uint256, sig: Bytes[97]):
    authorize(addr, sig)
    authcall token.approve(receiver, amount)
    authcall token.transfer(receiver, amount)

I feel like while it is not necessary to use a context manager based on how this EIP works, we might be able to ensure a little better level of safety by using one:

from ethereum.ercs import IERC20

@external
def do_transfer(token: IERC20, receiver: address, amount: uint256, sig: Bytes[97]):
    commit: bytes32 = abi_encode(...)  # could be an empty bytes32 literal
    with authorize(msg.sender, sig, commit=commit):  # Calls `AUTH`
        # NOTE: EIP-3074 doesn't require `token.approve` workflow
        authcall token.transfer(receiver, amount)  # happens within `authorize` context
    # authcall outside of context raises compile-time exception

prank? :)

with prank(msg.sender, sig):
    extcall token.transfer(receiver, amount)

but yea it's a somewhat interesting design space because AUTH and AUTHCALL are actually so closely coupled. another possibility is

authcall token.transfer(receiver, amount, auth=sig)

or

token = authorize(address, sig)
# "token" is a compile-time concept which gets erased at runtime
# it is invalidated at compile-time if any other call to `authorize()` happens
extcall token.transfer(receiver, amount, auth=token)

@fubuloubu
Copy link
Member

token = authorize(address, sig)
# "token" is a compile-time concept which gets erased at runtime
# it is invalidated at compile-time if any other call to `authorize()` happens
extcall token.transfer(receiver, amount, auth=token)

token is far too loaded, but this isn't bad

should still be authcall though

@charles-cooper
Copy link
Member Author

i think with any of the above techniques to link the AUTH invocation with AUTHCALL, we no longer need the authcall keyword, especially with the scoped thing. like is there any reason you would want to switch between regular and authcall inside of the with authorized(addr, sig): block?

@fubuloubu
Copy link
Member

fubuloubu commented Apr 16, 2024

token = authorize(address, sig)
# "token" is a compile-time concept which gets erased at runtime
# it is invalidated at compile-time if any other call to `authorize()` happens
extcall token.transfer(receiver, amount, auth=token)

token is far too loaded, but this isn't bad

should still be authcall though

actually, more I think about this the more I like it. the auth= kwarg precludes any usage without first doing authorize. we can keep track of authorization context when compiling and raise when it uses older:

assert extcall token.transfer(receiver, amount, auth=...)  # can't do this for obvious reasons
auth1 = authorize(acct1, sig1)
assert extcall token.transfer(receiver, amount, auth=auth1, default_return_value=True)
auth2 = authorize(acct2, sig2)  # auth1 is now "stale"
assert extcall token.transfer(receiver, amount, auth=auth2, default_return_value=True)
assert extcall token.transfer(receiver, amount, auth=auth1, default_return_value=True)  # raises

Best part is we don't have to argue what happens when you exit context, the context persists until we exit the call, or authorize is done again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants