如何在合约中同时支持NativeETH,ERC20,ERC-Permit,Permit2. 项目TTSWAP提供了一种解决方案

本文阐述了一种可以方案,让合约同时支持NativeETH,ERC20,ERC-permit,Permit2标准

概要

如何在合约代码中同时支持ERC20,ERC20-PERMIT,PERMIT2?在TTSWAP去中心交易所的代码中已经实现.

判定流程

graph LR;
  代币--传统先授权给协议再交易-->使用交易类型1,进行交易;
  代币--支持ERC20PERMIT-->使用交易类型2,然后进行签名,再交易;
  代币--不支持ERC20PERMIT-->查询是否授权给PERMIT2--是-->已经授权给PERMIT2合约;
  代币--不支持ERC20PERMIT-->查询是否授权给PERMIT2--否-->未授权给PERMIT2合约;
  已经授权给PERMIT2合约-->使用交易类型5,然后进行签名,再交易;
  未授权给PERMIT2合约-->先授权给PERMIT2合约;
  先授权给PERMIT2合约-->使用交易类型5,然后进行签名,再交易;

说明

交易数据

交易数据格式

    struct S_transferData {
        uint8 transfertype;  //交易类型
        bytes sigdata;  //签名数据
    }

交易类型的5种情况

序号 说明
1 1 表示用户用户代币的APPROVE进行授权,然后协议调用代币的tranferfrom进行转账,此时签名数据可以为空
2 2 表示用户根据代币的签名格式进行签名后,让协议调用permit进行授权,然后调用代币的tranferfrom进行转账
3 3 表示用户已经授权给PERMIT2合约,然后用户调用PERMIT2合约给协议进行授权,然后协议调用PERMIT2中的transferFrom进行转账
4 4 表示用户已经授权给PERMIT2合约,然后根据PERMIT2合约对应函数签名格式进行签名后,让协议调用PERMIT2后进行权限与转账
5 5 表示用户已经授权给PERMIT2合约,然后根据PERMIT2合约对应函数的签名格式进行签名,让协议调用Permit2的签名转账函数进行账

签名数据

签名数据根据下面格式进行组合,关于owner与spender可以通过函数参数中获取,故下面两个结构体中已经省略.请根据不同交易类型和代币地址以据下列结构来组织数据

    struct S_Permit {
        uint256 value;
        uint256 deadline;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    struct S_Permit2 {
        uint256 value;
        uint256 deadline;
        uint256 nonce;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

转账库的实现

下列代码由于太多,已经省略,完整代码在 https://github.com/tt-swap/下的 L_Currency.sol文件中

    function transferFrom(
        address token,
        address from,
        address to,
        uint256 amount,
        bytes memory detail
    ) internal {
        bool success;
        S_transferData memory _simplePermit = abi.decode(
            detail,
            (S_transferData)
        );
        if (token.isNative()) {
            // ...省略
        } else if (_simplePermit.transfertype == 1) {
            // ...省略
        } else if (_simplePermit.transfertype == 2) {
            S_Permit memory _permit = abi.decode(
                _simplePermit.transdata,
                (S_Permit)
            );
           // ...省略
        } else if (_simplePermit.transfertype == 3) {
            IAllowanceTransfer(_permit2).transferFrom(
                from,
                to,
                to_uint160(amount),
                token
            );
        } else if (_simplePermit.transfertype == 4) {
            S_Permit memory _permit = abi.decode(
                _simplePermit.transdata,
                (S_Permit)
            );
            permit2allowace(
                ERC20(token),
                from,
                to,
                _permit.value,
                _permit.deadline,
                _permit.v,
                _permit.r,
                _permit.s
            );
            IAllowanceTransfer(_permit2).transferFrom(
                from,
                to,
                to_uint160(amount),
                token
            );
        } else if (_simplePermit.transfertype == 5) {
            S_Permit2 memory _permit = abi.decode(
                _simplePermit.transdata,
                (S_Permit2)
            );
            ISignatureTransfer(_permit2).permitTransferFrom(
                ISignatureTransfer.PermitTransferFrom(
                    ISignatureTransfer.TokenPermissions({
                        token: token,
                        amount: _permit.value
                    }),
                    _permit.nonce,
                    _permit.deadline
                ),
                ISignatureTransfer.SignatureTransferDetails({
                    to: to,
                    requestedAmount: amount
                }),
                from,
                bytes.concat(_permit.r, _permit.s, bytes1(_permit.v))
            );
        }
    }

关于不同类型的调用测试以及数据获取与组装

1. 代币为NativeETH

    function testinitNativeMetaGoodaddress1() public {
        vm.startPrank(marketcreator);
        address nativeCurrency = address(1);
        uint256 goodconfig = 2 ** 255;
        vm.deal(marketcreator, 100000 * 10 ** 6);

        bytes defaultdata=abi.encode()
        // ...此处有代码省略  
        market.initMetaGood{value: 50000 * 10 ** 6}(
            nativeCurrency,
            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),
            goodconfig,
            abi.encode(L_CurrencyLibrary.S_transferData(1, ""))
        );
        // ...此处有代码省略  
        vm.stopPrank();
    }

2. 代币为ERC20代币

function testinitMetaGoodtype1() public {
        vm.startPrank(marketcreator);
        uint256 goodconfig = 2 ** 255;
        usdt.mint(marketcreator, 100000);
        usdt.approve(address(market), 50000 * 10 ** 6);
        // ... 此处有省略
        market.initMetaGood(
            address(usdt),
            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),
            goodconfig,
            abi.encode(L_CurrencyLibrary.S_transferData(1, ""))
        );
        // ... 此处有省略
        vm.stopPrank();
    }

