{"id":20093906,"url":"https://github.com/api3dao/migrate-from-chainlink-to-api3","last_synced_at":"2025-09-20T20:32:28.295Z","repository":{"id":254986383,"uuid":"843305946","full_name":"api3dao/migrate-from-chainlink-to-api3","owner":"api3dao","description":"Migration Guide from Chainlink to API3 Feeds","archived":false,"fork":false,"pushed_at":"2024-10-21T19:21:26.000Z","size":691,"stargazers_count":2,"open_issues_count":2,"forks_count":0,"subscribers_count":8,"default_branch":"main","last_synced_at":"2024-11-13T16:50:43.368Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/api3dao.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":"audit-reports/2024-08-27 Quantstamp.pdf","citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-16T08:17:28.000Z","updated_at":"2024-09-20T14:17:51.000Z","dependencies_parsed_at":"2024-08-27T11:07:59.608Z","dependency_job_id":null,"html_url":"https://github.com/api3dao/migrate-from-chainlink-to-api3","commit_stats":null,"previous_names":["api3dao/migrate-from-chainlink-to-api3"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/api3dao%2Fmigrate-from-chainlink-to-api3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/api3dao%2Fmigrate-from-chainlink-to-api3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/api3dao%2Fmigrate-from-chainlink-to-api3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/api3dao%2Fmigrate-from-chainlink-to-api3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/api3dao","download_url":"https://codeload.github.com/api3dao/migrate-from-chainlink-to-api3/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":233687587,"owners_count":18714345,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-11-13T16:48:47.912Z","updated_at":"2025-09-20T20:32:22.983Z","avatar_url":"https://github.com/api3dao.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Migration Guide from Chainlink to API3 Feeds\n\ndApps that are using Chainlink feeds may want to switch to API3 for reasons such as:\n\n- API3 feeds are trust-minimized due to being based on first-party oracles, while alternatives have middlemen (Chainlink node operators, Wormhole validators, etc.) as a point of failure in addition to the data sources.\n- API3 allows dApps to recoup the value they otherwise would have bled as MEV through the OEV mechanism, effectively providing dApps an entirely new revenue source.\n\nIn addition, it is a common need for a dApp that is designed to use Chainlink feeds to be deployed on a chain that Chainlink does not (adequately) support.\n[API3 Market](https://market.api3.org/) has excellent chain coverage and enables feeds to be spun up in an on-demand manner, which often makes API3 feeds the only managed alternative in such cases.\n\nA dApp can be designed to read a Chainlink feed in two ways:\n\n- The Chainlink feed interface is baked into the dApp.\n- The Chainlink feed interface is integrated into the dApp through an adapter contract.\n\nBoth alternatives can be migrated to using an API3 feed instead through the Api3PartialAggregatorV2V3Interface contract provided in this repo.\n\n## Instructions\n\n1.  Install the dependencies and build.\n\n    ```sh\n    yarn \u0026\u0026 yarn build\n    ```\n\n2.  Create a `.env` file similar to `example.env` with the mnemonic of the wallet that you will use to deploy Api3PartialAggregatorV2V3Interface.\n\n3.  Use [API3 Market](https://market.api3.org/) to find the API3 feed you want to use.\n\n4.  Get the address of the proxy contract that belongs to the feed.\n    For example, clicking the Integrate button at https://market.api3.org/polygon/eth-usd displays the proxy address `0x98643CB1BDA4060d8BD2dc19bceB0acF6F03ae17`.\n\n5.  Deploy the Api3PartialAggregatorV2V3Interface that wraps this proxy.\n    (Note that `NETWORK` is identical to what is in the Market URL.)\n\n    ```sh\n    NETWORK=polygon PROXY_ADDRESS=0x98643CB1BDA4060d8BD2dc19bceB0acF6F03ae17 yarn deploy-deterministically\n    ```\n\nYou can also just print the expected address after deployment.\n\n```sh\nPROXY_ADDRESS=0x98643CB1BDA4060d8BD2dc19bceB0acF6F03ae17 yarn print-deterministic-deployment-address\n```\n\nYou can flatten the contract to verify it on a block explorer.\n\n```sh\nyarn flatten\n```\n\n## Differences between API3 and Chainlink feed interfaces\n\nThere are many architectural, protocol-related and operational differences between Chainlink and API3 feeds.\nFor brevity, we will focus on the differences between the interfaces.\n\n### API3 feed interface\n\nThe API3 feed interface is simply\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IProxy {\n  function read() external view returns (int224 value, uint32 timestamp);\n}\n```\n\nLet us reiterate the related contract docstrings:\n\n\u003e The proxy contracts are generalized to support most types of numerical feeds.\n\u003e This means that the user of this proxy is expected to validate the read values according to the specific use-case.\n\u003e For example, `value` is a signed integer, yet it being negative may not make sense in the case that the feed represents the spot price of an asset.\n\u003e In that case, the user is responsible with ensuring that `value` is not negative.\n\u003e\n\u003e In the case that the feed is from a single source, `timestamp` is the system time of the Airnode (API3's first-party oracle node) when it signed the data.\n\u003e In the case that the feed is from multiple sources, `timestamp` is the median of system times of the Airnodes when they signed the respective data.\n\u003e There are two points to consider while using `timestamp` in your contract logic:\n\u003e\n\u003e 1. It is based on the system time of the Airnodes, and not the block timestamp.\n\u003e    This may be relevant when either of them drifts.\n\u003e 2. `timestamp` is an off-chain value that is being reported, similar to `value`.\n\u003e    Both should only be trusted as much as the Airnodes that report them.\n\u003e\n\u003e Try to be strict about validations, but be wary of:\n\u003e\n\u003e 1. Overly strict validation that may invalidate valid values.\n\u003e 2. Mutable validation parameters that are controlled by a trusted party (which eliminate the trust-minimization guarantees of first-party oracles).\n\u003e 3. Validation parameters that need to be tuned according to external conditions.\n\u003e    If these are not maintained as intended, the result will be equivalent to (1).\n\u003e    Look up the Venus Protocol exploit as a result of the LUNA feed malfunction as an example.\n\n### Chainlink feed interfaces\n\nAt the time of writing this, Chainlink supports two interfaces and a combination of them:\n\n- [AggregatorInterface](./contracts/vendor/AggregatorInterface.sol) (can also be thought of as AggregatorInterfaceV2)\n- [AggregatorV3Interface](./contracts/vendor/AggregatorV3Interface.sol)\n- [AggregatorV2V3Interface](./contracts/vendor/AggregatorV2V3Interface.sol)\n\nThere are two important points to note:\n\n1. Chainlink feeds are updated in rounds, and as such, their interface refers to a `roundId`.\n   According to the documentation:\n   \u003e Data feeds are updated in rounds.\n   \u003e Rounds are identified by their `roundId`, which increases with each new round.\n   \u003e This increase may not be monotonic.\n2. Chainlink feeds support past updates to be queried.\n\n## When to use Api3PartialAggregatorV2V3Interface\n\nApi3PartialAggregatorV2V3Interface should be used as is when the following apply:\n\n- The dApp mainly depends on the current feed value (`latestAnswer()` of AggregatorInterface or `answer` returned by `latestRoundData()` of AggregatorV3Interface).\n- If the dApp uses the current feed timestamp (`latestTimestamp()` of AggregatorInterface or `updatedAt` returned by `latestRoundData()` of AggregatorV3Interface), it is only for a staleness check, e.g., to check if the feed has been updated in the last heartbeat interval.\n- If any other values are used, they do not affect the contract or off-chain infrastructure logic.\n  For example, the dApp only emits `roundId` in an event, and strictly for logging purposes.\n- The off-chain infrastructure does not depend on the events defined in AggregatorInterface.\n\nIn contrast, Api3PartialAggregatorV2V3Interface should not be used as is, and a more specialized adapter contract should be implemented if any of the following applies:\n\n- The dApp logic depends on Chainlink feed idiosyncrasies, such as the round ID increasing with every update.\n- The dApp depends on being able to query past updates using `getAnswer()` or `getTimestamp()` of AggregatorInterface, or `getRoundData()` of AggregatorV3Interface.\n- The off-chain infrastructure depends on the events defined in AggregatorInterface.\n\n### Specialized adapter contracts\n\nAn adapter that simulates all Chainlink feed idiosyncrasies would need to create a new round each time its latest values are read and emit the respective events.\nFor example, `roundId` can be a storage variable that gets incremented each time a function starting with `latest-` is called, during which the respective value and timestamp would also be stored and the event would be emitted.\nFurthermore, users that are planning to refer to a round in the past would need to ensure that such a round has been created by sending a transaction that calls any of the functions that starts with `latest-` at the respective point in time.\n\nAnother inconsistency in behavior is that Api3PartialAggregatorV2V3Interface does not guarantee that the timestamp of a feed will never decrease.\nFor example, in the case that the data sources of an API3 feed are updated to a new set whose latest updates are less recent, the feed timestamp will decrease.\nIf the dApp depends on the feed timestamps to never decrease, `block.timestamp` can be used as the latest timestamp returned by the feed.\n\nFor gas optimization, specialized adapter contracts should simulate Chainlink feed idiosyncrasies only to the extent necessary.\nThe development of such alternative adapter contracts is beyond the scope of this repo.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapi3dao%2Fmigrate-from-chainlink-to-api3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapi3dao%2Fmigrate-from-chainlink-to-api3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapi3dao%2Fmigrate-from-chainlink-to-api3/lists"}