Vyper Permit2 Tutorial
Integrate Permit2 into your contracts
Permit21 is a contract that allows using Permit signatures2 with any token. You just approve a Permit2 contract once and then you can use it with any contract that integrates it.
It solves a crucial UX problem, but it has seen quite slow adoption. Possibly because its documentation leaves the developers scratching their heads instead of giving them an idea how to integrate Permit2 in their contracts.
This tutorial will teach you how to use Permit2 contract from Vyper and Python using copy and paste method.
Vyper contract
The first snippet defines everything you need to call the Permit2 contract. It defines all the needed structs and the interface you’ll be calling.
struct TokenPermissions:
token: address
amount: uint256
struct PermitTransferFrom:
permitted: TokenPermissions
nonce: uint256
deadline: uint256
struct SignatureTransferDetails:
to: address
requestedAmount: uint256
interface Permit2:def permitTransferFrom(
permit: PermitTransferFrom,
transferDetails: SignatureTransferDetails,
owner: address,65]
signature: Bytes[
): nonpayable
permit2: immutable(Permit2)
@external
def __init__():
= Permit2(0x000000000022D473030F116dDEE9F6B43aC78BA3) permit2
Add a new deposit method and use it as follows:
@external
def deposit_permit(amount: uint256, nonce: uint256, deadline: uint256, signature: Bytes[65]) -> uint256:
permit2.permitTransferFrom(
PermitTransferFrom({self.token, amount: amount}),
permitted: TokenPermissions({token:
nonce: nonce,
deadline: deadline
}),self, requestedAmount: amount}),
SignatureTransferDetails({to:
msg.sender,
signature,
)# you have the amount of token here
return self.vault.deposit(amount, msg.sender)
Note that requestedAmount
can be lower than permitted amount, but it cannot exceed it.
Generate a signature
Now that our contract accepts Permit2 signatures, we need to learn how to generate them. I’ll show how to do this with ape3.
$ ape console --network ethereum:mainnet-fork:foundry
Before using Permit2, the user would need to approve it. Luckily one approval is enough to use it with any number of contracts.
= accounts.load("harambe")
dev = project.YourContract.deploy(sender=dev)
your_contract
= Contract("choose your own")
token = Contract("0x000000000022D473030F116dDEE9F6B43aC78BA3")
permit2
2**256-1, sender=dev) token.approve(permit2,
Then you you need a valid EIP-712 signature. We’ll be using eip712
4 library maintained by ApeWorX.
You may notice the type you sign slightly differs from the Vyper struct. When verifying, the spender
is always set to the calling contract.
from eip712 import EIP712Message, EIP712Type
class TokenPermissions(EIP712Type):
"address"
token: "uint256"
amount:
class PermitTransferFrom(EIP712Message):
= "Permit2"
_name_ = 1
_chainId_ = "0x000000000022D473030F116dDEE9F6B43aC78BA3"
_verifyingContract_
permitted: TokenPermissions"address"
spender: "uint256"
nonce: "uint256" deadline:
In Permit2, nonce
is non-monotonic. You need to set it to any unused value. I recommend setting it to the the last block timestamp. The deadline
is a unix timestamp as usual.
= chain.blocks[-1].timestamp
nonce = nonce + 86400
deadline
= PermitTransferFrom(
permit
TokenPermissions(token, amount),
your_contract,
nonce,
deadline, )
The signature
is accepted in rsv
format. There is a handy method to encode it that way.
= accounts.load('harambe')
user = user.sign_message(permit).encode_rsv()
signature
= your_contract.deposit_permit(amount, nonce, deadline, signature)
tx tx.show_trace()
Congrats! You’ve just made a permit deposit into your toy vault.