I am using web3 library to sign a transaction and then verify it from the smart contract. The signature is not verifying.
This is the frontend code
const DomainSeparator = ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(
["string", "address"],
["0x01", contractAddress]
)
);
const message = ethers.keccak256(
ethers.AbiCoder.defaultAbiCoder().encode(
["address", "address", "uint256", "uint256"],
[
fromAddress,
toAddress,
1000, // amount
0, // nonce
]
)
);
let finalHash = ethers.keccak256(
ethers.solidityPacked(
["bytes1", "bytes1", "bytes32", "bytes32"],
["0x19", "0x01", DomainSeparator, message]
)
);
**This is the solidity smart contract code**
function transferWithPermit(address from, address to,uint256 amount,bytes memory signature) external {
bytes32 message = keccak256(abi.encode(from,to,amount,nonce[from]));
(uint8 v, bytes32 r, bytes32 s) = extractRSV(signature);
_validate(v, r, s, message, from);
_transfer(from,to,amount);
nonce[from]+=1;
}
function _validate(
uint8 v,
bytes32 r,
bytes32 s,
bytes32 encodeData,
address signer
) internal view {
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", getDomainSeparator(), encodeData));
address recoveredAddress = ecrecover(digest, v, r, s);
// Explicitly disallow authorizations for address(0) as ecrecover returns address(0) on malformed messages
console.log("recovered, signer :",recoveredAddress, signer);
require(recoveredAddress!= address(0) && (recoveredAddress == signer), "INVALID_SIGNATURE");
}
**This is my hardhat test case**
const provider = waffle.provider;
const web3 = require("web3");
const { ecsign } = require("ethereumjs-util");
describe('signature', () =>{
const [owner, accountOne, feeToSetter] = provider.getWallets();
let zerotoken;
before( async () =>{
const Zerotoken = await ethers.getContractFactory('ZeroToken');
zerotoken = await Zerotoken.deploy();
console.log("ZeroToken address: ",zerotoken.address)}) //same as the contractAddress on the //frontEnd
it('Digital Signature', async () =>{
let nonce =0;
let tokenAmount=1000
let toAddress = '0xd82b09990f96EDBd6e7731C67e7ea4c9b01AF150';
const DomainSeparator = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["string", "address"], ["0x01", toAddress]));
var message = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode([ "address", "address", "uint256", "uint256"],[owner.address,toAddress,tokenAmount,nonce]));
console.log("message:",message)
var finalHash = ethers.utils.keccak256(
ethers.utils.solidityPack(["bytes1", "bytes1", "bytes32", "bytes32"], ["0x19", "0x01", DomainSeparator, message]),
);
console.log("final Hash: ",finalHash)
const { v, r, s } = ecsign(Buffer.from(finalHash.slice(2), "hex"), Buffer.from(owner.privateKey.slice(2), "hex"));
console.log('response', v.toString(16), r.toString('hex'), s.toString('hex'))
const signature = `0x${v.toString(16)}${r.toString('hex')}${s.toString('hex')}`
let transferWithPermit = await zerotoken.transferWithPermit(owner.address,toAddress,tokenAmount,signature)
)
})
})
First we deployed the contract. We called "transferWithPermit" function which in turn calls "validate" function. The owner address in the test case is same as the signer address on the frontend. Inside "_validate" function the recovered address is not same as the signer address from the frontend.
Message Hammad Ali