3. 代币为支持permit的ERC20代币

3.1 通用erc20permit的调用方式

关于nonce通用 erc20permit(token).nonces[msg.sender]来进行获取

 function testERC20permitinitmetagood() public {
        deal(address(kkkk), marketcreator, 50000 * 10 ** 7, false);
        uint256 bltim = block.timestamp + 10000;
        bytes32 structHash = keccak256(
            abi.encode(
                _PERMIT_TYPEHASH,
                marketcreator, // owner
                address(market), //spender
                50000 * 10 ** 6, //amount
                erc20permit(token).nonces[marketcreator], //   nonce
                bltim // deadline
            )
        );

        bytes32 digest = kkkk.DOMAIN_SEPARATOR().toTypedDataHash(structHash);
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(marketcreatorkey, digest);

        S_Permit2 memory ef = S_Permit2(50000 * 10 ** 6, bltim, v, r, s);
        SimplePermit memory sp = SimplePermit(2, abi.encode(ef));
        vm.startPrank(marketcreator);
        market.initMetaGood(
            address(kkkk),
            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),
            2 ** 255,
            abi.encode(sp)
        );
        vm.stopPrank();
    }

3.1 通用dai代币permit的调用方式

关于nonce通用 erc20permit(token).nonces[msg.sender]来进行获取

 function testERC20permitinitmetagood() public {
       // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
        bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
        deal(address(kkkk), marketcreator, 50000 * 10 ** 7, false);
        uint256 bltim = block.timestamp + 10000;
        bytes32 structHash = keccak256(
            abi.encode(
                _PERMIT_TYPEHASH,
                marketcreator, // owner
                address(market), //spender
                erc20(kkkk).nonces[marketcreator], //   nonce
                bltim // deadline,
                true
            )
        );

        bytes32 digest = kkkk.DOMAIN_SEPARATOR().toTypedDataHash(structHash);
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(marketcreatorkey, digest);

        S_Permit2 memory ef = S_Permit2(50000 * 10 ** 6, bltim, v, r, s);
        SimplePermit memory sp = SimplePermit(2, abi.encode(ef));
        vm.startPrank(marketcreator);
        market.initMetaGood(
            address(kkkk),
            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),
            2 ** 255,
            abi.encode(sp)
        );
        vm.stopPrank();
    }

