Liquidity Cache Technical Reference
Contract Overview​
The LiquidityCache contract is a comprehensive ERC4626-compliant vault that serves as both a liquidity pool and staking mechanism for the MCAP gaming platform. It extends OpenZeppelin's battle-tested contracts to provide secure, efficient liquidity management.
Contract Architecture​
Inheritance Hierarchy​
LiquidityCache
├── ILiquidityCache (Interface)
├── ERC4626 (Vault Standard)
│ └── ERC20 (Token Standard)
├── AccessControl (Role-based permissions)
├── Pausable (Emergency controls)
└── ReentrancyGuard (Attack protection)
Core Dependencies​
- OpenZeppelin Contracts v4.x+
- Solidity ^0.8.28
- ERC20 compatible underlying asset
Contract Specification​
Constructor​
constructor(IERC20 _asset)
Parameters:
_asset: The ERC20 token used as the underlying asset for the vault
Behavior:
- Initializes ERC4626 vault with the specified asset
- Sets share token name to "Liquidity Cache Share" (symbol: "LCS")
- Grants
DEFAULT_ADMIN_ROLEandPAYOUT_ROLEto deployer
Access Control​
Roles​
| Role | Keccak256 Hash | Description |
|---|---|---|
DEFAULT_ADMIN_ROLE | 0x00... | Can pause/unpause, grant/revoke roles |
PAYOUT_ROLE | keccak256("PAYOUT_ROLE") | Can remove assets for payouts |
Role Functions​
// Admin functions
function pause() external onlyRole(DEFAULT_ADMIN_ROLE)
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE)
// Payout functions
function removeAssets(uint256 amount, address receiver) external onlyPayoutRole
Core Functions​
Asset Management​
Adding Assets (Game Profits)​
function addAssets(uint256 amount, address caller) external nonReentrant
Purpose: Allows external parties to add assets to the pool without minting shares, typically used for depositing game profits.
Parameters:
amount: Amount of underlying assets to addcaller: Address that will provide the assets
Requirements:
- Caller must have approved the contract to spend
amounttokens - Contract must not be paused
- Must be non-reentrant
Events: Emits AssetsAdded(amount, caller)
Removing Assets (Payouts)​
function removeAssets(uint256 amount, address receiver) external onlyPayoutRole whenNotPaused nonReentrant
Purpose: Allows authorized parties to remove assets for payouts.
Parameters:
amount: Amount of assets to removereceiver: Address to receive the assets
Requirements:
- Caller must have
PAYOUT_ROLE - Contract must not be paused
- Sufficient assets must be available in pool
- Must be non-reentrant
Events: Emits AssetsRemoved(amount, receiver)
ERC4626 Functions (Overridden)​
All standard ERC4626 functions are overridden to add pausability and reentrancy protection:
Deposit​
function deposit(uint256 assets, address receiver)
public virtual override whenNotPaused nonReentrant
returns (uint256 shares)
Mint​
function mint(uint256 shares, address receiver)
public virtual override whenNotPaused nonReentrant
returns (uint256 assets)
Withdraw​
function withdraw(uint256 assets, address receiver, address owner)
public virtual override whenNotPaused nonReentrant
returns (uint256 shares)
Redeem​
function redeem(uint256 shares, address receiver, address owner)
public virtual override whenNotPaused nonReentrant
returns (uint256 assets)
Events​
Custom Events​
event AssetsAdded(uint256 amount, address indexed sender);
event AssetsRemoved(uint256 amount, address indexed receiver);
Inherited Events​
From ERC4626/ERC20:
Transfer(address indexed from, address indexed to, uint256 value)Approval(address indexed owner, address indexed spender, uint256 value)Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares)Withdraw(address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares)
From AccessControl:
RoleGranted(bytes32 indexed role, address indexed account, address indexed sender)RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender)
From Pausable:
Paused(address account)Unpaused(address account)
Integration Patterns​
For Gaming Platforms​
Adding Game Profits​
// 1. Approve tokens
IERC20(underlyingToken).approve(liquidityCacheAddress, profitAmount);
// 2. Add assets to pool
LiquidityCache(liquidityCacheAddress).addAssets(profitAmount, address(this));
Processing Payouts​
// Only accounts with PAYOUT_ROLE can call this
LiquidityCache(liquidityCacheAddress).removeAssets(payoutAmount, userAddress);
For Stakers​
Staking (Depositing)​
// 1. Approve tokens
IERC20(underlyingToken).approve(liquidityCacheAddress, stakeAmount);
// 2. Deposit and receive shares
uint256 shares = LiquidityCache(liquidityCacheAddress).deposit(stakeAmount, userAddress);
Unstaking (Withdrawing)​
// Option 1: Withdraw specific asset amount
uint256 sharesBurned = LiquidityCache(liquidityCacheAddress).withdraw(
assetAmount,
userAddress,
userAddress
);
// Option 2: Redeem specific share amount
uint256 assetsReceived = LiquidityCache(liquidityCacheAddress).redeem(
shareAmount,
userAddress,
userAddress
);
Mathematical Formulas​
Share Calculation​
When depositing assets:
shares = assets * totalSupply() / totalAssets()
When minting shares:
assets = shares * totalAssets() / totalSupply()
Exchange Rate​
exchangeRate = totalAssets() / totalSupply()
Yield Calculation​
For a user who deposited at time T1 and checks at time T2:
initialShareValue = totalAssets(T1) / totalSupply(T1)
currentShareValue = totalAssets(T2) / totalSupply(T2)
yield = (currentShareValue - initialShareValue) / initialShareValue
Gas Optimization​
Typical Gas Costs​
| Function | Estimated Gas |
|---|---|
deposit() | ~100,000 |
withdraw() | ~80,000 |
addAssets() | ~60,000 |
removeAssets() | ~40,000 |
Note: Gas costs vary based on network conditions and contract state
Optimization Tips​
- Batch Operations: Group multiple deposits/withdrawals
- Approve Once: Use maximum approval to avoid repeated approval transactions
- Monitor Gas Prices: Execute during low-traffic periods
- Preview Functions: Use view functions to calculate before executing
Security Considerations​
Built-in Protections​
- Reentrancy Guard: Prevents reentrancy attacks
- Access Control: Role-based function restrictions
- Pausability: Emergency stop mechanism
- Safe Transfers: Protection against malicious tokens
- Overflow Protection: Solidity 0.8+ built-in overflow checks
Best Practices for Integrators​
- Verify Contract Address: Always verify you're interacting with the correct contract
- Check Return Values: Verify function calls succeed
- Handle Edge Cases: Account for scenarios like insufficient liquidity
- Monitor Events: Listen to events for transaction confirmation
- Test Thoroughly: Test all integration paths
Potential Risks​
- Smart Contract Risk: Bugs in contract code
- Admin Key Risk: Compromise of admin private keys
- Liquidity Risk: Insufficient assets for withdrawals
- Market Risk: Underlying asset price volatility
Interface Definition​
interface ILiquidityCache {
function addAssets(uint256 amount, address caller) external;
function removeAssets(uint256 amount, address receiver) external;
// Standard ERC4626 interface
function asset() external view returns (address);
function totalAssets() external view returns (uint256);
function convertToShares(uint256 assets) external view returns (uint256);
function convertToAssets(uint256 shares) external view returns (uint256);
function maxDeposit(address receiver) external view returns (uint256);
function previewDeposit(uint256 assets) external view returns (uint256);
function deposit(uint256 assets, address receiver) external returns (uint256);
function maxMint(address receiver) external view returns (uint256);
function previewMint(uint256 shares) external view returns (uint256);
function mint(uint256 shares, address receiver) external returns (uint256);
function maxWithdraw(address owner) external view returns (uint256);
function previewWithdraw(uint256 assets) external view returns (uint256);
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256);
function maxRedeem(address owner) external view returns (uint256);
function previewRedeem(uint256 shares) external view returns (uint256);
function redeem(uint256 shares, address receiver, address owner) external returns (uint256);
}
Testing Framework​
Unit Test Categories​
-
Access Control Tests
- Role assignment and revocation
- Function restrictions
- Emergency pause functionality
-
Asset Management Tests
- Adding assets without minting shares
- Removing assets with proper authorization
- Asset calculation accuracy
-
ERC4626 Compliance Tests
- Deposit/withdrawal flows
- Share calculation accuracy
- Preview function consistency
-
Security Tests
- Reentrancy attack prevention
- Overflow/underflow protection
- Malicious token handling
Integration Test Scenarios​
-
Multi-user Staking
- Multiple users deposit at different times
- Profits added between deposits
- Verify share value calculations
-
Gaming Platform Integration
- Simulate game profit additions
- Test payout processing
- Verify liquidity management
-
Edge Cases
- Empty pool operations
- Maximum withdrawal attempts
- Pause/unpause scenarios
Deployment Guide​
Prerequisites​
- Underlying Asset Contract: Deployed ERC20 token
- Network Configuration: Target blockchain network
- Admin Wallet: Secure wallet for admin functions
- Gas Budget: Sufficient native tokens for deployment
Deployment Steps​
// 1. Deploy with constructor parameters
LiquidityCache liquidityCache = new LiquidityCache(
IERC20(underlyingTokenAddress)
);
// 2. Grant additional roles if needed
liquidityCache.grantRole(PAYOUT_ROLE, payoutOperatorAddress);
// 3. Verify contract on explorer
// 4. Initialize with initial liquidity if desired
Post-Deployment Verification​
- Contract Verification: Verify source code on block explorer
- Role Assignment: Confirm correct roles are assigned
- Functionality Testing: Test all major functions
- Integration Testing: Test with actual gaming platforms
Monitoring and Maintenance​
Key Metrics to Monitor​
- Total Assets: Track pool growth over time
- Total Shares: Monitor share supply changes
- Exchange Rate: Watch for significant changes
- Asset Addition Events: Monitor profit deposits
- Asset Removal Events: Track payout activity
Alerting Recommendations​
- Large Withdrawals: Alert on significant asset removals
- Exchange Rate Changes: Monitor for unusual fluctuations
- Failed Transactions: Track transaction failures
- Role Changes: Alert on admin role modifications
- Pause Events: Immediate notification of contract pausing
Maintenance Tasks​
- Regular Audits: Periodic security reviews
- Performance Monitoring: Track gas costs and optimization opportunities
- Upgrade Planning: Plan for potential contract upgrades
- Documentation Updates: Keep documentation current with changes