Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.ifalabs.com/llms.txt

Use this file to discover all available pages before exploring further.

Integration Guide

Relayer infrastructure setup and integration patterns for the IFA Oracle Price Feed System.

Relayer Architecture

Basic Relayer Setup

Requirements

  • Node.js 18+
  • Stable internet connection
  • Funded wallet for gas fees

Simple Relayer Implementation

// relayer.js
const { ethers } = require('ethers');
const axios = require('axios');

class PriceFeedRelayer {
    constructor(config) {
        this.provider = new ethers.providers.JsonRpcProvider(config.rpcUrl);
        this.wallet = new ethers.Wallet(config.privateKey, this.provider);
        this.verifierContract = new ethers.Contract(
            config.verifierAddress,
            config.verifierAbi,
            this.wallet
        );
        this.updateInterval = 300000; // 5 minutes
        this.roundCounter = 1;
    }

    async start() {
        console.log('Starting relayer...');
        this.scheduleUpdates();
    }

    scheduleUpdates() {
        setInterval(async () => {
            try {
                await this.updatePrices();
            } catch (error) {
                console.error('Update failed:', error);
            }
        }, this.updateInterval);
    }

    async updatePrices() {
        const priceData = await this.fetchPrices();
        if (priceData.length > 0) {
            await this.submitPrices(priceData);
            this.roundCounter++;
        }
    }

    async fetchPrices() {
        // Fetch from your preferred price API
        const response = await axios.get('https://api.coingecko.com/api/v3/simple/price?ids=usd-coin,tether&vs_currencies=usd');
        
        return [
            {
                symbol: 'USDC',
                price: Math.floor(response.data['usd-coin'].usd * 1e30), // 30 decimals
                timestamp: Date.now()
            },
            {
                symbol: 'USDT', 
                price: Math.floor(response.data.tether.usd * 1e30),
                timestamp: Date.now()
            }
        ];
    }

    async submitPrices(priceData) {
        const assetIds = priceData.map(data => 
            ethers.utils.keccak256(ethers.utils.toUtf8Bytes(data.symbol))
        );
        
        const priceFeeds = priceData.map(data => ({
            decimal: 0, // Stored with 30 decimals
            lastUpdateTime: Math.floor(Date.now() / 1000),
            price: data.price,
            roundId: this.roundCounter
        }));

        const tx = await this.verifierContract.submitPriceFeed(
            assetIds,
            priceFeeds,
            { gasLimit: 500000 }
        );
        
        await tx.wait();
        console.log('Prices submitted:', tx.hash);
    }
}

// Usage
const relayer = new PriceFeedRelayer({
    rpcUrl: process.env.RPC_URL,
    privateKey: process.env.PRIVATE_KEY,
    verifierAddress: process.env.VERIFIER_ADDRESS,
    verifierAbi: [/* ABI here */]
});

relayer.start();

DApp Integration Patterns

Basic Price Query

contract MyDApp {
    IIfaPriceFeed public oracle;
    
    constructor(address _oracle) {
        oracle = IIfaPriceFeed(_oracle);
    }
    
    function getAssetPrice(string memory asset) external view returns (uint256) {
        bytes32 assetId = keccak256(bytes(asset));
        (IIfaPriceFeed.PriceFeed memory price, bool exists) = oracle.getAssetInfo(assetId);
        
        require(exists, "Price not available");
        require(block.timestamp - price.lastUpdateTime < 3600, "Price stale");
        
        return price.price;
    }
    
    function getExchangeRate(string memory from, string memory to) external view returns (uint256) {
        bytes32 fromId = keccak256(bytes(from));
        bytes32 toId = keccak256(bytes(to));
        
        IIfaPriceFeed.DerviedPair memory pair = oracle.getPairById(fromId, toId);
        return pair.derivedPrice;
    }
}

Batch Price Operations

contract BatchPriceConsumer {
    IIfaPriceFeed public oracle;
    
    function getBatchPrices(string[] calldata assets) external view returns (uint256[] memory prices) {
        bytes32[] memory assetIds = new bytes32[](assets.length);
        for (uint i = 0; i < assets.length; i++) {
            assetIds[i] = keccak256(bytes(assets[i]));
        }
        
        (IIfaPriceFeed.PriceFeed[] memory priceFeeds, bool[] memory exists) = 
            oracle.getAssetsInfo(assetIds);
            
        prices = new uint256[](assets.length);
        for (uint i = 0; i < assets.length; i++) {
            require(exists[i], "Price not available");
            prices[i] = priceFeeds[i].price;
        }
    }
}

Production Setup

Environment Configuration

# .env
RPC_URL=https://mainnet.base.org
PRIVATE_KEY=your_relayer_private_key
VERIFIER_ADDRESS=0x...
PRICE_FEED_ADDRESS=0x...

# Monitoring
DISCORD_WEBHOOK=https://discord.com/api/webhooks/...
TELEGRAM_BOT_TOKEN=your_bot_token
TELEGRAM_CHAT_ID=your_chat_id

Monitoring & Alerts

class MonitoringService {
    constructor(config) {
        this.webhookUrl = config.discordWebhook;
    }
    
    async alert(message, severity = 'info') {
        const payload = {
            content: `[${severity.toUpperCase()}] ${message}`,
            username: 'Oracle Relayer'
        };
        
        await axios.post(this.webhookUrl, payload);
    }
    
    async priceUpdateAlert(assets, txHash) {
        await this.alert(
            `Price update successful for ${assets.join(', ')}. Tx: ${txHash}`,
            'info'
        );
    }
    
    async errorAlert(error) {
        await this.alert(`Error: ${error.message}`, 'error');
    }
}

Error Handling & Retry Logic

class RobustRelayer extends PriceFeedRelayer {
    constructor(config) {
        super(config);
        this.maxRetries = 3;
        this.retryDelay = 5000; // 5 seconds
    }
    
    async submitPricesWithRetry(priceData) {
        for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
            try {
                await this.submitPrices(priceData);
                return;
            } catch (error) {
                console.log(`Attempt ${attempt} failed:`, error.message);
                
                if (attempt === this.maxRetries) {
                    throw error;
                }
                
                await new Promise(resolve => setTimeout(resolve, this.retryDelay));
            }
        }
    }
}

Security Best Practices

Key Management

  • Use hardware wallets or secure key management systems
  • Rotate keys regularly
  • Monitor relayer account balance and activity

Access Control

  • Whitelist relayer addresses in verifier contract
  • Implement multi-signature for critical operations
  • Regular security audits

Data Validation

  • Validate price ranges and outliers
  • Cross-reference multiple data sources
  • Implement circuit breakers for anomalous data