ERC-2612: EIP-20 签名授权的 Permit 扩展
通过 EIP-712 secp256k1 签名进行 EIP-20 授权
Authors | Martin Lundfall (@Mrchico) |
---|---|
Created | 2020-04-13 |
Requires | EIP-20, EIP-712 |
摘要
可以说,EIP-20 代币成功的主要原因之一在于 approve
和 transferFrom
之间的相互作用,这使得代币不仅可以在外部拥有账户 (EOA) 之间转移,还可以通过抽象出 msg.sender
作为代币访问控制的定义机制,在其他合约中用于特定于应用程序的条件下。
然而,这种设计的一个限制因素源于 EIP-20 approve
函数本身是根据 msg.sender
定义的。这意味着用户涉及 EIP-20 代币的 初始操作 必须由 EOA 执行(但请参阅下面的注释)。如果用户需要与智能合约交互,那么他们需要进行 2 笔交易(approve
和智能合约调用,该调用将在内部调用 transferFrom
)。即使在支付给另一个人的简单用例中,他们也需要持有 ETH 来支付交易 gas 费用。
此 ERC 使用一个新函数 permit
扩展了 EIP-20 标准,该函数允许用户使用签名消息修改 allowance
映射,而不是通过 msg.sender
。
为了改善用户体验,签名数据按照 EIP-712 进行结构化,该结构已在主要 RPC 提供商中得到广泛采用。
注意: EIP-20 必须由 EOA 执行,除非拥有代币的地址实际上是一个合约钱包。虽然合约钱包解决了许多与此 EIP 相同的动机问题,但它们目前在生态系统中的采用率很低。合约钱包存在 UX 问题 – 因为它们将合约钱包的 EOA owner
与合约钱包本身(旨在代表 owner
执行操作并持有其所有资金)分离,因此需要专门设计用户界面来支持它们。permit
模式获得了许多相同的好处,同时几乎不需要更改用户界面。
动机
虽然 EIP-20 代币在以太坊生态系统中已经无处不在,但从协议的角度来看,它们的地位仍然是二等代币。允许用户在不持有任何 ETH 的情况下与以太坊交互的能力一直是一个长期存在的目标,并且是许多 EIP 的主题。
到目前为止,这些提案中的许多提案的采用率非常低,而已被采纳的提案(例如 EIP-777)引入了许多附加功能,导致主流合约中出现意外行为。
此 ERC 提出了一种替代解决方案,该解决方案旨在尽可能地小,并且仅解决 一个问题:EIP-20 approve
方法中缺少抽象。
虽然可能很想为每个 EIP-20 函数引入 *_by_signature
对等项,但出于以下两个原因,它们有意地从该 EIP-20 中排除:
- 此类函数的所需细节(例如关于
transfer_by_signature
的费用、可能的批处理算法的决策)因用例而异,并且, - 它们可以使用
permit
和其他辅助合约的组合来实现,而不会损失通用性。
规范
除了 EIP-20 之外,符合要求的合约还必须实现 3 个新函数:
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external
function nonces(address owner) external view returns (uint)
function DOMAIN_SEPARATOR() external view returns (bytes32)
它们的语义如下:
对于所有地址 owner
、spender
、uint256s value
、deadline
和 nonce
、uint8 v
、bytes32 r
和 s
,
调用 permit(owner, spender, value, deadline, v, r, s)
将设置
allowance[owner][spender]
为 value
,
将 nonces[owner]
递增 1,
并发出相应的 Approval
事件,
当且仅当满足以下条件时:
- 当前区块时间小于或等于
deadline
。 owner
不是零地址。nonces[owner]
(在状态更新之前)等于nonce
。r
、s
和v
是来自owner
的消息的有效secp256k1
签名:
如果未满足任何这些条件,则 permit
调用必须恢复。
keccak256(abi.encodePacked(
hex"1901",
DOMAIN_SEPARATOR,
keccak256(abi.encode(
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"),
owner,
spender,
value,
nonce,
deadline))
))
其中 DOMAIN_SEPARATOR
根据 EIP-712 定义。DOMAIN_SEPARATOR
应该对于合约和链是唯一的,以防止来自其他域的重放攻击,
并且满足 EIP-712 的要求,但在其他方面不受约束。
DOMAIN_SEPARATOR
的常见选择是:
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
keccak256(bytes(name)),
keccak256(bytes(version)),
chainid,
address(this)
));
换句话说,该消息是 EIP-712 类型结构:
{
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"Permit": [
{
"name": "owner",
"type": "address"
},
{
"name": "spender",
"type": "address"
},
{
"name": "value",
"type": "uint256"
},
{
"name": "nonce",
"type": "uint256"
},
{
"name": "deadline",
"type": "uint256"
}
],
},
"primaryType": "Permit",
"domain": {
"name": erc20name,
"version": version,
"chainId": chainid,
"verifyingContract": tokenAddress
},
"message": {
"owner": owner,
"spender": spender,
"value": value,
"nonce": nonce,
"deadline": deadline
}
}
请注意,在此定义中,我们没有提到 msg.sender
。permit
函数的调用者可以是任何地址。
理由
permit
函数足以使涉及 EIP-20 代币的任何操作都可以使用代币本身来支付,而不是使用 ETH。
给出 nonces
映射是为了进行重放保护。
permit
的常见用例是,中继器代表 owner
提交 Permit
。在这种情况下,中继方实际上被赋予了一个免费选项来提交或扣留 Permit
。如果这引起关注,owner
可以通过将 deadline
设置为不久将来的值来限制 Permit
的有效时间。可以将 deadline
参数设置为 uint(-1)
以创建实际上永不过期的 Permit
。
包含EIP-712类型消息是因为它在许多钱包提供商中得到了广泛采用。
向后兼容性
在实际的合约中实现的代币合约中,已经有一些 permit
函数,最著名的是在 dai.sol
中引入的函数。
它的实现与此处的演示略有不同,因为:
- 它没有采用
value
参数,而是采用一个布尔值allowed
,将批准设置为 0 或uint(-1)
。 deadline
参数改为称为expiry
。这不仅仅是一个语法更改,因为它会影响签名消息的内容。
在代币 Stake
(以太坊地址 0x0Ae055097C6d159879521C384F1D2123D1f195e6
)中也有一种实现,其 ABI 与 dai
相同,但语义不同:它允许用户发布“过期的批准”,该批准仅允许在 expiry >= block.timestamp
时发生 transferFrom
。
此处提供的规范与 Uniswap V2 中的实现一致。
当 EIP 已经广泛部署时,添加了在 permit 无效时恢复的要求,但目前它与所有已发现的实现一致。
安全考虑
虽然 Permit
的签署人可能心中有一个特定的提交其交易的方,但另一方始终可以抢先执行此交易并在预期方之前调用 permit
。但是,对于 Permit
签署人来说,最终结果是相同的。
由于当给定格式错误的消息时,ecrecover 预编译会静默失败,并且仅返回零地址作为 signer
,因此务必确保 owner != address(0)
,以避免 permit
创建批准以花费属于零地址的“僵尸资金”。
签名的 Permit
消息是可审查的。中继方始终可以选择在收到 Permit
后不提交它,从而扣留提交它的选项。deadline
参数是对其的一种缓解措施。如果签名方持有 ETH,他们也可以自己提交 Permit
,这可能会使先前签名的 Permit
无效。
批准的标准 EIP-20 竞争条件 (SWC-114) 也适用于 permit
。
如果 DOMAIN_SEPARATOR
包含 chainId
并且是在合约部署时定义的,而不是为每个签名重建的,则在未来链分裂的情况下,存在链之间可能发生重放攻击的风险。
版权
通过 CC0 放弃版权和相关权利。
Citation
Please cite this document as:
Martin Lundfall (@Mrchico), "ERC-2612: EIP-20 签名授权的 Permit 扩展," Ethereum Improvement Proposals, no. 2612, April 2020. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-2612.