Genius Router
The GeniusRouter is a contract that simplifies the process of creating cross-chain orders in the Genius bridge protocol. It provides convenient functions for users to swap tokens and create bridge orders in a single transaction, with optional Permit2 integration for gasless approvals and enhanced user experience.
The router acts as a bridge between users and the core vault functionality, handling token transfers, swaps, and order creation in a streamlined manner. It supports both traditional token approvals and gasless Permit2 transactions, making it accessible to a wide range of users and wallet types.
The GeniusRouter contract - overview
The GeniusRouter contract is an immutable contract that serves as the primary user interface for the Genius bridge protocol. It integrates with the vault, proxy call, fee collector, and Permit2 contracts to provide a seamless experience for creating cross-chain orders.
The contract handles the complexity of token management, fee calculation, and order creation, allowing users to focus on their desired cross-chain operations rather than the underlying technical details.
The GeniusRouter contract - key features
Integrated swap and order creation
The router allows users to swap tokens and create cross-chain orders in a single transaction, eliminating the need for multiple separate transactions and reducing gas costs.
Permit2 integration
Supports gasless token approvals through Uniswap's Permit2 system, enabling users to create orders without requiring pre-approved token allowances.
Flexible token input
Supports multiple input tokens in a single transaction, allowing users to swap various tokens for stablecoins before creating bridge orders.
Automatic fee calculation
Integrates with the fee collector to automatically calculate and include the appropriate fees for cross-chain orders based on destination chain and order size.
Proxy call integration
Leverages the GeniusProxyCall contract for executing complex swap operations and ensuring secure token transfers.
Immutable architecture
Uses immutable state variables for critical contract addresses, ensuring security and preventing unauthorized modifications.
User-friendly interface
Provides simple, intuitive functions that abstract away the complexity of the underlying protocol while maintaining full functionality.
Comprehensive error handling
Implements detailed error messages and validation to help users understand and resolve any issues with their transactions.
Understanding the workflow of GeniusRouter
Token Input: Users provide input tokens either through direct transfers or Permit2 permits.
Token Swapping: The router executes swaps using the proxy call contract to convert input tokens to stablecoins.
Fee Calculation: The fee collector calculates the appropriate fees for the destination chain and order size.
Order Creation: The router creates a cross-chain order with the vault using the swapped stablecoins.
Order Execution: The order is then executed by orchestrators on the destination chain.
Core functions and their purposes
Swap and Create Order
function swapAndCreateOrder(
bytes32 seed,
address[] calldata tokensIn,
uint256[] calldata amountsIn,
address target,
address toApprove,
bytes calldata data,
address owner,
uint256 destChainId,
uint256 feeSurplus,
bytes32 receiver,
uint256 minAmountOut,
bytes32 tokenOut
) public payable override {
if (tokensIn.length != amountsIn.length)
revert GeniusErrors.ArrayLengthsMismatch();
// Transfer input tokens to proxy call
for (uint256 i = 0; i < tokensIn.length; i++) {
if (msg.sender != address(PROXYCALL)) {
IERC20(tokensIn[i]).safeTransferFrom(
msg.sender,
address(PROXYCALL),
amountsIn[i]
);
}
}
// Execute swap operation
if (target == address(PROXYCALL))
PROXYCALL.execute{value: msg.value}(target, data);
else
PROXYCALL.approveAddressAndExecute{value: msg.value}(
tokensIn,
target,
data,
toApprove
);
uint256 delta = STABLECOIN.balanceOf(address(this));
// Calculate fees
IFeeCollector.FeeBreakdown memory feeBreakdown = FEE_COLLECTOR
.getOrderFees(delta, destChainId);
// Create order
IGeniusVault.Order memory order = IGeniusVault.Order({
seed: seed,
trader: VAULT.addressToBytes32(owner),
receiver: receiver,
tokenIn: VAULT.addressToBytes32(address(STABLECOIN)),
tokenOut: tokenOut,
amountIn: delta,
minAmountOut: minAmountOut,
destChainId: destChainId,
srcChainId: block.chainid,
fee: feeBreakdown.totalFee + feeSurplus
});
VAULT.createOrder(order);
}
Permit2 Swap and Create Order
function swapAndCreateOrderPermit2(
bytes32 seed,
IAllowanceTransfer.PermitBatch calldata permitBatch,
bytes calldata permitSignature,
address target,
address toApprove,
bytes calldata data,
uint256 destChainId,
uint256 feeSurplus,
bytes32 receiver,
uint256 minAmountOut,
bytes32 tokenOut
) public payable override {
address owner = msg.sender;
// Handle Permit2 token transfers
address[] memory tokensIn = _permitAndBatchTransfer(
permitBatch,
permitSignature,
owner
);
// Execute swap operation
if (target == address(PROXYCALL))
PROXYCALL.execute{value: msg.value}(target, data);
else
PROXYCALL.approveAddressAndExecute{value: msg.value}(
tokensIn,
target,
data,
toApprove
);
uint256 delta = STABLECOIN.balanceOf(address(this));
// Calculate fees and create order
IFeeCollector.FeeBreakdown memory feeBreakdown = FEE_COLLECTOR
.getOrderFees(delta, destChainId);
IGeniusVault.Order memory order = IGeniusVault.Order({
seed: seed,
trader: VAULT.addressToBytes32(owner),
receiver: receiver,
tokenIn: VAULT.addressToBytes32(address(STABLECOIN)),
tokenOut: tokenOut,
amountIn: delta,
minAmountOut: minAmountOut,
destChainId: destChainId,
srcChainId: block.chainid,
fee: feeBreakdown.totalFee + feeSurplus
});
VAULT.createOrder(order);
}
Permit2 Order Creation
function createOrderPermit2(
bytes32 seed,
IAllowanceTransfer.PermitBatch calldata permitBatch,
bytes calldata permitSignature,
uint256 destChainId,
uint256 fee,
bytes32 receiver,
uint256 minAmountOut,
bytes32 tokenOut
) external payable override {
address owner = msg.sender;
if (permitBatch.details.length != 0)
revert GeniusErrors.ArrayLengthsMismatch();
if (permitBatch.details[0].token != address(STABLECOIN))
revert GeniusErrors.InvalidTokenIn();
// Transfer stablecoins using Permit2
_permitAndBatchTransfer(permitBatch, permitSignature, owner);
uint256 delta = STABLECOIN.balanceOf(address(this));
// Create order directly with stablecoins
IGeniusVault.Order memory order = IGeniusVault.Order({
seed: seed,
trader: VAULT.addressToBytes32(owner),
receiver: receiver,
tokenIn: VAULT.addressToBytes32(address(STABLECOIN)),
tokenOut: tokenOut,
amountIn: delta,
minAmountOut: minAmountOut,
destChainId: destChainId,
srcChainId: block.chainid,
fee: fee
});
VAULT.createOrder(order);
}
Permit2 Token Transfer Helper
function _permitAndBatchTransfer(
IAllowanceTransfer.PermitBatch calldata permitBatch,
bytes calldata permitSignature,
address owner
) private returns (address[] memory tokensIn) {
tokensIn = new address[](permitBatch.details.length);
if (permitBatch.spender != address(this))
revert GeniusErrors.InvalidSpender();
// Execute permit
PERMIT2.permit(owner, permitBatch, permitSignature);
// Prepare transfer details
IAllowanceTransfer.AllowanceTransferDetails[]
memory transferDetails = new IAllowanceTransfer.AllowanceTransferDetails[](
permitBatch.details.length
);
for (uint i; i < permitBatch.details.length; i++) {
tokensIn[i] = permitBatch.details[i].token;
transferDetails[i] = IAllowanceTransfer.AllowanceTransferDetails({
from: owner,
to: address(PROXYCALL),
amount: permitBatch.details[i].amount,
token: permitBatch.details[i].token
});
}
// Execute transfers
PERMIT2.transferFrom(transferDetails);
}
Function types and use cases
Traditional Token Approvals
Function:
swapAndCreateOrder()
Use case: Users who prefer traditional token approvals
Process: Direct token transfers followed by swap and order creation
Gasless Permit2 Operations
Function:
swapAndCreateOrderPermit2()
Use case: Users wanting gasless token approvals
Process: Permit2 signature validation followed by swap and order creation
Direct Stablecoin Orders
Function:
createOrderPermit2()
Use case: Users with stablecoins wanting direct bridge orders
Process: Permit2 stablecoin transfer followed by order creation
Integration with other contracts
The GeniusRouter integrates with several core contracts in the Genius ecosystem:
GeniusVault: Creates cross-chain orders
GeniusProxyCall: Executes swap operations
FeeCollector: Calculates order fees
Permit2: Handles gasless token approvals
Stablecoin: The underlying stablecoin for bridge operations
Key state variables
STABLECOIN
: The stablecoin token used for bridge operationsVAULT
: The GeniusVault contract for order creationPERMIT2
: The Permit2 contract for gasless approvalsPROXYCALL
: The GeniusProxyCall contract for swap executionFEE_COLLECTOR
: The FeeCollector contract for fee calculation
Security features
Immutable Architecture
All critical contract addresses are immutable, preventing unauthorized modifications and ensuring contract integrity.
Input Validation
Comprehensive validation of input parameters including:
Array length matching
Valid token addresses
Proper spender addresses
Sufficient token amounts
Safe Token Transfers
All token transfers use SafeERC20 to prevent common token-related vulnerabilities.
Permit2 Security
Proper validation of Permit2 signatures and spender addresses to prevent unauthorized token transfers.
Native Token Protection
The contract rejects direct native token transfers to prevent accidental fund loss.
User experience features
Single Transaction Operations
Users can swap tokens and create bridge orders in a single transaction, reducing gas costs and improving convenience.
Gasless Approvals
Permit2 integration allows users to create orders without requiring pre-approved token allowances, saving gas and improving user experience.
Flexible Token Support
Supports multiple input tokens, allowing users to bridge various assets by first converting them to stablecoins.
Automatic Fee Handling
Fees are automatically calculated and included, eliminating the need for users to manually determine appropriate fee amounts.
Clear Error Messages
Comprehensive error handling provides clear feedback when transactions fail, helping users understand and resolve issues.
Order creation process
Step 1: Token Input
Users provide input tokens through direct transfers or Permit2 permits
Multiple tokens can be provided in a single transaction
Step 2: Token Swapping
Input tokens are transferred to the proxy call contract
Swaps are executed to convert tokens to stablecoins
Swap operations can target any DEX or DeFi protocol
Step 3: Fee Calculation
The fee collector calculates appropriate fees based on:
Destination chain
Order amount
Current fee tiers
Step 4: Order Creation
A cross-chain order is created with the vault
Order includes all necessary parameters for execution
Order is queued for processing by orchestrators
Error handling and validation
Input Validation
Array lengths must match
Token addresses must be valid
Spender addresses must be correct
Permit signatures must be valid
Operation Validation
Token transfers must succeed
Swap operations must complete
Fee calculations must be valid
Order creation must succeed
Security Checks
Native tokens are rejected
Unauthorized spenders are blocked
Invalid permits are rejected
The GeniusRouter is a critical user-facing component of the Genius bridge protocol, providing a simple and efficient interface for creating cross-chain orders. Its integration with Permit2, support for complex swaps, and automatic fee handling make it accessible to users of all technical levels while maintaining the security and functionality of the underlying protocol.
Last updated