From 637b6ccddb99baecc06d065668f4bbd8b4b940a8 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Fri, 4 Jul 2025 15:13:46 +0200 Subject: [PATCH 1/2] chore: remove obsolete files --- target_chains/ethereum/sdk/js/.eslintrc.js | 10 - target_chains/ethereum/sdk/js/README.md | 177 ------------------ .../sdk/js/src/EvmPriceServiceConnection.ts | 21 --- .../sdk/js/src/examples/EvmBenchmark.ts | 117 ------------ .../js/src/examples/EvmPriceServiceClient.ts | 64 ------- .../ethereum/sdk/js/src/examples/EvmRelay.ts | 120 ------------ target_chains/ethereum/sdk/js/src/index.ts | 175 ----------------- 7 files changed, 684 deletions(-) delete mode 100644 target_chains/ethereum/sdk/js/.eslintrc.js delete mode 100644 target_chains/ethereum/sdk/js/README.md delete mode 100644 target_chains/ethereum/sdk/js/src/EvmPriceServiceConnection.ts delete mode 100644 target_chains/ethereum/sdk/js/src/examples/EvmBenchmark.ts delete mode 100644 target_chains/ethereum/sdk/js/src/examples/EvmPriceServiceClient.ts delete mode 100644 target_chains/ethereum/sdk/js/src/examples/EvmRelay.ts delete mode 100644 target_chains/ethereum/sdk/js/src/index.ts diff --git a/target_chains/ethereum/sdk/js/.eslintrc.js b/target_chains/ethereum/sdk/js/.eslintrc.js deleted file mode 100644 index bb71486c02..0000000000 --- a/target_chains/ethereum/sdk/js/.eslintrc.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - root: true, - parser: "@typescript-eslint/parser", - plugins: ["@typescript-eslint"], - extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], - rules: { - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/ban-ts-comment": "off", - }, -}; diff --git a/target_chains/ethereum/sdk/js/README.md b/target_chains/ethereum/sdk/js/README.md deleted file mode 100644 index 8f1f6993da..0000000000 --- a/target_chains/ethereum/sdk/js/README.md +++ /dev/null @@ -1,177 +0,0 @@ -# Pyth EVM JS (DEPRECATED) - -> [!WARNING] -> **DEPRECATION NOTICE:** This package is deprecated and no longer maintained. Please use [hermes-client](https://github.com/pyth-network/pyth-crosschain/tree/main/apps/hermes/client/js) instead. - -[Pyth](https://pyth.network/) provides real-time pricing data in a variety of asset classes, including cryptocurrency, -equities, FX and commodities. This library allows you to use these real-time prices on EVM-based networks. - -## Installation - -### npm - -``` -$ npm install --save @pythnetwork/pyth-evm-js -``` - -### Yarn - -``` -$ yarn add @pythnetwork/pyth-evm-js -``` - -## Quickstart - -Pyth stores prices off-chain to minimize gas fees, which allows us to offer a wider selection of products and faster -update times. See [On-Demand Updates](https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand) for more -information about this approach. In order to use Pyth prices on chain, they must be fetched from an off-chain Hermes -instance. The `EvmPriceServiceConnection` class can be used to interact with these services, providing a way to fetch -these prices directly in your code. The following example wraps an existing RPC provider and shows how to obtain Pyth -prices and submit them to the network: - -```typescript -const connection = new EvmPriceServiceConnection("https://hermes.pyth.network"); // See Hermes endpoints section below for other endpoints - -const priceIds = [ - // You can find the ids of prices at https://pyth.network/developers/price-feed-ids#pyth-evm-stable - "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43", // BTC/USD price id - "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace", // ETH/USD price id -]; - -// In order to use Pyth prices in your protocol you need to submit the price update data to Pyth contract in your target -// chain. `getPriceFeedsUpdateData` creates the update data which can be submitted to your contract. Then your contract should -// call the Pyth Contract with this data. -const priceUpdateData = await connection.getPriceFeedsUpdateData(priceIds); - -// If the user is paying the price update fee, you need to fetch it from the Pyth contract. -// Please refer to https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand#fees for more information. -// -// `pythContract` below is a web3.js contract; if you wish to use ethers, you need to change it accordingly. -// You can find the Pyth interface ABI in @pythnetwork/pyth-sdk-solidity npm package. -const updateFee = await pythContract.methods - .getUpdateFee(priceUpdateData) - .call(); - -// Calling someContract method -// `someContract` below is a web3.js contract; if you wish to use ethers, you need to change it accordingly. -// Note: In Hedera you need to pass updateFee * 10^10 as value to the send method as there is an -// inconsistency in the way the value is handled in Hedera RPC and the way it is handled on-chain. -await someContract.methods - .doSomething(someArg, otherArg, priceUpdateData) - .send({ value: updateFee }); -``` - -`SomeContract` looks like so: - -```solidity -pragma solidity ^0.8.0; - -import "@pythnetwork/pyth-sdk-solidity/IPyth.sol"; -import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol"; - -contract SomeContract { - IPyth pyth; - - constructor(address pythContract) { - pyth = IPyth(pythContract); - } - - function doSomething( - uint someArg, - string memory otherArg, - bytes[] calldata priceUpdateData - ) public payable { - // Update the prices to be set to the latest values - uint fee = pyth.getUpdateFee(priceUpdateData); - pyth.updatePriceFeeds{ value: fee }(priceUpdateData); - - // Doing other things that uses prices - bytes32 priceId = 0xf9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b; - // Get the price if it is not older than 10 seconds from the current time. - PythStructs.Price price = pyth.getPriceNoOlderThan(priceId, 10); - } -} - -``` - -We strongly recommend reading our guide which explains [how to work with Pyth price feeds](https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices). - -### Off-chain prices - -Many applications additionally need to display Pyth prices off-chain, for example, in their frontend application. -The `EvmPriceServiceConnection` provides two different ways to fetch the current Pyth price. -The code blocks below assume that the `connection` and `priceIds` objects have been initialized as shown above. -The first method is a single-shot query: - -```typescript -// `getLatestPriceFeeds` returns a `PriceFeed` for each price id. It contains all information about a price and has -// utility functions to get the current and exponentially-weighted moving average price, and other functionality. -const priceFeeds = await connection.getLatestPriceFeeds(priceIds); -// Get the price if it is not older than 60 seconds from the current time. -console.log(priceFeeds[0].getPriceNoOlderThan(60)); // Price { conf: '1234', expo: -8, price: '12345678' } -// Get the exponentially-weighted moving average price if it is not older than 60 seconds from the current time. -console.log(priceFeeds[1].getEmaPriceNoOlderThan(60)); -``` - -The object also supports a streaming websocket connection that allows you to subscribe to every new price update for a given feed. -This method is useful if you want to show continuously updating real-time prices in your frontend: - -```typescript -// Subscribe to the price feeds given by `priceId`. The callback will be invoked every time the requested feed -// gets a price update. -connection.subscribePriceFeedUpdates(priceIds, (priceFeed) => { - console.log( - `Received update for ${priceFeed.id}: ${priceFeed.getPriceNoOlderThan(60)}` - ); -}); - -// When using the subscription, make sure to close the websocket upon termination to finish the process gracefully. -setTimeout(() => { - connection.closeWebSocket(); -}, 60000); -``` - -### Examples - -There are two examples in [examples](./src/examples/). - -#### EvmPriceServiceClient - -[This example](./src/examples/EvmPriceServiceClient.ts) fetches `PriceFeed` updates using both a HTTP-request API and a streaming websocket API. You can run it with `npm run example-client`. A full command that prints BTC and ETH price feeds, in the testnet network, looks like so: - -```bash -npm run example-client -- \ - --endpoint https://hermes.pyth.network \ - --price-ids \ - 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 \ - 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace -``` - -#### EvmRelay - -[This example](./src/examples/EvmRelay.ts) shows how to update prices on an EVM network. It does the following: - -1. Gets update data to update given price feeds. -2. Calls the pyth contract with the update data. -3. Submits it to the network and prints the txhash if successful. - -You can run this example with `npm run example-relay`. A full command that updates BTC and ETH prices on the BNB Chain -testnet network looks like so: - -```bash -npm run example-relay -- \ - --network "https://data-seed-prebsc-1-s1.binance.org:8545" \ - --pyth-contract "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb"\ - --mnemonic "my good mnemonic" \ - --endpoint https://hermes.pyth.network \ - --price-ids \ - "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" \ - "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace" -``` - -## Hermes endpoints - -Pyth offers a free public endpoint at [https://hermes.pyth.network](https://hermes.pyth.network). However, it is -recommended to obtain a private endpoint from one of the Hermes RPC providers for more reliability. You can find more -information about Hermes RPC providers -[here](https://docs.pyth.network/documentation/pythnet-price-feeds/hermes#public-endpoint). diff --git a/target_chains/ethereum/sdk/js/src/EvmPriceServiceConnection.ts b/target_chains/ethereum/sdk/js/src/EvmPriceServiceConnection.ts deleted file mode 100644 index b985c9a3b9..0000000000 --- a/target_chains/ethereum/sdk/js/src/EvmPriceServiceConnection.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { - PriceServiceConnection, - HexString, -} from "@pythnetwork/price-service-client"; -import { Buffer } from "buffer"; - -export class EvmPriceServiceConnection extends PriceServiceConnection { - /** - * Gets price update data which then can be submitted to Pyth contract to update the prices. - * This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price ids) - * - * @param priceIds Array of hex-encoded price ids. - * @returns Array of price update data. - */ - async getPriceFeedsUpdateData(priceIds: HexString[]): Promise { - const latestVaas = await this.getLatestVaas(priceIds); - return latestVaas.map( - (vaa) => "0x" + Buffer.from(vaa, "base64").toString("hex"), - ); - } -} diff --git a/target_chains/ethereum/sdk/js/src/examples/EvmBenchmark.ts b/target_chains/ethereum/sdk/js/src/examples/EvmBenchmark.ts deleted file mode 100644 index b8ddf95861..0000000000 --- a/target_chains/ethereum/sdk/js/src/examples/EvmBenchmark.ts +++ /dev/null @@ -1,117 +0,0 @@ -import Web3 from "web3"; -import yargs from "yargs"; -import { hideBin } from "yargs/helpers"; - -import { EvmPriceServiceConnection } from "../index"; -import HDWalletProvider from "@truffle/hdwallet-provider"; -import PythInterfaceAbi from "@pythnetwork/pyth-sdk-solidity/abis/IPyth.json"; - -const argv = yargs(hideBin(process.argv)) - .option("network", { - description: "RPC of the network to relay on.", - type: "string", - required: true, - }) - .option("endpoint", { - description: - "Endpoint URL for the price service. e.g: https://endpoint/example", - type: "string", - required: true, - }) - .option("pyth-contract", { - description: "Pyth contract address.", - type: "string", - required: true, - }) - .option("price-id", { - description: - "Price feed id (in hex) to fetch" + " e.g: 0xf9c0172ba10dfa4d19088d...", - type: "string", - required: true, - }) - .option("timestamp", { - description: "Timestamp of the prices to fetch" + " e.g., 2022-", // TODO - type: "string", - required: true, - }) - .option("mnemonic", { - description: "Mnemonic (private key) for sender", - type: "string", - required: true, - }) - .help() - .alias("help", "h") - .parserConfiguration({ - "parse-numbers": false, - }) - .parseSync(); - -const network = argv.network; -const pythContractAddr = argv.pythContract; - -const connection = new EvmPriceServiceConnection(argv.endpoint); - -async function run() { - const provider = new HDWalletProvider({ - mnemonic: { - phrase: argv.mnemonic, - }, - providerOrUrl: network, - }); - - // @ts-ignore - const web3 = new Web3(provider); - const priceId = argv.priceId as string; - // The unix timestamp in seconds - const unixTimestamp = Date.parse(argv.timestamp) / 1000; - - console.log(`Querying unix timestamp: ${unixTimestamp}`); - - const [priceFeedUpdateVaa, updateTimestamp] = await connection.getVaa( - priceId, - unixTimestamp, - ); - console.log(`Next pyth update was at: ${updateTimestamp}`); - console.log(priceFeedUpdateVaa); - - const priceFeedUpdate = - "0x" + Buffer.from(priceFeedUpdateVaa, "base64").toString("hex"); - - const pythContract = new web3.eth.Contract( - PythInterfaceAbi as any, - pythContractAddr, - { - from: provider.getAddress(0), - }, - ); - - const updateFee = await pythContract.methods - .getUpdateFee([priceFeedUpdate]) - .call(); - console.log(`Update fee: ${updateFee}`); - - // In real use cases, you would pass the update to your contract, then call parsePriceFeedUpdates within your contract. - // When invoked on-chain, this function will return a PriceFeed struct containing the data in the price update - // (such as the current price). - await pythContract.methods - .parsePriceFeedUpdates( - [priceFeedUpdate], - [priceId], - // parsePriceFeedUpdates will reject any price update outside of the time window provided in the following - // two arguments. Integrators can use this to specify the timestamp of the update they are expecting. - unixTimestamp, - unixTimestamp + 5, - ) - .send({ value: updateFee }) - .on("transactionHash", (hash: string) => { - console.log(`Tx hash: ${hash}`); - }) - .on("error", (err: any, receipt: any) => { - console.error(receipt); - throw err; - }); - - provider.engine.stop(); -} - -run(); diff --git a/target_chains/ethereum/sdk/js/src/examples/EvmPriceServiceClient.ts b/target_chains/ethereum/sdk/js/src/examples/EvmPriceServiceClient.ts deleted file mode 100644 index 3c24285ff2..0000000000 --- a/target_chains/ethereum/sdk/js/src/examples/EvmPriceServiceClient.ts +++ /dev/null @@ -1,64 +0,0 @@ -import yargs from "yargs"; -import { hideBin } from "yargs/helpers"; - -import { EvmPriceServiceConnection } from "../index"; - -function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -const argv = yargs(hideBin(process.argv)) - .option("endpoint", { - description: - "Endpoint URL for the Price Service. e.g: https://endpoint/example", - type: "string", - required: true, - }) - .option("price-ids", { - description: - "Space separated price feed ids (in hex) to fetch" + - " e.g: 0xf9c0172ba10dfa4d19088d...", - type: "array", - required: true, - }) - .help() - .alias("help", "h") - .parserConfiguration({ - "parse-numbers": false, - }) - .parseSync(); - -async function run() { - const connection = new EvmPriceServiceConnection(argv.endpoint, { - logger: console, // Providing logger will allow the connection to log its events. - }); - - const priceIds = argv.priceIds as string[]; - console.log(priceIds); - const priceFeeds = await connection.getLatestPriceFeeds(priceIds); - console.log(priceFeeds); - console.log(priceFeeds?.at(0)?.getPriceNoOlderThan(60)); - - const updateData = await connection.getPriceFeedsUpdateData(priceIds); - console.log(updateData); - - console.log("Subscribing to price feed updates."); - - await connection.subscribePriceFeedUpdates(priceIds, (priceFeed) => { - console.log( - `Current price for ${priceFeed.id}: ${JSON.stringify( - priceFeed.getPriceNoOlderThan(60), - )}.`, - ); - }); - - await sleep(600000); - - // To close the websocket you should either unsubscribe from all - // price feeds or call `connection.closeWebSocket()` directly. - - console.log("Unsubscribing from price feed updates."); - await connection.unsubscribePriceFeedUpdates(priceIds); -} - -run(); diff --git a/target_chains/ethereum/sdk/js/src/examples/EvmRelay.ts b/target_chains/ethereum/sdk/js/src/examples/EvmRelay.ts deleted file mode 100644 index 19582c9d38..0000000000 --- a/target_chains/ethereum/sdk/js/src/examples/EvmRelay.ts +++ /dev/null @@ -1,120 +0,0 @@ -import Web3 from "web3"; -import yargs from "yargs"; -import { hideBin } from "yargs/helpers"; - -import { EvmPriceServiceConnection } from "../index"; -import HDWalletProvider from "@truffle/hdwallet-provider"; -import PythInterfaceAbi from "@pythnetwork/pyth-sdk-solidity/abis/IPyth.json"; - -const argv = yargs(hideBin(process.argv)) - .option("network", { - description: "RPC of the network to relay on.", - type: "string", - required: true, - }) - .option("endpoint", { - description: - "Endpoint URL for the price service. e.g: https://endpoint/example", - type: "string", - required: true, - }) - .option("pyth-contract", { - description: "Pyth contract address.", - type: "string", - required: true, - }) - .option("price-ids", { - description: - "Space separated price feed ids (in hex) to fetch" + - " e.g: 0xf9c0172ba10dfa4d19088d...", - type: "array", - required: true, - }) - .option("mnemonic", { - description: "Mnemonic (private key) for sender", - type: "string", - required: true, - }) - .help() - .alias("help", "h") - .parserConfiguration({ - "parse-numbers": false, - }) - .parseSync(); - -const network = argv.network; -const pythContractAddr = argv.pythContract; - -const connection = new EvmPriceServiceConnection(argv.endpoint); - -async function run() { - const provider = new HDWalletProvider({ - mnemonic: { - phrase: argv.mnemonic, - }, - providerOrUrl: network, - }); - - // @ts-ignore - const web3 = new Web3(provider); - const priceIds = argv.priceIds as string[]; - - const priceFeeds = await connection.getLatestPriceFeeds(priceIds); - console.log(priceFeeds); - - const priceFeedUpdateData = - await connection.getPriceFeedsUpdateData(priceIds); - console.log(priceFeedUpdateData); - - const pythContract = new web3.eth.Contract( - PythInterfaceAbi as any, - pythContractAddr, - { - from: provider.getAddress(0), - }, - ); - - const updateFee = await pythContract.methods - .getUpdateFee(priceFeedUpdateData) - .call(); - console.log(`Update fee: ${updateFee}`); - - let txHash = undefined; - await pythContract.methods - .updatePriceFeeds(priceFeedUpdateData) - .send({ value: updateFee }) - .on("transactionHash", (hash: string) => { - txHash = hash; - }) - .on("error", (err: any, receipt: any) => { - console.error(receipt); - throw err; - }); - - console.log(`Tx hash: ${txHash}`); - if (txHash === undefined) { - console.error("Something went wrong. Could not send price update tx."); - } else { - console.log("Awaiting tx confirmation..."); - let receipt = undefined; - while (!receipt) { - receipt = await web3.eth.getTransactionReceipt(txHash); - } - - // For on-chain use, you will typically perform the getPriceNoOlderThan call within the same transaction as updatePriceFeeds. - // The call to getPriceNoOlderThan below simply demonstrates that the on-chain price was in fact updated. - // Note that the code above for waiting for tx confirmation is a little flaky -- if so, you may see an old price printed here. - for (const priceId of priceIds) { - const [price, conf, expo, publishTime] = await pythContract.methods - .getPriceNoOlderThan(priceId, 60) // 60 seconds staleness tolerance - .call(); - console.log( - `Updated ${priceId} to (${price} +- ${conf}) * 10^${expo} at unix timestamp ${publishTime}`, - ); - } - } - - provider.engine.stop(); -} - -run(); diff --git a/target_chains/ethereum/sdk/js/src/index.ts b/target_chains/ethereum/sdk/js/src/index.ts deleted file mode 100644 index f21f6f3558..0000000000 --- a/target_chains/ethereum/sdk/js/src/index.ts +++ /dev/null @@ -1,175 +0,0 @@ -export { EvmPriceServiceConnection } from "./EvmPriceServiceConnection"; - -export { - DurationInMs, - HexString, - Price, - PriceFeed, - PriceServiceConnectionConfig, - UnixTimestamp, -} from "@pythnetwork/price-service-client"; - -export const CONTRACT_ADDR: Record = { - // Mainnets - apechain_mainnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - arbitrum: "0xff1a0f4744e8582DF1aE09D5611b887B6a12925C", - astar_zkevm: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - aurora: "0xF89C7b475821EC3fDC2dC8099032c05c6c0c9AB9", - avalanche: "0x4305FB66699C3B2702D4d05CF36551390A4c69C6", - blast: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - bnb: "0x4D7E825f80bDf85e913E0DD2A2D54927e9dE1594", - base: "0x8250f4aF4B972684F7b336503E2D6dFeDeB1487a", - boba: "0x4374e5a8b9C22271E9EB878A2AA31DE97DF15DAF", - bttc: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - canto: "0x98046Bd286715D3B0BC227Dd7a956b83D8978603", - celo: "0xff1a0f4744e8582DF1aE09D5611b887B6a12925C", - chiliz: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - conflux_espace: "0xe9d69CdD6Fe41e7B621B4A688C5D1a68cB5c8ADc", - core_dao: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - cronos: "0xE0d0e68297772Dd5a1f1D99897c581E2082dbA5B", - cronos_zkevm_mainnet: "0x056f829183ec806a78c26c98961678c24fab71af", - eos: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - evmos: "0x354bF866A4B006C9AF9d9e06d9364217A8616E12", - ethereum: "0x4305FB66699C3B2702D4d05CF36551390A4c69C6", - etherlink: "0x2880aB155794e7179c9eE2e38200202908C17B43", - eventum_mainnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - fantom: "0xff1a0f4744e8582DF1aE09D5611b887B6a12925C", - fantom_sonic_mainnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - filecoin: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - flow_mainnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - gnosis: "0x2880aB155794e7179c9eE2e38200202908C17B43", - gravity: "0x2880aB155794e7179c9eE2e38200202908C17B43", - hedera: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - hemi_mainnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - horizen_eon: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - idex_xchain_mainnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - injective_inevm: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - iota: "0x8D254a21b3C86D32F7179855531CE99164721933", - kava: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - kcc: "0xE0d0e68297772Dd5a1f1D99897c581E2082dbA5B", - kaia: "0x2880aB155794e7179c9eE2e38200202908C17B43", - lightlink: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - linea: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - manta: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - mantle: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - merlin: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - meter: "0xbFe3f445653f2136b2FD1e6DdDb5676392E3AF16", - mode: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - morph: "0x2880aB155794e7179c9eE2e38200202908C17B43", - neon: "0x7f2dB085eFC3560AFF33865dD727225d91B4f9A5", - opbnb: "0x2880aB155794e7179c9eE2e38200202908C17B43", - optimism: "0xff1a0f4744e8582DF1aE09D5611b887B6a12925C", - parallel: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - polygon: "0xff1a0f4744e8582DF1aE09D5611b887B6a12925C", - polygon_zkevm: "0xC5E56d6b40F3e3B5fbfa266bCd35C37426537c65", - polynomial: "0x2880aB155794e7179c9eE2e38200202908C17B43", - ronin: "0x2880aB155794e7179c9eE2e38200202908C17B43", - scroll: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - sei_evm_mainnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - shimmer: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - skate: "0x2880aB155794e7179c9eE2e38200202908C17B43", - superseed_mainnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - viction: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - wemix: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - zetachain: "0x2880aB155794e7179c9eE2e38200202908C17B43", - zkfair: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - zksync_era: "0xf087c864AEccFb6A2Bf1Af6A0382B0d0f6c5D834", - // Testnets (Stable sources) - abstract_testnet: "0x47F2A9BDAd52d65b66287253cf5ca0D2b763b486", - apechain_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - arbitrum_blueberry: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - arbitrum_sepolia: "0x4374e5a8b9C22271E9EB878A2AA31DE97DF15DAF", - astar_zkevm_testnet: "0x8D254a21b3C86D32F7179855531CE99164721933", - astar_zkyoto_testnet: "0x8D254a21b3C86D32F7179855531CE99164721933", - aurora_testnet: "0x74f09cb3c7e2A01865f424FD14F6dc9A14E3e94E", - bnb_testnet: "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb", - base_sepolia: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - berachain_testnet_v2: "0x2880aB155794e7179c9eE2e38200202908C17B43", - blackbird: "0x2880aB155794e7179c9eE2e38200202908C17B43", - blast_s2_testnet: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - boba_goerli: "0x8D254a21b3C86D32F7179855531CE99164721933", - boba_sepolia: "0x8D254a21b3C86D32F7179855531CE99164721933", - bttc_testnet: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - cant_testnet: "0x26DD80569a8B23768A1d80869Ed7339e07595E85", - celo_alfajores: "0x74f09cb3c7e2A01865f424FD14F6dc9A14E3e94E", - chiado: "0x98046Bd286715D3B0BC227Dd7a956b83D8978603", - chiliz_testnet: "0x23f0e8FAeE7bbb405E7A7C3d60138FCfd43d7509", - conflux_espace_testnet: "0xDd24F84d36BF92C65F92307595335bdFab5Bbd21", - core_dao_testnet: "0x8D254a21b3C86D32F7179855531CE99164721933", - coredao_testnet_v2: "0x2880aB155794e7179c9eE2e38200202908C17B43", - cronos_testnet: "0x36825bf3Fbdf5a29E2d5148bfe7Dcf7B5639e320", - cronos_zkevm_testnet: "0xB1DB1498902F08E16E11F1a423ec9CCB9537E1D6", - dela_deperp_testnet: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - dela_mithreum_deperp_testnet: "0xe9d69CdD6Fe41e7B621B4A688C5D1a68cB5c8ADc", - ethena_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - etherlink_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - eos_testnet: "0x0708325268dF9F66270F1401206434524814508b", - eventum_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - evmos_testnet: "0x74f09cb3c7e2A01865f424FD14F6dc9A14E3e94E", - sonic_blaze_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - fantom_sonic_testnet: "0x96124d1F6E44FfDf1fb5D6d74BB2DE1B7Fbe7376", - fantom_testnet: "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb", - filecoin_calibration: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - flow_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - fuji: "0x23f0e8FAeE7bbb405E7A7C3d60138FCfd43d7509", - hedera_testnet: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - idex_xchain_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - injective_inevm_testnet: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - kakarot_sepolia: "0xe9d69CdD6Fe41e7B621B4A688C5D1a68cB5c8ADc", - kava_testnet: "0xfA25E653b44586dBbe27eE9d252192F0e4956683", - kcc_testnet: "0x74f09cb3c7e2A01865f424FD14F6dc9A14E3e94E", - kinto: "0x2880aB155794e7179c9eE2e38200202908C17B43", - kaia_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - kraken_ink_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - lightlink_pegasus_testnet: "0x5D289Ad1CE59fCC25b6892e7A303dfFf3a9f7167", - linea_goerli: "0xdF21D137Aadc95588205586636710ca2890538d5", - linea_sepolia: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - manta_testnet: "0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c", - manta_sepolia: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - mantle_sepolia: "0x98046Bd286715D3B0BC227Dd7a956b83D8978603", - merlin_testnet: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - meter_testnet: "0x5a71C07a0588074443545eE0c08fb0375564c3E4", - mode_testnet: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - monad_devnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - monad_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - morph_holesky_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - morph_testnet: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - movement_evm_devnet: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - movement_evm_devnet_imola: "0x2880aB155794e7179c9eE2e38200202908C17B43", - mumbai: "0xFC6bd9F9f0c6481c6Af3A7Eb46b296A5B85ed379", - neon_devnet: "0x0708325268dF9F66270F1401206434524814508b", - nighthawk: "0x2880aB155794e7179c9eE2e38200202908C17B43", - olive_testnet: "0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c", - opbnb_testnet: "0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c", - optimism_celestia_raspberry: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - optimism_goerli: "0xDd24F84d36BF92C65F92307595335bdFab5Bbd21", - optimism_sepolia: "0x0708325268dF9F66270F1401206434524814508b", - orange_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - parallel_testnet: "0x23f0e8FAeE7bbb405E7A7C3d60138FCfd43d7509", - polygon_amoy: "0x2880aB155794e7179c9eE2e38200202908C17B43", - polygon_blackberry: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - polygon_zkevm_testnet: "0xFf255f800044225f54Af4510332Aa3D67CC77635", - polynomial_testnet: "0x23f0e8FAeE7bbb405E7A7C3d60138FCfd43d7509", - reya_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - saigon: "0xEbe57e8045F2F230872523bbff7374986E45C486", - sei_evm_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - scroll_sepolia: "0x41c9e39574F40Ad34c79f1C99B66A45eFB830d4c", - sepolia: "0xDd24F84d36BF92C65F92307595335bdFab5Bbd21", - shimmer_testnet: "0x8D254a21b3C86D32F7179855531CE99164721933", - skate_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - soneium_minato_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - story_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - superseed_testnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - tabi_testnet: "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb", - taiko_hekla: "0x2880aB155794e7179c9eE2e38200202908C17B43", - taiko_mainnet: "0x2880aB155794e7179c9eE2e38200202908C17B43", - unichain_sepolia: "0x2880aB155794e7179c9eE2e38200202908C17B43", - viction_testnet: "0x5D289Ad1CE59fCC25b6892e7A303dfFf3a9f7167", - wemix_testnet: "0x26DD80569a8B23768A1d80869Ed7339e07595E85", - zetachain_testnet: "0x0708325268dF9F66270F1401206434524814508b", - zkfair_testnet: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", - zksync_era_goerli: "0x8739d5024B5143278E2b15Bd9e7C26f6CEc658F1", - zksync_era_sepolia: "0x056f829183Ec806A78c26C98961678c24faB71af", - // Testnets (Beta sources) - horizen_gobi_testnet: "0xA2aa501b19aff244D90cc15a4Cf739D2725B5729", -}; From b443c3083f67c361eda53c98aac6edb060002059 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Fri, 4 Jul 2025 15:02:03 +0200 Subject: [PATCH 2/2] feat(evm/sdk/js): add pyth filler --- pnpm-lock.yaml | 435 +++--------- target_chains/ethereum/sdk/js/README.md | 78 +++ .../ethereum/sdk/js/eslint.config.js | 1 + target_chains/ethereum/sdk/js/package.json | 21 +- target_chains/ethereum/sdk/js/src/filler.ts | 218 ++++++ target_chains/ethereum/sdk/js/src/index.ts | 3 + .../ethereum/sdk/js/src/multicall3-bundler.ts | 79 +++ target_chains/ethereum/sdk/js/src/pyth-abi.ts | 660 ++++++++++++++++++ .../sdk/js/src/tracer/debug-trace-call.ts | 126 ++++ .../sdk/js/src/tracer/trace-call-many.ts | 117 ++++ 10 files changed, 1379 insertions(+), 359 deletions(-) create mode 100644 target_chains/ethereum/sdk/js/README.md create mode 100644 target_chains/ethereum/sdk/js/eslint.config.js create mode 100644 target_chains/ethereum/sdk/js/src/filler.ts create mode 100644 target_chains/ethereum/sdk/js/src/index.ts create mode 100644 target_chains/ethereum/sdk/js/src/multicall3-bundler.ts create mode 100644 target_chains/ethereum/sdk/js/src/pyth-abi.ts create mode 100644 target_chains/ethereum/sdk/js/src/tracer/debug-trace-call.ts create mode 100644 target_chains/ethereum/sdk/js/src/tracer/trace-call-many.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8755975b1c..5bed667663 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,315 +6,24 @@ settings: catalogs: default: - '@amplitude/analytics-browser': - specifier: ^2.13.0 - version: 2.13.0 - '@amplitude/plugin-autocapture-browser': - specifier: ^1.0.0 - version: 1.0.0 - '@axe-core/react': - specifier: ^4.10.1 - version: 4.10.1 - '@babel/cli': - specifier: ^7.27.2 - version: 7.27.2 - '@babel/core': - specifier: ^7.27.1 - version: 7.27.1 - '@babel/preset-typescript': - specifier: ^7.27.1 - version: 7.27.1 - '@bonfida/spl-name-service': - specifier: ^3.0.10 - version: 3.0.10 - '@clickhouse/client': - specifier: ^1.11.0 - version: 1.11.0 - '@coral-xyz/anchor': - specifier: ^0.30.1 - version: 0.30.1 '@cprussin/eslint-config': specifier: ^4.0.2 version: 4.0.2 - '@cprussin/jest-config': - specifier: ^2.0.2 - version: 2.0.2 - '@cprussin/prettier-config': - specifier: ^2.2.2 - version: 2.2.2 - '@cprussin/tsconfig': - specifier: ^3.1.2 - version: 3.1.2 - '@floating-ui/react': - specifier: ^0.27.6 - version: 0.27.6 - '@headlessui/react': - specifier: ^2.2.0 - version: 2.2.0 - '@heroicons/react': - specifier: ^2.2.0 - version: 2.2.0 - '@next/third-parties': - specifier: ^15.3.2 - version: 15.3.2 - '@phosphor-icons/react': - specifier: ^2.1.7 - version: 2.1.7 - '@pythnetwork/client': - specifier: ^2.22.1 - version: 2.22.1 - '@radix-ui/react-select': - specifier: ^2.1.6 - version: 2.1.6 - '@radix-ui/react-slot': - specifier: ^1.1.2 - version: 1.1.2 - '@radix-ui/react-switch': - specifier: ^1.1.3 - version: 1.1.3 - '@react-hookz/web': - specifier: ^25.1.0 - version: 25.1.0 - '@solana/wallet-adapter-base': - specifier: ^0.9.24 - version: 0.9.24 - '@solana/wallet-adapter-react': - specifier: ^0.15.36 - version: 0.15.36 - '@solana/wallet-adapter-react-ui': - specifier: ^0.9.36 - version: 0.9.36 - '@solana/wallet-adapter-wallets': - specifier: ^0.19.33 - version: 0.19.33 - '@solana/web3.js': - specifier: ^1.98.0 - version: 1.98.0 - '@storybook/addon-essentials': - specifier: ^8.6.12 - version: 8.6.12 - '@storybook/addon-styling-webpack': - specifier: ^1.0.1 - version: 1.0.1 - '@storybook/addon-themes': - specifier: ^8.6.12 - version: 8.6.12 - '@storybook/blocks': - specifier: ^8.6.12 - version: 8.6.12 - '@storybook/nextjs': - specifier: ^8.6.12 - version: 8.6.12 - '@storybook/react': - specifier: ^8.6.12 - version: 8.6.12 - '@svgr/webpack': - specifier: ^8.1.0 - version: 8.1.0 - '@tailwindcss/forms': - specifier: ^0.5.10 - version: 0.5.10 - '@tailwindcss/postcss': - specifier: ^4.1.6 - version: 4.1.6 - '@tanstack/react-query': - specifier: ^5.71.5 - version: 5.71.5 - '@types/jest': - specifier: ^29.5.14 - version: 29.5.14 - '@types/mdx': - specifier: ^2.0.13 - version: 2.0.13 - '@types/node': - specifier: ^22.14.0 - version: 22.14.0 - '@types/react': - specifier: ^19.1.0 - version: 19.1.0 - '@types/react-dom': - specifier: ^19.1.1 - version: 19.1.1 - '@vercel/functions': - specifier: ^2.0.0 - version: 2.0.0 - autoprefixer: - specifier: ^10.4.21 - version: 10.4.21 - babel-plugin-react-compiler: - specifier: 19.1.0-rc.1 - version: 19.1.0-rc.1 - bcp-47: - specifier: ^2.1.0 - version: 2.1.0 - bs58: - specifier: ^6.0.0 - version: 6.0.0 - class-variance-authority: - specifier: ^0.7.1 - version: 0.7.1 - clsx: - specifier: ^2.1.1 - version: 2.1.1 - connectkit: - specifier: ^1.9.0 - version: 1.9.0 - copyfiles: - specifier: ^2.4.1 - version: 2.4.1 - cryptocurrency-icons: - specifier: ^0.18.1 - version: 0.18.1 - css-loader: - specifier: ^7.1.2 - version: 7.1.2 - dnum: - specifier: ^2.14.0 - version: 2.14.0 eslint: specifier: ^9.23.0 version: 9.23.0 - framer-motion: - specifier: ^12.6.3 - version: 12.6.3 - fumadocs-core: - specifier: ^15.3.0 - version: 15.3.0 - fumadocs-mdx: - specifier: ^11.6.3 - version: 11.6.3 - fumadocs-ui: - specifier: ^15.3.0 - version: 15.3.0 - highlight.js: - specifier: ^11.11.1 - version: 11.11.1 - ip-range-check: - specifier: ^0.2.0 - version: 0.2.0 - jest: - specifier: ^29.7.0 - version: 29.7.0 - lightweight-charts: - specifier: ^5.0.5 - version: 5.0.5 - lucide-react: - specifier: ^0.487.0 - version: 0.487.0 - modern-normalize: - specifier: ^3.0.1 - version: 3.0.1 - motion: - specifier: ^12.9.2 - version: 12.9.2 - next: - specifier: ^15.3.2 - version: 15.3.2 - next-themes: - specifier: ^0.4.6 - version: 0.4.6 - nuqs: - specifier: ^2.4.1 - version: 2.4.1 - pino: - specifier: ^9.6.0 - version: 9.6.0 - postcss: - specifier: ^8.5.3 - version: 8.5.3 - postcss-loader: - specifier: ^8.1.1 - version: 8.1.1 prettier: specifier: ^3.5.3 version: 3.5.3 - prettier-plugin-solidity: - specifier: ^1.4.2 - version: 1.4.2 - proxycheck-ts: - specifier: ^0.0.11 - version: 0.0.11 - react: - specifier: ^19.1.0 - version: 19.1.0 - react-aria: - specifier: ^3.38.1 - version: 3.38.1 - react-aria-components: - specifier: ^1.7.1 - version: 1.7.1 - react-dom: - specifier: ^19.1.0 - version: 19.1.0 - react-markdown: - specifier: ^10.1.0 - version: 10.1.0 - react-timeago: - specifier: ^8.2.0 - version: 8.2.0 - recharts: - specifier: ^2.15.1 - version: 2.15.1 - sass: - specifier: ^1.86.1 - version: 1.86.1 - sass-loader: - specifier: ^16.0.5 - version: 16.0.5 - shiki: - specifier: ^3.2.1 - version: 3.2.1 - storybook: - specifier: ^8.6.12 - version: 8.6.12 - style-loader: - specifier: ^4.0.0 - version: 4.0.0 - stylelint: - specifier: ^16.17.0 - version: 16.17.0 - stylelint-config-standard-scss: - specifier: ^14.0.0 - version: 14.0.0 - superjson: - specifier: ^2.2.2 - version: 2.2.2 - swr: - specifier: ^2.3.3 - version: 2.3.3 - tailwind-merge: - specifier: ^3.1.0 - version: 3.1.0 - tailwindcss: - specifier: ^3.0.0 - version: 3.4.17 - tailwindcss-animate: - specifier: ^1.0.7 - version: 1.0.7 - tailwindcss-react-aria-components: - specifier: ^2.0.0 - version: 2.0.0 ts-node: specifier: ^10.9.2 version: 10.9.2 typescript: specifier: ^5.8.2 version: 5.8.2 - vercel: - specifier: ^41.4.1 - version: 41.4.1 viem: specifier: ^2.24.3 version: 2.24.3 - wagmi: - specifier: ^2.14.16 - version: 2.14.16 - zod: - specifier: ^3.24.2 - version: 3.24.2 - zod-validation-error: - specifier: ^3.4.0 - version: 3.4.0 overrides: '@solana/web3.js@1.77.4>rpc-websockets': 7.11.0 @@ -1217,10 +926,10 @@ importers: version: 0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) '@solana/wallet-adapter-react-ui': specifier: 'catalog:' - version: 0.9.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) + version: 0.9.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) '@solana/wallet-adapter-wallets': specifier: 'catalog:' - version: 0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2) + version: 0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2) '@solana/web3.js': specifier: 'catalog:' version: 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) @@ -1520,7 +1229,7 @@ importers: version: 3.1.2(typescript@5.8.2) '@solana/wallet-adapter-react': specifier: 'catalog:' - version: 0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) + version: 0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) '@types/jest': specifier: 'catalog:' version: 29.5.14 @@ -2714,13 +2423,16 @@ importers: target_chains/ethereum/sdk/js: dependencies: - '@pythnetwork/price-service-client': + '@pythnetwork/hermes-client': specifier: workspace:* - version: link:../../../../price_service/client/js - buffer: - specifier: ^6.0.3 - version: 6.0.3 + version: link:../../../../apps/hermes/client/js + viem: + specifier: 'catalog:' + version: 2.24.3(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.4) devDependencies: + '@cprussin/eslint-config': + specifier: 'catalog:' + version: 4.0.2(@testing-library/dom@10.4.0)(@typescript-eslint/eslint-plugin@8.29.0(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(@typescript-eslint/parser@8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.23.0(jiti@2.4.2))(jest@29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.2)))(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.2))(turbo@2.4.4)(typescript@5.8.2) '@pythnetwork/pyth-sdk-solidity': specifier: workspace:* version: link:../solidity @@ -2742,33 +2454,24 @@ importers: '@types/yargs': specifier: ^17.0.10 version: 17.0.33 - '@typescript-eslint/eslint-plugin': - specifier: ^5.21.0 - version: 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.56.0)(typescript@4.9.5))(eslint@8.56.0)(typescript@4.9.5) - '@typescript-eslint/parser': - specifier: ^5.21.0 - version: 5.62.0(eslint@8.56.0)(typescript@4.9.5) eslint: - specifier: ^8.14.0 - version: 8.56.0 + specifier: 'catalog:' + version: 9.23.0(jiti@2.4.2) jest: specifier: ^29.4.1 - version: 29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@4.9.5)) + version: 29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.2)) prettier: specifier: 'catalog:' version: 3.5.3 ts-jest: specifier: ^29.0.5 - version: 29.3.1(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(jest@29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@4.9.5)))(typescript@4.9.5) + version: 29.3.1(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(jest@29.7.0(@types/node@18.19.86)(ts-node@10.9.2(@types/node@18.19.86)(typescript@5.8.2)))(typescript@5.8.2) + ts-node: + specifier: 'catalog:' + version: 10.9.2(@types/node@18.19.86)(typescript@5.8.2) typescript: - specifier: ^4.6.3 - version: 4.9.5 - web3: - specifier: ^1.8.2 - version: 1.10.4(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) - yargs: - specifier: ^17.4.1 - version: 17.7.2 + specifier: 'catalog:' + version: 5.8.2 target_chains/ethereum/sdk/solidity: devDependencies: @@ -23429,7 +23132,7 @@ snapshots: '@babel/traverse': 7.27.1 '@babel/types': 7.27.1 convert-source-map: 2.0.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -23558,7 +23261,7 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-compilation-targets': 7.27.0 '@babel/helper-plugin-utils': 7.26.5 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 lodash.debounce: 4.0.8 resolve: 1.22.10 transitivePeerDependencies: @@ -25141,7 +24844,7 @@ snapshots: '@babel/parser': 7.27.0 '@babel/template': 7.27.0 '@babel/types': 7.27.1 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -25165,7 +24868,7 @@ snapshots: '@babel/parser': 7.27.2 '@babel/template': 7.27.2 '@babel/types': 7.27.1 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -26456,7 +26159,7 @@ snapshots: '@eslint/config-array@0.19.2': dependencies: '@eslint/object-schema': 2.1.6 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -26488,7 +26191,7 @@ snapshots: '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 espree: 10.3.0 globals: 14.0.0 ignore: 5.3.2 @@ -30550,10 +30253,11 @@ snapshots: crypto-js: 4.2.0 uuidv4: 6.2.13 - '@particle-network/solana-wallet@1.3.2(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))': + '@particle-network/solana-wallet@1.3.2(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)': dependencies: '@particle-network/auth': 1.3.1 '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + bs58: 5.0.0 '@paulmillr/qr@0.2.1': {} @@ -34386,18 +34090,18 @@ snapshots: '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-base-ui@0.1.3(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)': + '@solana/wallet-adapter-base-ui@0.1.3(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)': dependencies: - '@solana/wallet-adapter-react': 0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) + '@solana/wallet-adapter-react': 0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) react: 19.1.0 transitivePeerDependencies: - bs58 - react-native - '@solana/wallet-adapter-base-ui@0.1.3(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)': + '@solana/wallet-adapter-base-ui@0.1.3(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)': dependencies: - '@solana/wallet-adapter-react': 0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) + '@solana/wallet-adapter-react': 0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) react: 19.1.0 transitivePeerDependencies: @@ -34513,9 +34217,9 @@ snapshots: '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-particle@0.1.13(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-particle@0.1.13(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)': dependencies: - '@particle-network/solana-wallet': 1.3.2(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@particle-network/solana-wallet': 1.3.2(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0) '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) transitivePeerDependencies: @@ -34526,11 +34230,11 @@ snapshots: '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-react-ui@0.9.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)': + '@solana/wallet-adapter-react-ui@0.9.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)': dependencies: '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-base-ui': 0.1.3(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) - '@solana/wallet-adapter-react': 0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) + '@solana/wallet-adapter-base-ui': 0.1.3(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) + '@solana/wallet-adapter-react': 0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) @@ -34538,11 +34242,11 @@ snapshots: - bs58 - react-native - '@solana/wallet-adapter-react-ui@0.9.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)': + '@solana/wallet-adapter-react-ui@0.9.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)': dependencies: '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-base-ui': 0.1.3(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) - '@solana/wallet-adapter-react': 0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) + '@solana/wallet-adapter-base-ui': 0.1.3(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) + '@solana/wallet-adapter-react': 0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) @@ -34561,11 +34265,22 @@ snapshots: - bs58 - react-native + '@solana/wallet-adapter-react@0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0)(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)': + dependencies: + '@solana-mobile/wallet-adapter-mobile': 2.1.5(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) + '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@solana/wallet-standard-wallet-adapter-react': 1.1.4(@solana/wallet-adapter-base@0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0)(react@19.1.0) + '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + react: 19.1.0 + transitivePeerDependencies: + - bs58 + - react-native + '@solana/wallet-adapter-react@0.15.36(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)': dependencies: '@solana-mobile/wallet-adapter-mobile': 2.1.5(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(react-native@0.78.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0) '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) - '@solana/wallet-standard-wallet-adapter-react': 1.1.4(@solana/wallet-adapter-base@0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@19.1.0) + '@solana/wallet-standard-wallet-adapter-react': 1.1.4(@solana/wallet-adapter-base@0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0)(react@19.1.0) '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) react: 19.1.0 transitivePeerDependencies: @@ -34771,7 +34486,7 @@ snapshots: '@solana/wallet-adapter-nightly': 0.1.17(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-nufi': 0.1.18(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-onto': 0.1.8(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-particle': 0.1.13(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-particle': 0.1.13(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0) '@solana/wallet-adapter-phantom': 0.9.25(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-safepal': 0.5.19(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-saifu': 0.1.16(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) @@ -34827,7 +34542,7 @@ snapshots: - ws - zod - '@solana/wallet-adapter-wallets@0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)': + '@solana/wallet-adapter-wallets@0.19.33(@babel/runtime@7.27.0)(@react-native-async-storage/async-storage@1.24.0(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10)))(@solana/sysvars@2.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.2))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.0(react@19.1.0))(react-native@0.78.2(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@19.1.0)(bufferutil@4.0.9)(react@19.1.0)(utf-8-validate@5.0.10))(react@19.1.0)(tslib@2.8.1)(typescript@5.8.2)(utf-8-validate@5.0.10)(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)': dependencies: '@solana/wallet-adapter-alpha': 0.1.11(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-avana': 0.1.14(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) @@ -34848,7 +34563,7 @@ snapshots: '@solana/wallet-adapter-nightly': 0.1.17(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-nufi': 0.1.18(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-onto': 0.1.8(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-particle': 0.1.13(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-particle': 0.1.13(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0) '@solana/wallet-adapter-phantom': 0.9.25(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-safepal': 0.5.19(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-saifu': 0.1.16(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) @@ -34943,6 +34658,19 @@ snapshots: '@wallet-standard/wallet': 1.1.0 bs58: 5.0.0 + '@solana/wallet-standard-wallet-adapter-base@1.1.4(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0)': + dependencies: + '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@solana/wallet-standard-chains': 1.1.1 + '@solana/wallet-standard-features': 1.3.0 + '@solana/wallet-standard-util': 1.1.2 + '@solana/web3.js': 1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@wallet-standard/app': 1.1.0 + '@wallet-standard/base': 1.1.0 + '@wallet-standard/features': 1.1.0 + '@wallet-standard/wallet': 1.1.0 + bs58: 6.0.0 + '@solana/wallet-standard-wallet-adapter-react@1.1.4(@solana/wallet-adapter-base@0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@19.1.0)': dependencies: '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) @@ -34954,6 +34682,17 @@ snapshots: - '@solana/web3.js' - bs58 + '@solana/wallet-standard-wallet-adapter-react@1.1.4(@solana/wallet-adapter-base@0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0)(react@19.1.0)': + dependencies: + '@solana/wallet-adapter-base': 0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@solana/wallet-standard-wallet-adapter-base': 1.1.4(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@6.0.0) + '@wallet-standard/app': 1.1.0 + '@wallet-standard/base': 1.1.0 + react: 19.1.0 + transitivePeerDependencies: + - '@solana/web3.js' + - bs58 + '@solana/wallet-standard-wallet-adapter@1.1.4(@solana/wallet-adapter-base@0.9.24(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@19.1.0)': dependencies: '@solana/wallet-standard-wallet-adapter-base': 1.1.4(@solana/web3.js@1.98.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(bs58@5.0.0) @@ -35825,7 +35564,7 @@ snapshots: '@testing-library/dom@10.4.0': dependencies: - '@babel/code-frame': 7.26.2 + '@babel/code-frame': 7.27.1 '@babel/runtime': 7.27.0 '@types/aria-query': 5.0.4 aria-query: 5.3.0 @@ -37454,7 +37193,7 @@ snapshots: '@typescript-eslint/types': 8.29.0 '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2) '@typescript-eslint/visitor-keys': 8.29.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 eslint: 9.23.0(jiti@2.4.2) typescript: 5.8.2 transitivePeerDependencies: @@ -37544,7 +37283,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.8.2) '@typescript-eslint/utils': 8.29.0(eslint@9.23.0(jiti@2.4.2))(typescript@5.8.2) - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 eslint: 9.23.0(jiti@2.4.2) ts-api-utils: 2.1.0(typescript@5.8.2) typescript: 5.8.2 @@ -37621,7 +37360,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.29.0 '@typescript-eslint/visitor-keys': 8.29.0 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 @@ -41310,6 +41049,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.0: + dependencies: + ms: 2.1.3 + debug@4.4.0(supports-color@5.5.0): dependencies: ms: 2.1.3 @@ -42628,7 +42371,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 escape-string-regexp: 4.0.0 eslint-scope: 8.3.0 eslint-visitor-keys: 4.2.0 @@ -45225,7 +44968,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.4.0(supports-color@8.1.1) + debug: 4.4.0 istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: diff --git a/target_chains/ethereum/sdk/js/README.md b/target_chains/ethereum/sdk/js/README.md new file mode 100644 index 0000000000..4c4e0fb5e8 --- /dev/null +++ b/target_chains/ethereum/sdk/js/README.md @@ -0,0 +1,78 @@ +# Pyth EVM JS + +[Pyth](https://pyth.network/) provides real-time pricing data in a variety of asset classes, including cryptocurrency, +equities, FX and commodities. This library allows you to use these real-time prices on EVM-based networks. + +## Installation + +### npm + +``` +$ npm install --save @pythnetwork/pyth-evm-js +``` + +### Yarn + +``` +$ yarn add @pythnetwork/pyth-evm-js +``` + +## Quickstart + +### Filling Pyth Data for Transactions + +The `fillPythUpdate` function helps you automatically determine what Pyth price updates are needed for a transaction and creates the necessary update call. +This function uses the `trace_callMany` method by default but can be used with `debug_traceCall` and a bundler as well. See the example below for more information. + +```typescript +import { fillPythUpdate, multicall3Bundler } from "@pythnetwork/pyth-evm-js"; +import { createPublicClient, http } from "viem"; +import { optimismSepolia } from "viem/chains"; + +async function example() { + const client = createPublicClient({ + chain: optimismSepolia, + transport: http("YOUR_RPC_ENDPOINT"), + }); + + // Fill Pyth update data using "trace_callMany" + const pythUpdate = await fillPythUpdate( + client, + { + to: "0x3252c2F7962689fA17f892C52555613f36056f22", + data: "0xd09de08a", // Your transaction calldata + from: "0x78357316239040e19fC823372cC179ca75e64b81", + }, + "0x0708325268df9f66270f1401206434524814508b", // Pyth contract address + "https://hermes.pyth.network", // Hermes endpoint + { + method: "trace_callMany" + maxIter: 5, + }, + ); + + // Fill Pyth update data using "debug_traceCall" + const pythUpdateWithDebugTrace = await fillPythUpdate( + client, + { + to: "0x3252c2F7962689fA17f892C52555613f36056f22", + data: "0xd09de08a", // Your transaction calldata + from: "0x78357316239040e19fC823372cC179ca75e64b81", + }, + "0x0708325268df9f66270f1401206434524814508b", // Pyth contract address + "https://hermes.pyth.network", // Hermes endpoint + { + method: "debug_traceCall", + bundler: multicall3Bundler // or any function that takes a PythUpdate and a CallRequest and produces a CallRequest + maxIter: 5, + }, + ); + + if (pythUpdate) { + console.log("Pyth update needed:", pythUpdate); + // Bundle the calls together, or pass the pythUpdate.updateData to your contract. + } else { + console.log("No Pyth data needed for this transaction"); + } +} +``` \ No newline at end of file diff --git a/target_chains/ethereum/sdk/js/eslint.config.js b/target_chains/ethereum/sdk/js/eslint.config.js new file mode 100644 index 0000000000..9058bfef8c --- /dev/null +++ b/target_chains/ethereum/sdk/js/eslint.config.js @@ -0,0 +1 @@ +export { base as default } from "@cprussin/eslint-config"; diff --git a/target_chains/ethereum/sdk/js/package.json b/target_chains/ethereum/sdk/js/package.json index b62d6ca8c9..07a04e8e0e 100644 --- a/target_chains/ethereum/sdk/js/package.json +++ b/target_chains/ethereum/sdk/js/package.json @@ -1,6 +1,6 @@ { "name": "@pythnetwork/pyth-evm-js", - "version": "1.83.0", + "version": "2.0.0-alpha", "description": "Pyth Network EVM Utils in JS", "homepage": "https://pyth.network", "author": { @@ -21,16 +21,13 @@ }, "scripts": { "build": "tsc", - "example-client": "pnpm run build && node lib/examples/EvmPriceServiceClient.js", - "example-relay": "pnpm run build && node lib/examples/EvmRelay.js", - "example-benchmark": "pnpm run build && node lib/examples/EvmBenchmark.js", "test:format": "prettier --check \"src/**/*.ts\"", "test:lint": "eslint src/ --max-warnings 0", "fix:format": "prettier --write \"src/**/*.ts\"", "fix:lint": "eslint src/ --fix --max-warnings 0", "prepublishOnly": "pnpm run build && pnpm run test:lint", "preversion": "pnpm run test:lint", - "version": "pnpm run format && git add -A src" + "version": "pnpm run test:format && git add -A src" }, "keywords": [ "pyth", @@ -38,6 +35,7 @@ ], "license": "Apache-2.0", "devDependencies": { + "@cprussin/eslint-config": "catalog:", "@pythnetwork/pyth-sdk-solidity": "workspace:*", "@truffle/hdwallet-provider": "^2.1.5", "@types/ethereum-protocol": "^1.0.2", @@ -45,18 +43,15 @@ "@types/node": "^18.11.18", "@types/web3-provider-engine": "^14.0.1", "@types/yargs": "^17.0.10", - "@typescript-eslint/eslint-plugin": "^5.21.0", - "@typescript-eslint/parser": "^5.21.0", - "eslint": "^8.14.0", + "eslint": "catalog:", "jest": "^29.4.1", "prettier": "catalog:", "ts-jest": "^29.0.5", - "typescript": "^4.6.3", - "web3": "^1.8.2", - "yargs": "^17.4.1" + "ts-node": "catalog:", + "typescript": "catalog:" }, "dependencies": { - "@pythnetwork/price-service-client": "workspace:*", - "buffer": "^6.0.3" + "@pythnetwork/hermes-client": "workspace:*", + "viem": "catalog:" } } diff --git a/target_chains/ethereum/sdk/js/src/filler.ts b/target_chains/ethereum/sdk/js/src/filler.ts new file mode 100644 index 0000000000..b59a36c83f --- /dev/null +++ b/target_chains/ethereum/sdk/js/src/filler.ts @@ -0,0 +1,218 @@ +import { HermesClient } from "@pythnetwork/hermes-client"; +import { + Address, + PublicClient, + encodeFunctionData, + Hex, + Transport, + Chain, +} from "viem"; + +import { multicall3Bundler } from "./multicall3-bundler"; +import { IPythAbi } from "./pyth-abi"; +import { + debugTraceCallAction, + extractPythPriceFeedsFromDebugTraceCall, +} from "./tracer/debug-trace-call"; +import { + extractPythPriceFeedsFromTraceCallMany, + traceCallManyAction, +} from "./tracer/trace-call-many"; + +/** + * Represents a call request to be executed on the blockchain + */ +export type CallRequest = { + /** The address making the call (optional) */ + from?: Address; + /** The target contract address */ + to: Address; + /** The encoded function call data (optional) */ + data?: `0x${string}`; + /** The amount of ETH to send with the call (optional) */ + value?: bigint; +}; + +/** + * Get the update fee for a given set of update data + * + * @param client - The public client instance + * @param pythContractAddress - The Pyth contract address + * @param updateData - Array of hex-encoded update data + * @returns Promise resolving to the update fee in wei + */ +export async function getUpdateFee< + transport extends Transport, + chain extends Chain | undefined, +>( + client: PublicClient, + pythContractAddress: Address, + updateData: Hex[], +): Promise { + return await client.readContract({ + address: pythContractAddress, + abi: IPythAbi, + functionName: "getUpdateFee", + args: [updateData], + }); +} + +/** + * A function that takes a Pyth update and a call request and returns a single bundled call request. + * This is used to combine the Pyth update with the original call. + */ +export type Bundler = ( + pythUpdate: PythUpdate, + call: CallRequest, +) => CallRequest; + +/** + * Configuration for debug_traceCall method. + * Use this when you want to trace a single bundled transaction that combines the Pyth update with the original call. + * The bundler function is responsible for creating a single transaction that executes both operations. + * + * The bundler is crucial because debug_traceCall can only trace one transaction at a time. The bundler + * must create a single call that includes both the Pyth price update and the original transaction logic. + * This allows the tracer to see all the Pyth price feed calls that would be made in the actual execution. + */ +export type DebugTraceCallConfig = { + /** Must be "debug_traceCall" */ + method: "debug_traceCall"; + /** Function that takes a Pyth update and original call, returns a single bundled call request. + * Common bundlers include multicall3Bundler for combining calls via Multicall3 contract. + * The bundler must create a single transaction that executes both the Pyth update and the original call. */ + bundler: Bundler; + /** Maximum number of iterations to find all required price feeds. Default is 5. + * Each iteration traces the current transaction to find new Pyth price feed calls. */ + maxIter: number; +}; + +/** + * Configuration for trace_callMany method. + * Use this when you want to trace multiple separate transactions (Pyth update + original call). + * This method traces each call independently, which may be more accurate but requires more RPC calls. + */ +export type TraceCallManyConfig = { + /** Must be "trace_callMany" */ + method: "trace_callMany"; + /** Maximum number of iterations to find all required price feeds. Default is 5. + * Each iteration traces the current set of transactions to find new Pyth price feed calls. */ + maxIter: number; +}; + +/** + * Union type for tracing configuration options + */ +export type Config = DebugTraceCallConfig | TraceCallManyConfig; + +/** + * Represents a Pyth price update transaction + */ +export type PythUpdate = { + /** The call request to update Pyth price feeds */ + call: CallRequest; + /** Array of hex-encoded price update data */ + updateData: Hex[]; + /** The fee required for the update in wei */ + updateFee: bigint; +}; + +/** + * Fill the Pyth data for a given call request. + * Requires a client that supports trace_callMany or debug_traceCall with a bundler. + * + * @param client - The public client instance + * @param call - The call request to fill with Pyth data + * @param pythContractAddress - The Pyth contract address + * @param hermesEndpoint - The Hermes endpoint URL for fetching price updates + * @param config - Configuration options for tracing and bundling. Can be either: + * - `DebugTraceCallConfig`: For debug_traceCall method with a bundler function to combine Pyth update with original call. + * The bundler creates a single transaction that executes both the Pyth update and the original call. + * - `TraceCallManyConfig`: For trace_callMany method which traces multiple calls separately. + * This method traces the Pyth update and original call as separate transactions. + * @returns Promise resolving to Pyth update object or undefined if no Pyth data needed + */ +export async function fillPythUpdate< + transport extends Transport, + chain extends Chain | undefined, +>( + client: PublicClient, + call: CallRequest, + pythContractAddress: Address, + hermesEndpoint: string, + config?: Config, +): Promise { + const defaultConfig: Config = { + method: "debug_traceCall", + bundler: multicall3Bundler, + maxIter: 5, + }; + const finalConfig = config ?? defaultConfig; + const traceActionsClient = client + .extend(debugTraceCallAction) + .extend(traceCallManyAction); + const hermesClient = new HermesClient(hermesEndpoint); + + let requiredPriceFeeds = new Set<`0x${string}`>(); + + let pythUpdate: PythUpdate | undefined; + + for (let i = 0; i < finalConfig.maxIter; i++) { + let priceFeeds = new Set<`0x${string}`>(); + + if (finalConfig.method === "debug_traceCall") { + const bundledCall = pythUpdate + ? finalConfig.bundler(pythUpdate, call) + : call; + const traceResult = await traceActionsClient.debugTraceCall(bundledCall); + priceFeeds = extractPythPriceFeedsFromDebugTraceCall( + traceResult, + pythContractAddress, + ); + } else { + const calls = pythUpdate ? [pythUpdate.call, call] : [call]; + const traceResult = await traceActionsClient.traceCallMany(calls); + priceFeeds = extractPythPriceFeedsFromTraceCallMany( + traceResult, + pythContractAddress, + ); + } + + const oldSize = requiredPriceFeeds.size; + requiredPriceFeeds = new Set([...requiredPriceFeeds, ...priceFeeds]); + + if (oldSize === requiredPriceFeeds.size) { + break; + } + + const hermesResponse = await hermesClient.getLatestPriceUpdates([ + ...requiredPriceFeeds, + ]); + const updateData = hermesResponse.binary.data.map( + (data) => ("0x" + data) as `0x${string}`, + ); + + const updateFee = await getUpdateFee( + client, + pythContractAddress, + updateData, + ); + + pythUpdate = { + call: { + to: pythContractAddress, + data: encodeFunctionData({ + abi: IPythAbi, + functionName: "updatePriceFeeds", + args: [updateData], + }), + from: call.from, + value: updateFee, + }, + updateData, + updateFee, + }; + } + + return pythUpdate; +} diff --git a/target_chains/ethereum/sdk/js/src/index.ts b/target_chains/ethereum/sdk/js/src/index.ts new file mode 100644 index 0000000000..1eea6b36e1 --- /dev/null +++ b/target_chains/ethereum/sdk/js/src/index.ts @@ -0,0 +1,3 @@ +export * from "./filler"; +export * from "./multicall3-bundler"; +export * from "./pyth-abi"; diff --git a/target_chains/ethereum/sdk/js/src/multicall3-bundler.ts b/target_chains/ethereum/sdk/js/src/multicall3-bundler.ts new file mode 100644 index 0000000000..e07c3253c3 --- /dev/null +++ b/target_chains/ethereum/sdk/js/src/multicall3-bundler.ts @@ -0,0 +1,79 @@ +import { encodeFunctionData } from "viem"; + +import { CallRequest, PythUpdate } from "./filler"; + +// Multicall3 contract address (deployed on most chains) +export const MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11"; + +// Multicall3 ABI for the aggregate3 and aggregate3Value functions +export const MULTICALL3_ABI = [ + { + inputs: [ + { + components: [ + { name: "target", type: "address" }, + { name: "allowFailure", type: "bool" }, + { name: "value", type: "uint256" }, + { name: "callData", type: "bytes" }, + ], + name: "calls", + type: "tuple[]", + }, + ], + name: "aggregate3Value", + outputs: [ + { + components: [ + { name: "success", type: "bool" }, + { name: "returnData", type: "bytes" }, + ], + name: "returnData", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, +] as const; + +/** + * Bundle multiple calls using Multicall3. This bundler will change the msg.sender of the calls to the + * Multicall3 contract and this might affect the result of the calls. + */ +export function multicall3Bundler( + pythUpdate: PythUpdate, + call: CallRequest, +): CallRequest { + // Encode the multicall3 aggregate3 function call + const multicallData = encodeFunctionData({ + abi: MULTICALL3_ABI, + functionName: "aggregate3Value", + args: [ + [ + { + target: pythUpdate.call.to, + allowFailure: false, + value: pythUpdate.call.value ?? 0n, + callData: pythUpdate.call.data ?? "0x", + }, + { + target: call.to, + allowFailure: false, + value: call.value ?? 0n, + callData: call.data ?? "0x", + }, + ], + ], + }); + + // Calculate total value needed + const totalValue = (call.value ?? 0n) + (pythUpdate.call.value ?? 0n); + + // Create the bundled transaction that calls multicall3 + return { + to: MULTICALL3_ADDRESS, + data: multicallData, + value: totalValue, + from: call.from, + }; +} diff --git a/target_chains/ethereum/sdk/js/src/pyth-abi.ts b/target_chains/ethereum/sdk/js/src/pyth-abi.ts new file mode 100644 index 0000000000..fe6649c1f5 --- /dev/null +++ b/target_chains/ethereum/sdk/js/src/pyth-abi.ts @@ -0,0 +1,660 @@ +export const IPythAbi = [ + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "id", + type: "bytes32", + }, + { + indexed: false, + internalType: "uint64", + name: "publishTime", + type: "uint64", + }, + { + indexed: false, + internalType: "int64", + name: "price", + type: "int64", + }, + { + indexed: false, + internalType: "uint64", + name: "conf", + type: "uint64", + }, + ], + name: "PriceFeedUpdate", + type: "event", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "id", + type: "bytes32", + }, + ], + name: "getEmaPrice", + outputs: [ + { + components: [ + { + internalType: "int64", + name: "price", + type: "int64", + }, + { + internalType: "uint64", + name: "conf", + type: "uint64", + }, + { + internalType: "int32", + name: "expo", + type: "int32", + }, + { + internalType: "uint256", + name: "publishTime", + type: "uint256", + }, + ], + internalType: "struct PythStructs.Price", + name: "price", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "id", + type: "bytes32", + }, + { + internalType: "uint256", + name: "age", + type: "uint256", + }, + ], + name: "getEmaPriceNoOlderThan", + outputs: [ + { + components: [ + { + internalType: "int64", + name: "price", + type: "int64", + }, + { + internalType: "uint64", + name: "conf", + type: "uint64", + }, + { + internalType: "int32", + name: "expo", + type: "int32", + }, + { + internalType: "uint256", + name: "publishTime", + type: "uint256", + }, + ], + internalType: "struct PythStructs.Price", + name: "price", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "id", + type: "bytes32", + }, + ], + name: "getEmaPriceUnsafe", + outputs: [ + { + components: [ + { + internalType: "int64", + name: "price", + type: "int64", + }, + { + internalType: "uint64", + name: "conf", + type: "uint64", + }, + { + internalType: "int32", + name: "expo", + type: "int32", + }, + { + internalType: "uint256", + name: "publishTime", + type: "uint256", + }, + ], + internalType: "struct PythStructs.Price", + name: "price", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "id", + type: "bytes32", + }, + ], + name: "getPrice", + outputs: [ + { + components: [ + { + internalType: "int64", + name: "price", + type: "int64", + }, + { + internalType: "uint64", + name: "conf", + type: "uint64", + }, + { + internalType: "int32", + name: "expo", + type: "int32", + }, + { + internalType: "uint256", + name: "publishTime", + type: "uint256", + }, + ], + internalType: "struct PythStructs.Price", + name: "price", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "id", + type: "bytes32", + }, + { + internalType: "uint256", + name: "age", + type: "uint256", + }, + ], + name: "getPriceNoOlderThan", + outputs: [ + { + components: [ + { + internalType: "int64", + name: "price", + type: "int64", + }, + { + internalType: "uint64", + name: "conf", + type: "uint64", + }, + { + internalType: "int32", + name: "expo", + type: "int32", + }, + { + internalType: "uint256", + name: "publishTime", + type: "uint256", + }, + ], + internalType: "struct PythStructs.Price", + name: "price", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes32", + name: "id", + type: "bytes32", + }, + ], + name: "getPriceUnsafe", + outputs: [ + { + components: [ + { + internalType: "int64", + name: "price", + type: "int64", + }, + { + internalType: "uint64", + name: "conf", + type: "uint64", + }, + { + internalType: "int32", + name: "expo", + type: "int32", + }, + { + internalType: "uint256", + name: "publishTime", + type: "uint256", + }, + ], + internalType: "struct PythStructs.Price", + name: "price", + type: "tuple", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes[]", + name: "updateData", + type: "bytes[]", + }, + ], + name: "getUpdateFee", + outputs: [ + { + internalType: "uint256", + name: "feeAmount", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getValidTimePeriod", + outputs: [ + { + internalType: "uint256", + name: "validTimePeriod", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes[]", + name: "updateData", + type: "bytes[]", + }, + { + internalType: "bytes32[]", + name: "priceIds", + type: "bytes32[]", + }, + { + internalType: "uint64", + name: "minPublishTime", + type: "uint64", + }, + { + internalType: "uint64", + name: "maxPublishTime", + type: "uint64", + }, + ], + name: "parsePriceFeedUpdates", + outputs: [ + { + components: [ + { + internalType: "bytes32", + name: "id", + type: "bytes32", + }, + { + components: [ + { + internalType: "int64", + name: "price", + type: "int64", + }, + { + internalType: "uint64", + name: "conf", + type: "uint64", + }, + { + internalType: "int32", + name: "expo", + type: "int32", + }, + { + internalType: "uint256", + name: "publishTime", + type: "uint256", + }, + ], + internalType: "struct PythStructs.Price", + name: "price", + type: "tuple", + }, + { + components: [ + { + internalType: "int64", + name: "price", + type: "int64", + }, + { + internalType: "uint64", + name: "conf", + type: "uint64", + }, + { + internalType: "int32", + name: "expo", + type: "int32", + }, + { + internalType: "uint256", + name: "publishTime", + type: "uint256", + }, + ], + internalType: "struct PythStructs.Price", + name: "emaPrice", + type: "tuple", + }, + ], + internalType: "struct PythStructs.PriceFeed[]", + name: "priceFeeds", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes[]", + name: "updateData", + type: "bytes[]", + }, + { + internalType: "bytes32[]", + name: "priceIds", + type: "bytes32[]", + }, + { + internalType: "uint64", + name: "minPublishTime", + type: "uint64", + }, + { + internalType: "uint64", + name: "maxPublishTime", + type: "uint64", + }, + ], + name: "parsePriceFeedUpdatesUnique", + outputs: [ + { + components: [ + { + internalType: "bytes32", + name: "id", + type: "bytes32", + }, + { + components: [ + { + internalType: "int64", + name: "price", + type: "int64", + }, + { + internalType: "uint64", + name: "conf", + type: "uint64", + }, + { + internalType: "int32", + name: "expo", + type: "int32", + }, + { + internalType: "uint256", + name: "publishTime", + type: "uint256", + }, + ], + internalType: "struct PythStructs.Price", + name: "price", + type: "tuple", + }, + { + components: [ + { + internalType: "int64", + name: "price", + type: "int64", + }, + { + internalType: "uint64", + name: "conf", + type: "uint64", + }, + { + internalType: "int32", + name: "expo", + type: "int32", + }, + { + internalType: "uint256", + name: "publishTime", + type: "uint256", + }, + ], + internalType: "struct PythStructs.Price", + name: "emaPrice", + type: "tuple", + }, + ], + internalType: "struct PythStructs.PriceFeed[]", + name: "priceFeeds", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes[]", + name: "updateData", + type: "bytes[]", + }, + ], + name: "updatePriceFeeds", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes[]", + name: "updateData", + type: "bytes[]", + }, + { + internalType: "bytes32[]", + name: "priceIds", + type: "bytes32[]", + }, + { + internalType: "uint64[]", + name: "publishTimes", + type: "uint64[]", + }, + ], + name: "updatePriceFeedsIfNecessary", + outputs: [], + stateMutability: "payable", + type: "function", + }, +] as const; + +export const IPythEventsAbi = [ + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes32", + name: "id", + type: "bytes32", + }, + { + indexed: false, + internalType: "uint64", + name: "publishTime", + type: "uint64", + }, + { + indexed: false, + internalType: "int64", + name: "price", + type: "int64", + }, + { + indexed: false, + internalType: "uint64", + name: "conf", + type: "uint64", + }, + ], + name: "PriceFeedUpdate", + type: "event", + }, +] as const; + +export const PythErrorsAbi = [ + { + inputs: [], + name: "InsufficientFee", + type: "error", + }, + { + inputs: [], + name: "InvalidArgument", + type: "error", + }, + { + inputs: [], + name: "InvalidGovernanceDataSource", + type: "error", + }, + { + inputs: [], + name: "InvalidGovernanceMessage", + type: "error", + }, + { + inputs: [], + name: "InvalidGovernanceTarget", + type: "error", + }, + { + inputs: [], + name: "InvalidUpdateData", + type: "error", + }, + { + inputs: [], + name: "InvalidUpdateDataSource", + type: "error", + }, + { + inputs: [], + name: "InvalidWormholeAddressToSet", + type: "error", + }, + { + inputs: [], + name: "InvalidWormholeVaa", + type: "error", + }, + { + inputs: [], + name: "NoFreshUpdate", + type: "error", + }, + { + inputs: [], + name: "OldGovernanceMessage", + type: "error", + }, + { + inputs: [], + name: "PriceFeedNotFound", + type: "error", + }, + { + inputs: [], + name: "PriceFeedNotFoundWithinRange", + type: "error", + }, + { + inputs: [], + name: "StalePrice", + type: "error", + }, +] as const; + +export const PythAbi = [ + ...IPythAbi, + ...IPythEventsAbi, + ...PythErrorsAbi, +] as const; diff --git a/target_chains/ethereum/sdk/js/src/tracer/debug-trace-call.ts b/target_chains/ethereum/sdk/js/src/tracer/debug-trace-call.ts new file mode 100644 index 0000000000..e15be62a9e --- /dev/null +++ b/target_chains/ethereum/sdk/js/src/tracer/debug-trace-call.ts @@ -0,0 +1,126 @@ +import { + Address, + BlockTag, + CallParameters, + Client, + decodeFunctionData, + ExactPartial, + formatTransactionRequest, + Hex, + RpcTransactionRequest, +} from "viem"; + +import { IPythAbi } from "../pyth-abi"; + +/** + * Extract Pyth price feed IDs from a transaction call trace. + */ +export function extractPythPriceFeedsFromDebugTraceCall( + trace: RpcCallTrace, + pythContractAddress: Address, + ignoreParsingErrors = false, +): Set<`0x${string}`> { + const result = new Set<`0x${string}`>(); + if (trace.to === pythContractAddress) { + // Decode the calldata to see what function is being called + try { + const decoded = decodeFunctionData({ + abi: IPythAbi, + data: trace.input, + }); + + let priceFeedId: `0x${string}` | undefined; + switch (decoded.functionName) { + case "getPrice": + case "getPriceNoOlderThan": + case "getPriceUnsafe": + case "getEmaPrice": + case "getEmaPriceNoOlderThan": + case "getEmaPriceUnsafe": { + priceFeedId = decoded.args[0]; + break; + } + default: { + break; + } + } + if (priceFeedId !== undefined) { + result.add(priceFeedId); + } + } catch { + if (!ignoreParsingErrors) { + throw new Error( + `Failed to decode calldata: ${trace.input}. Make sure correct Pyth contract address is used.`, + ); + } + } + } + if (trace.calls === undefined) { + return new Set(); + } + return new Set([ + ...result, + ...trace.calls.flatMap((call) => [ + ...extractPythPriceFeedsFromDebugTraceCall(call, pythContractAddress), + ]), + ]); +} + +export type TraceCallRpcSchema = { + Method: "debug_traceCall"; + Parameters: + | [ExactPartial, Hex | BlockTag] + | [ + ExactPartial, + BlockTag | Hex, + { + tracer: "callTracer" | "prestateTracer"; + tracerConfig?: { onlyTopCall?: boolean; withLog?: boolean }; + }, + ]; + ReturnType: RpcCallTrace; +}; + +export type RpcCallType = + | "CALL" + | "STATICCALL" + | "DELEGATECALL" + | "CREATE" + | "CREATE2" + | "SELFDESTRUCT" + | "CALLCODE"; + +export type RpcLogTrace = { + address: Address; + data: Hex; + position: Hex; + topics: [Hex, ...Hex[]]; +}; + +export type RpcCallTrace = { + from: Address; + gas: Hex; + gasUsed: Hex; + to: Address; + input: Hex; + output: Hex; + error?: string; + revertReason?: string; + calls?: RpcCallTrace[]; + logs?: RpcLogTrace[]; + value?: Hex; + type: RpcCallType; +}; + +export const debugTraceCallAction = (client: Client) => ({ + async debugTraceCall(args: CallParameters) { + return client.request({ + method: "debug_traceCall", + params: [ + formatTransactionRequest(args), + "latest", + { tracer: "callTracer" }, + ], + }); + }, +}); diff --git a/target_chains/ethereum/sdk/js/src/tracer/trace-call-many.ts b/target_chains/ethereum/sdk/js/src/tracer/trace-call-many.ts new file mode 100644 index 0000000000..04d60dd2be --- /dev/null +++ b/target_chains/ethereum/sdk/js/src/tracer/trace-call-many.ts @@ -0,0 +1,117 @@ +import { + Address, + BlockTag, + decodeFunctionData, + RpcTransactionRequest, + ExactPartial, + Hex, + Client, + formatTransactionRequest, + CallParameters, +} from "viem"; + +import { IPythAbi } from "../pyth-abi"; + +/** + * Extract Pyth price feed IDs from a transaction call trace. + */ +export function extractPythPriceFeedsFromTraceCallMany( + traceResults: TraceCallResult[], + pythContractAddress: Address, + ignoreParsingErrors = false, +): Set<`0x${string}`> { + const result = new Set<`0x${string}`>(); + for (const traceResult of traceResults) { + for (const trace of traceResult.trace) { + if (trace.action.to === pythContractAddress) { + // Decode the calldata to see what function is being called + try { + const decoded = decodeFunctionData({ + abi: IPythAbi, + data: trace.action.input, + }); + + let priceFeedId: `0x${string}` | undefined; + switch (decoded.functionName) { + case "getPrice": + case "getPriceNoOlderThan": + case "getPriceUnsafe": + case "getEmaPrice": + case "getEmaPriceNoOlderThan": + case "getEmaPriceUnsafe": { + priceFeedId = decoded.args[0]; + break; + } + default: { + break; + } + } + if (priceFeedId !== undefined) { + result.add(priceFeedId); + } + } catch { + if (!ignoreParsingErrors) { + throw new Error( + `Failed to decode calldata: ${trace.action.input}. Make sure correct Pyth contract address is used.`, + ); + } + } + } + } + } + + return result; +} + +export type TraceCallResult = { + output: Hex; + stateDiff: undefined; + trace: RpcCallTrace[]; + vmTrace: undefined; +}; + +export type TraceCallRpcSchema = { + Method: "trace_callMany"; + Parameters: [ + [ExactPartial, ["trace"]][], + Hex | BlockTag, + ]; + ReturnType: TraceCallResult[]; +}; + +export type RpcCallTrace = { + action: { + from: Address; + callType: + | "call" + | "staticcall" + | "delegatecall" + | "create" + | "create2" + | "selfdestruct" + | "callcode"; + gas: Hex; + input: Hex; + to: Address; + value: Hex; + }; + result: { + gasUsed: Hex; + output: Hex; + }; + subtraces: number; + traceAddress: number[]; + type: "call" | "create"; +}; + +export const traceCallManyAction = (client: Client) => ({ + async traceCallMany(args: CallParameters[]) { + return client.request({ + method: "trace_callMany", + params: [ + args.map((a) => [formatTransactionRequest(a), ["trace"]]), + "latest", + ], + }); + }, +});