Token Transfers
IXFI Protocol enables seamless token transfers across multiple blockchain networks through a secure burn-and-mint mechanism powered by a decentralized relayer network.
Overview
Cross-chain token transfers allow users to move IXFI tokens and other supported assets between different blockchains while maintaining security and decentralization principles.
Transfer Mechanism
Burn-and-Mint Model
Lock-and-Release Model (For Native Tokens)
For non-IXFI tokens, the protocol uses a lock-and-release mechanism:
Implementation
Basic Token Transfer
interface IIXFIGateway {
function sendToken(
string memory destinationChain,
string memory destinationAddress,
string memory symbol,
uint256 amount
) external;
}
Example Usage
contract TokenBridge {
IIXFIGateway public gateway;
IERC20 public ixfiToken;
function transferToChain(
string memory targetChain,
string memory recipient,
uint256 amount
) external {
// Transfer tokens to gateway
ixfiToken.transferFrom(msg.sender, address(gateway), amount);
// Initiate cross-chain transfer
gateway.sendToken(
targetChain,
recipient,
"IXFI",
amount
);
}
}
Advanced Transfer with Data
function sendTokenWithData(
string memory destinationChain,
string memory destinationAddress,
string memory symbol,
uint256 amount,
bytes memory data
) external {
// Transfer tokens and execute contract call
gateway.callContractWithToken(
destinationChain,
destinationAddress,
data,
symbol,
amount
);
}
Supported Tokens
Native IXFI Token
CrossFi
0x...
18
Native
Ethereum
0x...
18
Wrapped
BSC
0x...
18
Wrapped
Polygon
0x...
18
Wrapped
Arbitrum
0x...
18
Wrapped
Optimism
0x...
18
Wrapped
Avalanche
0x...
18
Wrapped
Wrapped Tokens
The protocol supports wrapping of various tokens for cross-chain transfers:
struct WrappedToken {
string symbol;
uint8 decimals;
mapping(string => address) chainAddresses;
bool isActive;
}
mapping(string => WrappedToken) public wrappedTokens;
Transfer Types
1. Simple Transfer
Direct token transfer between addresses on different chains:
// Transfer 100 IXFI from Ethereum to BSC
await gateway.sendToken(
"bsc", // destination chain
"0x742d35Cc6aB8C...", // recipient address
"IXFI", // token symbol
ethers.parseEther("100") // amount
);
2. Transfer with Contract Call
Transfer tokens and execute a function on the destination chain:
// Transfer tokens and stake them on destination chain
const stakePayload = ethers.AbiCoder.defaultAbiCoder().encode(
["address", "uint256"],
[userAddress, stakingPeriod]
);
await gateway.callContractWithToken(
"polygon", // destination chain
stakingContractAddress, // staking contract
stakePayload, // function call data
"IXFI", // token symbol
ethers.parseEther("500") // amount to stake
);
3. Batch Transfers
Transfer multiple tokens in a single transaction:
struct TokenTransfer {
string destinationChain;
string destinationAddress;
string symbol;
uint256 amount;
}
function batchTransfer(TokenTransfer[] memory transfers) external {
for (uint256 i = 0; i < transfers.length; i++) {
sendToken(
transfers[i].destinationChain,
transfers[i].destinationAddress,
transfers[i].symbol,
transfers[i].amount
);
}
}
Security Features
Multi-Signature Validation
All token transfers require validation from multiple relayers:
struct MintCommand {
bytes32 commandId;
string sourceChain;
address recipient;
uint256 amount;
bytes32[] signatures;
}
function validateMintCommand(MintCommand memory cmd) internal view returns (bool) {
require(cmd.signatures.length >= minSignatures, "Insufficient signatures");
bytes32 hash = keccak256(abi.encode(cmd.commandId, cmd.sourceChain, cmd.recipient, cmd.amount));
uint256 validSignatures = 0;
for (uint256 i = 0; i < cmd.signatures.length; i++) {
if (_verifyRelayerSignature(hash, cmd.signatures[i])) {
validSignatures++;
}
}
return validSignatures >= minSignatures;
}
Rate Limiting
Transfer limits prevent large-scale attacks:
mapping(address => uint256) public dailyTransferred;
mapping(address => uint256) public lastTransferDay;
uint256 public dailyLimit = 10000 ether;
modifier rateLimited(uint256 amount) {
uint256 today = block.timestamp / 1 days;
if (lastTransferDay[msg.sender] < today) {
dailyTransferred[msg.sender] = 0;
lastTransferDay[msg.sender] = today;
}
require(
dailyTransferred[msg.sender] + amount <= dailyLimit,
"Daily limit exceeded"
);
dailyTransferred[msg.sender] += amount;
_;
}
Replay Protection
Command IDs prevent duplicate token minting:
mapping(bytes32 => bool) public executedCommands;
function mint(
bytes32 commandId,
address recipient,
uint256 amount
) external onlyRelayer {
require(!executedCommands[commandId], "Command already executed");
executedCommands[commandId] = true;
_mint(recipient, amount);
}
Fee Structure
Transfer Fees
struct TransferFees {
uint256 baseFee; // Fixed fee per transfer
uint256 percentageFee; // Percentage of transfer amount
uint256 gasReimbursement; // Estimated gas cost
}
mapping(string => TransferFees) public chainFees;
Fee Calculation
function calculateTransferFee(
string memory destinationChain,
uint256 amount
) public view returns (uint256) {
TransferFees memory fees = chainFees[destinationChain];
uint256 percentageFee = (amount * fees.percentageFee) / 10000;
return fees.baseFee + percentageFee + fees.gasReimbursement;
}
Fee Payment Options
Native Token: Pay fees in the source chain's native token
IXFI Token: Pay fees using IXFI tokens
Deducted from Transfer: Fees deducted from transfer amount
Gas Management
Gas Estimation
function estimateTransferGas(
string memory destinationChain,
string memory symbol,
uint256 amount
) external view returns (uint256) {
// Base gas for token minting
uint256 baseGas = 50000;
// Additional gas for complex tokens
if (wrappedTokens[symbol].isActive) {
baseGas += 30000;
}
// Chain-specific gas adjustments
uint256 chainMultiplier = chainGasMultipliers[destinationChain];
return (baseGas * chainMultiplier) / 100;
}
Gas Payment Models
Prepaid Gas
function transferWithGas(
string memory destinationChain,
string memory destinationAddress,
string memory symbol,
uint256 amount,
uint256 gasLimit
) external payable {
uint256 gasCost = calculateGasCost(destinationChain, gasLimit);
require(msg.value >= gasCost, "Insufficient gas payment");
// Process transfer with guaranteed gas
_sendTokenWithGas(destinationChain, destinationAddress, symbol, amount, gasLimit);
}
Token-Based Gas
function transferWithTokenGas(
string memory destinationChain,
string memory destinationAddress,
string memory symbol,
uint256 amount,
uint256 gasTokenAmount
) external {
// Deduct gas tokens
ixfiToken.transferFrom(msg.sender, gasVault, gasTokenAmount);
// Process transfer
sendToken(destinationChain, destinationAddress, symbol, amount);
}
Error Handling
Transfer Failures
event TransferFailed(
bytes32 indexed transferId,
string reason,
bool canRetry
);
function retryTransfer(bytes32 transferId) external {
TransferData memory transfer = failedTransfers[transferId];
require(transfer.canRetry, "Transfer cannot be retried");
require(block.timestamp >= transfer.retryAfter, "Too early to retry");
// Retry the transfer
_executeTransfer(transfer);
}
Automatic Rollback
function rollbackTransfer(bytes32 transferId) external onlyRelayer {
TransferData memory transfer = pendingTransfers[transferId];
require(transfer.deadline < block.timestamp, "Transfer not expired");
// Mint tokens back to original sender
_mint(transfer.sender, transfer.amount);
emit TransferRolledBack(transferId, transfer.sender, transfer.amount);
}
Advanced Features
Scheduled Transfers
struct ScheduledTransfer {
address sender;
string destinationChain;
string destinationAddress;
string symbol;
uint256 amount;
uint256 executeAt;
bool executed;
}
mapping(bytes32 => ScheduledTransfer) public scheduledTransfers;
function scheduleTransfer(
string memory destinationChain,
string memory destinationAddress,
string memory symbol,
uint256 amount,
uint256 executeAt
) external returns (bytes32) {
require(executeAt > block.timestamp, "Invalid execution time");
bytes32 transferId = keccak256(abi.encode(
msg.sender,
destinationChain,
destinationAddress,
symbol,
amount,
executeAt,
block.number
));
scheduledTransfers[transferId] = ScheduledTransfer({
sender: msg.sender,
destinationChain: destinationChain,
destinationAddress: destinationAddress,
symbol: symbol,
amount: amount,
executeAt: executeAt,
executed: false
});
return transferId;
}
Conditional Transfers
function transferOnCondition(
string memory destinationChain,
string memory destinationAddress,
string memory symbol,
uint256 amount,
bytes memory condition
) external {
bytes32 transferId = _generateTransferId();
conditionalTransfers[transferId] = ConditionalTransfer({
sender: msg.sender,
destinationChain: destinationChain,
destinationAddress: destinationAddress,
symbol: symbol,
amount: amount,
condition: condition,
validated: false
});
// Lock tokens until condition is met
_lockTokens(msg.sender, symbol, amount);
}
Monitoring and Analytics
Transfer Tracking
event TokenTransferInitiated(
bytes32 indexed transferId,
address indexed sender,
string indexed destinationChain,
string destinationAddress,
string symbol,
uint256 amount
);
event TokenTransferCompleted(
bytes32 indexed transferId,
string indexed sourceChain,
address indexed recipient,
string symbol,
uint256 amount
);
Transfer Status
enum TransferStatus {
PENDING,
PROCESSING,
COMPLETED,
FAILED,
CANCELLED
}
function getTransferStatus(bytes32 transferId) external view returns (TransferStatus) {
return transferStatuses[transferId];
}
Integration Examples
DeFi Integration
contract CrossChainYieldFarm {
function depositAcrossChains(
string memory sourceChain,
uint256 amount
) external {
bytes memory depositData = abi.encodeWithSignature(
"deposit(address,uint256)",
msg.sender,
amount
);
gateway.callContractWithToken(
sourceChain,
address(this),
depositData,
"IXFI",
amount
);
}
function executeWithToken(
bytes32,
string memory,
string memory,
bytes memory payload,
string memory,
uint256 amount
) external override onlyGateway {
(address user, uint256 depositAmount) = abi.decode(
payload,
(address, uint256)
);
require(amount == depositAmount, "Amount mismatch");
_deposit(user, amount);
}
}
Gaming Integration
contract CrossChainGameAssets {
function transferAssetToChain(
uint256 assetId,
string memory targetChain,
address newOwner
) external {
require(ownerOf(assetId) == msg.sender, "Not asset owner");
// Burn asset on current chain
_burn(assetId);
bytes memory mintData = abi.encodeWithSignature(
"mintAsset(address,uint256)",
newOwner,
assetId
);
gateway.callContract(targetChain, address(this), mintData);
}
}
Best Practices
For Users
Verify Addresses: Double-check destination addresses and chains
Check Fees: Understand fee structure before transferring
Monitor Status: Track transfer status until completion
Use Proper Gas: Provide adequate gas for complex transfers
For Developers
Implement Timeouts: Set reasonable timeouts for transfers
Handle Failures: Implement proper error handling and recovery
Test Thoroughly: Test transfers on all supported chains
Monitor Events: Listen for transfer events and status updates
Security Guidelines
Validate Inputs: Always validate transfer parameters
Rate Limiting: Implement appropriate rate limiting
Access Control: Restrict sensitive functions properly
Audit Regularly: Regular security audits for transfer logic
Resources
Last updated