Gasless tx/ Transfer with authorization - ERC20

This guide describes how to execute a gasless transaction or transferWithAuthorization (EIP-3009) with ERC20 tokens.

Intro

Gasless transactions with "Transfer with Authorization" for ERC-20 tokens offer a seamless way to send tokens without needing to hold or spend gas fees. This feature empowers users to authorize an ERC20 token transfer via a signed message, allowing someone else to cover the gas costs. It simplifies the process by eliminating the need for a gas balance in your wallet, making transactions more accessible and user-friendly.

Gasless/ transferWithAuthorization on ERC20 Diagram

Gasless/ transferWithAuthorization on ERC20 Diagram

Scope

🚧

This only works for ERC20 token contracts supporting transferWithAuthorization function or EIP-3009.

Code Example

📘

View the code example for the implementation of ERC20 gasless transaction.

Prerequisites

This example describes a gasless transaction on the Avalanche Fuji Testnet which carries out an ERC20 transfer. Make sure you have the following params ready:

ParamsDescription/ Value
ERC20 token holder wallet address0x8952fcc43F55c60C3Bf4FD565ABdd066CC1aD60c
Name of ERC20 token contractZap
ERC20 token contract address0x8c2e84c37fa226fdaa13999a725a638dbf2bc6a0
Wallet address where the tokens are to be transferred0xe56a14Cc9DbbA42D1DE1314051CA00AA742BaEfa
chainId43113 for Avalanche Fuji Testnet (find the chainId from the chain list)
nonceIt can be generated with this JSFiddle by running the web3.utils.randomHex(32) command in the console. (the nonce has to be unique for every tx)
validBeforeThe time until the signed message is valid in Unix time format
validAfterThe time after which the signed message will be valid in Unix time format

Terminologies Explained

  • User Wallet: The user wallet is the wallet associated with the end-user or the person who holds the ERC20 tokens.
  • Payer Wallet: The wallet that pays for the transaction fees associated with executing a smart contract or a transaction on the blockchain.
  • from address: The public wallet address of the user wallet.
  • to address (Wallet X): The public wallet address where the ERC20 tokens will be transferred to.

Prepare EIP712 JSON document

{
    "types": { // copy the types section as it is
        "EIP712Domain": [
            {
                "name": "name",
                "type": "string"
            },
            {
                "name": "version",
                "type": "string"
            },
            {
                "name": "chainId",
                "type": "uint256"
            },
            {
                "name": "verifyingContract",
                "type": "address"
            }
        ],
        "TransferWithAuthorization": [
            {
                "name": "from",
                "type": "address"
            },
            {
                "name": "to",
                "type": "address"
            },
            {
                "name": "value",
                "type": "uint256"
            },
            {
                "name": "validAfter",
                "type": "uint256"
            },
            {
                "name": "validBefore",
                "type": "uint256"
            },
            {
                "name": "nonce",
                "type": "bytes32"
            }
        ]
    },
    "primaryType": "TransferWithAuthorization", // this will be TransferWithAuthorization
    "domain": {
        "name": "Zap", // name of the ERC20 token contract
        "version": "1", // hardcoded to 1
        "chainId": "43113", // the chainId
        "verifyingContract": "0x8c2e84c37fa226fdaa13999a725a638dbf2bc6a0" // the ERC20 token contract address
    },
    "message": {
        "from": "0x8952fcc43F55c60C3Bf4FD565ABdd066CC1aD60c", // from wallet address
        "to": "0xe56a14Cc9DbbA42D1DE1314051CA00AA742BaEfa", // to wallet address
        "value": "1000000000000000000", // value being transferred
        "validAfter": "0", // time after which the signed message will be valid
        "validBefore": "1726207709", // time until the signed message will be valid
        "nonce": "0x0d42cac740099322f948214abb0b6b9aff59ed712ed7d85d6922ee5da0b43a2e" // random byte32 nonce
    }
}

Sign the EIP712 JSON document

🚧

  • This has to be done by the user wallet (that holds the ERC20 tokens)!
  • Signing is done off chain, so no gas fee has to be paid.

Request: reference

