Skip to content

Commit da11879

Browse files
committed
feat(evm/sdk/js): add pyth filler
1 parent 84e8057 commit da11879

File tree

10 files changed

+1356
-688
lines changed

10 files changed

+1356
-688
lines changed

pnpm-lock.yaml

Lines changed: 89 additions & 346 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 55 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
# Pyth EVM JS (DEPRECATED)
2-
3-
> [!WARNING]
4-
> **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.
1+
# Pyth EVM JS
52

63
[Pyth](https://pyth.network/) provides real-time pricing data in a variety of asset classes, including cryptocurrency,
74
equities, FX and commodities. This library allows you to use these real-time prices on EVM-based networks.
@@ -22,156 +19,60 @@ $ yarn add @pythnetwork/pyth-evm-js
2219

2320
## Quickstart
2421

25-
Pyth stores prices off-chain to minimize gas fees, which allows us to offer a wider selection of products and faster
26-
update times. See [On-Demand Updates](https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand) for more
27-
information about this approach. In order to use Pyth prices on chain, they must be fetched from an off-chain Hermes
28-
instance. The `EvmPriceServiceConnection` class can be used to interact with these services, providing a way to fetch
29-
these prices directly in your code. The following example wraps an existing RPC provider and shows how to obtain Pyth
30-
prices and submit them to the network:
31-
32-
```typescript
33-
const connection = new EvmPriceServiceConnection("https://hermes.pyth.network"); // See Hermes endpoints section below for other endpoints
34-
35-
const priceIds = [
36-
// You can find the ids of prices at https://pyth.network/developers/price-feed-ids#pyth-evm-stable
37-
"0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43", // BTC/USD price id
38-
"0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace", // ETH/USD price id
39-
];
40-
41-
// In order to use Pyth prices in your protocol you need to submit the price update data to Pyth contract in your target
42-
// chain. `getPriceFeedsUpdateData` creates the update data which can be submitted to your contract. Then your contract should
43-
// call the Pyth Contract with this data.
44-
const priceUpdateData = await connection.getPriceFeedsUpdateData(priceIds);
45-
46-
// If the user is paying the price update fee, you need to fetch it from the Pyth contract.
47-
// Please refer to https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand#fees for more information.
48-
//
49-
// `pythContract` below is a web3.js contract; if you wish to use ethers, you need to change it accordingly.
50-
// You can find the Pyth interface ABI in @pythnetwork/pyth-sdk-solidity npm package.
51-
const updateFee = await pythContract.methods
52-
.getUpdateFee(priceUpdateData)
53-
.call();
54-
55-
// Calling someContract method
56-
// `someContract` below is a web3.js contract; if you wish to use ethers, you need to change it accordingly.
57-
// Note: In Hedera you need to pass updateFee * 10^10 as value to the send method as there is an
58-
// inconsistency in the way the value is handled in Hedera RPC and the way it is handled on-chain.
59-
await someContract.methods
60-
.doSomething(someArg, otherArg, priceUpdateData)
61-
.send({ value: updateFee });
62-
```
63-
64-
`SomeContract` looks like so:
65-
66-
```solidity
67-
pragma solidity ^0.8.0;
68-
69-
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
70-
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
22+
### Filling Pyth Data for Transactions
7123

72-
contract SomeContract {
73-
IPyth pyth;
74-
75-
constructor(address pythContract) {
76-
pyth = IPyth(pythContract);
77-
}
78-
79-
function doSomething(
80-
uint someArg,
81-
string memory otherArg,
82-
bytes[] calldata priceUpdateData
83-
) public payable {
84-
// Update the prices to be set to the latest values
85-
uint fee = pyth.getUpdateFee(priceUpdateData);
86-
pyth.updatePriceFeeds{ value: fee }(priceUpdateData);
87-
88-
// Doing other things that uses prices
89-
bytes32 priceId = 0xf9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b;
90-
// Get the price if it is not older than 10 seconds from the current time.
91-
PythStructs.Price price = pyth.getPriceNoOlderThan(priceId, 10);
92-
}
93-
}
94-
95-
```
96-
97-
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).
98-
99-
### Off-chain prices
100-
101-
Many applications additionally need to display Pyth prices off-chain, for example, in their frontend application.
102-
The `EvmPriceServiceConnection` provides two different ways to fetch the current Pyth price.
103-
The code blocks below assume that the `connection` and `priceIds` objects have been initialized as shown above.
104-
The first method is a single-shot query:
24+
The `fillPythUpdate` function helps you automatically determine what Pyth price updates are needed for a transaction and creates the necessary update call.
25+
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.
10526

