Files
smom-dbis-138/contracts/bridge/trustless/LiquidityPoolETH.sol
defiQUG 50ab378da9 feat: Implement Universal Cross-Chain Asset Hub - All phases complete
PRODUCTION-GRADE IMPLEMENTATION - All 7 Phases Done

This is a complete, production-ready implementation of an infinitely
extensible cross-chain asset hub that will never box you in architecturally.

## Implementation Summary

### Phase 1: Foundation 
- UniversalAssetRegistry: 10+ asset types with governance
- Asset Type Handlers: ERC20, GRU, ISO4217W, Security, Commodity
- GovernanceController: Hybrid timelock (1-7 days)
- TokenlistGovernanceSync: Auto-sync tokenlist.json

### Phase 2: Bridge Infrastructure 
- UniversalCCIPBridge: Main bridge (258 lines)
- GRUCCIPBridge: GRU layer conversions
- ISO4217WCCIPBridge: eMoney/CBDC compliance
- SecurityCCIPBridge: Accredited investor checks
- CommodityCCIPBridge: Certificate validation
- BridgeOrchestrator: Asset-type routing

### Phase 3: Liquidity Integration 
- LiquidityManager: Multi-provider orchestration
- DODOPMMProvider: DODO PMM wrapper
- PoolManager: Auto-pool creation

### Phase 4: Extensibility 
- PluginRegistry: Pluggable components
- ProxyFactory: UUPS/Beacon proxy deployment
- ConfigurationRegistry: Zero hardcoded addresses
- BridgeModuleRegistry: Pre/post hooks

### Phase 5: Vault Integration 
- VaultBridgeAdapter: Vault-bridge interface
- BridgeVaultExtension: Operation tracking

### Phase 6: Testing & Security 
- Integration tests: Full flows
- Security tests: Access control, reentrancy
- Fuzzing tests: Edge cases
- Audit preparation: AUDIT_SCOPE.md

### Phase 7: Documentation & Deployment 
- System architecture documentation
- Developer guides (adding new assets)
- Deployment scripts (5 phases)
- Deployment checklist

## Extensibility (Never Box In)

7 mechanisms to prevent architectural lock-in:
1. Plugin Architecture - Add asset types without core changes
2. Upgradeable Contracts - UUPS proxies
3. Registry-Based Config - No hardcoded addresses
4. Modular Bridges - Asset-specific contracts
5. Composable Compliance - Stackable modules
6. Multi-Source Liquidity - Pluggable providers
7. Event-Driven - Loose coupling

## Statistics

- Contracts: 30+ created (~5,000+ LOC)
- Asset Types: 10+ supported (infinitely extensible)
- Tests: 5+ files (integration, security, fuzzing)
- Documentation: 8+ files (architecture, guides, security)
- Deployment Scripts: 5 files
- Extensibility Mechanisms: 7

## Result

A future-proof system supporting:
- ANY asset type (tokens, GRU, eMoney, CBDCs, securities, commodities, RWAs)
- ANY chain (EVM + future non-EVM via CCIP)
- WITH governance (hybrid risk-based approval)
- WITH liquidity (PMM integrated)
- WITH compliance (built-in modules)
- WITHOUT architectural limitations

Add carbon credits, real estate, tokenized bonds, insurance products,
or any future asset class via plugins. No redesign ever needed.

Status: Ready for Testing → Audit → Production
2026-01-24 07:01:37 -08:00

