Skip to content

feat: enhance near pyth docs #508

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 26, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
385 changes: 306 additions & 79 deletions pages/price-feeds/use-real-time-data/near.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,83 +9,315 @@ price feeds, handling the retrieval and updating of price data.
The two Key functions in the Pyth receiver contract to get started
are as follows:

- `update_price_feeds`: Updates multiple price feeds if they are fresh.
- `get_price`: Retrieves the current price from a specific price feed.
1. [`update_price_feeds`](#update_price_feeds)
_(updates Pyth smart contract with the price feed you provide)_

- args: `data`
- type: `object`
- example: `{ "data": "504e41...' }`

2. [`get_price`](#get_price) (fetches the most recent price stored in the contract)\_
- args: `price_identifier`
- type: `object`
- example: `{ price_identifier: 'f9c0172ba10dfa8...' }`

These functions are core for interacting with Pyth price feeds in
NEAR-based applications, providing a reliable and up-to-date source of
price information. For a full overview of methods provided by the NEAR
contract, see [the interface][] exposed by the receiver contract.

[the interface]: https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/near/receiver/src/ext.rs

## How to Update and Consume Price Feeds

1. Install NEAR JavaScript SDK: First, add the NEAR JavaScript SDK to
your project. You can do this using npm or yarn:
```
npm install near-api-js
```
or
```
yarn add near-api-js
```
2. Interact with the NEAR contract. This can be done with the Contract
interface. Updating a price feed can be achieved using the NEAR JS
API. See the [official docs](https://docs.near.org/tools/near-api-js/quick-reference)
for a guide on setting up a keystore correctly as this will depend
heavily on your app. The skeleton you can use to get started for
calling the Pyth contract looks like so:

```js
// @data: A payload containing price feed update information fetched from Hermes.
async function updatePriceFeeds(data) {
const near = await connect(config);
const result = await account.functionCall({
"pyth.testnet",
methodName: "update_price_feeds",
args: { data },
gas: new utils.BN("30000000000000"),
attachedDeposit: utils.format.parseNearAmount("1"),
});
console.log("Update Price Feeds Result: ", result);
}
```

You can find an update to submit with this call from the Hermes API
which for example [can be found for testnet here.](https://hermes-beta.pyth.network/)
To try this out, use the `get_vaa` endpoint to request a price feed
update for a price feed. You must convert the returned base64 blob to
hex before using it in the `update_price_feeds` call due to NEAR passing
bytes around with hex encoding.

It's also possible to integrate this process into your contract itself
to reduce the number of transactions required. See the example contract
linked below.

Note: gas and attachedDeposit are NEAR-specific parameters that you
may need to set depending on the contract's requirements. Unused
deposit will be refunded, but you can calculate an esimtate by calling
the `get_update_fee_estimate` method against the Pyth contract.

Fetching a price feed is similar:

```js
async function fetchPriceFeed() {
const near = await connect(config);
const account = await near.account();
const contractId = "pyth-oracle.testnet";
const identifier = "PriceIdentifier";

const priceFeed = await account.viewFunction(
contractId,
"get_price",
args: { identifier }
);

console.log("Price Feed Data: ", priceFeed);
}
```
price information.

For a full overview of methods provided by the NEAR
contract, see [the interface](https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/near/receiver/src/ext.rs)] exposed by the receiver contract.

## Getting Started

To get started with Pyth oracle you will need to gather the following information which differ between networks:

- Price ID(s)
- HermesAPI Endpoint
- Smart contract address

| Network | Price Feed IDs | Hermes API Address | Contract Address |
| --------- | ------------------------------------------------------------------------------------------------ | -------------------------- | -------------------------------------------------------------------------------- |
| `testnet` | [NEAR `testnet` Price Feed IDs](https://www.pyth.network/developers/price-feed-ids#near-testnet) | `hermes-beta.pyth.network` | [pyth-oracle.testnet](https://testnet.nearblocks.io/address/pyth-oracle.testnet) |
| `mainnet` | [NEAR `mainnet` Price Feed IDs](https://www.pyth.network/developers/price-feed-ids#near-mainnet) | `hermes.pyth.network` | [pyth-oracle.near](https://nearblocks.io/address/pyth-oracle.near) |

Note: When using Price Feed IDs, you will need to remove the `0x` prefix.

---

### `update_price_feeds`

> Updates the Pyth Oracle contract data with the price feed you provide.

- args: `data` _(off-chain hex-encoded price feed)_
- type: `object`
- example: `{ "data": "504e41...' }`

Update the Pyth Oracle contract with new price feed data in two main steps:

1. [Fetch off-chain price feed](#1-fetch-off-chain-price-feed)
2. [Update Pyth Oracle contract with off-chain price feed](#2-update-pyth-oracle-contract-price-feed)

#### 1) Fetch off-chain price feed

You can obtain an off-chain price feed using Pyth's [Hermes API](https://hermes-beta.pyth.network/docs/).

To use these endpoints, you will need to provide a Price Feed ID and ensure you are targeting the correct network. See [Getting Started](#getting-started) for more information.

Here is a node.js example of fetching the latest price feed using `/v2/updates/price/latest` endpoint:

`Example:`

```js
const axios = require("axios");
Copy link
Contributor

@ali-behjati ali-behjati Nov 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can use the Hermes SDK here. We will update it later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually tried using the SDK but was getting 404 errors when using the hermes-beta base URL for testnet. Is that endpoint supported?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried this example with it and it worked. We will try it separately later and fix any issues that arise.


// There are separate endpoints for testnet and mainnet
const HERMES_TESTNET_URL = "https://hermes-beta.pyth.network";
const HERMES_MAINNET_URL = "https://hermes.pyth.network";

async function getHermesPriceData(priceId, network) {
try {
let url;
network === "testnet"
? (url = HERMES_TESTNET_URL)
: (url = HERMES_MAINNET_URL);

// Fetch the price data from the Hermes API
const response = await axios.get(
`${url}/v2/updates/price/latest?ids[]=${priceId}`
);

return response.data.binary.data[0];
} catch (error) {
console.error(
"Error:",
error.response ? error.response.data : error.message
);
}
}

module.exports = { getHermesPriceData };
```

<center>
z [See full example on
GitHub](https://github.com/near-examples/near-js/blob/main/node-js/utils/fetch-hermes-price-data.js)
</center>

---

### 2) Update Pyth Oracle Contract Price Feed

After [fetching an off-chain price feed](#1-fetch-off-chain-price-feed), you can now perform a contract call to the Pyth Oracle contract to update.
Call `update_price_feeds` on the Pyth Oracle contract deployed on NEAR with `data` as your arguments.

`example args:`

```json
{
"data": "504e41550100000000a00100000000010070b0ee3a00d1a3c07ee440887eb34a5a35860e6f4b9230fd62f0593fe35c8a3561735a6a37d269c5f166b84ead8918f710dc1be2ee6b51db5b22340ea2c173fc01673d544b00000000001ae101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa7100000000061bc18c014155575600000000000ab0f04600002710f41bc8c224ed983c68dbf5dab7dd34c9129fecfa03005500ca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a600000047e2eb4ef0000000000692480ffffffff800000000673d544b00000000673d544b00000048200e66a00000000005e495a60bb9370c458dd50558b34699b5b179f45e56be22f0a1a0feb1db8469adc8c5efeb53988495bac07bf9efed07f5eee43818150c55055882f6872a228e8e9bc78459ed3ea7fe0b86f3048f6bf0aad34befc46063ab7d200beb8bc9fe5839844d2233546f0742bb665f1e610370fcf8ce5be83d0f47e584b685af87cf3ebcb79e714827dcb99dba579e1a03785052ab3c7c7147d3f7bba822b04dbda159670e9a8d29e7ccf68474b2ca85e00224d29bf65b06b09f95e91703313e053b697b48ac1e4d1c57605a71ab77e7ef276bfe8a369c268333b9a37461bf2b7cb7fd4c005500ecf553770d9b10965f8fb64771e93f5690a182edc32be4a3236e0caaa6e0581a0000000e2ba8cd280000000001b40517fffffff800000000673d544b00000000673d544b0000000e3ea44c6800000000016aee120b47b853f55949284cb8ba0b63824ff9b48cd1da8417f45421b79ee3195fc8d107540a0bbb95c2445b66065754f135cb842db09a7e7ab33f79c546a48db872bd7197b04e3d7b52fbb55b3b9f51707c5a55fac3707cb563dbcde4aadeecc3649c237454cecf519dc567c0da03d81808523aa4fa71815eab25ce7da61b48647bac645d403208135002aab5fde2d7ab3c7c7147d3f7bba822b04dbda159670e9a8d29e7ccf68474b2ca85e00224d29bf65b06b09f95e91703313e053b697b48ac1e4d1c57605a71ab77e7ef276bfe8a369c268333b9a37461bf2b7cb7fd4c"
}
```

To perform this contract call you must first create a NEAR account which can be done using `near-cli`.

Fist, install `near-cli`:

```bash

npm install -g near-cli-rs@latest

```

This CLI allows you to simply run `near` and let the prompts guide you through the process.

To quickly create a NEAR account, run the following command (replacing `your-new-account.testnet` with your desired account name):

```bash
near account \
create-account sponsor-by-faucet-service \
your-new-account.testnet \
autogenerate-new-keypair save-to-legacy-keychain \
network-config testnet \
create
```

To perform a contract call to the Pyth Oracle contract, run the following command:

Replace:

- `your-account.testnet` with your account name
- `'{"data": "504e41550100..."}'` with your off-chain price feed

```
near contract \
call-function \
as-transaction pyth-oracle.testnet update_price_feeds \
json-args '{"data": "504e41550100..."}' \
prepaid-gas '300.0 Tgas' \
attached-deposit '0.01 NEAR' \
sign-as your-account.testnet \
network-config testnet \
sign-with-legacy-keychain \
send
```

Alternatively, you can use `near-js` libraries to perform the contract call. For this example we will create a simple node.js project.

First, install the `near-js` libraries we will use:

```bash
npm install @near-js/client @near-js/keystores-node
```

To setup a NEAR connection, we'll create a `connect.js` file that will initialize an RPC provider and signer. This will look for your NEAR credentials in your `.near-credentials` directory.

```js
// node.js imports
const { join } = require("node:path");
const { homedir } = require("node:os");

// near-js imports
const {
getTestnetRpcProvider,
getSignerFromKeystore,
} = require("@near-js/client");
const { UnencryptedFileSystemKeyStore } = require("@near-js/keystores-node");

// initialize RPC provider and signer
const nearConnect = (sender, network) => ({
rpcProvider: getTestnetRpcProvider(),
signer: getSignerFromKeystore(
sender,
network,
new UnencryptedFileSystemKeyStore(join(homedir(), ".near-credentials"))
),
});

module.exports = { nearConnect };
```

Next we can create a `update-oracle.js` file that will perform the contract call to update the Pyth Oracle contract's price feed.

```js
// near-js imports
// https://www.npmjs.com/package/@near-js/client
const { nearConnect } = require("../utils/connect");
const { functionCall } = require("@near-js/client");

const sender = "your-account.testnet";
const receiver = "pyth-oracle.testnet";
const network = "testnet";

const PRICE_IDS = [
// Price ids can be found at https://www.pyth.network/developers/price-feed-ids#near-testnet
// NOTE: Ensure you are using NEAR specific price ids & remove the '0x' prefix before using them
"f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b", // BTC/USD price id
"ca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a6", // ETH/USD price id
];

async function updatePythContractPriceFeeds(network) {
// Connect to the NEAR network
const { rpcProvider, signer } = nearConnect(sender, network);

// Update the Pyth Oracle contract with the price data
// Performs a NEAR function call to the Pyth Oracle contract
// Deposit for transaction fee (balance will be refunded)
const result = await functionCall({
sender,
receiver,
method: "update_price_feeds",
args: { data: "504e4155010..." },
deposit: 10000000000000000000000,
deps: { rpcProvider, signer },
});

console.log(
`Transaction 👉 https://testnet.nearblocks.io/txns/${result.outcome.transaction.hash}`
);
return result;
}

updatePythOracle();
```

<center>
[See full example on
GitHub](https://github.com/near-examples/near-js/blob/main/node-js/oracle-example/pyth-oracle-update.js)
</center>

Although unused deposit will be refunded, you can calculate an estimate by calling the `get_update_fee_estimate` method against the Pyth contract.

---

### `get_price`

> Fetches the most recent price feed stored in the Pyth Oracle contract. Is a view method, so does not require a signature or payment.

- args: `price_identifier` _(unique [price feed identifier](#environment-variables))_
- type: `object`
- example: `{ price_identifier: 'f9c0172ba10dfa8...' }`

After [updating the price feed](#update_price_feeds), you can view the feed on-chain by calling `get_price` on the Pyth Oracle contract. Note that this is a view method and does not require a signature or deposit.

#### NEAR CLI example

```bash
near contract \
call-function \
as-read-only pyth-oracle.testnet get_price \
json-args '{"price_identifier": "f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b"}' \
network-config testnet \
now

```

#### NEAR-JS Example

For this example we will create a simple node.js project. First, install the [`near-js\client`](https://www.npmjs.com/package/@near-js/client) library:

```bash
npm install @near-js/client
```

Create a `get-price.js` file that will perform the view call from the Pyth Oracle contract. Note that this does not require a signature or deposit.

```js
// near-js import
// https://www.npmjs.com/package/@near-js/client
const { getTestnetRpcProvider, view } = require("@near-js/client");

const PRICE_IDS = [
// Price ids can be found at https://www.pyth.network/developers/price-feed-ids#near-testnet
// NOTE: Ensure you are using NEAR specific price ids & remove the '0x' prefix before using them
"f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b", // BTC/USD price id
"ca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a6", // ETH/USD price id
];

async function getPrice(price_ID, symbol) {
try {
const rpcProvider = getTestnetRpcProvider();
const result = await view({
account: "pyth-oracle.testnet",
method: "get_price",
args: { price_identifier: price_ID },
deps: { rpcProvider },
});
console.log(symbol, result);
} catch (error) {
console.error(`Error fetching ${symbol} price:`, error.message);
}
}

getPrice(PRICE_IDS[0], "BTC/USD:");
```

<center>
[See full example on
GitHub](https://github.com/near-examples/near-js/blob/main/node-js/oracle-example/pyth-oracle-view.js)
</center>

---

## On-Chain Prices

Expand All @@ -100,8 +332,3 @@ see the [update.sh][] example script in the repository to see how to
pull prices with the official NEAR cli.

[update.sh]: https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/near/scripts/update.sh

## Contract Addresses

Developers will need the address of the Pyth price feed contract on their blockchain in order to use Pyth.
Please consult [Near Contract Addresses](../contract-addresses/near) to find the address for your blockchain.