iT邦幫忙

2024 iThome 鐵人賽

DAY 15
0
Modern Web

web3 短篇集系列 第 15

允許代付手續費的代幣 (ERC-2612)

  • 分享至 

  • xImage
  •  

Permit Token 是 ERC20 代幣的擴充,在代幣合約中新增 permit 函式,能夠透過提交簽章來實現代幣的授權,用戶便可以透過簽章請別人代送交易。

規格說 ERC20 代幣需要新增以下三個函式:

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)

直接看 openzeppelin ERC20Permit.sol 對 permit function 的實作:

function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        if (block.timestamp > deadline) {
            revert ERC2612ExpiredSignature(deadline);
        }

        bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        if (signer != owner) {
            revert ERC2612InvalidSigner(signer, owner);
        }

        _approve(owner, spender, value);
    }
  • 以 ERC712 的格式去簽簽章,合格的錢包商通常會在用戶簽名之前,秀出 ERC712 格式的訊息。
  • 持有簽章的任何人能夠呼叫此函式,最後會 approve 代幣。

如果一個 ERC20 代幣有支援 permit 功能,那可以寫一份簡單的合約,透過簽章來轉代幣:

contract GaslessTransfer {
    function send(
        address token,
        address sender,
        address receiver,
        uint256 amount,
        uint256 fee,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        IERC20Permit(token).permit(sender, address(this), amount + fee, deadline, v, r, s);
        IERC20(token).transferFrom(sender, receiver, amount);
        IERC20(token).transferFrom(sender, msg.sender, fee);
    }
}

透過簽章轉代幣的好處是,只要持有簽章就能提交交易到區塊鏈,不一定要是傳送代幣的本人,因此可以做到「代付手續費」的功能。

免手續費 (gasless) 不是代表鏈上操作真的不用手續費,而是背後可能有其他人或公司願意幫你出手續費。

實作 Permit 功能的安全考量

實作通常會直接引套件,以下筆記來自 ERC-2612 的 Security Considerations:

  • 任何人都可以拿簽章送交易,其他人可以搶走後提前送,但不論誰送最後在鏈上的結果皆相同。(而且送交易需要手續費,通常不會有誘因去做。)
  • 小心 ecrecover 接受錯誤訊息時,會得到零地址,確保 owner != address(0)
  • 幫送交易的人可以選擇「不送」,你的簽章就永遠不會上鏈,因此簽名有一欄 deadline 讓你限制這份簽章的有效期限。若有人卡你的簽章不送時,你也可以自行提交簽章送交易(自付手續費),那之前簽過的簽章會因為 nonce 不對而無效。
  • 避免在合約部署時定義 DOMAIN_SEPARATOR 內的 chainid,應該為每筆簽章重新取一次 block.chainid,避免未來鏈分裂時,簽章拿來重送。

延伸

Permit2

Permit2 是 Uniswap 實作的一個合約的名字。

對於已部署的代幣,無法新增 permit 功能,用戶要跟合約互動時,還是得送兩筆交易 (approve 和 transferFrom)。

Uniswap 的 Permit2 合約,提供一基礎建設,讓用戶一次性 approve 給 Permit2 合約,之後就能以送簽章來授權的方式,跟整合 Permit2 合約的合約進行互動,解決無 Permit 功能的代幣也能透過簽章做授權,前提是用戶還是得先 approve 給 Permit2 合約。詳情見:Uniswap Permit2 實作與設計

Meta Transactions

Permit 功能僅針對 ERC20 approve 函式進行簽章化的功能,目的是讓未來的 ERC20 代幣有此功能。若你要讓你合約內所有函式都能透過簽章來交易,可以使用 openzeppelin 的 Meta Transactions 套件,來自於 ERC2771

錢包合約 (SCA)

  • SCA 因為是合約,無法簽名,所以有 ERC-1271 提供一個可遵循的介面 isValidSignature,建議實作 SCA 時支援此函式,用來驗證代表此 SCA 的「訊息」與「簽章」是否有效。
  • 遵循 ERC-4337 的 SCA,有 paymaster 機制能做到代付手續費的功能,不論 ERC20 代幣有無 Permit 功能,不論合約應用有無支援 Meta Transaction,都能請別人代付手續費。

PS: 同時參賽中的作者剛好有一篇類似主題:Web3:從新手村開始 - Lv13 新手訓練營 : 惡意簽名


上一篇
合約驗證 EOA 的簽章
下一篇
結構化的簽章 (ERC-712)
系列文
web3 短篇集30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言