Gasless tx/ permit & transferFrom - ERC20
This guide describes how to execute a gasless transaction using permit and transferFrom functions (EIP-2612) with ERC20 tokens.
Intro
Gasless transactions with "permit and signed approvals" for ERC-20 tokens offer a seamless way to sponsor token transfers using a payer account where the holder of the tokens does not have to 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.
Scope
This only works for ERC20 token contracts supporting
permit
andtransferFrom
function. Read about EIP-2612.
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:
Params | Description/ Value |
---|---|
ERC20 token holder wallet address (owner /User Wallet) | 0xb636F53AFc9Fe7fa7246E1c220DF2436936b4315 |
Wallet address that will pay for the gas fee (spender /Payer Wallet) | 0x36CD949d0ccb506C52cB40972391C1E5AA1D5c78 |
Name of ERC20 token contract | Infinity |
ERC20 token contract address | 0x022a39f06413e249b3739c573bdb28b98eac051b |
Wallet address where the tokens are to be transferred | 0x6E59e504d1b64e8F344a7af3A3384c9Ae9E02371 |
chainId | 80002 for Matic Amoy testnet (find the chainId from the chain list) |
nonce | Fetch the nonce using the nonces read function on token contract. Function: nonces(address owner) . External view returns (uint ). |
deadline | The time until the signed message is valid in Unix time format |
Terminologies Explained
- User Wallet/
owner
: The user wallet is the wallet associated with the end-user or the person who holds the ERC20 tokens. - Payer Wallet/
spender
: 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 to which the ERC20 tokens will be transferred.
Prepare EIP712 JSON document
The first step is preparing the EIP 712 JSON document that the user wallet will sign.
{
"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"
}
],
"Permit": [
{
"name": "owner",
"type": "address"
},
{
"name": "spender",
"type": "address"
},
{
"name": "value",
"type": "uint256"
},
{
"name": "nonce",
"type": "uint256"
},
{
"name": "deadline",
"type": "uint256"
}
]
},
"primaryType": "Permit", // this will be Permit
"domain": {
"name": "Infinity", // name of the ERC20 token contract
"version": "1", // hardcoded to 1
"chainId": "80002", // the chainId
"verifyingContract": "0x022a39f06413e249b3739c573bdb28b98eac051b" // the ERC20 token contract address
},
"message": {
"owner": "0xb636F53AFc9Fe7fa7246E1c220DF2436936b4315", // from wallet address (address that holds the ERC20 tokens)
"spender": "0x36CD949d0ccb506C52cB40972391C1E5AA1D5c78", // wallet address of PAYER WALLET (who will pay the gas fee)
"value": "2000000000000000000", // value being transferred, (value needs to be in WEI)
"nonce": "1", // nonce you got from the nonces read function on token contract
"deadline": "1726390448" // time until the signed message will be valid
}
}
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 fees have to be paid.
Request: reference
POST /api/signatures
Parameter | Param Type | Value | Description | Example Value |
---|---|---|---|---|
Signing-Method | Header | id:value | id : This is the ID of the signing methodvalue : This is the value of the signing method | 756ae7a7-3713-43ee-9936-0dff50306488:123456 |
{
"signatureRequest": {
"secretType": "MATIC", // blockhain of the tx
"walletId": "c701b419-b0bc-4391-8f91-f99bbcf67ab5", // the wallet ID that will sign the message (user wallet)
"type": "EIP712",
"data": {
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"Permit": [
{
"name": "owner",
"type": "address"
},
{
"name": "spender",
"type": "address"
},
{
"name": "value",
"type": "uint256"
},
{
"name": "nonce",
"type": "uint256"
},
{
"name": "deadline",
"type": "uint256"
}
]
},
"primaryType": "Permit",
"domain": {
"name": "Infinity",
"version": "1",
"chainId": "80002",
"verifyingContract": "0x022a39f06413e249b3739c573bdb28b98eac051b"
},
"message": {
"owner": "0xb636F53AFc9Fe7fa7246E1c220DF2436936b4315",
"spender": "0x36CD949d0ccb506C52cB40972391C1E5AA1D5c78",
"value": "2000000000000000000",
"nonce": "1",
"deadline": "1726390448"
}
}
}
}
Response body
The
r
,s
, andv
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": "0x225a86d284b3b02fd76e49cdefb157b329a67a1fb2dcb50310de14b854251001",
"s": "0x5f198ef763d42c27d39b65e3ba032aa39015fa7c88864ad9652b212a2237fc14",
"v": "0x1c",
"signature": "0x225a86d284b3b02fd76e49cdefb157b329a67a1fb2dcb50310de14b8542510015f198ef763d42c27d39b65e3ba032aa39015fa7c88864ad9652b212a2237fc141c"
}
}
Executing the transactions
After that user wallet has signed the EIP712 message, they provide the r
, s
, and v
values to the payer wallet. Next, the payer wallet needs to do two contract function calls.
The two transactions have to be executed by the payer wallet (who will be paying for the gas fee)!
1. Call permit
function
permit
functionThe payer wallet will call the permit
function to set the allowance. Allowance is permitting someone else to move your tokens.
Request: reference
POST /api/transactions/execute
Parameter | Param Type | Value | Description | Example Value |
---|---|---|---|---|
Signing-Method | Header | id:value | id : This is the ID of the signing methodvalue : This is the value of the signing method | 756ae7a7-3713-43ee-9936-0dff50306488:123456 |
{
"transactionRequest": {
"type": "CONTRACT_EXECUTION",
"functionName": "permit", // the function name
"value": 0.0, // this will be 0.0
"inputs": [
{
"type": "address", // the from address (User Wallet)
"value": "0xb636F53AFc9Fe7fa7246E1c220DF2436936b4315"
},
{
"type": "address", // the spender address (Payer Wallet)
"value": "0x36CD949d0ccb506C52cB40972391C1E5AA1D5c78"
},
{
"type": "uint256", // the amount being transferred
"value": "2000000000000000000"
},
{
"type": "uint256", // deadline from the EIP172.message request
"value": "1726390448"
},
{
"type": "uint8", // v from the sign eip712 message response
"value": "0x1c"
},
{
"type": "bytes32", // r from the sign eip712 message response
"value": "0x225a86d284b3b02fd76e49cdefb157b329a67a1fb2dcb50310de14b854251001"
},
{
"type": "bytes32", // s from the sign eip712 message response
"value": "0x5f198ef763d42c27d39b65e3ba032aa39015fa7c88864ad9652b212a2237fc14"
}
],
"walletId": "6249865f-d413-4ebc-bfa8-6cb4c89cc930", // wallet ID of Payer Wallet
"to": "0x022a39f06413e249b3739c573bdb28b98eac051b", // the token contract address
"secretType": "MATIC" // blockchain of the tx
}
}
Response body
Now the payer wallet has successfully set an allowance of 2000000000000000000
(assuming the erc20 token uses 18 decimals, equivalent to 2 tokens), and they can move them anywhere by paying the gas fee.
{
"success": true,
"result": {
"id": "1481f1a0-997a-47e4-8f96-523d36348b2d",
"transactionHash": "0x12ad50765c59cae07f122346bcfeba2946ec49b7a4c6c770c720ea38f13a9abd"
}
}
2. Call transferFrom
function
transferFrom
functionThe final step is calling the transferFrom
function by the payer wallet. In this function call the from address
will be the user wallet. The to address
is where the tokens will be transferred and the payer wallet will pay the gas fee.
The payer wallet can call the
transferFrom
function and transfer the same amount of tokens that were set in the allowance.
Request: reference
POST /api/transactions/execute
Parameter | Param Type | Value | Description | Example Value |
---|---|---|---|---|
Signing-Method | Header | id:value | id : This is the ID of the signing methodvalue : This is the value of the signing method | 756ae7a7-3713-43ee-9936-0dff50306488:123456 |
{
"transactionRequest": {
"type": "CONTRACT_EXECUTION",
"functionName": "transferFrom", // the function name
"value": 0.0, // this will be 0.0
"inputs": [
{
"type": "address", // the from address (User Wallet)
"value": "0xb636F53AFc9Fe7fa7246E1c220DF2436936b4315"
},
{
"type": "address", // the to address (where the tokens will be sent to)
"value": "0x6E59e504d1b64e8F344a7af3A3384c9Ae9E02371"
},
{
"type": "uint256", // the amount being transferred
"value": "2000000000000000000"
}
],
"walletId": "6249865f-d413-4ebc-bfa8-6cb4c89cc930", // wallet ID of the Payer Wallet
"to": "0x022a39f06413e249b3739c573bdb28b98eac051b", // the token contract address
"secretType": "MATIC" // blockchain of the tx
}
}
Response body
{
"success": true,
"result": {
"id": "7464177f-237c-4393-b7ec-ef01b58560f5",
"transactionHash": "0x1d1e28d80420029d35ba4de7c407d6493c8a7c0b8a807558d52bd72930918d1f"
}
}
Result: Successful Gasless Transaction for ERC20 token
Updated about 2 months ago