Permit3 is an approval system that enables cross-chain token approvals and transfers with a single signature. It unlocks a one-signature cross-chain future through Unbalanced Merkle Trees and non-sequential nonces, while maintaining Permit2 compatibility.
Permit3 is deployed using ERC-2470 Singleton Factory for deterministic addresses across all chains:
- Deployment Address:
0xEc00030C0000245E27d1521Cc2EE88F071c2Ae34
This ensures the same contract address on all supported networks, enabling seamless cross-chain operations.
- Cross-Chain Operations: Authorize token operations across multiple blockchains with one signature
- Multi-Token Support: Unified interface for ERC20, ERC721 NFTs, and ERC1155 semi-fungible tokens
- Direct Permit Execution: Execute permit operations without signatures when caller has authority
- ERC-7702 Integration: Account Abstraction support for enhanced user experience
- Witness Functionality: Attach arbitrary data to permits for enhanced verification and complex permission patterns
- NFT & Semi-Fungible Token Features:
- Dual-allowance system (per-token and collection-wide)
- TokenId encoding for signed permits
- Batch operations for multiple token types
- ERC1155 gaming asset support
- Flexible Allowance Management:
- Increase/decrease allowances asynchronously
- Time-bound permissions with automatic expiration
- Account locking for enhanced security
- Gas-Optimized Design:
- Non-sequential nonces for concurrent operations
- Bitmap-based nonce tracking for efficient gas usage
- Standard merkle proofs using OpenZeppelin's MerkleProof library
- Emergency Security Controls:
- Cross-chain revocation system
- Account locking mechanism
- Time-bound permissions
- Full Permit2 Compatibility:
- Implements basic transfer Permit2 interfaces
- Drop-in replacement for existing integrations
- Unbalanced Merkle Trees: Hybrid two-part structure for cross-chain proofs:
[H1] β [H2] β [H3] β ROOT β Unbalanced upper structure / \ \ \ [BR] [D5] [D6] [D7] β Additional chain data / \ [BH1] [BH2] β Balanced tree (bottom part) / \ / \ [D1] [D2] [D3] [D4] β Leaf data- Unbalanced Design: Combines balanced subtrees with unbalanced upper structure for efficiency
- Bottom Part: Efficient membership proofs with O(log n) complexity
- Top Part: Unbalanced structure minimizes proof size for expensive chains
- Gas Optimization: Chain ordering (cheapest chains first, expensive last)
- "Unbalanced": Deliberate deviation from balanced trees at top level
- Security: Uses merkle tree verification for compatibility
Comprehensive documentation is available in the docs directory:
| Section | Description | Quick Links |
|---|---|---|
| Overview | Getting started with Permit3 | Introduction |
| Core Concepts | Understanding the fundamentals | Architecture Β· Multi-Token Β· Witnesses Β· Cross-Chain Β· Merkle Trees Β· Nonces Β· Allowances Β· Permit2 Compatibility |
| Guides | Step-by-step tutorials | Quick Start Β· Multi-Token Β· NFT Permits Β· ERC-7702 Β· Witness Β· Cross-Chain Β· Signatures Β· Security |
| API Reference | Technical specifications | Full API Β· Data Structures Β· Interfaces Β· Events Β· Error Codes |
| Examples | Code samples | Multi-Token Β· ERC-7702 Β· Witness Β· Cross-Chain Β· Allowance Β· Security Β· Integration |
The protocol centers around the AllowanceOrTransfer structure:
struct AllowanceOrTransfer {
uint48 modeOrExpiration; // Operation mode/expiration
address token; // Token address
address account; // Approved spender/recipient
uint160 amountDelta; // Amount change/transfer amount
}struct Allowance {
uint160 amount;
uint48 expiration;
uint48 timestamp;
}- Timestamps order operations across chains
- Most recent timestamp takes precedence in expiration updates
- Prevents cross-chain race conditions
- Critical for async allowance updates
Locked accounts have special restrictions:
- Cannot increase/decrease allowances
- Cannot execute transfers
- Must submit unlock command with timestamp validation to disable
- Provides emergency security control
// Access Permit2 compatibility
IPermit permit = IPermit(PERMIT3_ADDRESS);
permit.transferFrom(msg.sender, recipient, 1000e6, USDC);
// Access Permit3 features
IPermit3 permit3 = IPermit3(PERMIT3_ADDRESS);For direct transfers without signatures, use specialized functions:
// NFT transfer
permit3.transferFromERC721(from, to, nftContract, tokenId);
// ERC1155 transfer
permit3.transferFromERC1155(from, to, erc1155Contract, tokenId, amount);
// Batch mixed tokens
TokenTypeTransfer[] memory transfers = [...];
permit3.batchTransferMultiToken(transfers);// 1. Create permits array directly
AllowanceOrTransfer[] memory permits = new AllowanceOrTransfer[](3);
// 2. Increase Allowance
permits[0] = AllowanceOrTransfer({
modeOrExpiration: uint48(block.timestamp + 1 days),
token: USDC,
account: DEX,
amountDelta: 1000e6
});
// 3. Lock Account
permits[1] = AllowanceOrTransfer({
modeOrExpiration: 2,
token: USDC,
account: address(0),
amountDelta: 0
});
// 4. Execute Transfer
permits[2] = AllowanceOrTransfer({
modeOrExpiration: 0,
token: USDC,
account: recipient,
amountDelta: 500e6
});
// Execute the permits
permit3.permit(owner, salt, deadline, timestamp, permits, signature);// Create permits for each chain
const ethPermits = {
chainId: 1,
permits: [{
modeOrExpiration: futureTimestamp,
token: USDC_ETH,
account: DEX_ETH,
amountDelta: 1000e6
}]
};
const arbPermits = {
chainId: 42161,
permits: [{
modeOrExpiration: 1, // Decrease mode
token: USDC_ARB,
account: DEX_ARB,
amountDelta: 500e6
}]
};
// Hash each chain's permits to create leaf nodes
const ethLeaf = permit3.hashChainPermits(ethPermits);
const arbLeaf = permit3.hashChainPermits(arbPermits);
// Build merkle tree and get root (typically done off-chain)
const leaves = [ethLeaf, arbLeaf];
const merkleRoot = buildMerkleRoot(leaves);
// Generate merkle proof for specific chain
const arbProof = generateMerkleProof(leaves, 1); // Index 1 for Arbitrum
const proof = { nodes: arbProof };
// Create and sign with the unbalanced root
const signature = signPermit3(owner, salt, deadline, timestamp, merkleRoot);-
Allowance Management
- Set reasonable expiration times
- Use lock mode for sensitive accounts
- Monitor allowance changes across chains
-
Timestamp Validation
- Validate operation ordering
- Check for expired timestamps
- Handle locked state properly
-
Cross-Chain Security
- Verify chain IDs match
- Use unique nonces
- Monitor pending operations
# Install
forge install
# Test
forge test
# Deploy
forge script script/DeployPermit3.s.sol:DeployPermit3 \
--rpc-url <RPC_URL> \
--private-key <KEY> \
--broadcastSee Audits
MIT License - see LICENSE
