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.
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:
Params | Description/ Value |
---|---|
ERC20 token holder wallet address | 0x8952fcc43F55c60C3Bf4FD565ABdd066CC1aD60c |
Name of ERC20 token contract | Zap |
ERC20 token contract address | 0x8c2e84c37fa226fdaa13999a725a638dbf2bc6a0 |
Wallet address where the tokens are to be transferred | 0xe56a14Cc9DbbA42D1DE1314051CA00AA742BaEfa |
chainId | 43113 for Avalanche Fuji Testnet (find the chainId from the chain list) |
nonce | It 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) |
validBefore | The time until the signed message is valid in Unix time format |
validAfter | The 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
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": "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
, 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": "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
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": "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
Updated about 1 month ago