A Solidity implementation of EIP-3009 for gas-less ERC-20 token transfers using EIP-712 signatures.
The EIP3009Forwarder contract enables meta-transactions for ERC-20 tokens, allowing users to authorize token transfers via cryptographic signatures without paying gas fees themselves. A third party (relayer) can submit these pre-signed authorizations and pay the gas costs.
- Gas-less transfers: Users sign authorizations off-chain, relayers submit on-chain
- EIP-712 compliant: Uses structured data signing for security
- Replay protection: Unique nonces prevent transaction replays
- Time-bounded authorizations: Support for
validAfterandvalidBeforetimestamps - Authorization cancellation: Users can invalidate unused authorizations
- Two transfer modes:
transferWithAuthorization: Any relayer can submitreceiveWithAuthorization: Only the recipient can submit
- USDC-style signature bytes:
bytes signaturevariants support EOA (r,s,v) and contract wallets (ERC-1271)
This project uses Foundry. Install dependencies:
forge installforge buildforge testforge fmtforge snapshot- Create a deployment script in
script/ - Deploy using private key:
forge script script/DeployForwarder.s.sol:DeployForwarder \
--rpc-url <your_rpc_url> \
--private-key <your_private_key> \
--broadcast \
--sig "run(address,string)" \
<erc20_token_address> \
"<version>"- Deploy using environment account:
forge script script/DeployForwarder.s.sol:DeployForwarder \
--rpc-url <your_rpc_url> \
--account <your_account_name> \
--broadcast \
--sig "run(address,string)" \
<erc20_token_address> \
"<version>"- Deploy using environment account on SKALE:
forge script script/DeployForwarder.s.sol:DeployForwarder \
--rpc-url <your_rpc_url> \
--account <your_account_name> \
--broadcast \
--legacy \
--sig "run(address,string)" \
<erc20_token_address> \
"<version>"Executes a token transfer using a signed authorization. Can be called by any relayer.
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes calldata signature
) external;
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external;Executes a token transfer where the recipient must be the transaction submitter.
function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes calldata signature
) external;
function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external;Cancels an unused authorization to prevent future execution.
function cancelAuthorization(
address authorizer,
bytes32 nonce,
bytes calldata signature
) external;
function cancelAuthorization(
address authorizer,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external;authorizationState(address, bytes32): Check if a nonce has been usedDOMAIN_SEPARATOR(): Get the EIP-712 domain separatorunderlyingToken(): Get the wrapped token addresshasApproval(address, uint256): Check if sufficient allowance existsgetAllowance(address): Get current allowance amount
The contract uses the following EIP-712 structured data types:
// TransferWithAuthorization
keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
// ReceiveWithAuthorization
keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
// CancelAuthorization
keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")// 1. Deploy forwarder for a token
EIP3009Forwarder forwarder = new EIP3009Forwarder(
tokenAddress,
"MyForwarder",
"1"
);
// 2. User approves forwarder to spend tokens
IERC20(tokenAddress).approve(address(forwarder), amount);
// 3. User signs authorization off-chain (using web3, ethers.js, etc.)
// 4. Relayer submits the signed authorization
forwarder.transferWithAuthorization(
from,
to,
value,
validAfter,
validBefore,
nonce,
signature // EOA signatures are packed as r || s || v
);- Token Approval Required: Users must approve the forwarder contract before authorizations can be executed
- Nonce Management: Use unique, unpredictable nonces to prevent replay attacks
- Time Bounds: Set appropriate
validAfterandvalidBeforetimestamps - Signature Validation: All signatures are validated against EIP-712 structured data
- Reentrancy Protection: Contract includes reentrancy guards on state-changing functions
# Run all tests
forge test
# Run tests with verbosity
forge test -vvv
# Run specific test
forge test --match-test test_succeeds_transferWithAuthorizationStart a local Ethereum node:
anvilFor more information on Foundry:
- Forge: Ethereum testing framework
- Cast: CLI for interacting with contracts
- Anvil: Local Ethereum node
- Chisel: Solidity REPL
Visit Foundry Book for complete documentation.
See MIT License in License
This software is provided for educational and experimental purposes. Smart contracts involve significant risk and this code has not undergone professional security auditing.
BY USING THIS SOFTWARE, YOU ACKNOWLEDGE AND AGREE THAT:
-
No Warranty: This software is provided "as is" without any warranties or guarantees of any kind.
-
Use at Your Own Risk: You use this software entirely at your own risk. The authors and contributors are not responsible for any losses, damages, or security vulnerabilities.
-
Not Financial Advice: This software does not constitute financial, investment, or legal advice.
-
Security Auditing Required: Before using in production, you must conduct thorough security audits by qualified professionals.
-
Regulatory Compliance: You are solely responsible for ensuring compliance with applicable laws and regulations in your jurisdiction.
-
No Liability: The authors, contributors, and associated parties shall not be liable for any direct, indirect, incidental, special, consequential, or exemplary damages arising from the use of this software.
ALWAYS CONDUCT THOROUGH TESTING AND SECURITY AUDITS BEFORE DEPLOYING TO MAINNET OR HANDLING REAL VALUE.