4. 已经授权给Permit2的代币,用户使用Permit2给协议进行授权

  function testPermit2AllownancePermitInitMetaGood() public {
        bytes memory code = address(aabbpermit).code;
        address targetAddr = 0x2d1d989af240B673C84cEeb3E6279Ea98a2CFd05;
        vm.etch(targetAddr, code);
        vm.startPrank(marketcreator);
        deal(address(kkkk), marketcreator, 50000 * 10 ** 6, false);
        kkkk.approve(targetAddr, 50000 * 10 ** 6);
        uint256 blt = block.timestamp;

        console2.log(1, 1);
        Permit2(targetAddr).approve(
            address(kkkk),
            address(market),
            50000 * 10 ** 6,
            uint48(blt + 100000)
        );
        bytes32 _PERMIT_SINGLE_TYPEHASH = keccak256(
            "PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)"
        );
        bytes32 _PERMIT_DETAILS_TYPEHASH = keccak256(
            "PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)"
        );

        (, , uint48 nonce) = Permit2(targetAddr).allowance(
            owner,
            address(kkkk),
            address(market)
        );

        IAllowanceTransfer.PermitSingle memory _pd = IAllowanceTransfer
            .PermitSingle({
                details: IAllowanceTransfer.PermitDetails({
                    token: address(kkkk),
                    amount: uint160(50000 * 10 ** 6),
                    expiration: type(uint48).max,
                    nonce: 0
                }),
                spender: address(market),
                sigDeadline: uint48(blt + 100000)
            });
        bytes32 permitHash = keccak256(
            abi.encode(_PERMIT_DETAILS_TYPEHASH, _pd.details)
        );
        bytes32 domainSeparator = Permit2(targetAddr).DOMAIN_SEPARATOR();
        bytes32 msgHash = keccak256(
            abi.encodePacked(
                "\x19\x01",
                domainSeparator,
                keccak256(
                    abi.encode(
                        _PERMIT_SINGLE_TYPEHASH,
                        permitHash,
                        _pd.spender,
                        _pd.sigDeadline
                    )
                )
            )
        );

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(marketcreatorkey, msgHash);

        S_Permit2 memory ef = S_Permit2(
            50000 * 10 ** 6,
            uint48(blt + 100000),
            v,
            r,
            s
        );
        SimplePermit memory sp = SimplePermit(4, abi.encode(ef));
        assertEq(
            0,
            kkkk.balanceOf(address(market)),
            "before trnasferform error"
        );
        console2.log(3, kkkk.balanceOf(address(market)));
        market.initMetaGood(
            address(kkkk),
            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),
            2 ** 255,
            abi.encode(sp)
        );
        console2.log(4, kkkk.balanceOf(address(market)));
        assertEq(
            50000 * 10 ** 6,
            kkkk.balanceOf(address(market)),
            "after trnasferform error"
        );

        vm.stopPrank();
    }

5. 已经授权给Permit2的代币,用户使用Permit2给协议进行授权

 function testPermit2PermitInitMetaGood() public {
        bytes memory code = address(aabbpermit).code;
        address targetAddr = 0x2d1d989af240B673C84cEeb3E6279Ea98a2CFd05;
        vm.etch(targetAddr, code);
        vm.startPrank(marketcreator);
        deal(address(kkkk), marketcreator, 50000 * 10 ** 6, false);
        kkkk.approve(targetAddr, 50000 * 10 ** 6);
        uint256 blt = block.timestamp;

        Permit2(targetAddr).approve(
            address(kkkk),
            address(market),
            50000 * 10 ** 6,
            uint48(blt + 100000)
        );

        ISignatureTransfer.PermitTransferFrom memory _pd = ISignatureTransfer
            .PermitTransferFrom(
                ISignatureTransfer.TokenPermissions({
                    token: address(kkkk),
                    amount: uint256(50000 * 10 ** 6)
                }),
                uint256(0),
                uint256(blt + 100000)
            );
        bytes32 _TOKEN_PERMISSIONS_TYPEHASH = keccak256(
            "TokenPermissions(address token,uint256 amount)"
        );
        //permit2 PermitTransferFrom的SELECTOR
        bytes32 _PERMIT_TRANSFER_FROM_TYPEHASH = keccak256(
            "PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)"
        );
        //组装tokenpermission
        bytes32 tokenPermissions = keccak256(
            abi.encode(_TOKEN_PERMISSIONS_TYPEHASH, _pd.permitted)
        );
        //获取permit2的DOMAIN_SEPARATOR
        bytes32 domainSeparator = Permit2(targetAddr).DOMAIN_SEPARATOR();
        //构建需要签名的消息
        bytes32 msgHash = keccak256(
            abi.encodePacked(
                "\x19\x01",
                domainSeparator,
                keccak256(
                    abi.encode(
                        _PERMIT_TRANSFER_FROM_TYPEHASH,
                        tokenPermissions,
                        address(market),
                        _pd.nonce,
                        _pd.deadline
                    )
                )
            )
        );
        // 此处,如果前端调用,可以调用钱包来进行签名
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(marketcreatorkey, msgHash);

        // 根据签名,构建数据
        S_Permit memory ef = S_Permit(
            50000 * 10 ** 6, //amount
            blt + 100000, //deadline
            _pd.nonce,  //nonce
            v,
            r,
            s
        );
        SimplePermit memory sp = SimplePermit(5, abi.encode(ef));
        // 协议进行初始化商品
        market.initMetaGood(
            address(kkkk),
            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),
            2 ** 255,
            abi.encode(sp)
        );
        // ...此处有省略,请参看原文件
        vm.stopPrank();
    }

联系方式

X:@ttswapFinance
TG:@ttswap01
Discord:https://discord.com/invite/XygqnmQgX3 github:https://github.com/tt-swap/ website: ttswap.io

其它

现在正在进行测试网公测活动,交互有就代币空投,欢迎大家参与

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
三寸时光
三寸时光
江湖只有他的大名,没有他的介绍。