我目前正在编写一些可靠的智能契约,并通过web3与它们进行交互。前提是令牌可以存放在池中,然后可以在稍后的日期提取。
存款运行良好--目前,应用程序要求用户“批准”该应用程序代表他们使用令牌,并将其存入池中。下面是处理将令牌存放到池中的相关智能契约代码
function deposit(
address token,
uint tokenAmount,
bytes32 request,
address to,
uint deadline
) external virtual override ensure(deadline) returns (uint amount, uint liquidity) {
address pool = ISafepayFactory(factory).getPool(token);
require(pool != address(0), 'SFPY_ROUTER: UNSUPPORTED POOL');
TransferHelper.safeTransferFrom(token, msg.sender, pool, tokenAmount);
liquidity = ISafepayPool(pool).mint(to);
amount = tokenAmount;
emit Deposit(msg.sender, to, token, request, amount);
}这假设已经为该令牌预先创建了一个池。一旦令牌被转移,池“造币厂”的流动性令牌就会传递给用户。
当用户想要撤回这些令牌时,应用程序首先要求用户签署一条“许可”消息(目前正在使用MetaMask)。这是相关的JS代码
等待poolContract.nonces(帐户)
const EIP712Domain = [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' }
]
const domain = {
name: 'Safepay LP Token',
version: '1',
chainId: chainId,
verifyingContract: pool.liquidityToken.address
}
const Permit = [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' }
]
const message = {
owner: account,
spender: ROUTER_ADDRESS,
value: liquidityAmount.raw.toString(),
nonce: nonce.toHexString(),
deadline: deadline.toNumber()
}
const data = JSON.stringify({
types: {
EIP712Domain,
Permit
},
domain,
primaryType: 'Permit',
message
})
library
.send('eth_signTypedData_v4', [account, data])
.then(sig => {
console.log(sig)
const recovered = recoverTypedSignature_v4({
data: JSON.parse(data),
sig,
})
// Weirdly the address is recovered correctly here.
console.log(getAddress(recovered) === getAddress(account))
return splitSignature(sig)
})
.then(signature => {
setSignatureData({
v: signature.v,
r: signature.r,
s: signature.s,
deadline: deadline.toNumber()
})
})
.catch(error => {
console.log(error)
// for all errors other than 4001 (EIP-1193 user rejected request), fall back to manual approve
if (error?.code !== 4001) {
approveCallback()
}
})在签署消息后,该应用程序在带有签名参数的路由器智能合同上调用了一个“估计气体”函数。这是withdrawWithPermit函数
function withdrawWithPermit(
address token,
uint liquidity,
uint amountMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external virtual override returns (uint amount) {
address pool = ISafepayFactory(factory).getPool(token);
require(pool != address(0), 'SFPY_ROUTER: UNSUPPORTED POOL');
uint value = approveMax ? uint(-1) : liquidity;
ISafepayPool(pool).permit(msg.sender, address(this), value, deadline, v, r, s);
amount = withdraw(token, liquidity, amountMin, to, deadline);
}“池”合同继承自ERC20合同,并具有一些额外的功能,以“薄荷”和“烧掉”流动性令牌。但这里是游泳池的相关“许可”功能
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
require(deadline >= block.timestamp, 'SFPY: EXPIRED');
bytes32 hashStruct = keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
value,
nonces[owner]++,
deadline
)
);
bytes32 digest = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
hashStruct
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, 'SFPY: INVALID_SIGNATURE');
_approve(owner, spender, value);
}这是PERMIT_TYPEHASH
bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");和DOMAIN_SEPARATOR
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
keccak256(bytes(name)),
keccak256(bytes('1')),
chainId,
address(this)
)
);(为非常长的上下文表示歉意,但如果你已经做到了这一点,这就是乐趣的起点)
每当应用程序(通过MetaMask)向该函数发出请求时,智能契约就会抛出SFPY: EXPIRED错误或SFPY: INVALID_SIGNATURE错误。
INVALID_SIGNATURE很有趣,因为在提出请求之前,应用程序能够正确恢复签名者的地址(如JS片段所示)。
然而,对智能契约的调用总是失败的。我假设DOMAIN_SEPARATOR、PERMIT_TYPEHASH或其他方面都有错误,但我希望有人能帮上忙!
乐意添加更多的代码片段,屏幕截图,视需要。
(示例错误屏幕截图附后)

发布于 2021-02-14 19:24:00
经过大量的调试,我终于找到了这个问题。原来,chainid OPCODE在Ganche中不能正常工作(如果您使用他们的UI,修复程序已经在ganache-cli项目中发布)。
缺少正确的功能链会导致DOMAIN_TYPEHASH计算错误,从而阻止ecrecover从签名中生成正确的地址。
您可以跟踪这些问题。
希望这对其他人有帮助
https://ethereum.stackexchange.com/questions/93440
复制相似问题