My app lets users make a signed token approval to transfer an ERC20Permit token, using web3.js injected.
Token contract is canonical from OpenZeppelin.
Signature seems to be valid (permit
call does not revert) but calling allowance
on the token afterward shows an allowance of 0
.
Also, I notice that subsequent signature calls still have a nonces
of 0 for the address, so the nonce seemingly does not increase even though permit
calls go through.
Here is the react app code:
contract = // my contract
contractAddress = 0x... // my contract address
amount = BigNumber(amount) // with confirmed decimal job
deadline = Date.now() + 20 * 60 * 1000 // valid deadline
await contract.methods.permit(account.address, contractAddress, amount, deadline, v, r, s).call();
console.log(await contract.methods.allowance(account.address, contractAddress).call()) // 0
Here is the detail of the signature built:
const EIP712Domain = [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "verifyingContract", type: "address" },
];
const contractName = await contract.methods.name().call();
const domain = {
name: contractName,
version: "1",
chainId: 5,
verifyingContract: contractAddress
};
const Permit = [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
{ name: "value", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
];
const nonce = await contract.methods.nonces(account.address).call();
const deadline = Date.now() + 20 * 60;
const message = {
owner: account.address,
spender: contractAddress,
value: amount,
nonce: nonce.toString(16),
deadline: deadline,
};
const params = JSON.stringify({
types: {
EIP712Domain,
Permit,
},
domain,
primaryType: "Permit",
message,
});
const _signMessage = async (userAddress, web3, dataToSign, signatureMethod = 'eth_signTypedData_v4') => {
const signature = await web3.currentProvider.send(signatureMethod, [userAddress, dataToSign]);
return signature;
};
const signature = await _signMessage(account.address, web3, params);
const getSignatureParameters = signature => {
if (!web3.utils.isHexStrict(signature)) {
throw new Error(
'Given value "'.concat(signature, '" is not a valid hex string.')
);
}
var r = signature.slice(0, 66);
var s = "0x".concat(signature.slice(66, 130));
var v = "0x".concat(signature.slice(130, 132));
v = web3.utils.hexToNumber(v);
if (![27, 28].includes(v)) v += 27;
return {
r: r,
s: s,
v: v
}
}
const { r, s, v } = getSignatureParameters(signature.result);