feat(coordinator-api): integrate dynamic pricing engine with GPU marketplace and add agent identity router

- Add DynamicPricingEngine and MarketDataCollector dependencies to GPU marketplace endpoints
- Implement dynamic pricing calculation for GPU registration with market_balance strategy
- Calculate real-time dynamic prices at booking time with confidence scores and pricing factors
- Enhance /marketplace/pricing/{model} endpoint with comprehensive dynamic pricing analysis
  - Add static vs dynamic price
This commit is contained in:
oib
2026-02-28 22:57:10 +01:00
parent 85ae21a568
commit 0e6c9eda72
83 changed files with 30189 additions and 134 deletions

View File

@@ -0,0 +1,492 @@
// 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/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @title AIServiceAMM
* @dev Automated Market Making protocol for AI service tokens
* @notice Enables creation of liquidity pools and automated trading for AI services
*/
contract AIServiceAMM is Ownable, ReentrancyGuard, Pausable {
using SafeERC20 for IERC20;
using Math for uint256;
// Constants
uint256 public constant MINIMUM_LIQUIDITY = 1000; // Minimum liquidity to prevent rounding errors
uint256 public constant BASIS_POINTS = 10000; // 100% in basis points
uint256 public constant MAX_FEE = 1000; // Maximum 10% fee
uint256 public constant FEE_PRECISION = 1000000; // Fee calculation precision
// State variables
uint256 public poolCounter;
uint256 public defaultFee = 30; // 0.3% default fee
uint256 public protocolFeePercentage = 20; // 20% of fees go to protocol
address public protocolFeeRecipient;
// Structs
struct LiquidityPool {
uint256 poolId;
address tokenA;
address tokenB;
uint256 reserveA;
uint256 reserveB;
uint256 totalLiquidity;
uint256 feePercentage; // Pool-specific fee in basis points
address lpToken; // LP token address for this pool
bool isActive;
uint256 created_at;
uint256 lastTradeTime;
uint256 volume24h;
uint256 fee24h;
}
struct LiquidityPosition {
uint256 poolId;
address provider;
uint256 liquidityAmount;
uint256 sharesOwned;
uint256 lastDepositTime;
uint256 unclaimedFees;
}
struct SwapParams {
uint256 poolId;
address tokenIn;
address tokenOut;
uint256 amountIn;
uint256 minAmountOut;
address recipient;
uint256 deadline;
}
struct PoolMetrics {
uint256 totalVolume;
uint256 totalFees;
uint256 tvl; // Total Value Locked
uint256 apr; // Annual Percentage Rate for liquidity providers
uint256 utilizationRate;
}
// Mappings
mapping(uint256 => LiquidityPool) public pools;
mapping(address => mapping(uint256 => LiquidityPosition)) public liquidityPositions;
mapping(address => uint256[]) public providerPools;
mapping(address => mapping(address => uint256)) public poolByTokenPair; // tokenA -> tokenB -> poolId
// Arrays
uint256[] public activePoolIds;
// Events
event PoolCreated(uint256 indexed poolId, address indexed tokenA, address indexed tokenB, uint256 fee);
event LiquidityAdded(uint256 indexed poolId, address indexed provider, uint256 amountA, uint256 amountB, uint256 liquidity);
event LiquidityRemoved(uint256 indexed poolId, address indexed provider, uint256 amountA, uint256 amountB, uint256 liquidity);
event SwapExecuted(uint256 indexed poolId, address indexed recipient, address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOut);
event FeesCollected(uint256 indexed poolId, uint256 protocolFees, uint256 lpFees);
event PoolUpdated(uint256 indexed poolId, uint256 reserveA, uint256 reserveB);
// Modifiers
modifier validPool(uint256 poolId) {
require(poolId > 0 && poolId <= poolCounter, "Invalid pool ID");
require(pools[poolId].isActive, "Pool not active");
_;
}
modifier validDeadline(uint256 deadline) {
require(block.timestamp <= deadline, "Transaction expired");
_;
}
modifier nonZeroAmount(uint256 amount) {
require(amount > 0, "Amount must be greater than 0");
_;
}
constructor(address _protocolFeeRecipient) {
protocolFeeRecipient = _protocolFeeRecipient;
}
/**
* @dev Creates a new liquidity pool for two tokens
* @param tokenA Address of the first token
* @param tokenB Address of the second token
* @param feePercentage Fee percentage in basis points
* @return poolId The ID of the created pool
*/
function createPool(
address tokenA,
address tokenB,
uint256 feePercentage
) external nonReentrant whenNotPaused returns (uint256) {
require(tokenA != tokenB, "Identical tokens");
require(tokenA != address(0) && tokenB != address(0), "Zero address");
require(feePercentage <= MAX_FEE, "Fee too high");
require(poolByTokenPair[tokenA][tokenB] == 0 && poolByTokenPair[tokenB][tokenA] == 0, "Pool exists");
// Ensure tokenA < tokenB for consistency
if (tokenA > tokenB) {
(tokenA, tokenB) = (tokenB, tokenA);
}
poolCounter++;
uint256 poolId = poolCounter;
pools[poolId] = LiquidityPool({
poolId: poolId,
tokenA: tokenA,
tokenB: tokenB,
reserveA: 0,
reserveB: 0,
totalLiquidity: 0,
feePercentage: feePercentage,
lpToken: address(this), // Simplified LP token representation
isActive: true,
created_at: block.timestamp,
lastTradeTime: 0,
volume24h: 0,
fee24h: 0
});
poolByTokenPair[tokenA][tokenB] = poolId;
poolByTokenPair[tokenB][tokenA] = poolId;
activePoolIds.push(poolId);
emit PoolCreated(poolId, tokenA, tokenB, feePercentage);
return poolId;
}
/**
* @dev Adds liquidity to a pool
* @param poolId The pool ID
* @param amountA Amount of tokenA to add
* @param amountB Amount of tokenB to add
* @param minAmountA Minimum amount of tokenA (slippage protection)
* @param minAmountB Minimum amount of tokenB (slippage protection)
* @return liquidityAmount The amount of liquidity tokens received
*/
function addLiquidity(
uint256 poolId,
uint256 amountA,
uint256 amountB,
uint256 minAmountA,
uint256 minAmountB
)
external
nonReentrant
whenNotPaused
validPool(poolId)
nonZeroAmount(amountA)
nonZeroAmount(amountB)
returns (uint256 liquidityAmount)
{
LiquidityPool storage pool = pools[poolId];
// Calculate optimal amounts based on current reserves
uint256 optimalAmountB = _calculateOptimalAmountB(poolId, amountA);
if (pool.reserveA == 0 && pool.reserveB == 0) {
// First liquidity provider - set initial prices
optimalAmountB = amountB;
} else {
require(amountB >= optimalAmountB, "Insufficient tokenB amount");
}
// Transfer tokens to contract
IERC20(pool.tokenA).safeTransferFrom(msg.sender, address(this), amountA);
IERC20(pool.tokenB).safeTransferFrom(msg.sender, address(this), amountB);
// Calculate liquidity to mint
if (pool.totalLiquidity == 0) {
liquidityAmount = Math.sqrt(amountA * amountB) - MINIMUM_LIQUIDITY;
pool.totalLiquidity += MINIMUM_LIQUIDITY; // Lock minimum liquidity
} else {
liquidityAmount = Math.min(
(amountA * pool.totalLiquidity) / pool.reserveA,
(amountB * pool.totalLiquidity) / pool.reserveB
);
}
require(liquidityAmount > 0, "Insufficient liquidity minted");
// Update pool reserves and liquidity
pool.reserveA += amountA;
pool.reserveB += amountB;
pool.totalLiquidity += liquidityAmount;
// Update or create liquidity position
LiquidityPosition storage position = liquidityPositions[msg.sender][poolId];
position.poolId = poolId;
position.provider = msg.sender;
position.liquidityAmount += liquidityAmount;
position.sharesOwned = (position.liquidityAmount * BASIS_POINTS) / pool.totalLiquidity;
position.lastDepositTime = block.timestamp;
// Add to provider's pool list if new
if (position.liquidityAmount == liquidityAmount) {
providerPools[msg.sender].push(poolId);
}
emit LiquidityAdded(poolId, msg.sender, amountA, amountB, liquidityAmount);
emit PoolUpdated(poolId, pool.reserveA, pool.reserveB);
}
/**
* @dev Removes liquidity from a pool
* @param poolId The pool ID
* @param liquidityAmount Amount of liquidity to remove
* @param minAmountA Minimum amount of tokenA to receive
* @param minAmountB Minimum amount of tokenB to receive
* @return amountA Amount of tokenA received
* @return amountB Amount of tokenB received
*/
function removeLiquidity(
uint256 poolId,
uint256 liquidityAmount,
uint256 minAmountA,
uint256 minAmountB
)
external
nonReentrant
whenNotPaused
validPool(poolId)
nonZeroAmount(liquidityAmount)
returns (uint256 amountA, uint256 amountB)
{
LiquidityPool storage pool = pools[poolId];
LiquidityPosition storage position = liquidityPositions[msg.sender][poolId];
require(position.liquidityAmount >= liquidityAmount, "Insufficient liquidity");
// Calculate amounts to receive
amountA = (liquidityAmount * pool.reserveA) / pool.totalLiquidity;
amountB = (liquidityAmount * pool.reserveB) / pool.totalLiquidity;
require(amountA >= minAmountA && amountB >= minAmountB, "Slippage protection");
// Update pool reserves and liquidity
pool.reserveA -= amountA;
pool.reserveB -= amountB;
pool.totalLiquidity -= liquidityAmount;
// Update position
position.liquidityAmount -= liquidityAmount;
position.sharesOwned = (position.liquidityAmount * BASIS_POINTS) / pool.totalLiquidity;
// Transfer tokens to user
IERC20(pool.tokenA).safeTransfer(msg.sender, amountA);
IERC20(pool.tokenB).safeTransfer(msg.sender, amountB);
emit LiquidityRemoved(poolId, msg.sender, amountA, amountB, liquidityAmount);
emit PoolUpdated(poolId, pool.reserveA, pool.reserveB);
}
/**
* @dev Executes a token swap
* @param params Swap parameters
* @return amountOut Amount of tokens received
*/
function swap(SwapParams calldata params)
external
nonReentrant
whenNotPaused
validPool(params.poolId)
validDeadline(params.deadline)
nonZeroAmount(params.amountIn)
returns (uint256 amountOut)
{
LiquidityPool storage pool = pools[params.poolId];
// Validate tokens
require(
params.tokenIn == pool.tokenA || params.tokenIn == pool.tokenB,
"Invalid input token"
);
require(
params.tokenOut == pool.tokenA || params.tokenOut == pool.tokenB,
"Invalid output token"
);
require(params.tokenIn != params.tokenOut, "Same token swap");
// Calculate output amount
amountOut = _calculateSwapOutput(params.poolId, params.amountIn, params.tokenIn);
require(amountOut >= params.minAmountOut, "Insufficient output amount");
// Transfer input tokens
IERC20(params.tokenIn).safeTransferFrom(msg.sender, address(this), params.amountIn);
// Update reserves
if (params.tokenIn == pool.tokenA) {
pool.reserveA += params.amountIn;
pool.reserveB -= amountOut;
} else {
pool.reserveB += params.amountIn;
pool.reserveA -= amountOut;
}
// Transfer output tokens
IERC20(params.tokenOut).safeTransfer(params.recipient, amountOut);
// Update pool metrics
pool.lastTradeTime = block.timestamp;
pool.volume24h += params.amountIn;
emit SwapExecuted(params.poolId, params.recipient, params.tokenIn, params.tokenOut, params.amountIn, amountOut);
emit PoolUpdated(params.poolId, pool.reserveA, pool.reserveB);
}
/**
* @dev Calculates the optimal amount of tokenB for adding liquidity
* @param poolId The pool ID
* @param amountA Amount of tokenA
* @return optimalAmountB Optimal amount of tokenB
*/
function calculateOptimalSwap(uint256 poolId, uint256 amountIn)
external
view
validPool(poolId)
returns (uint256 amountOut)
{
return _calculateSwapOutput(poolId, amountIn, pools[poolId].tokenA);
}
/**
* @dev Gets pool metrics
* @param poolId The pool ID
* @return metrics Pool metrics
*/
function getPoolMetrics(uint256 poolId)
external
view
validPool(poolId)
returns (PoolMetrics memory metrics)
{
LiquidityPool storage pool = pools[poolId];
uint256 totalValue = pool.reserveA + pool.reserveB; // Simplified TVL calculation
uint256 annualFees = pool.fee24h * 365; // Simplified APR calculation
metrics = PoolMetrics({
totalVolume: pool.volume24h,
totalFees: pool.fee24h,
tvl: totalValue,
apr: totalValue > 0 ? (annualFees * BASIS_POINTS) / totalValue : 0,
utilizationRate: pool.totalLiquidity > 0 ? ((pool.volume24h * BASIS_POINTS) / pool.totalLiquidity) : 0
});
}
/**
* @dev Gets the amount of liquidity tokens a user owns in a pool
* @param provider The liquidity provider address
* @param poolId The pool ID
* @return liquidityAmount Amount of liquidity tokens
*/
function getLiquidityAmount(address provider, uint256 poolId)
external
view
returns (uint256 liquidityAmount)
{
return liquidityPositions[provider][poolId].liquidityAmount;
}
// Internal functions
function _calculateOptimalAmountB(uint256 poolId, uint256 amountA) internal view returns (uint256) {
LiquidityPool storage pool = pools[poolId];
if (pool.reserveA == 0) return 0;
return (amountA * pool.reserveB) / pool.reserveA;
}
function _calculateSwapOutput(uint256 poolId, uint256 amountIn, address tokenIn)
internal
view
returns (uint256 amountOut)
{
LiquidityPool storage pool = pools[poolId];
uint256 reserveIn;
uint256 reserveOut;
if (tokenIn == pool.tokenA) {
reserveIn = pool.reserveA;
reserveOut = pool.reserveB;
} else {
reserveIn = pool.reserveB;
reserveOut = pool.reserveA;
}
// Apply fee
uint256 feeAmount = (amountIn * pool.feePercentage) / BASIS_POINTS;
uint256 amountInAfterFee = amountIn - feeAmount;
// Calculate output using constant product formula
amountOut = (amountInAfterFee * reserveOut) / (reserveIn + amountInAfterFee);
// Ensure minimum output
require(amountOut > 0, "Insufficient output amount");
require(reserveOut > amountOut, "Insufficient liquidity");
}
function _collectFees(uint256 poolId) internal {
LiquidityPool storage pool = pools[poolId];
if (pool.fee24h == 0) return;
uint256 protocolFees = (pool.fee24h * protocolFeePercentage) / BASIS_POINTS;
uint256 lpFees = pool.fee24h - protocolFees;
// Distribute fees to liquidity providers
if (lpFees > 0 && pool.totalLiquidity > 0) {
for (uint i = 0; i < activePoolIds.length; i++) {
uint256 currentPoolId = activePoolIds[i];
if (currentPoolId == poolId) {
// Simplified fee distribution
// In production, this would be more sophisticated
break;
}
}
}
emit FeesCollected(poolId, protocolFees, lpFees);
pool.fee24h = 0;
}
// Admin functions
function setProtocolFeeRecipient(address newRecipient) external onlyOwner {
require(newRecipient != address(0), "Invalid address");
protocolFeeRecipient = newRecipient;
}
function setProtocolFeePercentage(uint256 newPercentage) external onlyOwner {
require(newPercentage <= BASIS_POINTS, "Invalid percentage");
protocolFeePercentage = newPercentage;
}
function setDefaultFee(uint256 newFee) external onlyOwner {
require(newFee <= MAX_FEE, "Fee too high");
defaultFee = newFee;
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
// Emergency functions
function emergencyWithdraw(address token, uint256 amount) external onlyOwner {
IERC20(token).safeTransfer(owner(), amount);
}
function emergencyPause() external onlyOwner {
_pause();
}
}

View File

@@ -0,0 +1,562 @@
// 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/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @title AgentPortfolioManager
* @dev Advanced portfolio management protocol for autonomous AI agents
* @notice Enables agents to manage portfolios, execute trades, and automate rebalancing
*/
contract AgentPortfolioManager is Ownable, ReentrancyGuard, Pausable {
using SafeERC20 for IERC20;
using Math for uint256;
// State variables
IERC20 public aitbcToken;
uint256 public portfolioCounter;
uint256 public strategyCounter;
uint256 public rebalanceThreshold = 500; // 5% threshold for rebalancing (in basis points)
uint256 public maxRiskScore = 10000; // Maximum risk score (100% in basis points)
uint256 public platformFeePercentage = 50; // 0.5% platform fee
// Enums
enum StrategyType { CONSERVATIVE, BALANCED, AGGRESSIVE, DYNAMIC }
enum TradeStatus { PENDING, EXECUTED, FAILED, CANCELLED }
enum RiskLevel { LOW, MEDIUM, HIGH, CRITICAL }
// Structs
struct Asset {
address tokenAddress;
string symbol;
bool isActive;
uint256 decimals;
uint256 priceOracle; // Price in USD (scaled by 1e8)
}
struct AgentPortfolio {
uint256 portfolioId;
address agentAddress;
mapping(string => uint256) assetBalances; // Token symbol -> balance (in wei)
mapping(string => uint256) targetAllocations; // Token symbol -> target allocation (in basis points)
uint256 totalValue; // Total portfolio value in USD (scaled by 1e8)
uint256 riskScore; // Risk score (0-10000 basis points)
uint256 lastRebalance;
StrategyType strategy;
bool isActive;
uint256 created_at;
}
struct TradingStrategy {
uint256 strategyId;
string name;
StrategyType strategyType;
mapping(string => uint256) targetAllocations; // Token symbol -> target allocation
uint256 maxDrawdown;
uint256 rebalanceFrequency;
bool isActive;
}
struct Trade {
uint256 tradeId;
uint256 portfolioId;
string sellToken;
string buyToken;
uint256 sellAmount;
uint256 buyAmount;
uint256 price;
TradeStatus status;
uint256 timestamp;
bytes32 executionHash;
}
struct RiskMetrics {
uint256 volatility;
uint256 maxDrawdown;
uint256 sharpeRatio;
uint256 beta;
uint256 alpha;
uint256 var95; // Value at Risk at 95% confidence
RiskLevel riskLevel;
}
// Mappings
mapping(uint256 => AgentPortfolio) public portfolios;
mapping(address => uint256) public agentPortfolio;
mapping(string => Asset) public supportedAssets;
mapping(uint256 => TradingStrategy) public strategies;
mapping(uint256 => Trade) public trades;
mapping(uint256 => uint256[]) public portfolioTrades;
mapping(uint256 => RiskMetrics) public portfolioRiskMetrics;
// Arrays
address[] public supportedAssetAddresses;
string[] public supportedAssetSymbols;
uint256[] public activePortfolioIds;
// Events
event PortfolioCreated(uint256 indexed portfolioId, address indexed agent, StrategyType strategy);
event PortfolioRebalanced(uint256 indexed portfolioId, uint256 totalValue, uint256 riskScore);
event TradeExecuted(uint256 indexed tradeId, uint256 indexed portfolioId, string sellToken, string buyToken, uint256 amount);
event StrategyCreated(uint256 indexed strategyId, string name, StrategyType strategyType);
event RiskAssessment(uint256 indexed portfolioId, uint256 riskScore, RiskLevel riskLevel);
event AssetAdded(address indexed tokenAddress, string symbol, uint256 price);
// Modifiers
modifier onlyPortfolioOwner(uint256 portfolioId) {
require(portfolios[portfolioId].agentAddress == msg.sender, "Not portfolio owner");
_;
}
modifier validAsset(string memory symbol) {
require(supportedAssets[symbol].isActive, "Asset not supported");
_;
}
modifier validPortfolio(uint256 portfolioId) {
require(portfolioId > 0 && portfolioId <= portfolioCounter, "Invalid portfolio ID");
require(portfolios[portfolioId].isActive, "Portfolio not active");
_;
}
constructor(address _aitbcToken) {
aitbcToken = IERC20(_aitbcToken);
// Initialize with basic assets
_addAsset(address(aitbcToken), "AITBC", 18, 100000000); // $1.00 USD
}
/**
* @dev Creates a new portfolio for an agent
* @param agentAddress The address of the agent
* @param strategyId The strategy ID to use
* @return portfolioId The ID of the created portfolio
*/
function createPortfolio(address agentAddress, uint256 strategyId)
external
nonReentrant
whenNotPaused
returns (uint256)
{
require(agentAddress != address(0), "Invalid agent address");
require(strategies[strategyId].isActive, "Strategy not active");
require(agentPortfolio[agentAddress] == 0, "Portfolio already exists");
portfolioCounter++;
uint256 portfolioId = portfolioCounter;
AgentPortfolio storage portfolio = portfolios[portfolioId];
portfolio.portfolioId = portfolioId;
portfolio.agentAddress = agentAddress;
portfolio.strategy = strategies[strategyId].strategyType;
portfolio.lastRebalance = block.timestamp;
portfolio.isActive = true;
portfolio.created_at = block.timestamp;
// Copy target allocations from strategy
TradingStrategy storage strategy = strategies[strategyId];
for (uint i = 0; i < supportedAssetSymbols.length; i++) {
string memory symbol = supportedAssetSymbols[i];
portfolio.targetAllocations[symbol] = strategy.targetAllocations[symbol];
}
agentPortfolio[agentAddress] = portfolioId;
activePortfolioIds.push(portfolioId);
emit PortfolioCreated(portfolioId, agentAddress, portfolio.strategy);
return portfolioId;
}
/**
* @dev Executes a trade within a portfolio
* @param portfolioId The portfolio ID
* @param sellToken The token symbol to sell
* @param buyToken The token symbol to buy
* @param sellAmount The amount to sell (in wei)
* @param minBuyAmount The minimum amount to buy (slippage protection)
* @return tradeId The ID of the executed trade
*/
function executeTrade(
uint256 portfolioId,
string memory sellToken,
string memory buyToken,
uint256 sellAmount,
uint256 minBuyAmount
)
external
nonReentrant
whenNotPaused
validPortfolio(portfolioId)
onlyPortfolioOwner(portfolioId)
validAsset(sellToken)
validAsset(buyToken)
returns (uint256)
{
require(sellAmount > 0, "Invalid sell amount");
require(portfolios[portfolioId].assetBalances[sellToken] >= sellAmount, "Insufficient balance");
// Calculate buy amount based on current prices
uint256 sellPrice = supportedAssets[sellToken].priceOracle;
uint256 buyPrice = supportedAssets[buyToken].priceOracle;
uint256 sellValue = (sellAmount * sellPrice) / (10 ** supportedAssets[sellToken].decimals);
uint256 buyAmount = (sellValue * (10 ** supportedAssets[buyToken].decimals)) / buyPrice;
require(buyAmount >= minBuyAmount, "Insufficient buy amount (slippage)");
// Update portfolio balances
portfolios[portfolioId].assetBalances[sellToken] -= sellAmount;
portfolios[portfolioId].assetBalances[buyToken] += buyAmount;
// Create trade record
uint256 tradeId = _createTradeRecord(portfolioId, sellToken, buyToken, sellAmount, buyAmount, buyPrice);
// Update portfolio value and risk
_updatePortfolioValue(portfolioId);
_calculateRiskScore(portfolioId);
emit TradeExecuted(tradeId, portfolioId, sellToken, buyToken, sellAmount);
return tradeId;
}
/**
* @dev Automatically rebalances a portfolio based on target allocations
* @param portfolioId The portfolio ID to rebalance
* @return success Whether the rebalancing was successful
*/
function rebalancePortfolio(uint256 portfolioId)
external
nonReentrant
whenNotPaused
validPortfolio(portfolioId)
returns (bool success)
{
AgentPortfolio storage portfolio = portfolios[portfolioId];
// Check if rebalancing is needed
if (!_needsRebalancing(portfolioId)) {
return false;
}
// Get current allocations
mapping(string => uint256) storage currentAllocations = portfolio.assetBalances;
uint256 totalValue = portfolio.totalValue;
// Calculate required trades
for (uint i = 0; i < supportedAssetSymbols.length; i++) {
string memory symbol = supportedAssetSymbols[i];
uint256 targetAllocation = portfolio.targetAllocations[symbol];
uint256 targetValue = (totalValue * targetAllocation) / 10000;
uint256 currentBalance = currentAllocations[symbol];
uint256 currentValue = (currentBalance * supportedAssets[symbol].priceOracle) /
(10 ** supportedAssets[symbol].decimals);
if (currentValue > targetValue) {
// Sell excess
uint256 excessValue = currentValue - targetValue;
uint256 sellAmount = (excessValue * (10 ** supportedAssets[symbol].decimals)) /
supportedAssets[symbol].priceOracle;
// Find underweight asset to buy
for (uint j = 0; j < supportedAssetSymbols.length; j++) {
string memory buySymbol = supportedAssetSymbols[j];
uint256 buyTargetValue = (totalValue * portfolio.targetAllocations[buySymbol]) / 10000;
uint256 buyCurrentValue = (currentAllocations[buySymbol] * supportedAssets[buySymbol].priceOracle) /
(10 ** supportedAssets[buySymbol].decimals);
if (buyCurrentValue < buyTargetValue) {
// Execute rebalancing trade
_executeRebalancingTrade(portfolioId, symbol, buySymbol, sellAmount);
break;
}
}
}
}
portfolio.lastRebalance = block.timestamp;
_calculateRiskScore(portfolioId);
emit PortfolioRebalanced(portfolioId, portfolio.totalValue, portfolio.riskScore);
return true;
}
/**
* @dev Creates a new trading strategy
* @param name The strategy name
* @param strategyType The strategy type
* @param allocations Target allocations for each supported asset
* @param maxDrawdown Maximum allowed drawdown
* @param rebalanceFrequency Rebalancing frequency in seconds
* @return strategyId The ID of the created strategy
*/
function createStrategy(
string memory name,
StrategyType strategyType,
mapping(string => uint256) storage allocations,
uint256 maxDrawdown,
uint256 rebalanceFrequency
)
external
onlyOwner
returns (uint256)
{
strategyCounter++;
uint256 strategyId = strategyCounter;
TradingStrategy storage strategy = strategies[strategyId];
strategy.strategyId = strategyId;
strategy.name = name;
strategy.strategyType = strategyType;
strategy.maxDrawdown = maxDrawdown;
strategy.rebalanceFrequency = rebalanceFrequency;
strategy.isActive = true;
// Copy allocations
uint256 totalAllocation = 0;
for (uint i = 0; i < supportedAssetSymbols.length; i++) {
string memory symbol = supportedAssetSymbols[i];
strategy.targetAllocations[symbol] = allocations[symbol];
totalAllocation += allocations[symbol];
}
require(totalAllocation == 10000, "Allocations must sum to 100%");
emit StrategyCreated(strategyId, name, strategyType);
return strategyId;
}
/**
* @dev Calculates the risk score for a portfolio
* @param portfolioId The portfolio ID
* @return riskScore The calculated risk score (0-10000)
*/
function calculateRiskScore(uint256 portfolioId)
external
view
returns (uint256 riskScore)
{
if (!portfolios[portfolioId].isActive) {
return 0;
}
AgentPortfolio storage portfolio = portfolios[portfolioId];
uint256 totalRisk = 0;
// Calculate risk based on asset volatility and allocation
for (uint i = 0; i < supportedAssetSymbols.length; i++) {
string memory symbol = supportedAssetSymbols[i];
uint256 balance = portfolio.assetBalances[symbol];
if (balance > 0) {
uint256 value = (balance * supportedAssets[symbol].priceOracle) /
(10 ** supportedAssets[symbol].decimals);
uint256 allocation = (value * 10000) / portfolio.totalValue;
// Risk contribution based on asset type and allocation
uint256 assetRisk = _getAssetRisk(symbol);
totalRisk += (allocation * assetRisk) / 10000;
}
}
// Adjust for strategy type
uint256 strategyMultiplier = _getStrategyRiskMultiplier(portfolio.strategy);
riskScore = (totalRisk * strategyMultiplier) / 10000;
return Math.min(riskScore, maxRiskScore);
}
/**
* @dev Gets the current portfolio value in USD
* @param portfolioId The portfolio ID
* @return totalValue The total portfolio value (scaled by 1e8)
*/
function getPortfolioValue(uint256 portfolioId)
external
view
returns (uint256 totalValue)
{
if (!portfolios[portfolioId].isActive) {
return 0;
}
AgentPortfolio storage portfolio = portfolios[portfolioId];
totalValue = 0;
for (uint i = 0; i < supportedAssetSymbols.length; i++) {
string memory symbol = supportedAssetSymbols[i];
uint256 balance = portfolio.assetBalances[symbol];
if (balance > 0) {
uint256 value = (balance * supportedAssets[symbol].priceOracle) /
(10 ** supportedAssets[symbol].decimals);
totalValue += value;
}
}
}
// Internal functions
function _addAsset(address tokenAddress, string memory symbol, uint256 decimals, uint256 price) internal {
supportedAssets[symbol] = Asset({
tokenAddress: tokenAddress,
symbol: symbol,
isActive: true,
decimals: decimals,
priceOracle: price
});
supportedAssetAddresses.push(tokenAddress);
supportedAssetSymbols.push(symbol);
emit AssetAdded(tokenAddress, symbol, price);
}
function _createTradeRecord(
uint256 portfolioId,
string memory sellToken,
string memory buyToken,
uint256 sellAmount,
uint256 buyAmount,
uint256 price
) internal returns (uint256) {
uint256 tradeId = portfolioTrades[portfolioId].length + 1;
trades[tradeId] = Trade({
tradeId: tradeId,
portfolioId: portfolioId,
sellToken: sellToken,
buyToken: buyToken,
sellAmount: sellAmount,
buyAmount: buyAmount,
price: price,
status: TradeStatus.EXECUTED,
timestamp: block.timestamp,
executionHash: keccak256(abi.encodePacked(portfolioId, sellToken, buyToken, sellAmount, block.timestamp))
});
portfolioTrades[portfolioId].push(tradeId);
return tradeId;
}
function _updatePortfolioValue(uint256 portfolioId) internal {
uint256 totalValue = 0;
AgentPortfolio storage portfolio = portfolios[portfolioId];
for (uint i = 0; i < supportedAssetSymbols.length; i++) {
string memory symbol = supportedAssetSymbols[i];
uint256 balance = portfolio.assetBalances[symbol];
if (balance > 0) {
uint256 value = (balance * supportedAssets[symbol].priceOracle) /
(10 ** supportedAssets[symbol].decimals);
totalValue += value;
}
}
portfolio.totalValue = totalValue;
}
function _calculateRiskScore(uint256 portfolioId) internal {
uint256 riskScore = this.calculateRiskScore(portfolioId);
portfolios[portfolioId].riskScore = riskScore;
// Determine risk level
RiskLevel riskLevel;
if (riskScore < 2500) riskLevel = RiskLevel.LOW;
else if (riskScore < 5000) riskLevel = RiskLevel.MEDIUM;
else if (riskScore < 7500) riskLevel = RiskLevel.HIGH;
else riskLevel = RiskLevel.CRITICAL;
portfolioRiskMetrics[portfolioId].riskLevel = riskLevel;
emit RiskAssessment(portfolioId, riskScore, riskLevel);
}
function _needsRebalancing(uint256 portfolioId) internal view returns (bool) {
AgentPortfolio storage portfolio = portfolios[portfolioId];
// Check time-based rebalancing
if (block.timestamp - portfolio.lastRebalance > strategies[1].rebalanceFrequency) {
return true;
}
// Check threshold-based rebalancing
uint256 totalValue = portfolio.totalValue;
for (uint i = 0; i < supportedAssetSymbols.length; i++) {
string memory symbol = supportedAssetSymbols[i];
uint256 targetAllocation = portfolio.targetAllocations[symbol];
uint256 targetValue = (totalValue * targetAllocation) / 10000;
uint256 currentBalance = portfolio.assetBalances[symbol];
uint256 currentValue = (currentBalance * supportedAssets[symbol].priceOracle) /
(10 ** supportedAssets[symbol].decimals);
uint256 deviation = currentValue > targetValue ?
((currentValue - targetValue) * 10000) / targetValue :
((targetValue - currentValue) * 10000) / targetValue;
if (deviation > rebalanceThreshold) {
return true;
}
}
return false;
}
function _executeRebalancingTrade(
uint256 portfolioId,
string memory sellToken,
string memory buyToken,
uint256 sellAmount
) internal {
// Calculate buy amount
uint256 sellPrice = supportedAssets[sellToken].priceOracle;
uint256 buyPrice = supportedAssets[buyToken].priceOracle;
uint256 sellValue = (sellAmount * sellPrice) / (10 ** supportedAssets[sellToken].decimals);
uint256 buyAmount = (sellValue * (10 ** supportedAssets[buyToken].decimals)) / buyPrice;
// Update balances
portfolios[portfolioId].assetBalances[sellToken] -= sellAmount;
portfolios[portfolioId].assetBalances[buyToken] += buyAmount;
// Create trade record
_createTradeRecord(portfolioId, sellToken, buyToken, sellAmount, buyAmount, buyPrice);
}
function _getAssetRisk(string memory symbol) internal pure returns (uint256) {
// Return risk score for different asset types (in basis points)
if (keccak256(bytes(symbol)) == keccak256(bytes("AITBC"))) return 3000; // Medium risk
if (keccak256(bytes(symbol)) == keccak256(bytes("USDC"))) return 500; // Low risk
if (keccak256(bytes(symbol)) == keccak256(bytes("ETH"))) return 6000; // High risk
return 4000; // Default medium risk
}
function _getStrategyRiskMultiplier(StrategyType strategyType) internal pure returns (uint256) {
if (strategyType == StrategyType.CONSERVATIVE) return 5000; // 0.5x
if (strategyType == StrategyType.BALANCED) return 10000; // 1.0x
if (strategyType == StrategyType.AGGRESSIVE) return 15000; // 1.5x
if (strategyType == StrategyType.DYNAMIC) return 12000; // 1.2x
return 10000; // Default 1.0x
}
// Admin functions
function updateAssetPrice(string memory symbol, uint256 newPrice) external onlyOwner {
require(supportedAssets[symbol].isActive, "Asset not supported");
supportedAssets[symbol].priceOracle = newPrice;
}
function setRebalanceThreshold(uint256 newThreshold) external onlyOwner {
require(newThreshold <= 10000, "Invalid threshold");
rebalanceThreshold = newThreshold;
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
}

View File

@@ -0,0 +1,565 @@
// 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/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
/**
* @title CrossChainBridge
* @dev Secure cross-chain asset transfer protocol with ZK proof validation
* @notice Enables bridging of assets between different blockchain networks
*/
contract CrossChainBridge is Ownable, ReentrancyGuard, Pausable {
using SafeERC20 for IERC20;
using ECDSA for bytes32;
// Constants
uint256 public constant BRIDGE_FEE_PERCENTAGE = 50; // 0.5% bridge fee
uint256 public constant BASIS_POINTS = 10000;
uint256 public constant MAX_FEE = 500; // Maximum 5% fee
uint256 public constant MIN_CONFIRMATIONS = 3;
uint256 public constant BRIDGE_TIMEOUT = 24 hours;
uint256 public constant MAX_BRIDGE_AMOUNT = 1000000 * 1e18; // Max 1M tokens per bridge
// Enums
enum BridgeStatus { PENDING, CONFIRMED, COMPLETED, FAILED, CANCELLED }
enum ChainType { ETHEREUM, POLYGON, BSC, ARBITRUM, OPTIMISM, AVALANCHE }
// Structs
struct BridgeRequest {
uint256 requestId;
address sourceToken;
address targetToken;
uint256 amount;
uint256 sourceChainId;
uint256 targetChainId;
address recipient;
address sender;
uint256 fee;
bytes32 lockTxHash;
bytes32 unlockTxHash;
BridgeStatus status;
uint256 createdAt;
uint256 confirmedAt;
uint256 completedAt;
uint256 confirmations;
mapping(address => bool) hasConfirmed;
}
struct SupportedToken {
address tokenAddress;
bool isActive;
uint256 bridgeLimit;
uint256 feePercentage;
bool requiresWhitelist;
}
struct ChainConfig {
uint256 chainId;
ChainType chainType;
string name;
bool isActive;
address bridgeContract;
uint256 minConfirmations;
uint256 avgBlockTime;
}
struct Validator {
address validatorAddress;
bool isActive;
uint256 weight;
uint256 lastValidation;
uint256 totalValidations;
}
// State variables
uint256 public requestCounter;
uint256 public totalBridgedAmount;
uint256 public totalFeesCollected;
address public feeRecipient;
bytes32 public merkleRoot;
// Mappings
mapping(uint256 => BridgeRequest) public bridgeRequests;
mapping(address => SupportedToken) public supportedTokens;
mapping(uint256 => ChainConfig) public supportedChains;
mapping(address => Validator) public validators;
mapping(uint256 => address[]) public chainValidators;
mapping(bytes32 => bool) public processedTxHashes;
mapping(address => uint256[]) public userBridgeHistory;
// Arrays
address[] public activeValidators;
uint256[] public supportedChainIds;
address[] public supportedTokenAddresses;
// Events
event BridgeInitiated(
uint256 indexed requestId,
address indexed sender,
address indexed recipient,
address sourceToken,
uint256 amount,
uint256 sourceChainId,
uint256 targetChainId
);
event BridgeConfirmed(uint256 indexed requestId, address indexed validator, bytes32 lockTxHash);
event BridgeCompleted(uint256 indexed requestId, bytes32 unlockTxHash);
event BridgeFailed(uint256 indexed requestId, string reason);
event BridgeCancelled(uint256 indexed requestId);
event ValidatorAdded(address indexed validator, uint256 weight);
event ValidatorRemoved(address indexed validator);
event TokenSupported(address indexed token, uint256 bridgeLimit, uint256 fee);
event ChainSupported(uint256 indexed chainId, string name, ChainType chainType);
// Modifiers
modifier onlyActiveValidator() {
require(validators[msg.sender].isActive, "Not an active validator");
_;
}
modifier validRequest(uint256 requestId) {
require(requestId > 0 && requestId <= requestCounter, "Invalid request ID");
_;
}
modifier supportedToken(address token) {
require(supportedTokens[token].isActive, "Token not supported");
_;
}
modifier supportedChain(uint256 chainId) {
require(supportedChains[chainId].isActive, "Chain not supported");
_;
}
modifier withinBridgeLimit(address token, uint256 amount) {
require(amount <= supportedTokens[token].bridgeLimit, "Amount exceeds bridge limit");
_;
}
constructor(address _feeRecipient) {
feeRecipient = _feeRecipient;
// Initialize with Ethereum mainnet
_addChain(1, ChainType.ETHEREUM, "Ethereum", true, address(0), 12, 12);
}
/**
* @dev Initiates a cross-chain bridge transfer
* @param sourceToken Address of the source token
* @param targetToken Address of the target token
* @param amount Amount to bridge
* @param targetChainId Target chain ID
* @param recipient Recipient address on target chain
* @return requestId The bridge request ID
*/
function initiateBridge(
address sourceToken,
address targetToken,
uint256 amount,
uint256 targetChainId,
address recipient
)
external
nonReentrant
whenNotPaused
supportedToken(sourceToken)
supportedChain(targetChainId)
withinBridgeLimit(sourceToken, amount)
returns (uint256 requestId)
{
require(sourceToken != address(0), "Invalid source token");
require(targetToken != address(0), "Invalid target token");
require(recipient != address(0), "Invalid recipient");
require(amount > 0, "Amount must be greater than 0");
require(targetChainId != block.chainid, "Same chain bridging not allowed");
// Calculate bridge fee
uint256 fee = (amount * supportedTokens[sourceToken].feePercentage) / BASIS_POINTS;
uint256 totalAmount = amount + fee;
// Transfer tokens to contract
IERC20(sourceToken).safeTransferFrom(msg.sender, address(this), totalAmount);
// Create bridge request
requestCounter++;
requestId = requestCounter;
BridgeRequest storage request = bridgeRequests[requestId];
request.requestId = requestId;
request.sourceToken = sourceToken;
request.targetToken = targetToken;
request.amount = amount;
request.sourceChainId = block.chainid;
request.targetChainId = targetChainId;
request.recipient = recipient;
request.sender = msg.sender;
request.fee = fee;
request.status = BridgeStatus.PENDING;
request.createdAt = block.timestamp;
// Update statistics
totalBridgedAmount += amount;
totalFeesCollected += fee;
userBridgeHistory[msg.sender].push(requestId);
// Transfer fee to fee recipient
if (fee > 0) {
IERC20(sourceToken).safeTransfer(feeRecipient, fee);
}
emit BridgeInitiated(
requestId,
msg.sender,
recipient,
sourceToken,
amount,
block.chainid,
targetChainId
);
return requestId;
}
/**
* @dev Confirms a bridge request by a validator
* @param requestId The bridge request ID
* @param lockTxHash The transaction hash of the lock transaction
* @param signature Validator signature
*/
function confirmBridge(
uint256 requestId,
bytes32 lockTxHash,
bytes memory signature
)
external
onlyActiveValidator
validRequest(requestId)
{
BridgeRequest storage request = bridgeRequests[requestId];
require(request.status == BridgeStatus.PENDING, "Request not pending");
require(!request.hasConfirmed[msg.sender], "Already confirmed");
require(block.timestamp <= request.createdAt + BRIDGE_TIMEOUT, "Bridge request expired");
// Verify signature
bytes32 messageHash = keccak256(abi.encodePacked(requestId, lockTxHash, block.chainid));
require(_verifySignature(messageHash, signature, msg.sender), "Invalid signature");
// Record confirmation
request.hasConfirmed[msg.sender] = true;
request.confirmations++;
request.lockTxHash = lockTxHash;
validators[msg.sender].lastValidation = block.timestamp;
validators[msg.sender].totalValidations++;
// Check if we have enough confirmations
uint256 requiredConfirmations = _getRequiredConfirmations(request.sourceChainId);
if (request.confirmations >= requiredConfirmations) {
request.status = BridgeStatus.CONFIRMED;
request.confirmedAt = block.timestamp;
}
emit BridgeConfirmed(requestId, msg.sender, lockTxHash);
}
/**
* @dev Completes a bridge request on the target chain
* @param requestId The bridge request ID
* @param unlockTxHash The transaction hash of the unlock transaction
* @param proof Merkle proof for verification
*/
function completeBridge(
uint256 requestId,
bytes32 unlockTxHash,
bytes32[] calldata proof
)
external
nonReentrant
whenNotPaused
validRequest(requestId)
{
BridgeRequest storage request = bridgeRequests[requestId];
require(request.status == BridgeStatus.CONFIRMED, "Request not confirmed");
require(block.chainid == request.targetChainId, "Wrong chain");
require(!processedTxHashes[unlockTxHash], "Transaction already processed");
// Verify Merkle proof
bytes32 leaf = keccak256(abi.encodePacked(requestId, request.recipient, request.amount));
require(MerkleProof.verify(proof, merkleRoot, leaf), "Invalid Merkle proof");
// Mark transaction as processed
processedTxHashes[unlockTxHash] = true;
request.unlockTxHash = unlockTxHash;
request.status = BridgeStatus.COMPLETED;
request.completedAt = block.timestamp;
// Transfer tokens to recipient
IERC20(request.targetToken).safeTransfer(request.recipient, request.amount);
emit BridgeCompleted(requestId, unlockTxHash);
}
/**
* @dev Cancels a bridge request
* @param requestId The bridge request ID
* @param reason Reason for cancellation
*/
function cancelBridge(uint256 requestId, string memory reason)
external
nonReentrant
validRequest(requestId)
{
BridgeRequest storage request = bridgeRequests[requestId];
require(request.status == BridgeStatus.PENDING, "Request not pending");
require(
msg.sender == request.sender || msg.sender == owner(),
"Not authorized to cancel"
);
require(block.timestamp > request.createdAt + BRIDGE_TIMEOUT, "Bridge not expired");
// Refund tokens to sender
uint256 refundAmount = request.amount + request.fee;
IERC20(request.sourceToken).safeTransfer(request.sender, refundAmount);
// Update status
request.status = BridgeStatus.CANCELLED;
emit BridgeCancelled(requestId);
emit BridgeFailed(requestId, reason);
}
/**
* @dev Gets bridge request details
* @param requestId The bridge request ID
* @return request The bridge request details
*/
function getBridgeRequest(uint256 requestId)
external
view
validRequest(requestId)
returns (BridgeRequest memory)
{
return bridgeRequests[requestId];
}
/**
* @dev Gets user's bridge history
* @param user The user address
* @return requestIds Array of bridge request IDs
*/
function getUserBridgeHistory(address user)
external
view
returns (uint256[] memory requestIds)
{
return userBridgeHistory[user];
}
/**
* @dev Validates a bridge request signature
* @param requestId The bridge request ID
* @param lockTxHash The lock transaction hash
* @param signature The signature to validate
* @return isValid Whether the signature is valid
*/
function validateBridgeRequest(uint256 requestId, bytes32 lockTxHash, bytes memory signature)
external
view
returns (bool isValid)
{
bytes32 messageHash = keccak256(abi.encodePacked(requestId, lockTxHash, block.chainid));
address recoveredAddress = messageHash.recover(signature);
return validators[recoveredAddress].isActive;
}
// Admin functions
/**
* @dev Adds support for a new token
* @param tokenAddress The token address
* @param bridgeLimit Maximum bridge amount
* @param feePercentage Bridge fee percentage
* @param requiresWhitelist Whether whitelist is required
*/
function addSupportedToken(
address tokenAddress,
uint256 bridgeLimit,
uint256 feePercentage,
bool requiresWhitelist
) external onlyOwner {
require(tokenAddress != address(0), "Invalid token address");
require(feePercentage <= MAX_FEE, "Fee too high");
supportedTokens[tokenAddress] = SupportedToken({
tokenAddress: tokenAddress,
isActive: true,
bridgeLimit: bridgeLimit,
feePercentage: feePercentage,
requiresWhitelist: requiresWhitelist
});
supportedTokenAddresses.push(tokenAddress);
emit TokenSupported(tokenAddress, bridgeLimit, feePercentage);
}
/**
* @dev Adds support for a new blockchain
* @param chainId The chain ID
* @param chainType The chain type
* @param name The chain name
* @param bridgeContract The bridge contract address on that chain
* @param minConfirmations Minimum confirmations required
* @param avgBlockTime Average block time in seconds
*/
function addSupportedChain(
uint256 chainId,
ChainType chainType,
string memory name,
address bridgeContract,
uint256 minConfirmations,
uint256 avgBlockTime
) external onlyOwner {
require(chainId > 0, "Invalid chain ID");
require(chainId != block.chainid, "Cannot add current chain");
supportedChains[chainId] = ChainConfig({
chainId: chainId,
chainType: chainType,
name: name,
isActive: true,
bridgeContract: bridgeContract,
minConfirmations: minConfirmations,
avgBlockTime: avgBlockTime
});
supportedChainIds.push(chainId);
emit ChainSupported(chainId, name, chainType);
}
/**
* @dev Adds a new validator
* @param validatorAddress The validator address
* @param weight The validator weight
*/
function addValidator(address validatorAddress, uint256 weight) external onlyOwner {
require(validatorAddress != address(0), "Invalid validator address");
require(!validators[validatorAddress].isActive, "Validator already exists");
validators[validatorAddress] = Validator({
validatorAddress: validatorAddress,
isActive: true,
weight: weight,
lastValidation: 0,
totalValidations: 0
});
activeValidators.push(validatorAddress);
emit ValidatorAdded(validatorAddress, weight);
}
/**
* @dev Removes a validator
* @param validatorAddress The validator address
*/
function removeValidator(address validatorAddress) external onlyOwner {
require(validators[validatorAddress].isActive, "Validator not active");
validators[validatorAddress].isActive = false;
// Remove from active validators array
for (uint i = 0; i < activeValidators.length; i++) {
if (activeValidators[i] == validatorAddress) {
activeValidators[i] = activeValidators[activeValidators.length - 1];
activeValidators.pop();
break;
}
}
emit ValidatorRemoved(validatorAddress);
}
/**
* @dev Updates the Merkle root for transaction verification
* @param newMerkleRoot The new Merkle root
*/
function updateMerkleRoot(bytes32 newMerkleRoot) external onlyOwner {
merkleRoot = newMerkleRoot;
}
/**
* @dev Sets the fee recipient address
* @param newFeeRecipient The new fee recipient
*/
function setFeeRecipient(address newFeeRecipient) external onlyOwner {
require(newFeeRecipient != address(0), "Invalid address");
feeRecipient = newFeeRecipient;
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
// Internal functions
function _verifySignature(
bytes32 messageHash,
bytes memory signature,
address signer
) internal pure returns (bool) {
bytes32 ethSignedMessageHash = keccak256(
abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash)
);
return ethSignedMessageHash.recover(signature) == signer;
}
function _getRequiredConfirmations(uint256 chainId) internal view returns (uint256) {
ChainConfig storage chain = supportedChains[chainId];
return chain.minConfirmations > 0 ? chain.minConfirmations : MIN_CONFIRMATIONS;
}
function _addChain(
uint256 chainId,
ChainType chainType,
string memory name,
bool isActive,
address bridgeContract,
uint256 minConfirmations,
uint256 avgBlockTime
) internal {
supportedChains[chainId] = ChainConfig({
chainId: chainId,
chainType: chainType,
name: name,
isActive: isActive,
bridgeContract: bridgeContract,
minConfirmations: minConfirmations,
avgBlockTime: avgBlockTime
});
supportedChainIds.push(chainId);
emit ChainSupported(chainId, name, chainType);
}
// Emergency functions
function emergencyWithdraw(address token, uint256 amount) external onlyOwner {
IERC20(token).safeTransfer(owner(), amount);
}
function emergencyPause() external onlyOwner {
_pause();
}
}