Permit2 Link to heading

问题 Link to heading

  • 有没有办法让为所有的 Token 合约实现离线授权?

Permit2交互流程: Link to heading

  • Alice在一个ERC20上调用 approve ,无限的授权给 Permit2 合约 (在各个链上有相同的地址)

  • Alice签署链下消息:表明协议合约被允许代表她转账代币

  • 协议合约上调用一个交互函数,将签署的 permit2 消息作为参数传入

  • Permit2合约上调用 permitTransferFrom, Permit2 按照消息指示转移 Token 到协议

流程图 Link to heading

example Link to heading

合约 Link to heading

// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity ^0.8.0;

import "./myToken.sol";
import "./interfaces/IPermit2.sol";

contract TokenBank {
    // 记录每个地址在银行中的余额
    mapping(address => uint256) public balances;
    
    // ERC20代币合约地址
    MyToken public token;
    
    // Permit2 合约实例
    IPermit2 public permit2;
    
    // 委托授权结构体
    struct DelegateAuthorization {
        address owner;        // 代币所有者
        address delegate;     // 被委托人
        uint256 amount;       // 授权金额
        uint256 deadline;     // 授权截止时间
        bool used;           // 是否已使用
    }
    
    // 委托授权映射:授权哈希 => 授权信息
    mapping(bytes32 => DelegateAuthorization) public delegateAuthorizations;
    
    // 事件
    event Deposit(address indexed user, uint256 amount);
    event Withdraw(address indexed user, uint256 amount);
    event DelegateAuthorizationCreated(address indexed owner, address indexed delegate, uint256 amount, uint256 deadline, bytes32 authHash);
    event DelegateDepositExecuted(address indexed owner, address indexed delegate, uint256 amount, bytes32 authHash);
    
    // 构造函数,设置要管理的ERC20代币
    constructor(address _tokenAddress, address _permit2Address) {
        token = MyToken(_tokenAddress);
        permit2 = IPermit2(_permit2Address);
    }
    
    // 存款函数
    function deposit(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than 0");
        require(token.balanceOf(msg.sender) >= _amount, "Insufficient token balance");
        
        // 从用户账户转移代币到合约
        require(token.transferFrom(msg.sender, address(this), _amount), "Transfer failed");
        
        // 更新用户在银行的余额
        balances[msg.sender] += _amount;
        
        emit Deposit(msg.sender, _amount);
    }
    
    // 取款函数
    function withdraw(uint256 _amount) external {
        require(_amount > 0, "Amount must be greater than 0");
        require(balances[msg.sender] >= _amount, "Insufficient bank balance");
        
        // 更新用户在银行的余额
        balances[msg.sender] -= _amount;
        
        // 从合约转移代币到用户账户
        require(token.transfer(msg.sender, _amount), "Transfer failed");
        
        emit Withdraw(msg.sender, _amount);
    }
    
    // 使用 Permit2 进行签名授权存款
    function depositWithPermit2(
        PermitTransferFrom memory permitData,
        SignatureTransferDetails calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external {
        require(permitData.permitted.amount > 0, "Amount must be greater than 0");
        require(transferDetails.to == address(this), "Transfer must be to TokenBank");
        require(transferDetails.requestedAmount > 0, "Requested amount must be greater than 0");
        require(transferDetails.requestedAmount <= permitData.permitted.amount, "Requested amount exceeds permitted amount");
        require(block.timestamp <= permitData.deadline, "Permit expired");
        
        // 使用 Permit2 执行签名转账
        permit2.permitTransferFrom(
            permitData,
            transferDetails,
            owner,
            signature
        );
        
        // 更新用户在银行的存款余额
        balances[owner] += transferDetails.requestedAmount;
        
        emit Deposit(owner, transferDetails.requestedAmount);
    }

}