10627
```typescript
107-
// `getLatestPriceFeeds` returns a `PriceFeed` for each price id. It contains all information about a price and has
108-
// utility functions to get the current and exponentially-weighted moving average price, and other functionality.
109-
const priceFeeds = await connection.getLatestPriceFeeds(priceIds);
110-
// Get the price if it is not older than 60 seconds from the current time.
111-
console.log(priceFeeds[0].getPriceNoOlderThan(60)); // Price { conf: '1234', expo: -8, price: '12345678' }
112-
// Get the exponentially-weighted moving average price if it is not older than 60 seconds from the current time.
113-
console.log(priceFeeds[1].getEmaPriceNoOlderThan(60));
114-
```
115-
116-
The object also supports a streaming websocket connection that allows you to subscribe to every new price update for a given feed.
117-
This method is useful if you want to show continuously updating real-time prices in your frontend:
118-
119-
```typescript
120-
// Subscribe to the price feeds given by `priceId`. The callback will be invoked every time the requested feed
121-
// gets a price update.
122-
connection.subscribePriceFeedUpdates(priceIds, (priceFeed) => {
123-
console.log(
124-
`Received update for ${priceFeed.id}: ${priceFeed.getPriceNoOlderThan(60)}`
125-
);
126-
});
127-
128-
// When using the subscription, make sure to close the websocket upon termination to finish the process gracefully.
129-
setTimeout(() => {
130-
connection.closeWebSocket();
131-
}, 60000);
132-
```
133-
134-
### Examples
135-
136-
There are two examples in [examples](./src/examples/).
137-
138-
#### EvmPriceServiceClient
139-
140-
[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:
141-
142-
```bash
143-
npm run example-client -- \
144-
--endpoint https://hermes.pyth.network \
145-
--price-ids \
146-
0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 \
147-
0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace
148-
```
149-
150-
#### EvmRelay
151-
152-
[This example](./src/examples/EvmRelay.ts) shows how to update prices on an EVM network. It does the following:
153-
154-
1. Gets update data to update given price feeds.
155-
2. Calls the pyth contract with the update data.
156-
3. Submits it to the network and prints the txhash if successful.
157-
158-
You can run this example with `npm run example-relay`. A full command that updates BTC and ETH prices on the BNB Chain
159-
testnet network looks like so:
160-
161-
```bash
162-
npm run example-relay -- \
163-
--network "https://data-seed-prebsc-1-s1.binance.org:8545" \
164-
--pyth-contract "0x5744Cbf430D99456a0A8771208b674F27f8EF0Fb"\
165-
--mnemonic "my good mnemonic" \
166-
--endpoint https://hermes.pyth.network \
167-
--price-ids \
168-
"0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" \
169-
"0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"
170-
```
171-
172-
## Hermes endpoints
173-
174-
Pyth offers a free public endpoint at [https://hermes.pyth.network](https://hermes.pyth.network). However, it is
175-
recommended to obtain a private endpoint from one of the Hermes RPC providers for more reliability. You can find more
176-
information about Hermes RPC providers
177-
[here](https://docs.pyth.network/documentation/pythnet-price-feeds/hermes#public-endpoint).
28+
import { fillPythUpdate, multicall3Bundler } from "@pythnetwork/pyth-evm-js";
29+
import { createPublicClient, http } from "viem";
30+
import { optimismSepolia } from "viem/chains";
31+
32+
async function example() {
33+
const client = createPublicClient({
34+
chain: optimismSepolia,
35+
transport: http("YOUR_RPC_ENDPOINT"),
36+
});
37+
38+
// Fill Pyth update data using "trace_callMany"
39+
const pythUpdate = await fillPythUpdate(
40+
client,
41+
{
42+
to: "0x3252c2F7962689fA17f892C52555613f36056f22",
43+
data: "0xd09de08a", // Your transaction calldata
44+
from: "0x78357316239040e19fC823372cC179ca75e64b81",
45+
},
46+
"0x0708325268df9f66270f1401206434524814508b", // Pyth contract address
47+
"https://hermes.pyth.network", // Hermes endpoint
48+
{
49+
method: "trace_callMany"
50+
maxIter: 5,
51+
},
52+
);
53+
54+
// Fill Pyth update data using "debug_traceCall"
55+
const pythUpdateWithDebugTrace = await fillPythUpdate(
56+
client,
57+
{
58+
to: "0x3252c2F7962689fA17f892C52555613f36056f22",
59+
data: "0xd09de08a", // Your transaction calldata
60+
from: "0x78357316239040e19fC823372cC179ca75e64b81",
61+
},
62+
"0x0708325268df9f66270f1401206434524814508b", // Pyth contract address
63+
"https://hermes.pyth.network", // Hermes endpoint
64+
{
65+
method: "debug_traceCall",
66+
bundler: multicall3Bundler // or any function that takes a PythUpdate and a CallRequest and produces a CallRequest
67+
maxIter: 5,
68+
},
69+
);
70+
71+
if (pythUpdate) {
72+
console.log("Pyth update needed:", pythUpdate);
73+
// Bundle the calls together, or pass the pythUpdate.updateData to your contract.
74+
} else {
75+
console.log("No Pyth data needed for this transaction");
76+
}
77+
}
78+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { base as default } from "@cprussin/eslint-config";
Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/pyth-evm-js",
3-
"version": "1.83.0",
3+
"version": "2.0.0-alpha",
44
"description": "Pyth Network EVM Utils in JS",
55
"homepage": "https://pyth.network",
66
"author": {
@@ -21,42 +21,37 @@
2121
},
2222
"scripts": {
2323
"build": "tsc",
24-
"example-client": "pnpm run build && node lib/examples/EvmPriceServiceClient.js",
25-
"example-relay": "pnpm run build && node lib/examples/EvmRelay.js",
26-
"example-benchmark": "pnpm run build && node lib/examples/EvmBenchmark.js",
2724
"test:format": "prettier --check \"src/**/*.ts\"",
2825
"test:lint": "eslint src/ --max-warnings 0",
2926
"fix:format": "prettier --write \"src/**/*.ts\"",
3027
"fix:lint": "eslint src/ --fix --max-warnings 0",
3128
"prepublishOnly": "pnpm run build && pnpm run test:lint",
3229
"preversion": "pnpm run test:lint",
33-
"version": "pnpm run format && git add -A src"
30+
"version": "pnpm run test:format && git add -A src"
3431
},
3532
"keywords": [
3633
"pyth",
3734
"oracle"
3835
],
3936
"license": "Apache-2.0",
4037
"devDependencies": {
38+
"@cprussin/eslint-config": "catalog:",
4139
"@pythnetwork/pyth-sdk-solidity": "workspace:*",
4240
"@truffle/hdwallet-provider": "^2.1.5",
4341
"@types/ethereum-protocol": "^1.0.2",
4442
"@types/jest": "^29.4.0",
4543
"@types/node": "^18.11.18",
4644
"@types/web3-provider-engine": "^14.0.1",
4745
"@types/yargs": "^17.0.10",
48-
"@typescript-eslint/eslint-plugin": "^5.21.0",
49-
"@typescript-eslint/parser": "^5.21.0",
50-
"eslint": "^8.14.0",
46+
"eslint": "catalog:",
5147
"jest": "^29.4.1",
5248
"prettier": "catalog:",
5349
"ts-jest": "^29.0.5",
54-
"typescript": "^4.6.3",
55-
"web3": "^1.8.2",
56-
"yargs": "^17.4.1"
50+
"ts-node": "catalog:",
51+
"typescript": "catalog:"
5752
},
5853
"dependencies": {
59-
"@pythnetwork/price-service-client": "workspace:*",
60-
"buffer": "^6.0.3"
54+
"@pythnetwork/hermes-client": "workspace:*",
55+
"viem": "catalog:"
6156
}
6257
}

0 commit comments

Comments
 (0)