EIPs
EIPs copied to clipboard
EIP-2612: 712-signed token approvals
This is a place for discussing https://github.com/ethereum/EIPs/pull/2612, which proposes an additional method permit
for making approvals by way of signed messages rather than direct transactions
IMO the 'security considerations' section should mention the front running attack that can occur when an allowance is reduced, either by approve
or the new permit
function. I don't think this proposal changes the risk or possible mitigations in any way, but I haven't thought hard about it.
If you have a strong desire to do some bike shedding, you could even suggest a specific mitigation for it. 😝
I was looking forward to talking about this. I'd like to talk about the need for a "deadline"
What aspects of Tx Relayer do we need to take into account "deadline"? The Tx Relayer may have a response that causes tx while setting tx fee low. or no response.
And when a user signs for an interface, it requires more information. or more knowledge. Is usability not compromised? and more complexity for Tx Relayer.
So I think the deadline is unnecessary. 🤔
I like deadline
, as it solves the problem of worrying about all the approvals one has floating out in the world.
That said, I am sympathetic to the argument that it is inconsistent with the existing approve
interface.
Thanks @maurelian, added a comment about the approval front running attack.
@nipol The deadline
parameter is a way to mitigate the free option that the relaying party gets when receiving a signed message (it is just a deadline for the Permit
itself, not for the approval so I don't think it solves the problem of random tokens being given allowance). In cases where the free option is not a concern, deadline
can simply be set to uint(-1)
, so it should be seen as an optional parameter
Actually @maurelian I was just made aware of the xDai staking token implementation of permit
which I think does what exactly what you are alluding to here. It shares the same ABI as Dai and Chai, but not the same semantics. I guess we better document all three flavors in the ERC
In terms of "Free Options," I think deadline
has sufficient requirements.
But in terms of EIP-712, deadline is not convincing.
deadline
is a difficult option for most signer to understand. at now, no example of an interface receiving a deadline
value exists.
We can't show you the "Free Options" article every time ask for a signature. If we cannot effectively explain deadline
to the signer and Tx Relayer, uint256(-1)
will be used in most cases.
Furthermore, deadline
is a consideration that does not appear in the EIP-712 specification. I think it does not have to appear as an exception in Permit
.
If all of these signatures necessary a "deadline" option, if all of EIP-712 signatures have the same security level, I think it's better to update the EIP-712 specification. 🙂
What is the best option? What do you think?
We can't show you the "Free Options" article every time ask for a signature. If we cannot effectively explain deadline to the signer and Tx Relayer, uint256(-1) will be used in most cases.
I'm perfectly happy with uint(-1)
being used in most cases.
deadline is a difficult option for most signer to understand. at now, no example of an interface receiving a deadline value exists.
There are already interfaces which deal with deadline, uniswap being one of them:
or, for Permit
https://stablecoin.services automatically sets deadline 4 minutes into the future.
I can agree with your point that it may a difficult point for users to understand, and one that may require some EIP-712 tweaking to improve. Another ux improving solution could also be to introduce Date
as a type to 712, to improve how it is rendered to the user.
I do think that these options fall outside the scope of this ERC though, especially since this in some sense more of a descriptive erc than a prescriptive erc; I am highlighting what has been implemented and the motivations behind it. We can certainly make tweaks in new versions, but I am mainly interested in documenting what already exists here.
I've seen on twitter that apparently there are at least 3 different semantics contracts follow and still call it "permits". @MrChico you proposed to discuss all in the EIP/ERC.
I think an EIP should be unambiguous and promote a single way of doing things. Is it possible to do that while perhaps mentioning "semi-compatible" contracts? If not, would it make sense having 3 different EIPs?
@axic I'm planning on mentioning the Stake-flavored implementation under "backwards compatibility", making that, the dai and chai implementations all belong to this " semi-compatible" class, but keep the specification as is for specificity
Nice, sounds good!
@MrChico Good! I think we've written quite a meaningful conversation in publicly.
It will be the data we or the next people will refer to when working in the future! 😉
The standard ERC-20 race condition for approvals applies to permit as well.
Why not take the opportunity to fix this? Otherwise, we might end up with proliferations of safePermit too. Is the assumption that the allowances will be spent immediately, so it's not actually a problem?
The standard ERC-20 race condition for approvals applies to permit as well.
Why not take the opportunity to fix this? Otherwise, we might end up with proliferations of safePermit too. Is the assumption that the allowances will be spent immediately, so it's not actually a problem?
To be honest I don't find the approval race condition to be much of an issue in practice – it's only ever a problem when allowing addresses that can "spontaneously" spend your allowance. In most use cases, you give your allowance to a smart contract in such a way that only you can later initialize the transferFrom
from your own address.
I'm all for people implementing safePermit
if they feel that this is a concern, but in order to limit the scope of what I'm trying to do here I think such alternatives should be in a different ERC
It wasn't clear to me from a cursory read of the EIP that the following getter is also part of the required interface, I'd make it more explicit:
function nonces(address owner) external view returns (uint256);
I'd also consider changing its name to something else, since nonces
is quite generic and non-descript.
I'd also consider changing its name to something else, since
nonces
is quite generic and non-descript.
Do you have a suggestion?
Keep in mind that a change in this name would make this etc incompatible with Uniswap v2 (and even more incompatible with dai and chai)
I'd also consider changing its name to something else, since
nonces
is quite generic and non-descript.Do you have a suggestion?
permitNonces
Its okay for nonces to be generic, in some cases it can be an advantage. The erc doesn't specify other methods can't use nonces
for other purpose
But most of the time, this doesn't seem like a particularly important concern
I think it would be useful to have a function that let smart contract know if the permit call will succeed if executed. For example, when a contract called via static_call need to check the validity of the signature before handing the operation to another contract/call.
This could be like :
function isValidPermit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external view returns (bool);
I think it would be useful to have a function that let smart contract know if the permit call will succeed if executed. For example, when a contract called via static_call need to check the validity of the signature before handing the operation to another contract/call.
This could be like :
function isValidPermit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external view returns (bool);
This is interesting. But if they were called with staticcall they wouldn't be able to execute the permit later anyway? In the cases I can imagine, it seems like you'd want to submit the permit whenever if it is well formed so it seems a try-catch pattern would be sufficient for this use case. Or is there some scenario you can think of in which you would NOT want to submit a well-formed permit?
Hmmm, I guess one could imagine a sort of pure
validity checking contract which would be called with staticcall and return true
to the original caller if all is ok, and the original caller would not be constrained to a STATIC
environment....
Did you have something like this in mind? Do people actually write contracts like this?
Yes, that's basically it. GSN (https://docs.opengsn.org/learn/index.html) have this architecture where a "view" call is made first to a "paymaster" contract that can answer whether it can pay for the metatx. If it answer no, the metatx do not proceed and it is not charged. If it says yes, another call will be made where the "paymaster" can now call permit. The gas consumed here will be charged
@wighawag couldn't you just as well check the validity of the permit in the "paymaster" in that case? As per the spec, you need to validate:
- The current blocktime is less than or equal to
deadline
. -
owner
is not the zero address. -
Token.nonces[owner]
is equal to nonce providednonce
. -
r
,s
andv
is a validsecp256k1
signature fromowner
of an ERC-712 typedPermit
message.
all of which can be verified in a static environment.
Although the nonce
is not given as an argument in the permit
function, it will equal to Token.nonces[owner]
if it is indeed a valid signature.
I think this EIP can do without the view
method described above. The reason why it's there in GSN is because part of that function is calling into a contract and performing an arbitrary query: by encapsulating the call there clients can get a boolean return value without having to know anything about the end recipient.
@MrChico @nventuro That's exactly what I do as a workaround, see here : https://github.com/wighawag/gsn-playground/blob/5f40cc949031129374a48e43c8a0b71b83e52bf1/contracts/src/DAIPaymaster.sol#L101-L119
but this does not scale if my paymaster wanted to support all token that support the permit standard (unless A DOMAIN_SEPARATOR view was also part of the standard), hence why in my opinion a function like isValidPermit
should be part of the standard.
By the way in regard to EIP-712 domain. I am not sure this should be part of the standard. EIP-2612 Contract implementer should be free to set the EIP-712 domain they see fit.
but this does not scale if my paymaster wanted to support all token that support the permit standard (unless A DOMAIN_SEPARATOR view was also part of the standard), hence why in my opinion a function like
isValidPermit
should be part of the standard.
Ah, yes you are right that this requires DOMAIN_SEPARATOR to be publicly accessible. I think requiring it to be accessible is a good idea and should be added to the spec
Is that because the version
field is unknown? All other members of the domain separator seem to be obtainble, but I'm not an expert on 712.
While the current mechanism for nonce management saves on gas (one storage slot per transaction rather than one storage slot per user), it means that a user cannot sign several permits and hand them out to multiple providers because inclusion order is not known at permit signing time. It may be worth the storage costs to have the nonce be included. An implementation can always do some tricks like storing 256 nonce-used flags in a single variable so there are 255 storage updates for every storage addition, thus greatly reducing the gas costs.