{"content":{"title":"如何在合约中同时支持NativeETH,ERC20,ERC-Permit,Permit2. 项目TTSWAP提供了一种解决方案","body":"# 概要\r\n如何在合约代码中同时支持ERC20,ERC20-PERMIT,PERMIT2?在TTSWAP去中心交易所的代码中已经实现.\r\n# 判定流程\r\n```mermaid\r\ngraph LR;\r\n  代币--传统先授权给协议再交易-->使用交易类型1,进行交易;\r\n  代币--支持ERC20PERMIT-->使用交易类型2,然后进行签名,再交易;\r\n  代币--不支持ERC20PERMIT-->查询是否授权给PERMIT2--是-->已经授权给PERMIT2合约;\r\n  代币--不支持ERC20PERMIT-->查询是否授权给PERMIT2--否-->未授权给PERMIT2合约;\r\n  已经授权给PERMIT2合约-->使用交易类型5,然后进行签名,再交易;\r\n  未授权给PERMIT2合约-->先授权给PERMIT2合约;\r\n  先授权给PERMIT2合约-->使用交易类型5,然后进行签名,再交易;\r\n```\r\n# 说明\r\n## 交易数据\r\n### 交易数据格式\r\n```\r\n    struct S_transferData {\r\n        uint8 transfertype;  //交易类型\r\n        bytes sigdata;  //签名数据\r\n    }\r\n```\r\n### 交易类型的5种情况\r\n| 序号 | 值  | 说明                                                                                                              |\r\n| ---- | --- | ----------------------------------------------------------------------------------------------------------------- |\r\n| 1    | 1   | 表示用户用户代币的APPROVE进行授权,然后协议调用代币的tranferfrom进行转账,此时签名数据可以为空                      |\r\n| 2    | 2   | 表示用户根据代币的签名格式进行签名后,让协议调用permit进行授权,然后调用代币的tranferfrom进行转账                   |\r\n| 3    | 3   | 表示用户已经授权给PERMIT2合约,然后用户调用PERMIT2合约给协议进行授权,然后协议调用PERMIT2中的transferFrom进行转账   |\r\n| 4    | 4   | 表示用户已经授权给PERMIT2合约,然后根据PERMIT2合约对应函数签名格式进行签名后,让协议调用PERMIT2后进行权限与转账     |\r\n| 5    | 5   | 表示用户已经授权给PERMIT2合约,然后根据PERMIT2合约对应函数的签名格式进行签名,让协议调用Permit2的签名转账函数进行账 |\r\n\r\n### 签名数据\r\n签名数据根据下面格式进行组合,关于owner与spender可以通过函数参数中获取,故下面两个结构体中已经省略.请根据不同交易类型和代币地址以据下列结构来组织数据\r\n```\r\n    struct S_Permit {\r\n        uint256 value;\r\n        uint256 deadline;\r\n        uint8 v;\r\n        bytes32 r;\r\n        bytes32 s;\r\n    }\r\n\r\n    struct S_Permit2 {\r\n        uint256 value;\r\n        uint256 deadline;\r\n        uint256 nonce;\r\n        uint8 v;\r\n        bytes32 r;\r\n        bytes32 s;\r\n    }\r\n```\r\n\r\n### 转账库的实现\r\n下列代码由于太多,已经省略,完整代码在 https://github.com/tt-swap/下的 L_Currency.sol文件中\r\n```\r\n    function transferFrom(\r\n        address token,\r\n        address from,\r\n        address to,\r\n        uint256 amount,\r\n        bytes memory detail\r\n    ) internal {\r\n        bool success;\r\n        S_transferData memory _simplePermit = abi.decode(\r\n            detail,\r\n            (S_transferData)\r\n        );\r\n        if (token.isNative()) {\r\n            // ...省略\r\n        } else if (_simplePermit.transfertype == 1) {\r\n            // ...省略\r\n        } else if (_simplePermit.transfertype == 2) {\r\n            S_Permit memory _permit = abi.decode(\r\n                _simplePermit.transdata,\r\n                (S_Permit)\r\n            );\r\n           // ...省略\r\n        } else if (_simplePermit.transfertype == 3) {\r\n            IAllowanceTransfer(_permit2).transferFrom(\r\n                from,\r\n                to,\r\n                to_uint160(amount),\r\n                token\r\n            );\r\n        } else if (_simplePermit.transfertype == 4) {\r\n            S_Permit memory _permit = abi.decode(\r\n                _simplePermit.transdata,\r\n                (S_Permit)\r\n            );\r\n            permit2allowace(\r\n                ERC20(token),\r\n                from,\r\n                to,\r\n                _permit.value,\r\n                _permit.deadline,\r\n                _permit.v,\r\n                _permit.r,\r\n                _permit.s\r\n            );\r\n            IAllowanceTransfer(_permit2).transferFrom(\r\n                from,\r\n                to,\r\n                to_uint160(amount),\r\n                token\r\n            );\r\n        } else if (_simplePermit.transfertype == 5) {\r\n            S_Permit2 memory _permit = abi.decode(\r\n                _simplePermit.transdata,\r\n                (S_Permit2)\r\n            );\r\n            ISignatureTransfer(_permit2).permitTransferFrom(\r\n                ISignatureTransfer.PermitTransferFrom(\r\n                    ISignatureTransfer.TokenPermissions({\r\n                        token: token,\r\n                        amount: _permit.value\r\n                    }),\r\n                    _permit.nonce,\r\n                    _permit.deadline\r\n                ),\r\n                ISignatureTransfer.SignatureTransferDetails({\r\n                    to: to,\r\n                    requestedAmount: amount\r\n                }),\r\n                from,\r\n                bytes.concat(_permit.r, _permit.s, bytes1(_permit.v))\r\n            );\r\n        }\r\n    }\r\n```\r\n## 关于不同类型的调用测试以及数据获取与组装\r\n### 1. 代币为NativeETH\r\n```\r\n    function testinitNativeMetaGoodaddress1() public {\r\n        vm.startPrank(marketcreator);\r\n        address nativeCurrency = address(1);\r\n        uint256 goodconfig = 2 ** 255;\r\n        vm.deal(marketcreator, 100000 * 10 ** 6);\r\n\r\n        bytes defaultdata=abi.encode()\r\n        // ...此处有代码省略  \r\n        market.initMetaGood{value: 50000 * 10 ** 6}(\r\n            nativeCurrency,\r\n            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),\r\n            goodconfig,\r\n            abi.encode(L_CurrencyLibrary.S_transferData(1, \"\"))\r\n        );\r\n        // ...此处有代码省略  \r\n        vm.stopPrank();\r\n    }\r\n```\r\n### 2. 代币为ERC20代币\r\n```\r\nfunction testinitMetaGoodtype1() public {\r\n        vm.startPrank(marketcreator);\r\n        uint256 goodconfig = 2 ** 255;\r\n        usdt.mint(marketcreator, 100000);\r\n        usdt.approve(address(market), 50000 * 10 ** 6);\r\n        // ... 此处有省略\r\n        market.initMetaGood(\r\n            address(usdt),\r\n            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),\r\n            goodconfig,\r\n            abi.encode(L_CurrencyLibrary.S_transferData(1, \"\"))\r\n        );\r\n        // ... 此处有省略\r\n        vm.stopPrank();\r\n    }\r\n```\r\n### 3. 代币为支持permit的ERC20代币\r\n#### 3.1 通用erc20permit的调用方式\r\n关于nonce通用 erc20permit(token).nonces[msg.sender]来进行获取\r\n```\r\n function testERC20permitinitmetagood() public {\r\n        deal(address(kkkk), marketcreator, 50000 * 10 ** 7, false);\r\n        uint256 bltim = block.timestamp + 10000;\r\n        bytes32 structHash = keccak256(\r\n            abi.encode(\r\n                _PERMIT_TYPEHASH,\r\n                marketcreator, // owner\r\n                address(market), //spender\r\n                50000 * 10 ** 6, //amount\r\n                erc20permit(token).nonces[marketcreator], //   nonce\r\n                bltim // deadline\r\n            )\r\n        );\r\n\r\n        bytes32 digest = kkkk.DOMAIN_SEPARATOR().toTypedDataHash(structHash);\r\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(marketcreatorkey, digest);\r\n        \r\n        S_Permit2 memory ef = S_Permit2(50000 * 10 ** 6, bltim, v, r, s);\r\n        SimplePermit memory sp = SimplePermit(2, abi.encode(ef));\r\n        vm.startPrank(marketcreator);\r\n        market.initMetaGood(\r\n            address(kkkk),\r\n            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),\r\n            2 ** 255,\r\n            abi.encode(sp)\r\n        );\r\n        vm.stopPrank();\r\n    }\r\n```\r\n\r\n#### 3.1 通用dai代币permit的调用方式\r\n关于nonce通用 erc20permit(token).nonces[msg.sender]来进行获取\r\n```\r\n function testERC20permitinitmetagood() public {\r\n       // bytes32 public constant PERMIT_TYPEHASH = keccak256(\"Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)\");\r\n        bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;\r\n        deal(address(kkkk), marketcreator, 50000 * 10 ** 7, false);\r\n        uint256 bltim = block.timestamp + 10000;\r\n        bytes32 structHash = keccak256(\r\n            abi.encode(\r\n                _PERMIT_TYPEHASH,\r\n                marketcreator, // owner\r\n                address(market), //spender\r\n                erc20(kkkk).nonces[marketcreator], //   nonce\r\n                bltim // deadline,\r\n                true\r\n            )\r\n        );\r\n\r\n        bytes32 digest = kkkk.DOMAIN_SEPARATOR().toTypedDataHash(structHash);\r\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(marketcreatorkey, digest);\r\n        \r\n        S_Permit2 memory ef = S_Permit2(50000 * 10 ** 6, bltim, v, r, s);\r\n        SimplePermit memory sp = SimplePermit(2, abi.encode(ef));\r\n        vm.startPrank(marketcreator);\r\n        market.initMetaGood(\r\n            address(kkkk),\r\n            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),\r\n            2 ** 255,\r\n            abi.encode(sp)\r\n        );\r\n        vm.stopPrank();\r\n    }\r\n```\r\n### 4. 已经授权给Permit2的代币,用户使用Permit2给协议进行授权\r\n```\r\n  function testPermit2AllownancePermitInitMetaGood() public {\r\n        bytes memory code = address(aabbpermit).code;\r\n        address targetAddr = 0x2d1d989af240B673C84cEeb3E6279Ea98a2CFd05;\r\n        vm.etch(targetAddr, code);\r\n        vm.startPrank(marketcreator);\r\n        deal(address(kkkk), marketcreator, 50000 * 10 ** 6, false);\r\n        kkkk.approve(targetAddr, 50000 * 10 ** 6);\r\n        uint256 blt = block.timestamp;\r\n\r\n        console2.log(1, 1);\r\n        Permit2(targetAddr).approve(\r\n            address(kkkk),\r\n            address(market),\r\n            50000 * 10 ** 6,\r\n            uint48(blt + 100000)\r\n        );\r\n        bytes32 _PERMIT_SINGLE_TYPEHASH = keccak256(\r\n            \"PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)\"\r\n        );\r\n        bytes32 _PERMIT_DETAILS_TYPEHASH = keccak256(\r\n            \"PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)\"\r\n        );\r\n\r\n        (, , uint48 nonce) = Permit2(targetAddr).allowance(\r\n            owner,\r\n            address(kkkk),\r\n            address(market)\r\n        );\r\n\r\n        IAllowanceTransfer.PermitSingle memory _pd = IAllowanceTransfer\r\n            .PermitSingle({\r\n                details: IAllowanceTransfer.PermitDetails({\r\n                    token: address(kkkk),\r\n                    amount: uint160(50000 * 10 ** 6),\r\n                    expiration: type(uint48).max,\r\n                    nonce: 0\r\n                }),\r\n                spender: address(market),\r\n                sigDeadline: uint48(blt + 100000)\r\n            });\r\n        bytes32 permitHash = keccak256(\r\n            abi.encode(_PERMIT_DETAILS_TYPEHASH, _pd.details)\r\n        );\r\n        bytes32 domainSeparator = Permit2(targetAddr).DOMAIN_SEPARATOR();\r\n        bytes32 msgHash = keccak256(\r\n            abi.encodePacked(\r\n                \"\\x19\\x01\",\r\n                domainSeparator,\r\n                keccak256(\r\n                    abi.encode(\r\n                        _PERMIT_SINGLE_TYPEHASH,\r\n                        permitHash,\r\n                        _pd.spender,\r\n                        _pd.sigDeadline\r\n                    )\r\n                )\r\n            )\r\n        );\r\n\r\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(marketcreatorkey, msgHash);\r\n\r\n        S_Permit2 memory ef = S_Permit2(\r\n            50000 * 10 ** 6,\r\n            uint48(blt + 100000),\r\n            v,\r\n            r,\r\n            s\r\n        );\r\n        SimplePermit memory sp = SimplePermit(4, abi.encode(ef));\r\n        assertEq(\r\n            0,\r\n            kkkk.balanceOf(address(market)),\r\n            \"before trnasferform error\"\r\n        );\r\n        console2.log(3, kkkk.balanceOf(address(market)));\r\n        market.initMetaGood(\r\n            address(kkkk),\r\n            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),\r\n            2 ** 255,\r\n            abi.encode(sp)\r\n        );\r\n        console2.log(4, kkkk.balanceOf(address(market)));\r\n        assertEq(\r\n            50000 * 10 ** 6,\r\n            kkkk.balanceOf(address(market)),\r\n            \"after trnasferform error\"\r\n        );\r\n\r\n        vm.stopPrank();\r\n    }\r\n```\r\n\r\n### 5. 已经授权给Permit2的代币,用户使用Permit2给协议进行授权\r\n\r\n```\r\n function testPermit2PermitInitMetaGood() public {\r\n        bytes memory code = address(aabbpermit).code;\r\n        address targetAddr = 0x2d1d989af240B673C84cEeb3E6279Ea98a2CFd05;\r\n        vm.etch(targetAddr, code);\r\n        vm.startPrank(marketcreator);\r\n        deal(address(kkkk), marketcreator, 50000 * 10 ** 6, false);\r\n        kkkk.approve(targetAddr, 50000 * 10 ** 6);\r\n        uint256 blt = block.timestamp;\r\n\r\n        Permit2(targetAddr).approve(\r\n            address(kkkk),\r\n            address(market),\r\n            50000 * 10 ** 6,\r\n            uint48(blt + 100000)\r\n        );\r\n\r\n        ISignatureTransfer.PermitTransferFrom memory _pd = ISignatureTransfer\r\n            .PermitTransferFrom(\r\n                ISignatureTransfer.TokenPermissions({\r\n                    token: address(kkkk),\r\n                    amount: uint256(50000 * 10 ** 6)\r\n                }),\r\n                uint256(0),\r\n                uint256(blt + 100000)\r\n            );\r\n        bytes32 _TOKEN_PERMISSIONS_TYPEHASH = keccak256(\r\n            \"TokenPermissions(address token,uint256 amount)\"\r\n        );\r\n        //permit2 PermitTransferFrom的SELECTOR\r\n        bytes32 _PERMIT_TRANSFER_FROM_TYPEHASH = keccak256(\r\n            \"PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)TokenPermissions(address token,uint256 amount)\"\r\n        );\r\n        //组装tokenpermission\r\n        bytes32 tokenPermissions = keccak256(\r\n            abi.encode(_TOKEN_PERMISSIONS_TYPEHASH, _pd.permitted)\r\n        );\r\n        //获取permit2的DOMAIN_SEPARATOR\r\n        bytes32 domainSeparator = Permit2(targetAddr).DOMAIN_SEPARATOR();\r\n        //构建需要签名的消息\r\n        bytes32 msgHash = keccak256(\r\n            abi.encodePacked(\r\n                \"\\x19\\x01\",\r\n                domainSeparator,\r\n                keccak256(\r\n                    abi.encode(\r\n                        _PERMIT_TRANSFER_FROM_TYPEHASH,\r\n                        tokenPermissions,\r\n                        address(market),\r\n                        _pd.nonce,\r\n                        _pd.deadline\r\n                    )\r\n                )\r\n            )\r\n        );\r\n        // 此处,如果前端调用,可以调用钱包来进行签名\r\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(marketcreatorkey, msgHash);\r\n\r\n        // 根据签名,构建数据\r\n        S_Permit memory ef = S_Permit(\r\n            50000 * 10 ** 6, //amount\r\n            blt + 100000, //deadline\r\n            _pd.nonce,  //nonce\r\n            v,\r\n            r,\r\n            s\r\n        );\r\n        SimplePermit memory sp = SimplePermit(5, abi.encode(ef));\r\n        // 协议进行初始化商品\r\n        market.initMetaGood(\r\n            address(kkkk),\r\n            toTTSwapUINT256(50000 * 10 ** 6, 50000 * 10 ** 6),\r\n            2 ** 255,\r\n            abi.encode(sp)\r\n        );\r\n        // ...此处有省略,请参看原文件\r\n        vm.stopPrank();\r\n    }\r\n```\r\n## 联系方式\r\nX:@ttswapFinance  \r\nTG:@ttswap01   \r\nDiscord:https://discord.com/invite/XygqnmQgX3 \r\ngithub:https://github.com/tt-swap/\r\nwebsite: ttswap.io\r\n\r\n## 其它\r\n现在正在进行测试网公测活动,交互有就代币空投,欢迎大家参与"},"author":{"user":"https://learnblockchain.cn/people/8583","address":null},"history":"bafkreifp67wloipbsk54t5g6dzmd6cmyvtufe5rwlgt47rrpqhu7nwtld4","timestamp":1734948834,"version":1}