EIPs icon indicating copy to clipboard operation
EIPs copied to clipboard

EIP-2612: 712-signed token approvals

Open MrChico opened this issue 4 years ago • 103 comments

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

MrChico avatar Apr 24 '20 11:04 MrChico

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. 😝

maurelian avatar Apr 24 '20 12:04 maurelian

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. 🤔

Nipol avatar Apr 24 '20 13:04 Nipol

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.

maurelian avatar Apr 24 '20 13:04 maurelian

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

MrChico avatar Apr 24 '20 13:04 MrChico

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

MrChico avatar Apr 24 '20 17:04 MrChico

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?

Nipol avatar Apr 27 '20 09:04 Nipol

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: Screen Shot 2020-04-27 at 12 21 12

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.

MrChico avatar Apr 27 '20 10:04 MrChico

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 avatar Apr 27 '20 12:04 axic

@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

MrChico avatar Apr 27 '20 12:04 MrChico

Nice, sounds good!

axic avatar Apr 27 '20 13:04 axic

@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! 😉

Nipol avatar Apr 27 '20 13:04 Nipol

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?

ptrwtts avatar Apr 29 '20 16:04 ptrwtts

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

MrChico avatar Apr 29 '20 17:04 MrChico

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.

nventuro avatar May 15 '20 18:05 nventuro

I'd also consider changing its name to something else, since nonces is quite generic and non-descript.

Do you have a suggestion?

MrChico avatar May 15 '20 20:05 MrChico

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)

MrChico avatar May 15 '20 20:05 MrChico

I'd also consider changing its name to something else, since nonces is quite generic and non-descript.

Do you have a suggestion?

permitNonces

petejkim avatar May 15 '20 20:05 petejkim

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

MrChico avatar May 15 '20 21:05 MrChico

But most of the time, this doesn't seem like a particularly important concern

MrChico avatar May 15 '20 21:05 MrChico

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);

wighawag avatar May 16 '20 14:05 wighawag

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?

MrChico avatar May 16 '20 23:05 MrChico

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?

MrChico avatar May 16 '20 23:05 MrChico

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 avatar May 17 '20 07:05 wighawag

@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 provided nonce.
  • r, s and v is a valid secp256k1 signature from owner of an ERC-712 typed Permit message.

all of which can be verified in a static environment.

MrChico avatar May 23 '20 00:05 MrChico

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.

MrChico avatar May 23 '20 00:05 MrChico

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.

nventuro avatar May 23 '20 01:05 nventuro

@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.

wighawag avatar May 23 '20 06:05 wighawag

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

MrChico avatar May 23 '20 11:05 MrChico

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.

nventuro avatar May 26 '20 21:05 nventuro

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.

MicahZoltu avatar Jul 27 '20 12:07 MicahZoltu