POST /api/signatures
ParameterParam TypeValueDescriptionExample Value
Signing-MethodHeaderid:valueid: This is the ID of the signing method
value: This is the value of the signing method
756ae7a7-3713-43ee-9936-0dff50306488:123456
{
    "signatureRequest": {
        "secretType": "AVAC", // blockhain of the tx
        "walletId": "e57350bd-5273-4905-9152-b848df0057b4", // the wallet ID that will sign the message (user wallet)
        "type": "EIP712", // the message type
        "data": {
            "types": {
                "EIP712Domain": [
                    {
                        "name": "name",
                        "type": "string"
                    },
                    {
                        "name": "version",
                        "type": "string"
                    },
                    {
                        "name": "chainId",
                        "type": "uint256"
                    },
                    {
                        "name": "verifyingContract",
                        "type": "address"
                    }
                ],
                "TransferWithAuthorization": [
                    {
                        "name": "from",
                        "type": "address"
                    },
                    {
                        "name": "to",
                        "type": "address"
                    },
                    {
                        "name": "value",
                        "type": "uint256"
                    },
                    {
                        "name": "validAfter",
                        "type": "uint256"
                    },
                    {
                        "name": "validBefore",
                        "type": "uint256"
                    },
                    {
                        "name": "nonce",
                        "type": "bytes32"
                    }
                ]
            },
            "primaryType": "TransferWithAuthorization", // this will be TransferWithAuthorization
            "domain": {
                "name": "Zap", // name of the token contract
                "version": "1", // hardcoded to 1
                "chainId": "43113", // the chainId
                "verifyingContract": "0x8c2e84c37fa226fdaa13999a725a638dbf2bc6a0" // the ERC20 token contract address
            },
            "message": {
                "from": "0x8952fcc43F55c60C3Bf4FD565ABdd066CC1aD60c", // from wallet address
                "to": "0xe56a14Cc9DbbA42D1DE1314051CA00AA742BaEfa", // to wallet address
                "value": "1000000000000000000", // value being transferred
                "validAfter": "0", // time after which the signed message will be valid
                "validBefore": "1726207709", // time until the signed message will be valid
                "nonce": "0x0d42cac740099322f948214abb0b6b9aff59ed712ed7d85d6922ee5da0b43a2e" // random byte32 nonce
            }
        }
    }
}

Response body

📘

The r, s, and v values are to be provided to the payer wallet who will execute the transaction and pay the gas fee.

{
    "success": true,
    "result": {
        "type": "HEX_SIGNATURE",
        "r": "0x270e08bda85dd5d78106c018753938bb4cc32a428e55446d08cdc486d3b05324",
        "s": "0x0f7d77217aaef9ea3cc4c2dda6e134cc5c7b556114e35ee2d55f15ad0291f271",
        "v": "0x1b",
        "signature": "0x270e08bda85dd5d78106c018753938bb4cc32a428e55446d08cdc486d3b053240f7d77217aaef9ea3cc4c2dda6e134cc5c7b556114e35ee2d55f15ad0291f2711b"
    }
}

Executing the transaction

🚧

This has to be done by the payer wallet (who will be paying for the gas fee)!

Request: reference

POST /api/transactions/execute
ParameterParam TypeValueDescriptionExample Value
Signing-MethodHeaderid:valueid: This is the ID of the signing method
value: This is the value of the signing method
756ae7a7-3713-43ee-9936-0dff50306488:123456
{
    "transactionRequest": {
        "type": "CONTRACT_EXECUTION",
        "functionName": "transferWithAuthorization", // the function name to call
        "value": 0.0, // this value will be 0.0
        "inputs": [
            {
                "type": "address", // the from wallet address
                "value": "0x8952fcc43F55c60C3Bf4FD565ABdd066CC1aD60c"
            },
            {
                "type": "address", // the to wallet address
                "value": "0xe56a14Cc9DbbA42D1DE1314051CA00AA742BaEfa"
            },
            {
                "type": "uint256", // the amount being transferred
                "value": "1000000000000000000"
            },
            {
                "type": "uint256", // validAfter from the EIP172.message
                "value": "0"
            },
            {
                "type": "uint256", // validBefore from the EIP172.message
                "value": "1726207709"
            },
            {
                "type": "bytes32", // random byte32 nonce from EIP712.message
                "value": "0x0d42cac740099322f948214abb0b6b9aff59ed712ed7d85d6922ee5da0b43a2e"
            },
            {
                "type": "uint8", // v value from the sign eip712 message response
                "value": "0x1b"
            },
            {
                "type": "bytes32", // r value from the sign eip712 message response
                "value": "0x270e08bda85dd5d78106c018753938bb4cc32a428e55446d08cdc486d3b05324"
            },
            {
                "type": "bytes32", // s value from the sign eip712 message response
                "value": "0x0f7d77217aaef9ea3cc4c2dda6e134cc5c7b556114e35ee2d55f15ad0291f271"
            }
        ],
        "walletId": "d1c28a8d-c2a3-443a-a4f8-f826f147cc91", // the wallet ID of the PAYER WALLET (who will pay the gas fee)
        "to": "0x8c2e84c37fa226fdaa13999a725a638dbf2bc6a0", // the token contract address for which we want to do a transfer
        "secretType": "AVAC" // blockchain of the tx
    }
}

Response body

{
    "success": true,
    "result": {
        "id": "2e1de5d9-cfba-47b9-b41c-5d722a9b3a9f",
        "transactionHash": "0x05f18312e116fcbc6292c981e9a169b389ceb531153fce91fa9f47f929912c9e"
    }
}

Result: Successful Gasless Transaction for ERC20 token

Successful Gasless Transaction for ERC20

Successful Gasless Transaction for ERC20