Skip to content

fioprotocol/fio.oracle

Repository files navigation

fio.oracle

Oracle:

  • wrapping FIO tokens and FIO domains from the FIO chain to other chains
  • unwrapping FIO tokens and FIO domains from other chains to FIO chain
  • burn FIO domains from FIO chain to selected NFT chain

Requirements

  • Node.js 22.14.0
  • Linux host (e.g., Ubuntu Server 20.04)

Config And Env Overview

The app now uses the config package with environment-specific JSON files. Most tunables moved from .env into config/*.json.

  • Base config: config/default.json
  • Overrides by network: config/mainnet.json, config/testnet.json
  • Scalar env injection (API keys, secrets): config/custom-environment-variables.json
  • Runtime resolver for ENV_VAR_* placeholders inside config: config/config.js

Environment selection is driven by NODE_ENV and matching dotenv file:

  • NODE_ENV=mainnet → loads config/mainnet.json and .env.mainnet
  • NODE_ENV=testnet → loads config/testnet.json and .env.testnet

References:

  • config/config.js:1 explains the load order and environment selection
  • config/custom-environment-variables.json:1 maps env vars to config paths

What Stays In .env

Create both .env.mainnet and .env.testnet in repo root (use .env.example as a guide): .env.example:1

Required/env-injected values:

  • FIO keys/account
    • FIO_ORACLE_PRIVATE_KEY, FIO_ORACLE_ACCOUNT
  • Chain oracle keys (used via ENV_VAR_* placeholders in config)
    • ETH_ORACLE_PRIVATE, ETH_ORACLE_PUBLIC
    • POLYGON_ORACLE_PRIVATE, POLYGON_ORACLE_PUBLIC
    • BASE_ORACLE_PRIVATE, BASE_ORACLE_PUBLIC
  • RPC/API keys
    • INFURA_ETH_TOKENS_API_KEY, INFURA_BASE_TOKENS_API_KEY, INFURA_POLYGON_NFTS_API_KEY
    • MORALIS_API_KEY
    • MORALIS_RPC_NODE_API_KEY_ETHEREUM, MORALIS_RPC_NODE_API_KEY_POLYGON, MORALIS_RPC_NODE_API_KEY_BASE
    • THIRDWEB_API_KEY
  • AWS creds for S3 log sync
    • AWS_S3_KEY, AWS_S3_SECRET, AWS_S3_PERMITTED_FOLDER

Moved to config (no longer in .env):

  • App port, restart/retry timing, gas settings, FIO servers, job timeouts, logging flags, AWS bucket/region, etc. See config/default.json:1.

Config File Structure

These are examples/skeletons showing the current shape and where values come from.

Main base config: config/default.json:1

{
  "app": { "port": 3000, "restartTimeout": 5000, "maxRetries": 5, "stabilityThreshold": 30000, "fetchTimeoutMs": 120000 },
  "autoRetryMissingActions": { "maxRetries": 5, "retryDelayMs": 5000, "timeRangeStart": 900000, "timeRangeEnd": 3600000, "maxBlockCacheSize": 5000 },
  "aws": { "s3Key": "", "s3Secret": "", "s3Bucket": "fio-oracle-logs", "s3Region": "us-east-1", "s3PermittedFolder": "" },
  "chainDefaults": { "useGasApi": 1, "gasPriceLevel": "average", "defaultHardfork": "london" },
  "fio": { "serverUrlHistory": [], "serverUrlAction": [], "getTableRowsOffset": 1000, "historyOffset": 1000, "lowestOracleId": 0, "maxRetries": 5, "privateKey": "", "account": "", "permission": "active", "serverStaleThresholdMinutes": 5 },
  "jobTimeouts": { "defaultJobTimeout": 60000, "burnDomainsJobTimeout": 86400000, "autoRetryMissingActionsTimeout": 600000, "jobLockTtlSeconds": 1800 },
  "logging": { "logToFile": false, "syncIntervalHours": 1, "enableS3Sync": false },
  "supportedChains": { "tokens": [], "nfts": [] },
  "web3Providers": {
    "infura": { "blocksRangeLimit": 3000, "blocksOffset": 7, "priority": 1, "getLogsPriority": 1 },
    "moralis": { "apiKey": "", "rpcBaseUrl": "https://site1.moralis-nodes.com", "rpcBaseUrlFallback": "https://site2.moralis-nodes.com", "defaultTimeoutBetweenCalls": 1000, "blocksRangeLimit": 95, "blocksOffset": 7, "priority": 2, "getLogsPriority": 3 },
    "thirdweb": { "apiKey": "", "blocksRangeLimit": 995, "blocksOffset": 7, "priority": 3, "getLogsPriority": 2 }
  }
}

Mainnet overrides: config/mainnet.json:1

{
  "app": { "port": 3030 },
  "fio": {
    "serverUrlHistory": ["FIO_SERVER_URL_1", "FIO_SERVER_URL_2"],
    "serverUrlAction":  ["FIO_SERVER_URL_1", "FIO_SERVER_URL_2"],
    "lowestOracleId": 900
  },
  "supportedChains": {
    "tokens": [
      {
        "chainParams": { "chainName": "ethereum", "chainCode": "ETH", "chainId": 1 },
        "contractAddress": "0x...",
        "contractTypeName": "fio.erc20",
        "gasLimit": 200000,
        "defaultGasPrice": 30,
        "infura": { "rpcUrl": "https://mainnet.infura.io/v3", "apiKey": "ENV_VAR_INFURA_ETH_TOKENS_API_KEY" },
        "moralis": { "rpcNodeApiKey": "ENV_VAR_MORALIS_RPC_NODE_API_KEY_ETHEREUM", "chainName": "eth" },
        "thirdweb": { "chainName": "ethereum" },
        "privateKey": "ENV_VAR_ETH_ORACLE_PRIVATE",
        "publicKey":  "ENV_VAR_ETH_ORACLE_PUBLIC"
      }
      // ...additional chains (BASE)
    ],
    "nfts": [
      {
        "chainParams": { "chainName": "polygon", "chainCode": "POL", "chainId": 137 },
        "contractAddress": "0x...",
        "contractTypeName": "fio.erc721",
        "gasLimit": 200000,
        "defaultGasPrice": 50,
        "infura": { "rpcUrl": "https://polygon-mainnet.infura.io/v3", "apiKey": "ENV_VAR_INFURA_POLYGON_NFTS_API_KEY" },
        "moralis": { "rpcNodeApiKey": "ENV_VAR_MORALIS_RPC_NODE_API_KEY_POLYGON", "chainName": "polygon" },
        "thirdweb": { "chainName": "polygon" },
        "privateKey": "ENV_VAR_POLYGON_ORACLE_PRIVATE",
        "publicKey":  "ENV_VAR_POLYGON_ORACLE_PUBLIC"
      }
    ]
  }
}

Testnet overrides: config/testnet.json:1

{
  "app": { "port": 3020 },
  "fio": {
    "serverUrlHistory": ["FIO_HISTORY_URL_V1", "FIO_HISTORY_URL_V1_2", "FIO_HISTORY_URL_V1_3"],
    "serverUrlAction":  ["FIO_SERVER_URL_1", "FIO_SERVER_URL_2", "FIO_SERVER_URL_3"],
    "lowestOracleId": 416
  },
  "supportedChains": {
    "tokens": [
      {
        "chainParams": { "chainName": "sepolia", "chainCode": "ETH", "chainId": 11155111 },
        "contractAddress": "0x...",
        "contractTypeName": "fio.erc20",
        "gasLimit": 200000,
        "defaultGasPrice": 30,
        "infura": { "rpcUrl": "https://sepolia.infura.io/v3", "apiKey": "ENV_VAR_INFURA_ETH_TOKENS_API_KEY" },
        "moralis": { "rpcNodeApiKey": "ENV_VAR_MORALIS_RPC_NODE_API_KEY_ETHEREUM", "chainName": "sepolia" },
        "thirdweb": { "chainName": "sepolia" },
        "privateKey": "ENV_VAR_ETH_ORACLE_PRIVATE",
        "publicKey":  "ENV_VAR_ETH_ORACLE_PUBLIC"
      }
      // ...additional chains (BASE sepolia)
    ],
    "nfts": [
      {
        "chainParams": { "chainName": "polygon amoy", "chainCode": "POL", "chainId": 80002 },
        "contractAddress": "0x...",
        "contractTypeName": "fio.erc721",
        "gasLimit": 200000,
        "defaultGasPrice": 50,
        "infura": { "rpcUrl": "https://polygon-amoy.infura.io/v3", "apiKey": "ENV_VAR_INFURA_POLYGON_NFTS_API_KEY" },
        "moralis": { "rpcNodeApiKey": "ENV_VAR_MORALIS_RPC_NODE_API_KEY_POLYGON", "chainName": "amoy" },
        "thirdweb": { "chainName": "polygonAmoy" },
        "privateKey": "ENV_VAR_POLYGON_ORACLE_PRIVATE",
        "publicKey":  "ENV_VAR_POLYGON_ORACLE_PUBLIC"
      }
    ]
  }
}

Notes:

  • Any string value starting with ENV_VAR_ is resolved at runtime to the respective environment variable by config/config.js:38.
  • Scalar values like aws.s3Key, fio.privateKey, web3Providers.moralis.apiKey, web3Providers.thirdweb.apiKey are mapped to env vars by config/custom-environment-variables.json:1.
  • The web3Providers section centralizes provider-level settings (rate limits, priorities) while per-chain RPC details remain in supportedChains entries.

Install And Run

  • Copy .env.example.env.mainnet and .env.testnet and fill required keys
  • Install deps: npm install

Run server:

  • Mainnet: npm run start (package.json:7)
  • Testnet: npm run start:testnet (package.json:8)

Server listens on app.port from config. Startup/retry behavior is controlled by values in config/default.json:1 and logged to console. See server.js:1 and controller/main.js:1.

Manual Oracle Scripts

There is a helper CLI for on-demand wrap/unwrap/burn actions with optional queueing.

  • Mainnet: npm run oracle
  • Testnet: npm run oracle:testnet

Usage (from scripts/oracle.js:1):

npm run oracle <action> <type> [key:value params]

<action>: wrap | unwrap | burn
<type>:   tokens | nfts

Params (key:value):
  chainCode:<code>          - required (ETH, POL, BASE, ...)
  nftName:<name>            - for nfts (wrap/unwrap/burn)
  tokenId:<id>              - for burn nfts (optional if nftName resolves to tokenId)
  amount:<value>            - for tokens (wrap/unwrap) amount in SUF
  address:<addr>            - EVM address for wrap; FIO handle for unwrap
  obtId:<id>                - wrap: oracle id from FIO table; unwrap/burn: FIO tx hash
  clean:true|false          - if true, enqueue into normal job log instead of immediate execution
  manualSetGasPrice:<wei>   - optional manual gas price override

Examples:
  npm run oracle wrap tokens chainCode:ETH amount:12000000000 address:0x... obtId:944 clean:true manualSetGasPrice:1650000016
  npm run oracle wrap nfts chainCode:POL nftName:fiohacker address:0x... obtId:945 clean:true
  npm run oracle unwrap tokens chainCode:BASE amount:12000000000 address:alice@fiotestnet obtId:<fioTxHash>
  npm run oracle burn nfts chainCode:POL nftName:fiodomainname obtId:<fioTxHash>

Web3 Provider Priority And Failover

The web3Providers section in config/default.json configures provider-level settings that apply across all chains:

"web3Providers": {
  "infura":   { "blocksRangeLimit": 3000, "blocksOffset": 7, "priority": 1, "getLogsPriority": 1 },
  "moralis":  { "blocksRangeLimit": 95,   "blocksOffset": 7, "priority": 2, "getLogsPriority": 3 },
  "thirdweb": { "blocksRangeLimit": 995,  "blocksOffset": 7, "priority": 3, "getLogsPriority": 2 }
}

Key settings:

  • priority – Provider preference order for general RPC calls (lower = preferred)
  • getLogsPriority – Provider preference order specifically for getLogs operations
  • blocksRangeLimit – Max block range per getLogs query (provider rate-limit dependent)
  • blocksOffset – Offset from latest block for finality safety

Per-chain RPC endpoints and API keys are still defined in supportedChains entries.

FIO Server Failover And Retry

Multiple FIO servers can be configured and the app rotates on failures. Configure in config:

  • fio.serverUrlHistory[] and fio.serverUrlAction[] in config/mainnet.json:1 or config/testnet.json:1.
  • Retries and backoff settings are in app, autoRetryMissingActions, and fio.maxRetries in config/default.json:1.

Health Check

Endpoint: GET /api/v1/health (controller/routes/health.js:1)

Example: http://localhost:<port>/api/v1/health

Returns 200 { status: "ok", timestamp: "..." } when healthy.

Logs And S3 Sync

  • Local log roots per environment are prepared under controller/api/.
  • S3 sync is controlled in config:
    • Enable/disable: logging.enableS3Sync (default false)
    • Interval hours: logging.syncIntervalHours
    • File vs console: logging.logToFile
  • AWS credentials come from env; bucket/region are in config:
    • Env: AWS_S3_KEY, AWS_S3_SECRET, AWS_S3_PERMITTED_FOLDER
    • Config: aws.s3Bucket, aws.s3Region

Tip: On startup the app prints chain balances and gas price suggestions when chainDefaults.useGasApi is enabled. See controller/main.js:1.

About

Oracle for moving tokens and code to and from the FIO chain

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors