{"id":49724579,"url":"https://github.com/fomoweth/chainlink-router","last_synced_at":"2026-05-09T03:47:00.256Z","repository":{"id":306305670,"uuid":"1025714396","full_name":"fomoweth/chainlink-router","owner":"fomoweth","description":"A price oracle router that aggregates and derives price feeds using Chainlink Aggregators","archived":false,"fork":false,"pushed_at":"2025-10-30T07:14:41.000Z","size":239,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-30T09:13:22.694Z","etag":null,"topics":["chainlink","ethereum","evm","solidity","yul"],"latest_commit_sha":null,"homepage":"","language":"Solidity","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fomoweth.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-24T17:21:08.000Z","updated_at":"2025-10-30T07:14:43.000Z","dependencies_parsed_at":"2025-07-24T23:51:23.695Z","dependency_job_id":"fd39b387-1ade-4873-92b7-064b2f457342","html_url":"https://github.com/fomoweth/chainlink-router","commit_stats":null,"previous_names":["fomoweth/chainlink-router"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/fomoweth/chainlink-router","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fomoweth%2Fchainlink-router","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fomoweth%2Fchainlink-router/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fomoweth%2Fchainlink-router/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fomoweth%2Fchainlink-router/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fomoweth","download_url":"https://codeload.github.com/fomoweth/chainlink-router/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fomoweth%2Fchainlink-router/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32806687,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"online","status_checked_at":"2026-05-09T02:00:06.633Z","response_time":123,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chainlink","ethereum","evm","solidity","yul"],"created_at":"2026-05-09T03:46:59.461Z","updated_at":"2026-05-09T03:47:00.246Z","avatar_url":"https://github.com/fomoweth.png","language":"Solidity","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Chainlink Router\n\n\u003e An on-chain oracle router that aggregates and derives prices across Chainlink price feeds — embodying the philosophy “derive, not depend.”\n\n## Table of Contents\n\n-   [Overview](#overview)\n-   [Key Features](#key-features)\n-   [Architecture](#architecture)\n    -   [Architectural Decisions and Design Principles](#architectural-decisions-and-design-principles)\n    -   [Project Structure](#project-structure)\n    -   [Core Components](#core-components)\n        -   [ChainlinkRouter](#chainlinkrouter)\n        -   [Libraries](#libraries)\n        -   [Custom Types](#custom-types)\n        -   [TypeScript Tooling Ecosystem](#typescript-tooling-ecosystem)\n-   [Deployments](#deployments)\n-   [Usage](#usage)\n    -   [Installation](#installation)\n    -   [Quick Examples](#quick-examples)\n        -   [Routing Queries](#routing-queries)\n        -   [Direct Path Routing](#direct-path-routing)\n        -   [Automatic Multi-hop Routing](#automatic-multi-hop-routing)\n        -   [Solidity Examples](#solidity-examples)\n    -   [Scripting](#scripting)\n        -   [Deploy](#deploy)\n        -   [Feed Registration](#feed-registration)\n        -   [TypeScript Utilities](#typescript-utilities)\n    -   [Testing](#testing)\n-   [API Reference](#api-reference)\n    -   [Query \u0026 Pricing](#query--pricing)\n    -   [System Management](#system-management-owner-only)\n    -   [View Functions](#view-functions)\n    -   [Known Limitations](#known-limitations)\n-   [Resources](#resources)\n\n## Overview\n\n**ChainlinkRouter** is an on-chain oracle routing system that provides dynamic price discovery across token pairs using **Chainlink Aggregators**.  \nIf a direct price feed doesn’t exist, the router derives the price through intermediate assets using multi-hop path-finding logic.\n\nAt its core, **ChainlinkRouter** follows a design philosophy — **“derive, not depend.”**  \nRather than relying on a single price feed, it **derives** prices across multiple connected feeds, ensuring complete price coverage even for non-direct or exotic pairs.\n\n## Key Features\n\n-   **ERC-7201 Storage Pattern**: Namespaced storage for proxy upgrade compatibility\n-   **Direct and Multi-Hop Price Resolution**: Automatically finds the shortest feed path using breadth-first traversal\n-   **Price Derivation Engine**: Supports derived computation (A/B = A/C × C/B), feed inversion, and precision normalization\n-   **Bitmap-Based Graph Storage**: Tracks assets in a paged bitmap structure for efficient, gas-optimized lookups\n-   **Batch Feed and Asset Management**: Register or deregister multiple feeds within a single transaction\n\n## Architecture\n\n### Architectural Decisions and Design Principles\n\nThe ChainlinkRouter architecture was guided by the principle of **derive, not depend** — ensuring that price discovery remains deterministic, decentralized, and fully composable across connected Chainlink feeds.\n\n-   **Gas Optimization Focus**:\n\n    -   Assembly-optimized bitmap operations for minimal runtime overhead\n    -   Packed structs to reduce slot usage in persistent storage\n    -   Batch-oriented feed management to minimize repeated state writes\n\n-   **Scalability Considerations**:\n\n    -   256-asset limit designed for efficient bitmap indexing and path resolution\n    -   Maximum 4-hop traversal depth to balance accuracy and gas efficiency\n    -   Lazy cleanup mechanisms to minimize gas impact on deregistration\n\n-   **Security \u0026 Reliability**:\n    -   Strict input validation for feed and asset registration\n    -   Positive-only price validation to prevent inverted feed corruption\n    -   Automatic dependency cleanup on feed removal to maintain referential integrity\n\n### Project Structure\n\n```text\nchainlink-router/\n├── config/\n│   ├── feeds/...\n│   └── tokens/...\n├── deployments/...\n├── script/\n│   ├── ...\n│   ├── Deploy.s.sol\n│   ├── Register.s.sol\n│   └── ts/\n│       └── src/\n│           ├── ...\n│           ├── encode-feeds.ts\n│           ├── extract.ts\n│           └── fetch-feeds.ts\n├── src/\n│   ├── base/\n│   │   ├── Initializable.sol\n│   │   └── Ownable.sol\n│   ├── interfaces/\n│   │   ├── external\n│   │   │   └── AggregatorInterface.sol\n│   │   └── IChainlinkRouter.sol\n│   ├── libraries/\n│   │   ├── BytesParser.sol\n│   │   ├── Denominations.sol\n│   │   ├── FullMath.sol\n│   │   └── PriceMath.sol\n│   ├── types/\n│   │   ├── BitMap.sol\n│   │   └── FeedConfig.sol\n│   └── ChainlinkRouter.sol\n└── test/\n    ├── ...\n    └── ChainlinkRouter.t.sol\n```\n\n### Core Components\n\n#### ChainlinkRouter\n\nThe centerpiece contract that implements a sophisticated price routing engine.\n\n#### Libraries\n\n-   **BytesParser**: Efficient batch parameter parsing for feed registration\n-   **PriceMath**: Price inversion, derivation, and decimal normalization\n\n#### Custom Types\n\n-   **BitMap**: Custom 256-bit bitmap with assembly-optimized operations\n-   **FeedConfig**: Packed struct containing feed metadata (160-bit address + 96-bit config)\n\n#### TypeScript Tooling Ecosystem\n\nUtilities in `script/ts/`:\n\n-   **Feed Management**: `fetch-feeds.ts` pulls live Chainlink data\n-   **Data Encoding**: `encode-feeds.ts` prepares batch registration parameters\n-   **Deployment Extraction**: `extract.ts` generates deployment documentation from broadcast results\n\n## Deployments\n\nChainlinkRouter is deployed at `0xbB4a04e5F24127440fA933343F2b34f309AebdDe` on [Ethereum](https://etherscan.io/address/0xbB4a04e5F24127440fA933343F2b34f309AebdDe), [Optimism](https://optimistic.etherscan.io/address/0xbB4a04e5F24127440fA933343F2b34f309AebdDe), [Base](https://basescan.org/address/0xbB4a04e5F24127440fA933343F2b34f309AebdDe), and [Arbitrum One](https://arbiscan.io/address/0xbB4a04e5F24127440fA933343F2b34f309AebdDe).\n\nYou can check out the deployment information [here](https://github.com/fomoweth/chainlink-router/blob/main/deployments/index.md).\n\n## Usage\n\n### Installation\n\n```bash\n# Clone the repository\ngit clone https://github.com/fomoweth/chainlink-router.git\n\n# Install dependencies and build the project\ncd chainlink-router \u0026\u0026 forge install \u0026\u0026 forge build\n\n# Install NPM dependencies\ncd script/ts \u0026\u0026 npm install\n```\n\nCreate a `.env` file:\n\n```bash\n# Populate your environment variables following the `.env.example`\ncp .env.example .env\n```\n\n### Quick Examples\n\n#### Routing Queries\n\nUse `cast` to query real-time prices and routing paths directly from the deployed router:\n\n```bash\ncast call \u003cROUTER_ADDRESS\u003e \\\n\"query(address,address)(address[],uint256)\" \\\n\u003cBASE_ADDRESS\u003e \\\n\u003cQUOTE_ADDRESS\u003e \\\n--rpc-url \u003cRPC_URL\u003e\n```\n\n#### Direct Path Routing\n\nExample: `ETH` → `BTC`\n\n```bash\nsource .env\n\ncast call 0xbB4a04e5F24127440fA933343F2b34f309AebdDe \\\n\"query(address,address)(address[],uint256)\" \\\n0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 \\\n0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 \\\n--rpc-url $RPC_ETHEREUM\n```\n\nOutput:\n\n```\npath: [0xAc559F25B1619171CbC396a50854A3240b6A4e99]\nprice: 3536665 (0.03536665 BTC per ETH)\n```\n\n#### Automatic Multi-hop Routing\n\nWhen no direct path exists, the router constructs a composite price route by chaining intermediate reference feeds such as `USD` or `ETH`.\n\n```\nBase ──[Base/USD]──\u003e USD ──[USD/Quote]──\u003e Quote\nBase/Quote = (Base/USD) ÷ (Quote/USD)\n\nBase ──[Base/ETH]──\u003e ETH ──[ETH/Quote]──\u003e Quote\nBase/Quote = (Base/ETH) ÷ (Quote/ETH)\n```\n\nExample: `LINK` → `USD` → `USDC`\n\n```bash\ncast call 0xbB4a04e5F24127440fA933343F2b34f309AebdDe \\\n\"query(address,address)(address[],uint256)\" \\\n0x514910771AF9Ca656af840dff83E8264EcF986CA \\\n0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 \\\n--rpc-url $RPC_ETHEREUM\n```\n\nOutput:\n\n```\npath: [0x76F8C9E423C228E83DCB11d17F0Bd8aEB0Ca01bb, 0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6]\nprice: 17929238 (17.929238 USDC per LINK)\n```\n\nThe router deterministically discovers composite price paths by linking intermediary reference feeds, ensuring complete price derivation coverage across all connected assets.\n\n```\nBase ──[Base/USD]──\u003e USD ──[USD/ETH]──\u003e ETH ──[ETH/Quote]──\u003e Quote\nBase/Quote = (Base/USD) × (USD/ETH) × (ETH/Quote)\n```\n\nThis routing logic ensures complete price discovery by leveraging existing Chainlink feed connectivity between reference assets, powered by a **bitmap-based breadth-first traversal** engine.\n\n#### Solidity Examples\n\nRouting Queries:\n\n```solidity\n// Query ETH/BTC price (may route through USD)\n(address[] memory path, uint256 price) = router.query(WETH_ADDRESS, WBTC_ADDRESS);\n\n// Check if direct feed exists\nbool hasDirect = router.queryFeed(WETH_ADDRESS, WBTC_ADDRESS) != address(0);\n\n// Get asset connections\nBitMap connections = router.getAssetConfiguration(WETH_ADDRESS);\n```\n\nFeed Registration:\n\n```solidity\n// https://en.wikipedia.org/wiki/ISO_4217\naddress USD_ADDRESS = 0x0000000000000000000000000000000000000348;\n\n// Register multiple feeds in batch\nbytes memory params = abi.encodePacked(\n    ETH_USD_FEED, WETH_ADDRESS, USD_ADDRESS,    // ETH/USD\n    BTC_USD_FEED, WBTC_ADDRESS, USD_ADDRESS,\t// BTC/USD\n    USDC_USD_FEED, USDC_ADDRESS, USD_ADDRESS,\t// USDC/USD\n\tETH_BTC_FEED, WETH_ADDRESS, WBTC_ADDRESS,\t// ETH/BTC\n\tBTC_ETH_FEED, WBTC_ADDRESS, WETH_ADDRESS,\t// BTC/ETH\n\tUSDC_ETH_FEED, USDC_ADDRESS, WETH_ADDRESS\t// USDC/ETH\n);\n\nrouter.register(params);\n\n// Deregister multiple feeds in batch\nbytes memory params = abi.encodePacked(\n    WETH_ADDRESS, USD_ADDRESS,  // ETH/USD\n    WBTC_ADDRESS, USD_ADDRESS,\t// BTC/USD\n    USDC_ADDRESS, USD_ADDRESS,  // USDC/USD\n\tWETH_ADDRESS, WBTC_ADDRESS,\t// ETH/BTC\n\tWBTC_ADDRESS, WETH_ADDRESS,\t// BTC/ETH\n\tUSDC_ADDRESS, WETH_ADDRESS\t// USDC/ETH\n);\n\nrouter.deregister(params);\n```\n\n### Scripting\n\n#### Deploy\n\nDeploy **ChainlinkRouter** across multiple networks (as defined in your `.env`):\n\n```bash\nforge script \\\nscript/Deploy.s.sol:Deploy \\\n--multi \\\n--slow \\\n--broadcast \\\n--verify\n```\n\n#### Feed Registration\n\nRegister all Chainlink feeds defined in `config/feeds/`:\n\n```bash\nforge script \\\nscript/Register.s.sol:Register \\\n--broadcast \\\n--chain \u003cCHAIN\u003e\n```\n\n#### TypeScript Utilities\n\nThe TypeScript scripts located under `script/ts/src/` streamline encoding, feed syncing, and deployment documentation.\n\n##### Encode Feed Registration Parameters\n\n```bash\ncd script/ts \u0026\u0026 npx ts-node src/encode-feeds.ts\n```\n\n##### Fetch Live Feed Metadata\n\n```bash\ncd script/ts \u0026\u0026 npx ts-node src/fetch-feeds.ts\n```\n\n##### Extract Deployment Details\n\n```bash\ncd script/ts \u0026\u0026 npx ts-node src/extract.ts\n```\n\n### Testing\n\n```bash\n# Run all tests\nforge test\n\n# Run specific test file\nforge test --match-path test/ChainlinkRouter.t.sol\n```\n\n## API Reference\n\nThe **ChainlinkRouter** exposes a set of view and management functions for price discovery, feed registration, and asset graph introspection.\n\n### Query \u0026 Pricing\n\n```solidity\nfunction query(address base, address quote) external view returns (address[] memory path, uint256 answer);\n```\n\nFinds the optimal path and computes the derived price between `base` and `quote` using breadth-first traversal.\n\n**Input Parameters**\n\n| Name  | Type      | Description                    |\n| ----- | --------- | ------------------------------ |\n| base  | `address` | The address of the base asset  |\n| quote | `address` | The address of the quote asset |\n\n**Return Values**\n\n| Name   | Type        | Description                                                    |\n| ------ | ----------- | -------------------------------------------------------------- |\n| path   | `address[]` | The ordered array of the aggregators used for price derivation |\n| answer | `uint256`   | The final price (normalized decimals)                          |\n\n```solidity\nfunction queryFeed(address base, address quote) external view returns (address feed);\n```\n\nRetrieves the Chainlink aggregator address for a given asset pair (bidirectional lookup).\n\n**Input Parameters**\n\n| Name  | Type      | Description                    |\n| ----- | --------- | ------------------------------ |\n| base  | `address` | The address of the base asset  |\n| quote | `address` | The address of the quote asset |\n\n**Return Values**\n\n| Name | Type      | Description                                                         |\n| ---- | --------- | ------------------------------------------------------------------- |\n| feed | `address` | The address of the Chainlink Aggregator, `address(0)` if not exists |\n\n---\n\n### System Management (Owner-Only)\n\n```solidity\nfunction register(bytes calldata params) external payable;\n```\n\nRegisters multiple feeds and automatically adds new assets if not exists.\n\n**Input Parameters**\n\n| Name   | Type    | Description                              |\n| ------ | ------- | ---------------------------------------- |\n| params | `bytes` | Packed bytes of `[feed][base][quote]...` |\n\n```solidity\nfunction deregister(bytes calldata params) external payable;\n```\n\nDeregisters multiple feeds and automatically cleans up unused assets.\n\n**Input Parameters**\n\n| Name   | Type    | Description                        |\n| ------ | ------- | ---------------------------------- |\n| params | `bytes` | Packed bytes of `[base][quote]...` |\n\n```solidity\nfunction registerAsset(address asset) external payable;\n```\n\nManually adds a new asset to the graph. Automatically called during feed registration.\n\n**Input Parameters**\n\n| Name  | Type      | Description              |\n| ----- | --------- | ------------------------ |\n| asset | `address` | The address of the asset |\n\n```solidity\nfunction deregisterAsset(address asset) external payable;\n```\n\nRemoves an asset and its associated feeds. `USD` **cannot be deregistered**, as it serves as the reference asset.\n\n**Input Parameters**\n\n| Name  | Type      | Description              |\n| ----- | --------- | ------------------------ |\n| asset | `address` | The address of the asset |\n\n---\n\n### View Functions\n\n```solidity\nfunction getFeed(address base, address quote) external view returns (address);\n```\n\nReturns the address of the associated feed for an asset pair.\n\n**Input Parameters**\n\n| Name  | Type      | Description                    |\n| ----- | --------- | ------------------------------ |\n| base  | `address` | The address of the base asset  |\n| quote | `address` | The address of the quote asset |\n\n**Return Values**\n\n| Type      | Description                             |\n| --------- | --------------------------------------- |\n| `address` | The address of the Chainlink Aggregator |\n\n```solidity\nfunction getFeedConfiguration(address base, address quote) external view returns (FeedConfig);\n```\n\nReturns the feed configuration for an asset pair.\n\n**Input Parameters**\n\n| Name  | Type      | Description                    |\n| ----- | --------- | ------------------------------ |\n| base  | `address` | The address of the base asset  |\n| quote | `address` | The address of the quote asset |\n\n**Return Values**\n\n| Type                   | Description                                            |\n| ---------------------- | ------------------------------------------------------ |\n| `FeedConfig (uint256)` | The packed feed configuration containing feed metadata |\n\n```solidity\nfunction getAsset(uint256 id) external view returns (address);\n```\n\nReturns the address of an asset associated with the given ID.\n\n**Input Parameters**\n\n| Name | Type      | Description                        |\n| ---- | --------- | ---------------------------------- |\n| id   | `uint256` | The unique identifier of the asset |\n\n**Return Values**\n\n| Type      | Description                                     |\n| --------- | ----------------------------------------------- |\n| `address` | The address of the asset associated with the ID |\n\n```solidity\nfunction getAssetConfiguration(address asset) external view returns (BitMap);\n```\n\nReturns the bitmap representation of an asset’s connected price feeds.\n\n**Input Parameters**\n\n| Name  | Type      | Description              |\n| ----- | --------- | ------------------------ |\n| asset | `address` | The address of the asset |\n\n**Return Values**\n\n| Type               | Description                              |\n| ------------------ | ---------------------------------------- |\n| `BitMap (uint256)` | The bitmap representing connected assets |\n\n```solidity\nfunction getAssetId(address asset) external view returns (uint256);\n```\n\nReturns the unique identifier for an asset.\n\n**Input Parameters**\n\n| Name  | Type      | Description              |\n| ----- | --------- | ------------------------ |\n| asset | `address` | The address of the asset |\n\n**Return Values**\n\n| Type      | Description                                 |\n| --------- | ------------------------------------------- |\n| `uint256` | The unique identifier assigned to the asset |\n\n```solidity\nfunction numAssets() external view returns (uint256);\n```\n\nReturns the total number of registered assets.\n\n**Return Values**\n\n| Type      | Description                                                             |\n| --------- | ----------------------------------------------------------------------- |\n| `uint256` | The total number of assets currently registered in the system (max 256) |\n\n---\n\n### Known Limitations\n\n-   Maximum 256 assets due to BitMap constraints\n-   Dependent on Chainlink feed reliability and freshness\n-   Gas costs increase with routing complexity\n-   Requires manual feed registration and maintenance\n\n## Resources\n\n-   [Chainlink Data Feeds](https://data.chain.link/feeds)\n-   [Chainlink Price Feeds Doc](https://docs.chain.link/data-feeds/price-feeds)\n-   [Getting a different price denomination](https://docs.chain.link/data-feeds/using-data-feeds#getting-a-different-price-denomination)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffomoweth%2Fchainlink-router","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffomoweth%2Fchainlink-router","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffomoweth%2Fchainlink-router/lists"}