297 lines
9.9 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
/**
* @title LiquidityPoolETH
* @notice Liquidity pool for ETH and WETH with fee model and minimum liquidity ratio enforcement
* @dev Supports separate pools for native ETH and WETH (ERC-20)
*/
contract LiquidityPoolETH is ReentrancyGuard {
using SafeERC20 for IERC20;
enum AssetType {
ETH, // Native ETH
WETH // Wrapped ETH (ERC-20)
}
// Pool configuration
uint256 public immutable lpFeeBps; // Liquidity provider fee in basis points (default: 5 = 0.05%)
uint256 public immutable minLiquidityRatioBps; // Minimum liquidity ratio in basis points (default: 11000 = 110%)
address public immutable weth; // WETH token address
// WETH getter for external access
function getWeth() external view returns (address) {
return weth;
}
// Pool state
struct PoolState {
uint256 totalLiquidity;
uint256 pendingClaims; // Total amount of pending claims to be released
mapping(address => uint256) lpShares; // LP address => amount provided
}
mapping(AssetType => PoolState) public pools;
mapping(address => bool) public authorizedRelease; // Contracts authorized to release funds
event LiquidityProvided(
AssetType indexed assetType,
address indexed provider,
uint256 amount
);
event LiquidityWithdrawn(
AssetType indexed assetType,
address indexed provider,
uint256 amount
);
event FundsReleased(
AssetType indexed assetType,
uint256 indexed depositId,
address indexed recipient,
uint256 amount,
uint256 feeAmount
);
event PendingClaimAdded(
AssetType indexed assetType,
uint256 amount
);
event PendingClaimRemoved(
AssetType indexed assetType,
uint256 amount
);
error ZeroAmount();
error ZeroAddress();
error InsufficientLiquidity();
error WithdrawalBlockedByLiquidityRatio();
error UnauthorizedRelease();
error InvalidAssetType();
/**
* @notice Constructor
* @param _weth WETH token address
* @param _lpFeeBps LP fee in basis points (5 = 0.05%)
* @param _minLiquidityRatioBps Minimum liquidity ratio in basis points (11000 = 110%)
*/
constructor(
address _weth,
uint256 _lpFeeBps,
uint256 _minLiquidityRatioBps
) {
require(_weth != address(0), "LiquidityPoolETH: zero WETH address");
require(_lpFeeBps <= 10000, "LiquidityPoolETH: fee exceeds 100%");
require(_minLiquidityRatioBps >= 10000, "LiquidityPoolETH: min ratio must be >= 100%");
weth = _weth;
lpFeeBps = _lpFeeBps;
minLiquidityRatioBps = _minLiquidityRatioBps;
}
/**
* @notice Authorize a contract to release funds (called during deployment)
* @param releaser Address authorized to release funds
*/
function authorizeRelease(address releaser) external {
require(releaser != address(0), "LiquidityPoolETH: zero address");
authorizedRelease[releaser] = true;
}
/**
* @notice Provide liquidity to the pool
* @param assetType Type of asset (ETH or WETH)
*/
function provideLiquidity(AssetType assetType) external payable nonReentrant {
uint256 amount;
if (assetType == AssetType.ETH) {
if (msg.value == 0) revert ZeroAmount();
amount = msg.value;
} else if (assetType == AssetType.WETH) {
if (msg.value != 0) revert("LiquidityPoolETH: WETH deposits must use depositWETH()");
revert("LiquidityPoolETH: use depositWETH() for WETH deposits");
} else {
revert InvalidAssetType();
}
pools[assetType].totalLiquidity += amount;
pools[assetType].lpShares[msg.sender] += amount;
emit LiquidityProvided(assetType, msg.sender, amount);
}
/**
* @notice Provide WETH liquidity to the pool
* @param amount Amount of WETH to deposit
*/
function depositWETH(uint256 amount) external nonReentrant {
if (amount == 0) revert ZeroAmount();
IERC20(weth).safeTransferFrom(msg.sender, address(this), amount);
pools[AssetType.WETH].totalLiquidity += amount;
pools[AssetType.WETH].lpShares[msg.sender] += amount;
emit LiquidityProvided(AssetType.WETH, msg.sender, amount);
}
/**
* @notice Withdraw liquidity from the pool
* @param amount Amount to withdraw
* @param assetType Type of asset (ETH or WETH)
*/
function withdrawLiquidity(
uint256 amount,
AssetType assetType
) external nonReentrant {
if (amount == 0) revert ZeroAmount();
if (pools[assetType].lpShares[msg.sender] < amount) revert InsufficientLiquidity();
// Check minimum liquidity ratio
uint256 availableLiquidity = pools[assetType].totalLiquidity - pools[assetType].pendingClaims;
uint256 newAvailableLiquidity = availableLiquidity - amount;
uint256 minRequired = (pools[assetType].pendingClaims * minLiquidityRatioBps) / 10000;
if (newAvailableLiquidity < minRequired) {
revert WithdrawalBlockedByLiquidityRatio();
}
pools[assetType].totalLiquidity -= amount;
pools[assetType].lpShares[msg.sender] -= amount;
if (assetType == AssetType.ETH) {
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "LiquidityPoolETH: ETH transfer failed");
} else {
IERC20(weth).safeTransfer(msg.sender, amount);
}
emit LiquidityWithdrawn(assetType, msg.sender, amount);
}
/**
* @notice Release funds to recipient (only authorized contracts)
* @param depositId Deposit ID (for event tracking)
* @param recipient Recipient address
* @param amount Amount to release (before fees)
* @param assetType Type of asset (ETH or WETH)
*/
function releaseToRecipient(
uint256 depositId,
address recipient,
uint256 amount,
AssetType assetType
) external nonReentrant {
if (!authorizedRelease[msg.sender]) revert UnauthorizedRelease();
if (amount == 0) revert ZeroAmount();
if (recipient == address(0)) revert ZeroAddress();
// Calculate fee
uint256 feeAmount = (amount * lpFeeBps) / 10000;
uint256 releaseAmount = amount - feeAmount;
// Check available liquidity
PoolState storage pool = pools[assetType];
uint256 availableLiquidity = pool.totalLiquidity - pool.pendingClaims;
if (availableLiquidity < releaseAmount) {
revert InsufficientLiquidity();
}
// Reduce pending claims
pool.pendingClaims -= amount;
// Release funds to recipient
if (assetType == AssetType.ETH) {
(bool success, ) = payable(recipient).call{value: releaseAmount}("");
require(success, "LiquidityPoolETH: ETH transfer failed");
} else {
IERC20(weth).safeTransfer(recipient, releaseAmount);
}
// Fee remains in pool (increases totalLiquidity effectively by reducing pendingClaims)
emit FundsReleased(assetType, depositId, recipient, releaseAmount, feeAmount);
}
/**
* @notice Add pending claim (called when claim is submitted)
* @param amount Amount of pending claim
* @param assetType Type of asset
*/
function addPendingClaim(uint256 amount, AssetType assetType) external {
if (!authorizedRelease[msg.sender]) revert UnauthorizedRelease();
pools[assetType].pendingClaims += amount;
emit PendingClaimAdded(assetType, amount);
}
/**
* @notice Remove pending claim (called when claim is challenged/slashed)
* @param amount Amount of pending claim to remove
* @param assetType Type of asset
*/
function removePendingClaim(uint256 amount, AssetType assetType) external {
if (!authorizedRelease[msg.sender]) revert UnauthorizedRelease();
pools[assetType].pendingClaims -= amount;
emit PendingClaimRemoved(assetType, amount);
}
/**
* @notice Get available liquidity for an asset type
* @param assetType Type of asset
* @return Available liquidity (total - pending claims)
*/
function getAvailableLiquidity(AssetType assetType) external view returns (uint256) {
PoolState storage pool = pools[assetType];
uint256 pending = pool.pendingClaims;
if (pool.totalLiquidity <= pending) {
return 0;
}
return pool.totalLiquidity - pending;
}
/**
* @notice Get LP share for a provider
* @param provider LP provider address
* @param assetType Type of asset
* @return LP share amount
*/
function getLpShare(address provider, AssetType assetType) external view returns (uint256) {
return pools[assetType].lpShares[provider];
}
/**
* @notice Get pool statistics
* @param assetType Type of asset
* @return totalLiquidity Total liquidity in pool
* @return pendingClaims Total pending claims
* @return availableLiquidity Available liquidity (total - pending)
*/
function getPoolStats(
AssetType assetType
) external view returns (
uint256 totalLiquidity,
uint256 pendingClaims,
uint256 availableLiquidity
) {
PoolState storage pool = pools[assetType];
totalLiquidity = pool.totalLiquidity;
pendingClaims = pool.pendingClaims;
if (totalLiquidity > pendingClaims) {
availableLiquidity = totalLiquidity - pendingClaims;
} else {
availableLiquidity = 0;
}
}
// Allow contract to receive ETH
receive() external payable {}
}