- System Overview
- Architecture
- Core Components
- Component Details
- Integration & Workflows
- Security Model
- Use Cases
- Technical Implementation
- Testing Strategy
This is a modular DeFi (Decentralized Finance) system built on Ethereum that provides:
- Automated Market Maker (AMM) Liquidity Pool - Enables token swaps with liquidity provision
- EIP-712 Meta-Transaction Support - Allows gasless transactions via off-chain signatures
- Fee Management System - Upgradeable fee calculation and management
- Multi-Signature Vault - Secure multi-party governance for fund management
- Role-Based Access Control - Granular permission system for different operations
- Modularity: Each component is independent and can be upgraded/replaced
- Security: Multiple layers of access control and validation
- Upgradeability: Critical components use proxy patterns for future improvements
- Gas Efficiency: Optimized for on-chain operations
- Composability: Components work together seamlessly
┌─────────────────────────────────────────────────────────────┐
│ Smart Modules System │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Liquidity │ │ EIP712Swap │ │ FeeManager │ │
│ │ Pool │◄───┤ (Relayer) │◄───┤ (Upgradeable)│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ └─────────────────────┼────────────────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ AccessManager │ │
│ │ (Role Management) │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ VaultMultisig │ │
│ │ (Multi-Sig Vault) │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
-
LiquidityPool - Core AMM functionality
- Uses
FeeManagerfor fee calculations - Grants permissions to
EIP712Swapfor meta-transactions - Managed by admins via
AccessManagerroles
- Uses
-
EIP712Swap - Meta-transaction relayer
- Verifies off-chain signatures
- Executes swaps on behalf of users
- Requires
ALLOWED_EIP712_SWAP_ROLEfrom pool
-
FeeManager - Upgradeable fee system
- Calculates fees based on swap parameters
- Can be upgraded via UUPS proxy pattern
- Managed by admins
-
AccessManager - Centralized role management
- Manages all role assignments
- Used by
VaultMultisigfor multisig admin permissions
-
VaultMultisig - Multi-signature vault
- Manages funds with quorum-based approvals
- Can execute arbitrary contract calls
- Uses
AccessManagerfor admin permissions
Purpose: Automated Market Maker (AMM) for token swaps
Key Features:
- Constant product formula (x * y = k) for price discovery
- Admin-controlled liquidity management
- Role-based swap authorization
- Support for different token decimals
Core Functions:
addLiquidity()- Admin-only liquidity provisionremoveLiquidity()- Admin-only liquidity withdrawalswap()- Execute token swaps (admin or authorized EIP712 contract)getPrice()- Query current exchange rategetReserves()- View current pool reserves
Access Control:
ADMIN_ROLE: Can add/remove liquidityALLOWED_EIP712_SWAP_ROLE: Can execute swaps (granted to EIP712Swap contract)
Purpose: Enable gasless transactions via EIP-712 typed data signatures
Key Features:
- EIP-712 compliant signature verification
- Nonce management to prevent replay attacks
- Deadline enforcement for time-bound operations
- Signature verification before execution
Core Functions:
verify()- Verify swap request signatureexecuteSwap()- Execute swap with valid signaturegetNonce()- Get current nonce for an addressgetDomainSeparator()- Get EIP-712 domain separator
Security Mechanisms:
- Nonce increment prevents replay attacks
- Deadline check prevents stale transactions
- Signature verification ensures request authenticity
Purpose: Calculate and manage swap fees (upgradeable)
Key Features:
- UUPS (Universal Upgradeable Proxy Standard) upgradeable
- Basis points fee calculation (1 bp = 0.01%)
- Fee calculated on output amount
- Admin-controlled fee updates
Core Functions:
initialize()- Initialize with initial feesetFee()- Update fee (admin only)getFee()- Calculate fee for swap parameters_authorizeUpgrade()- Control upgrade authorization
Fee Calculation:
// 1. Calculate output amount using AMM formula
amountOut = (amountIn * reserveOut) / (reserveIn + amountIn)
// 2. Calculate fee as percentage of output
fee = (amountOut * feeBasisPoints) / 10000Purpose: Centralized role-based access control
Key Features:
- Manages three distinct roles:
ADMIN_ROLE: General admin permissionsMULTISIG_ADMIN_ROLE: Vault multisig administrationALLOWED_EIP712_SWAP_ROLE: EIP712 swap permissions
- Role hierarchy with
DEFAULT_ADMIN_ROLEas root
Core Functions:
addAdmin()/removeAdmin()- Manage admin rolesaddMultisigAdmin()/removeMultisigAdmin()- Manage multisig adminsaddEIP712Swapper()/removeEIP712Swapper()- Manage EIP712 swappersisAdmin()/isMultisigAdmin()/isEIP712Swapper()- Check role membership
Purpose: Multi-signature vault for secure fund management
Key Features:
- Quorum-based transaction approval
- Support for ETH transfers and arbitrary contract calls
- Configurable signers and quorum
- Operation tracking and history
Core Functions:
initiateTransfer()- Propose ETH transferapproveTransfer()- Approve transfer proposalexecuteTransfer()- Execute approved transferinitiateOperation()- Propose contract callapproveOperation()- Approve operation proposalexecuteOperation()- Execute approved operationupdateSigners()- Update multisig signers (multisig admin only)updateQuorum()- Update required quorum (multisig admin only)
Workflow:
- Signer initiates transfer/operation
- Other signers approve until quorum reached
- Any signer executes when quorum met
- Operation marked as executed (prevents replay)
Purpose: Centralized role constant definitions
Key Features:
- Prevents role hash collisions
- Ensures consistent role usage across contracts
- Three defined roles with unique hashes
Roles:
ADMIN_ROLE:keccak256("ADMIN_ROLE")MULTISIG_ADMIN_ROLE:keccak256("MULTISIG_ADMIN_ROLE")ALLOWED_EIP712_SWAP_ROLE:keccak256("ALLOWED_EIP712_SWAP_ROLE")
The pool uses a simplified constant product formula:
amountOut = (amountIn * reserveOut) / (reserveIn + amountIn)
This formula ensures:
- Price impact increases with trade size
- Pool always maintains liquidity
- No need for external price oracles
-
Validation:
- Check token pair is valid (token0 or token1)
- Verify sender has sufficient allowance
- Ensure sufficient liquidity exists
-
Calculation:
- Calculate output amount using AMM formula
- Calculate fee using FeeManager
- Deduct fee from output amount
-
Execution:
- Transfer input tokens from user to pool
- Transfer output tokens (minus fee) to user
- Update reserves
-
Events:
- Emit
Swapevent with all swap details
- Emit
price = (reserveOut * 1e18) / reserveInReturns price normalized to 18 decimals for consistent comparison.
EIP-712 enables signing structured data (not just raw hashes), providing:
- Better UX (wallets show human-readable data)
- Type safety
- Domain separation (prevents cross-chain replay)
struct SwapRequest {
address pool;
address sender;
address tokenIn;
address tokenOut;
uint256 amountIn;
uint256 minAmountOut;
uint256 nonce;
uint256 deadline;
}-
Create Type Hash:
TYPEHASH = keccak256("SwapRequest(address pool,address sender,...)")
-
Create Struct Hash:
structHash = keccak256(abi.encode(TYPEHASH, pool, sender, ...))
-
Create Domain Separator:
domainSeparator = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256("EIP712Swap"), keccak256("1"), chainId, contractAddress ) )
-
Create Final Hash:
digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash))
-
Recover Signer:
signer = digest.recover(signature)
- Each address has a unique nonce counter
- Nonce increments after successful swap execution
- Prevents replay attacks and ensures order
Uses UUPS (Universal Upgradeable Proxy Standard):
- Implementation contract holds logic
- Proxy contract holds state
- Upgrade function in implementation (not proxy)
- More gas-efficient than Transparent Proxy
// Step 1: Calculate output using AMM formula
uint256 amountOut = (amountIn * reserveOut) / (reserveIn + amountIn);
// Step 2: Calculate fee as percentage of output
uint256 fee = (amountOut * feeBasisPoints) / FEE_DENOMINATOR; // FEE_DENOMINATOR = 10000Example:
- Input: 100 tokens
- Reserves: 1000 in, 2000 out
- Fee: 250 basis points (2.5%)
- Output: (100 * 2000) / (1000 + 100) = 181.81 tokens
- Fee: (181.81 * 250) / 10000 = 4.55 tokens
- Final output: 181.81 - 4.55 = 177.26 tokens
Only DEFAULT_ADMIN_ROLE can authorize upgrades, ensuring:
- Controlled upgrade process
- No unauthorized logic changes
- Audit trail via events
- Quorum: Minimum number of approvals required
- Signers: List of authorized addresses
- Validation: Quorum ≤ Signers.length
-
Initiation (
initiateTransfer):- Signer proposes transfer
- Automatically counts as 1 approval
- Creates Transfer struct with ID
-
Approval (
approveTransfer):- Other signers approve
- Approval count increments
- Prevents duplicate approvals
-
Execution (
executeTransfer):- Any signer can execute when quorum reached
- Validates balance sufficiency
- Executes ETH transfer
- Marks as executed (prevents replay)
Similar to transfers but for arbitrary contract calls:
initiateOperation: Propose contract call with encoded dataapproveOperation: Approve operationexecuteOperation: Execute when quorum reached
Use Cases:
- Call LiquidityPool functions
- Interact with other DeFi protocols
- Update contract parameters
- Replay Prevention: Executed operations cannot be re-executed
- Balance Checks: Validates sufficient balance before execution
- Quorum Validation: Ensures minimum approvals before execution
- Signer Verification: Only authorized signers can participate
- Admin Controls: Only multisig admins can update signers/quorum
User → LiquidityPool.swap()
├─ Check authorization (admin or EIP712 contract)
├─ Validate token pair
├─ Check allowance
├─ Calculate output (AMM formula)
├─ Calculate fee (FeeManager.getFee())
├─ Transfer tokens
└─ Update reserves
1. User (Off-chain):
├─ Create SwapRequest struct
├─ Sign with EIP-712
└─ Send signature to relayer
2. Relayer (On-chain):
├─ Call EIP712Swap.executeSwap()
├─ Verify signature
├─ Check nonce & deadline
├─ Call LiquidityPool.swap()
└─ Increment nonce
1. Signer 1:
└─ initiateTransfer(recipient, amount)
2. Signer 2:
└─ approveTransfer(transferId)
3. Signer 3 (or any signer):
└─ executeTransfer(transferId)
└─ ETH transferred to recipient
Admin → LiquidityPool.addLiquidity()
├─ Validate token address
├─ Check balance
├─ Transfer tokens to pool
└─ Update reserves
Admin → FeeManager.setFee()
├─ Check admin role
├─ Update fee value
└─ Future swaps use new fee
-
Role-Based Access Control (RBAC):
- OpenZeppelin AccessControl implementation
- Hierarchical role structure
- Role admin relationships
-
Function-Level Modifiers:
onlyRole()- OpenZeppelin modifieronlyAdminOrEIP712Swap()- Custom modifieronlyMultisigSigner()- VaultMultisig modifier
-
Contract-Level Authorization:
- EIP712Swap contract has special role
- Can execute swaps on behalf of users
- Signature verification ensures user consent
- No external calls before state updates in critical functions
- Checks-Effects-Interactions pattern followed
- Solidity 0.8.30 built-in overflow checks
- Safe math operations
- EIP-712 standard prevents signature manipulation
- Nonce prevents replay attacks
- Deadline prevents stale transactions
- Token address validation
- Amount validation (non-zero, sufficient balance)
- Quorum validation (≤ signers, > 0)
- UUPS pattern with admin-only upgrade authorization
- Implementation contract can be audited before upgrade
- Upgrade function protected by role
-
Centralization Risks:
- Admin roles have significant power
- Multisig reduces but doesn't eliminate risk
- Consider time-locks for critical operations
-
Front-Running:
- Public mempool allows front-running
- Consider commit-reveal schemes for large trades
-
Liquidity Risks:
- Low liquidity can cause high slippage
- Admin-controlled liquidity adds centralization
-
Signature Replay:
- EIP-712 domain separation prevents cross-chain replay
- Nonce prevents same-chain replay
- Deadline prevents stale transactions
Scenario: Users want to swap tokens without centralized exchange
Implementation:
- Deploy LiquidityPool with token pair
- Admin adds initial liquidity
- Users swap tokens via
swap()or EIP-712
Benefits:
- No order book needed
- Automated price discovery
- Always available liquidity
Scenario: Users want to trade without paying gas fees
Implementation:
- User signs swap request off-chain
- Relayer pays gas and executes
- Relayer may charge fee or be sponsored
Benefits:
- Better UX (no gas management)
- Enables mobile-first DeFi
- Reduces barrier to entry
Scenario: DAO or organization needs secure fund management
Implementation:
- Deploy VaultMultisig with DAO signers
- Set quorum (e.g., 3 of 5)
- All transfers require quorum approval
Benefits:
- No single point of failure
- Transparent approval process
- Audit trail via events
Scenario: Protocol wants to provide liquidity for token pairs
Implementation:
- Admin adds liquidity to pool
- Earns fees from swaps
- Can remove liquidity when needed
Benefits:
- Passive income from fees
- Supports token ecosystem
- Flexible liquidity management
Scenario: Protocol wants to adjust fees based on market conditions
Implementation:
- Admin updates FeeManager fee
- New fee applies to all future swaps
- Can be upgraded if needed
Benefits:
- Dynamic fee adjustment
- Upgradeable without migration
- Maintains fee revenue
- Version: 0.8.30
- Features Used:
- Built-in overflow/underflow checks
- Custom errors (gas efficient)
- Struct packing
- Library usage
- AccessControl: Role-based permissions
- AccessControlUpgradeable: Upgradeable access control
- UUPSUpgradeable: Upgradeable proxy pattern
- Initializable: Proxy initialization
- EIP712: Typed data signing
- ECDSA: Signature recovery
- Custom Errors: Instead of require strings (saves gas)
- Events: Efficient event emission
- Struct Packing: Efficient storage layout
- Library Functions: Reusable code without deployment overhead
- View Functions: No gas cost for read operations
address token0; // slot 0
uint256 token0Decimals; // slot 1
address token1; // slot 2
uint256 token1Decimals; // slot 3
uint256 reserveToken0; // slot 4
uint256 reserveToken1; // slot 5
FeeManager feeManager; // slot 6
EIP712Swap eip712Swap; // slot 7uint256 quorum; // slot 0
uint256 transfersCount; // slot 1
uint256 operationsCount; // slot 2
AccessManager accessManager; // slot 3
address[] currentMultiSigSigners; // slot 4 (array length)
mapping(uint256 => Transfer) transfers; // slot 5
mapping(uint256 => Operation) operations; // slot 6
mapping(address => bool) multiSigSigners; // slot 7Custom errors used throughout for gas efficiency:
error InsufficientTokenBalance();
error InvalidTokenAddress(address _token);
error InvalidTokenPair(address _tokenIn, address _tokenOut);
error InsufficientLiquidity();
error InsufficientOutputAmount(uint256 expected, uint256 actual);
error InsufficientAllowance();
error InvalidSignature();
error ExpiredSwapRequest();
error InvalidNonce();All state changes emit events for:
- Off-chain indexing
- Front-end updates
- Audit trails
- Analytics
Key events:
LiquidityAdded: When liquidity is addedSwap: When swap is executedTransferInitiated/Approved/Executed: Multisig workflowOperationInitiated/Approved/Executed: Multisig operations
The system includes comprehensive tests covering:
- Unit Tests: Individual component functionality
- Integration Tests: Component interactions
- Edge Cases: Boundary conditions and error scenarios
- Access Control: Role-based permission testing
-
LiquidityPool.t.sol (12 tests):
- Liquidity management
- Swap functionality
- Access control
- Error conditions
-
EIP712Swap.t.sol (3 tests):
- Signature verification
- Swap execution
- Multiple swaps
- Insufficient liquidity handling
-
FeeManager.t.sol (9 tests):
- Fee calculation
- Fee updates
- Different scenarios
- Access control
-
VaultMultisig.t.sol (27 tests):
- Transfer workflow
- Operation workflow
- Quorum management
- Signer management
- Error conditions
-
AccessManager.t.sol (5 tests):
- Role management
- Access control
- Authorization checks
-
Roles.t.sol (2 tests):
- Role constant validation
- Uniqueness checks
- Arrange-Act-Assert: Standard test structure
- Fuzz Testing: Random input generation (potential)
- Invariant Testing: State consistency checks
- Integration Testing: Multi-contract scenarios
- MockERC20: ERC20 token for testing
- Configurable decimals
- Mint/burn functions
- Standard ERC20 interface
- Foundry Test Framework:
forge-std/Test.sol - vm.prank(): Impersonate addresses
- vm.expectRevert(): Test error conditions
- vm.expectEmit(): Test event emission
- vm.deal(): Fund addresses with ETH
- Roles Library: Deploy first (no dependencies)
- AccessManager: Deploy with admin
- FeeManager Implementation: Deploy implementation contract
- FeeManager Proxy: Deploy proxy with initialization
- EIP712Swap: Deploy relayer contract
- LiquidityPool: Deploy with all dependencies
- VaultMultisig: Deploy with signers and AccessManager
- Initial fee (basis points, e.g., 250 = 2.5%)
- Admin address
- Token0 address and decimals
- Token1 address and decimals
- FeeManager address
- EIP712Swap address
- Signers array
- Quorum (must be ≤ signers.length, > 0)
- AccessManager address
-
FeeManager: Can be upgraded via UUPS pattern
- New implementation must be compatible
- Storage layout must match
- Admin must authorize upgrade
-
Other Contracts: Not upgradeable
- Consider proxy pattern if upgradeability needed
- Or deploy new version and migrate
- LiquidityPool.addLiquidity(): ~60,000 gas
- LiquidityPool.swap(): ~150,000-250,000 gas
- EIP712Swap.executeSwap(): ~200,000-300,000 gas
- VaultMultisig.initiateTransfer(): ~50,000 gas
- VaultMultisig.executeTransfer(): ~80,000 gas
- Always validate inputs: Check addresses, amounts, etc.
- Use events: Emit events for all state changes
- Handle errors gracefully: Use custom errors
- Test thoroughly: Cover edge cases
- Document code: Use NatSpec comments
- Secure private keys: Use hardware wallets
- Use multisig: For critical operations
- Monitor events: Track all contract interactions
- Test upgrades: On testnet first
- Gradual changes: Don't make drastic changes at once
- Check allowances: Before swapping
- Verify signatures: When using EIP-712
- Check deadlines: Don't use expired signatures
- Monitor nonces: Track your nonce for EIP-712 swaps
- Understand slippage: Set appropriate minAmountOut
-
Liquidity Provider Tokens (LP Tokens):
- Track liquidity provider shares
- Enable proportional liquidity removal
- Reward liquidity providers
-
Time-Weighted Average Price (TWAP):
- Oracle-free price feeds
- More accurate pricing
- Reduced manipulation risk
-
Flash Loans:
- Uncollateralized loans within transaction
- Enable arbitrage opportunities
- Require repayment in same transaction
-
Multi-Hop Swaps:
- Route through multiple pools
- Better prices for indirect pairs
- Automatic routing
-
Governance Token:
- Decentralized fee management
- Community-driven decisions
- Staking mechanisms
-
MEV Protection:
- Commit-reveal schemes
- Private transaction pools
- Fair ordering
This Smart Modules system provides a comprehensive, modular DeFi infrastructure with:
- Flexible AMM: Simple but effective liquidity pool
- Gasless Transactions: EIP-712 meta-transactions
- Upgradeable Fees: Future-proof fee management
- Secure Vault: Multi-signature fund management
- Role-Based Access: Granular permission system
The system is designed for:
- Security: Multiple layers of protection
- Modularity: Independent, composable components
- Upgradeability: Can evolve with needs
- Gas Efficiency: Optimized for on-chain operations
All components are thoroughly tested and ready for deployment on Ethereum-compatible networks.
- EIP-712: Typed Structured Data Hashing and Signing
- OpenZeppelin Contracts
- Foundry Testing Framework
- UUPS Proxy Pattern
- Constant Product Market Maker
Document Version: 1.0
Last Updated: 2024
Maintained By: Smart Modules Development Team