Skip to content

zpaynow/EIP-3009-Token

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

EIP-3009 Token

A modern implementation of ERC-20 token with EIP-3009 (Transfer with Authorization) and EIP-2612 (Permit) support, built with Foundry.

Features

  • ERC-20: Standard token functionality (transfer, approve, transferFrom)
  • EIP-2612: Gasless approvals using permit signatures
  • EIP-3009: Gasless transfers using signed authorizations
    • transferWithAuthorization: Execute transfers with signed authorization
    • receiveWithAuthorization: Receive transfers (payee must be caller)
    • cancelAuthorization: Cancel unused authorizations
  • Solidity 0.8.x: Modern Solidity with built-in overflow protection
  • Comprehensive Tests: Full test coverage for all functionality

What is EIP-3009?

EIP-3009 enables gasless token transfers by allowing users to authorize transfers via signatures. Instead of calling transfer() directly and paying gas, users can sign a transfer authorization off-chain, and a relayer can submit it on-chain.

Key Benefits

  1. Gasless Transfers: Users don't need ETH to transfer tokens
  2. Flexible Timing: Authorizations can have validity windows
  3. Replay Protection: Uses random nonces to prevent replay attacks
  4. Cancellation: Users can cancel authorizations before they're used

Project Structure

├── src/
│   ├── EIP3009Token.sol          # Main token contract
│   └── lib/
│       ├── EIP3009.sol            # EIP-3009 implementation
│       ├── EIP2612.sol            # EIP-2612 permit implementation
│       ├── EIP712.sol             # EIP-712 typed data signing
│       ├── EIP712Domain.sol       # Domain separator
│       ├── ECRecover.sol          # Safe signature recovery
│       └── IERC20Internal.sol     # Internal interface
├── test/
│   └── EIP3009Token.t.sol         # Comprehensive test suite
└── script/
    └── DeployEIP3009Token.s.sol   # Deployment script

Installation

This project uses Foundry. If you don't have it installed:

curl -L https://foundry.paradigm.xyz | bash
foundryup

Install dependencies:

forge install

Build

forge build

Test

Run all tests:

forge test

Run tests with verbosity:

forge test -vv

Run specific test:

forge test --match-test test_TransferWithAuthorization -vvvv

Gas report:

forge test --gas-report

Deploy

Local Deployment

forge script script/DeployEIP3009Token.s.sol:DeployEIP3009Token --fork-url http://localhost:8545 --broadcast

Testnet/Mainnet Deployment

forge script script/DeployEIP3009Token.s.sol:DeployEIP3009Token \
  --rpc-url <your_rpc_url> \
  --private-key <your_private_key> \
  --broadcast \
  --verify

Custom Token Parameters

Set environment variables to customize the token:

export TOKEN_NAME="My Token"
export TOKEN_VERSION="1"
export TOKEN_SYMBOL="MTK"
export TOKEN_DECIMALS=18
export TOKEN_INITIAL_SUPPLY=1000000000000000000000000

forge script script/DeployEIP3009Token.s.sol:DeployEIP3009Token --broadcast

Usage Examples

Standard ERC-20 Transfer

token.transfer(recipient, amount);

Gasless Transfer with Authorization

// Off-chain: User signs authorization
const authorization = {
  from: userAddress,
  to: recipientAddress,
  value: amount,
  validAfter: Math.floor(Date.now() / 1000) - 100,
  validBefore: Math.floor(Date.now() / 1000) + 3600,
  nonce: ethers.utils.randomBytes(32)
};

const signature = await signer._signTypedData(domain, types, authorization);
const { v, r, s } = ethers.utils.splitSignature(signature);

// On-chain: Relayer submits transaction
await token.transferWithAuthorization(
  authorization.from,
  authorization.to,
  authorization.value,
  authorization.validAfter,
  authorization.validBefore,
  authorization.nonce,
  v, r, s
);

Permit (EIP-2612)

// Off-chain: User signs permit
const permit = {
  owner: userAddress,
  spender: spenderAddress,
  value: amount,
  nonce: await token.nonces(userAddress),
  deadline: Math.floor(Date.now() / 1000) + 3600
};

const signature = await signer._signTypedData(domain, types, permit);
const { v, r, s } = ethers.utils.splitSignature(signature);

// On-chain: Anyone can submit permit
await token.permit(
  permit.owner,
  permit.spender,
  permit.value,
  permit.deadline,
  v, r, s
);

Test Coverage

The test suite covers:

  • ✅ Basic ERC-20 functionality
  • ✅ EIP-2612 permit functionality
  • ✅ EIP-3009 transfer with authorization
  • ✅ EIP-3009 receive with authorization
  • ✅ Authorization cancellation
  • ✅ Signature validation
  • ✅ Replay protection
  • ✅ Time-based validation
  • ✅ Edge cases and security scenarios
  • ✅ Fuzz testing

Security Considerations

  1. Nonce Management: EIP-3009 uses random nonces (bytes32) for better UX than sequential nonces
  2. Signature Validation: All signatures are validated using EIP-712 typed data
  3. Replay Protection: Used authorizations are tracked to prevent replay attacks
  4. Time Windows: Authorizations can specify validity periods
  5. Malleability Protection: ECRecover library prevents signature malleability

Migration from Old Contract

This implementation modernizes the old Solidity 0.6.12 contract:

  • ✅ Updated to Solidity ^0.8.20 (built-in overflow protection)
  • ✅ Removed SafeMath dependency (no longer needed)
  • ✅ Updated to OpenZeppelin v5.x conventions
  • ✅ Replaced now with block.timestamp
  • ✅ Removed public from constructor
  • ✅ Added comprehensive Foundry tests
  • ✅ Improved code organization and documentation

References

License

MIT

About

EIP-3009 token contracts for easy deploy.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published