Skip to content

Commit

Permalink
feat: add InitPriceFeedIndex method IDL (#80)
Browse files Browse the repository at this point in the history
* feat: add InitPriceFeedIndex method IDL

* fix: update tests to use pythnet/pythtest

* fix: address review comments

* feat: add tests for this instruction and setMaxLatency

* refactor: format

* fix: remove permissions account from the test as its pda

* feat: add account parsing

* fix: address lint issues

* fix: update oracle version in idl.json
  • Loading branch information
ali-bahjati authored Aug 28, 2024
1 parent 8a9c94b commit 1b9d17c
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 38 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pythnetwork/client",
"version": "2.21.1",
"version": "2.22.0",
"description": "Client for consuming Pyth price data",
"homepage": "https://pyth.network",
"main": "lib/index.js",
Expand Down
30 changes: 28 additions & 2 deletions src/__tests__/Anchor.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { AnchorProvider, Wallet } from '@coral-xyz/anchor'
import { Connection, Keypair, PublicKey } from '@solana/web3.js'
import { BN } from 'bn.js'
import { getPythClusterApiUrl } from '../cluster'
import { getPythProgramKeyForCluster, pythOracleProgram, pythOracleCoder } from '../index'

test('Anchor', (done) => {
jest.setTimeout(60000)
const provider = new AnchorProvider(
new Connection('https://api.mainnet-beta.solana.com'),
new Connection(getPythClusterApiUrl('pythnet')),
new Wallet(new Keypair()),
AnchorProvider.defaultOptions(),
)
const pythOracle = pythOracleProgram(getPythProgramKeyForCluster('mainnet-beta'), provider)
const pythOracle = pythOracleProgram(getPythProgramKeyForCluster('pythnet'), provider)
pythOracle.methods
.initMapping()
.accounts({ fundingAccount: PublicKey.unique(), freshMappingAccount: PublicKey.unique() })
Expand Down Expand Up @@ -202,5 +203,30 @@ test('Anchor', (done) => {
expect(decoded?.data.securityAuthority.equals(new PublicKey(8))).toBeTruthy()
})

pythOracle.methods
.setMaxLatency(1, [0, 0, 0])
.accounts({ fundingAccount: PublicKey.unique(), priceAccount: PublicKey.unique() })
.instruction()
.then((instruction) => {
expect(instruction.data).toStrictEqual(Buffer.from([2, 0, 0, 0, 18, 0, 0, 0, 1, 0, 0, 0]))
const decoded = pythOracleCoder().instruction.decode(instruction.data)
expect(decoded?.name).toBe('setMaxLatency')
expect(decoded?.data.maxLatency === 1).toBeTruthy()
})

pythOracle.methods
.initPriceFeedIndex()
.accounts({
fundingAccount: PublicKey.unique(),
priceAccount: PublicKey.unique(),
})
.instruction()
.then((instruction) => {
expect(instruction.data).toStrictEqual(Buffer.from([2, 0, 0, 0, 19, 0, 0, 0]))
const decoded = pythOracleCoder().instruction.decode(instruction.data)
expect(decoded?.name).toBe('initPriceFeedIndex')
expect(decoded?.data).toStrictEqual({})
})

done()
})
6 changes: 3 additions & 3 deletions src/__tests__/Example.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { clusterApiUrl, Connection, PublicKey } from '@solana/web3.js'
import { parseMappingData, parsePriceData, parseProductData } from '../index'
import { Connection, PublicKey } from '@solana/web3.js'
import { getPythClusterApiUrl, parseMappingData, parsePriceData, parseProductData } from '../index'

const SOLANA_CLUSTER_URL = clusterApiUrl('devnet')
const SOLANA_CLUSTER_URL = getPythClusterApiUrl('pythtest-crosschain')
const ORACLE_MAPPING_PUBLIC_KEY = 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2'

test('Mapping', (done) => {
Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/Mapping.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { clusterApiUrl, Connection, PublicKey } from '@solana/web3.js'
import { parseMappingData, Magic, Version } from '../index'
import { Connection, PublicKey } from '@solana/web3.js'
import { parseMappingData, Magic, Version, getPythClusterApiUrl } from '../index'

test('Mapping', (done) => {
jest.setTimeout(60000)
const url = clusterApiUrl('devnet')
const url = getPythClusterApiUrl('pythtest-crosschain')
const oraclePublicKey = 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2'
const connection = new Connection(url)
const publicKey = new PublicKey(oraclePublicKey)
Expand Down
38 changes: 36 additions & 2 deletions src/__tests__/Price.test.ts

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/__tests__/Product.ETH.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { clusterApiUrl, Connection, PublicKey } from '@solana/web3.js'
import { Magic, parseProductData, Version } from '../index'
import { Connection, PublicKey } from '@solana/web3.js'
import { getPythClusterApiUrl, Magic, parseProductData, Version } from '../index'

test('Product', (done) => {
jest.setTimeout(60000)
const url = clusterApiUrl('devnet')
const url = getPythClusterApiUrl('pythtest-crosschain')
const ethProductKey = '2ciUuGZiee5macAMeQ7bHGTJtwcYTgnt6jdmQnnKZrfu'
const connection = new Connection(url)
const publicKey = new PublicKey(ethProductKey)
Expand Down
5 changes: 3 additions & 2 deletions src/__tests__/Product.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { clusterApiUrl, Connection, PublicKey } from '@solana/web3.js'
import { Connection, PublicKey } from '@solana/web3.js'
import { getPythClusterApiUrl } from '../cluster'
import { parseMappingData, parseProductData, Magic, Version } from '../index'

test('Product', (done) => {
jest.setTimeout(60000)
const url = clusterApiUrl('devnet')
const url = getPythClusterApiUrl('pythtest-crosschain')
const oraclePublicKey = 'BmA9Z6FjioHJPpjT39QazZyhDRUdZy2ezwx4GiDdE2u2'
const connection = new Connection(url)
const publicKey = new PublicKey(oraclePublicKey)
Expand Down
15 changes: 8 additions & 7 deletions src/__tests__/PythNetworkRestClient.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { clusterApiUrl, Connection, PublicKey, SystemProgram } from '@solana/web3.js'
import { Connection, PublicKey, SystemProgram } from '@solana/web3.js'
import { getPythProgramKeyForCluster, parseProductData, PythHttpClient } from '..'
import { getPythClusterApiUrl } from '../cluster'

test('PythHttpClientCall: getData', (done) => {
jest.setTimeout(20000)
jest.setTimeout(60000)
try {
const programKey = getPythProgramKeyForCluster('testnet')
const currentConnection = new Connection(clusterApiUrl('testnet'))
const programKey = getPythProgramKeyForCluster('pythtest-conformance')
const currentConnection = new Connection(getPythClusterApiUrl('pythtest-conformance'))
const pyth_client = new PythHttpClient(currentConnection, programKey)
pyth_client.getData().then(
(result) => {
Expand All @@ -30,7 +31,7 @@ test('PythHttpClientCall: getAssetPricesFromAccounts for one account', (done) =>
const solUSDKey = new PublicKey('7VJsBtJzgTftYzEeooSDYyjKXvYRWJHdwvbwfBvTg9K')
try {
const programKey = getPythProgramKeyForCluster('testnet')
const currentConnection = new Connection(clusterApiUrl('testnet'))
const currentConnection = new Connection(getPythClusterApiUrl('pythtest-conformance'))
const pyth_client = new PythHttpClient(currentConnection, programKey)
pyth_client
.getAssetPricesFromAccounts([solUSDKey])
Expand Down Expand Up @@ -66,7 +67,7 @@ test('PythHttpClientCall: getAssetPricesFromAccounts for multiple accounts', (do

try {
const programKey = getPythProgramKeyForCluster('testnet')
const currentConnection = new Connection(clusterApiUrl('testnet'))
const currentConnection = new Connection(getPythClusterApiUrl('pythtest-conformance'))
const pyth_client = new PythHttpClient(currentConnection, programKey)
pyth_client
.getAssetPricesFromAccounts([solUSDKey, bonkUSDKey, usdcUSDKey])
Expand Down Expand Up @@ -106,7 +107,7 @@ test('PythHttpClientCall: getAssetPricesFromAccounts should throw for invalid ac

try {
const programKey = getPythProgramKeyForCluster('testnet')
const currentConnection = new Connection(clusterApiUrl('testnet'))
const currentConnection = new Connection(getPythClusterApiUrl('pythtest-conformance'))
const pyth_client = new PythHttpClient(currentConnection, programKey)
pyth_client
.getAssetPricesFromAccounts([solUSDKey, systemProgram, usdcUSDKey])
Expand Down
35 changes: 33 additions & 2 deletions src/anchor/idl.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "2.20.0",
"version": "2.33.0",
"name": "pyth_oracle",
"instructions": [
{
Expand Down Expand Up @@ -171,7 +171,7 @@
},
{
"name": "permissionsAccount",
"isMut": false,
"isMut": true,
"isSigner": false,
"pda": {
"seeds": [
Expand Down Expand Up @@ -640,6 +640,37 @@
}
}
]
},
{
"name": "initPriceFeedIndex",
"discriminant": { "value": [2, 0, 0, 0, 19, 0, 0, 0] },
"accounts": [
{
"name": "fundingAccount",
"isMut": true,
"isSigner": true
},
{
"name": "priceAccount",
"isMut": true,
"isSigner": false
},
{
"name": "permissionsAccount",
"isMut": true,
"isSigner": false,
"pda": {
"seeds": [
{
"kind": "const",
"type": "string",
"value": "permissions"
}
]
}
}
],
"args": []
}
],
"types": [
Expand Down
35 changes: 33 additions & 2 deletions src/anchor/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function pythOracleCoder(): PythOracleCoder {
export { default as pythIdl } from './idl.json'

export type PythOracle = {
version: '2.20.0'
version: '2.33.0'
name: 'pyth_oracle'
instructions: [
{
Expand Down Expand Up @@ -191,7 +191,7 @@ export type PythOracle = {
},
{
name: 'permissionsAccount'
isMut: false
isMut: true
isSigner: false
pda: {
seeds: [
Expand Down Expand Up @@ -661,6 +661,37 @@ export type PythOracle = {
},
]
},
{
name: 'initPriceFeedIndex'
discriminant: { value: [2, 0, 0, 0, 19, 0, 0, 0] }
accounts: [
{
name: 'fundingAccount'
isMut: true
isSigner: true
},
{
name: 'priceAccount'
isMut: true
isSigner: false
},
{
name: 'permissionsAccount'
isMut: true
isSigner: false
pda: {
seeds: [
{
kind: 'const'
type: 'string'
value: 'permissions'
},
]
}
},
]
args: []
},
]
types: [
{
Expand Down
2 changes: 1 addition & 1 deletion src/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function getPythClusterApiUrl(cluster: PythCluster): string {
if (cluster === 'pythtest-conformance' || cluster === 'pythtest-crosschain') {
return 'https://api.pythtest.pyth.network'
} else if (cluster === 'pythnet') {
return 'https://pythnet.rpcpool.com'
return 'https://api2.pythnet.pyth.network'
} else if (cluster === 'localnet') {
return 'http://localhost:8899'
} else {
Expand Down
29 changes: 21 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ export enum AccountType {
Permission,
}

export type Flags = {
accumulatorV2: boolean,
messageBufferCleared: boolean,
}

const empty32Buffer = Buffer.alloc(32)
const PKorNull = (data: Buffer) => (data.equals(empty32Buffer) ? null : new PublicKey(data))

Expand Down Expand Up @@ -105,8 +110,8 @@ export interface PriceData extends Base {
minPublishers: number
messageSent: number
maxLatency: number
drv3: number
drv4: number
flags: Flags,
feedIndex: number
productAccountKey: PublicKey
nextPriceAccountKey: PublicKey | null
previousSlot: bigint
Expand Down Expand Up @@ -290,10 +295,18 @@ export const parsePriceData = (data: Buffer, currentSlot?: number): PriceData =>
const messageSent = data.readUInt8(105)
// configurable max latency in slots between send and receive
const maxLatency = data.readUInt8(106)
// space for future derived values
const drv3 = data.readInt8(107)
// space for future derived values
const drv4 = data.readInt32LE(108)
// Various flags (used for operations)
const flagBits = data.readInt8(107)

/* tslint:disable:no-bitwise */
const flags = {
accumulatorV2: (flagBits & (1<<0)) !== 0,
messageBufferCleared: (flagBits & (1<<1)) !== 0,
}
/* tslint:enable:no-bitwise */

// Globally immutable unique price feed index used for publishing.
const feedIndex = data.readInt32LE(108)
// product id / reference account
const productAccountKey = new PublicKey(data.slice(112, 144))
// next price account in list
Expand Down Expand Up @@ -355,8 +368,8 @@ export const parsePriceData = (data: Buffer, currentSlot?: number): PriceData =>
minPublishers,
messageSent,
maxLatency,
drv3,
drv4,
flags,
feedIndex,
productAccountKey,
nextPriceAccountKey,
previousSlot,
Expand Down

0 comments on commit 1b9d17c

Please sign in to comment.