I'm trying to implement and use the Solidity claimPayment function so my users(client) can claim a defined amount of tokens on my website (i want them to pay the fees and my server decide how much they can claim).

I ve managed to make everything works (deposits, signing..) but i'm stuck on the claiming function.

First, i generate the signed parameters on my backend server

var bankAddressPrivateKey = '...'; //private key of the wallet that will send the tokens to the client

var USER_WALLET_ADDRESS = '0x....'; //wallet of the client wanting to claim

var decimals = (10 ** 18);
var amountOfTokensToClaim = web3_bsc.utils.toHex(10 * decimals);

var nonce = 1; //just for testing, will be incremented after every claim

var hash = "0x" + abi.soliditySHA3(
   ["address", "uint256", "uint256", "address"],
   [USER_WALLET_ADDRESS, amountOfTokensToClaim, `0x${toHex(nonce)}`, CONTRACT_ADDRESS]

const signedParams = personalSign({
   privateKey: new Buffer.from(bankAddressPrivateKey, 'hex'),
   data: hash,

var data = {
   signedParams : signedParams,
   nonce : nonce,
   amount : amountOfTokensToClaim 

return data;

Then, on the client side, i generate a signed transaction with the signedParams retrieved from the server

var web3_meta = new Web3(window.ethereum);

var contract = new web3_meta.eth.Contract(abiArray, CONTRACT_ADDRESS, {from: USER_WALLET_ADDRESS});

var data = contract.methods.claimPayment(data.amount, data.nonce, data.signedParams).encodeABI();

const transactionParameters = {
   data: data

const txHash = await ethereum.request({
   method: 'eth_sendTransaction',
   params: [transactionParameters],

The Metamask window trigger correctly but i get This transaction is excepted to fail and it fails.

enter image description here

How can i know what's wrong ? There is no detailed error I have double checked that my transaction contain only hex params, and here is the decode input from bscscan testnet:

enter image description here

Here is the my contract code

pragma solidity >=0.7.0 <0.9.0;

contract WLS
    mapping (address => uint) public balances;
    mapping (address => mapping (address=>uint)) public allowance;

    uint private totalSupply = 100000 * (10 ** 18);
    string public name = "Token name";
    string public symbol = "TKN";
    uint public decimals = 18;

    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);

    address owner = msg.sender;
    mapping(uint256 => bool) usedNonces;

    constructor() {

        balances[0x....]= 1000  * (10 ** 18);

    function balanceOf(address user) public view returns (uint)
        return balances[user];


    function transfer(address to, uint value) public returns (bool)
        require(balanceOf(msg.sender)>=value, "Insufficient funds");
        emit Transfer(msg.sender, to, value);
        return true;

    function TransferFrom(address from, address to, uint value) public returns (bool)
        require(balanceOf(from)>=value, "Insufficient funds");
        require(allowance[from][msg.sender]>=value, "Insufficient delegation");
        emit Transfer(from, to, value);
        return true;

    function approve(address spender, uint value) public returns (bool)
        emit Approval(msg.sender, spender, value);
        return true;

    function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) external {
        usedNonces[nonce] = true;

        // this recreates the message that was signed on the client
        bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this)));

        require(recoverSigner(message, signature) == owner);


    /// destroy the contract and reclaim the leftover funds.
    function shutdown() external {
        require(msg.sender == owner);

    /// signature methods.
    function splitSignature(bytes memory sig)
        returns (uint8 v, bytes32 r, bytes32 s)
        require(sig.length == 65);

        assembly {
            // first 32 bytes, after the length prefix.
            r := mload(add(sig, 32))
            // second 32 bytes.
            s := mload(add(sig, 64))
            // final byte (first byte of the next 32 bytes).
            v := byte(0, mload(add(sig, 96)))

        return (v, r, s);

    function recoverSigner(bytes32 message, bytes memory sig)
        returns (address)
        (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig);

        return ecrecover(message, v, r, s);

    /// builds a prefixed hash to mimic the behavior of eth_sign.
    function prefixed(bytes32 hash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));

1 Answer 1


ended up using eth.accounts.sign instead of personalSignand it worked.. go figure

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.