本文阐述了一种可以方案,让合约同时支持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; //签名数据
}
序号 | 值 | 说明 |
---|---|---|
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))
);
}
}
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();
}
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();
}
关于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();
}
关于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();
}
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();
}
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
现在正在进行测试网公测活动,交互有就代币空投,欢迎大家参与
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!