CrossChainAggregator
The CrossChainAggregator contract is the core component of IXFI's DEX aggregation system, enabling optimal token swaps across 37+ protocols and seamless cross-chain operations.
Contract Interface
interface ICrossChainAggregator {
// Quote functions
function getOptimalQuote(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256[] memory routerTypes
) external view returns (uint256 bestAmount, uint256 bestRouter);
function getAllQuotes(
address tokenIn,
address tokenOut,
uint256 amountIn
) external view returns (QuoteResult[] memory);
// Swap execution
function executeSwap(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 minAmountOut,
uint256 routerType,
bytes calldata swapData
) external payable returns (uint256 amountOut);
// Cross-chain operations
function crossChainSwap(
SwapData calldata swapData,
string calldata destinationChain,
address destinationToken,
bytes calldata destinationSwapData,
address destinationRouter,
uint256 amountOutMin
) external;
// Batch operations
function multiSwap(
SwapParams[] memory swaps
) external payable returns (uint256[] memory amountsOut);
}
Data Structures
SwapData
struct SwapData {
address tokenIn; // Input token address
address tokenOut; // Output token address (IXFI for source chain)
uint256 amountIn; // Input amount
uint256 amountOutMin; // Minimum output amount (slippage protection)
address to; // Recipient address
uint256 deadline; // Transaction deadline
bytes routerCalldata; // Encoded function call for DEX router
address router; // DEX router address
}
SwapParams
struct SwapParams {
address tokenIn;
address tokenOut;
uint256 amountIn;
uint256 minAmountOut;
uint256 routerType;
bytes swapData;
address recipient;
}
QuoteResult
struct QuoteResult {
uint256 routerType; // DEX protocol identifier (0-36)
uint256 amountOut; // Expected output amount
uint256 gasEstimate; // Estimated gas cost
uint256 priceImpact; // Price impact in basis points
bool success; // Whether quote was successful
}
CrossChainSwapRequest
struct CrossChainSwapRequest {
address user; // Original user address
string destinationChain; // Target chain name
address destinationToken; // Target token on destination chain
uint256 amountOutMin; // Minimum amount out on destination
uint256 deadline; // Swap deadline
bytes destinationSwapData; // Swap calldata for destination chain
address destinationRouter; // Router address on destination chain
}
Core Functions
getOptimalQuote
function getOptimalQuote(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256[] memory routerTypes
) external view returns (uint256 bestAmount, uint256 bestRouter)
Get the best quote from specified DEX protocols.
Parameters:
tokenIn
: Input token contract addresstokenOut
: Output token contract addressamountIn
: Amount of input tokens to swaprouterTypes
: Array of router type IDs to check (0-36)
Returns:
bestAmount
: Maximum output tokens from best routebestRouter
: Router type ID of the best quote
Usage Example:
// Get best quote from Uniswap V2, V3 and SushiSwap
uint256[] memory routers = new uint256[](3);
routers[0] = 0; // Uniswap V2
routers[1] = 10; // Uniswap V3
routers[2] = 1; // SushiSwap V2
(uint256 bestAmount, uint256 bestRouter) = aggregator.getOptimalQuote(
0xA0b86a33E6441e1a02c4e4670dd96EA0f25A632, // USDC
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, // WETH
1000e6, // 1000 USDC
routers
);
getAllQuotes
function getAllQuotes(
address tokenIn,
address tokenOut,
uint256 amountIn
) external view returns (QuoteResult[] memory)
Get quotes from all 37 supported DEX protocols.
Parameters:
tokenIn
: Input token contract addresstokenOut
: Output token contract addressamountIn
: Amount of input tokens to swap
Returns:
Array of
QuoteResult
structs with quotes from each protocol
Usage Example:
QuoteResult[] memory quotes = aggregator.getAllQuotes(
0xA0b86a33E6441e1a02c4e4670dd96EA0f25A632, // USDC
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, // WETH
1000e6 // 1000 USDC
);
// Find best non-zero quote
uint256 bestAmount = 0;
uint256 bestRouter = 0;
for (uint i = 0; i < quotes.length; i++) {
if (quotes[i].success && quotes[i].amountOut > bestAmount) {
bestAmount = quotes[i].amountOut;
bestRouter = quotes[i].routerType;
}
}
executeSwap
function executeSwap(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 minAmountOut,
uint256 routerType,
bytes calldata swapData
) external payable returns (uint256 amountOut)
Execute a token swap through the specified DEX protocol.
Parameters:
tokenIn
: Input token address (useaddress(0)
for ETH)tokenOut
: Output token addressamountIn
: Exact amount of input tokensminAmountOut
: Minimum acceptable output (slippage protection)routerType
: DEX protocol to use (0-36)swapData
: Protocol-specific calldata for the swap
Returns:
amountOut
: Actual amount of output tokens received
Usage Example:
// Approve tokens first
IERC20(tokenIn).approve(address(aggregator), amountIn);
// Execute swap
uint256 amountOut = aggregator.executeSwap{value: msg.value}(
0xA0b86a33E6441e1a02c4e4670dd96EA0f25A632, // USDC
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, // WETH
1000e6, // 1000 USDC
1.8e18, // Minimum 1.8 WETH (with slippage)
10, // Uniswap V3
swapCalldata
);
crossChainSwap
function crossChainSwap(
SwapData calldata swapData,
string calldata destinationChain,
address destinationToken,
bytes calldata destinationSwapData,
address destinationRouter,
uint256 amountOutMin
) external
Execute a cross-chain token swap: TokenA (Chain A) → IXFI → TokenB (Chain B).
Parameters:
swapData
: Source chain swap parameters (TokenA → IXFI)destinationChain
: Target chain name ("ethereum", "bsc", "polygon", etc.)destinationToken
: Target token address on destination chaindestinationSwapData
: Encoded swap data for destination chaindestinationRouter
: Router address on destination chainamountOutMin
: Minimum output on destination chain
Usage Example:
SwapData memory sourceSwap = SwapData({
tokenIn: 0xA0b86a33E6441e1a02c4e4670dd96EA0f25A632, // USDC on Ethereum
tokenOut: ixfiToken, // IXFI
amountIn: 1000e6,
amountOutMin: 990e18, // 990 IXFI minimum
to: address(this),
deadline: block.timestamp + 1800,
routerCalldata: uniswapCalldata,
router: uniswapRouter
});
// Swap USDC on Ethereum for BNB on BSC
aggregator.crossChainSwap(
sourceSwap,
"bsc",
0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c, // WBNB on BSC
pancakeSwapCalldata,
pancakeSwapRouter,
0.3e18 // Minimum 0.3 BNB
);
multiSwap
function multiSwap(
SwapParams[] memory swaps
) external payable returns (uint256[] memory amountsOut)
Execute multiple swaps in a single transaction for gas efficiency.
Parameters:
swaps
: Array of swap parameters
Returns:
amountsOut
: Array of output amounts for each swap
Usage Example:
SwapParams[] memory swaps = new SwapParams[](2);
// Swap 1: USDC → WETH
swaps[0] = SwapParams({
tokenIn: 0xA0b86a33E6441e1a02c4e4670dd96EA0f25A632,
tokenOut: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2,
amountIn: 500e6,
minAmountOut: 0.9e18,
routerType: 10, // Uniswap V3
swapData: uniswapCalldata,
recipient: msg.sender
});
// Swap 2: DAI → USDT
swaps[1] = SwapParams({
tokenIn: 0x6B175474E89094C44Da98b954EedeAC495271d0F,
tokenOut: 0xdAC17F958D2ee523a2206206994597C13D831ec7,
amountIn: 200e18,
minAmountOut: 199e6,
routerType: 30, // Curve
swapData: curveCalldata,
recipient: msg.sender
});
uint256[] memory amountsOut = aggregator.multiSwap(swaps);
View Functions
getSupportedTokens
function getSupportedTokens(uint256 chainId) external view returns (address[] memory)
Get all supported tokens on a specific chain.
getRouterAddress
function getRouterAddress(uint256 routerType, uint256 chainId) external view returns (address)
Get the router contract address for a specific protocol and chain.
isRouterSupported
function isRouterSupported(uint256 routerType, uint256 chainId) external view returns (bool)
Check if a router is supported on a specific chain.
getSwapFee
function getSwapFee(uint256 routerType) external view returns (uint256)
Get the protocol fee for a specific router type.
Events
SwapExecuted
event SwapExecuted(
address indexed user,
address indexed tokenIn,
address indexed tokenOut,
uint256 amountIn,
uint256 amountOut,
uint256 routerType
)
Emitted when a single swap is executed.
CrossChainSwapInitiated
event CrossChainSwapInitiated(
address indexed user,
address indexed tokenIn,
uint256 amountIn,
string destinationChain,
address indexed destinationToken,
bytes32 swapId
)
Emitted when a cross-chain swap is initiated on the source chain.
CrossChainSwapCompleted
event CrossChainSwapCompleted(
bytes32 indexed swapId,
address indexed user,
address indexed tokenOut,
uint256 amountOut
)
Emitted when a cross-chain swap is completed on the destination chain.
SwapFailed
event SwapFailed(
bytes32 indexed swapId,
address indexed user,
string reason
)
Emitted when a swap fails and requires manual intervention.
Error Codes
SwapErrors
error InsufficientOutputAmount(uint256 amountOut, uint256 minAmountOut);
error SwapExpired(uint256 deadline, uint256 currentTime);
error UnsupportedRouter(uint256 routerType);
error InvalidTokenPair(address tokenIn, address tokenOut);
error InsufficientBalance(address token, uint256 required, uint256 available);
error RouterCallFailed(address router, bytes calldata);
error InvalidChain(string chainName);
error AmountMismatch(uint256 expected, uint256 actual);
Usage Examples
try aggregator.executeSwap(
tokenIn,
tokenOut,
amountIn,
minAmountOut,
routerType,
swapData
) returns (uint256 amountOut) {
// Swap successful
emit SwapSuccess(amountOut);
} catch SwapErrors.InsufficientOutputAmount(uint256 actual, uint256 minimum) {
// Handle slippage exceeded
emit SlippageExceeded(actual, minimum);
} catch SwapErrors.SwapExpired(uint256 deadline, uint256 current) {
// Handle expired transaction
emit TransactionExpired(deadline, current);
} catch {
// Handle other errors
emit SwapFailed("Unknown error");
}
Gas Optimization
Batch Operations
// Instead of individual swaps (high gas)
aggregator.executeSwap(params1);
aggregator.executeSwap(params2);
aggregator.executeSwap(params3);
// Use batch operation (lower gas)
SwapParams[] memory swaps = new SwapParams[](3);
swaps[0] = params1;
swaps[1] = params2;
swaps[2] = params3;
aggregator.multiSwap(swaps);
Calldata Optimization
// Optimize swap data size
bytes memory optimizedCalldata = abi.encodeWithSelector(
IRouter.swapExactTokensForTokens.selector,
amountIn,
amountOutMin,
path,
to,
deadline
);
Security Best Practices
Input Validation
// Always validate inputs
require(tokenIn != address(0), "Invalid token");
require(amountIn > 0, "Invalid amount");
require(minAmountOut > 0, "Invalid minimum");
require(deadline > block.timestamp, "Expired deadline");
Slippage Protection
// Calculate appropriate slippage
uint256 minAmountOut = expectedOut.mul(995).div(1000); // 0.5% slippage
// Or use dynamic slippage based on market conditions
uint256 dynamicSlippage = calculateSlippage(tokenIn, tokenOut, amountIn);
uint256 minAmountOut = expectedOut.mul(10000 - dynamicSlippage).div(10000);
Deadline Management
// Use reasonable deadlines
uint256 deadline = block.timestamp + 300; // 5 minutes
// For cross-chain operations, use longer deadlines
uint256 crossChainDeadline = block.timestamp + 1800; // 30 minutes
Integration Patterns
Basic Integration
contract MyDApp {
ICrossChainAggregator public aggregator;
constructor(address _aggregator) {
aggregator = ICrossChainAggregator(_aggregator);
}
function swap(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 slippage
) external {
// Get optimal quote
uint256[] memory allRouters = new uint256[](37);
for (uint i = 0; i < 37; i++) {
allRouters[i] = i;
}
(uint256 bestAmount, uint256 bestRouter) = aggregator.getOptimalQuote(
tokenIn,
tokenOut,
amountIn,
allRouters
);
// Calculate minimum output
uint256 minAmountOut = bestAmount.mul(10000 - slippage).div(10000);
// Execute swap
aggregator.executeSwap(
tokenIn,
tokenOut,
amountIn,
minAmountOut,
bestRouter,
generateSwapData(tokenIn, tokenOut, amountIn, bestRouter)
);
}
}
Advanced Integration with MEV Protection
contract MEVProtectedSwap {
using SafeERC20 for IERC20;
ICrossChainAggregator public aggregator;
mapping(bytes32 => bool) public usedNonces;
struct SwapOrder {
address tokenIn;
address tokenOut;
uint256 amountIn;
uint256 minAmountOut;
uint256 deadline;
bytes32 nonce;
bytes signature;
}
function executeSwapWithSignature(SwapOrder calldata order) external {
// Verify signature and nonce
require(block.timestamp <= order.deadline, "Order expired");
require(!usedNonces[order.nonce], "Nonce used");
bytes32 hash = keccak256(abi.encode(order));
address signer = ECDSA.recover(hash, order.signature);
usedNonces[order.nonce] = true;
// Execute swap
IERC20(order.tokenIn).safeTransferFrom(signer, address(this), order.amountIn);
uint256 amountOut = aggregator.executeSwap(
order.tokenIn,
order.tokenOut,
order.amountIn,
order.minAmountOut,
getBestRouter(order.tokenIn, order.tokenOut, order.amountIn),
""
);
IERC20(order.tokenOut).safeTransfer(signer, amountOut);
}
}
Next Steps
Learn about MulticallLibraryV2 - Batch operations
Explore QuoteLibrary - Price calculation engine
Study Integration Examples - Real-world patterns
Read Security Guide - Best practices
Last updated