Skip to content

Commit 174e708

Browse files
authored
Merge pull request #67 from pyth-network/guibescos/add-product-sets-metadata
AddProduct creates the Product metadata
2 parents 6f64b84 + ffaef51 commit 174e708

8 files changed

+67
-21
lines changed

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/client",
3-
"version": "2.14.0",
3+
"version": "2.15.0",
44
"description": "Client for consuming Pyth price data",
55
"homepage": "https://pyth.network",
66
"main": "lib/index.js",

src/PythConnection.ts

+14-10
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,12 @@ export class PythConnection {
105105
/** Create a PythConnection that reads its data from an underlying solana web3 connection.
106106
* pythProgramKey is the public key of the Pyth program running on the chosen solana cluster.
107107
*/
108-
constructor(connection: Connection, pythProgramKey: PublicKey, commitment: Commitment = 'finalized', feedIds?: PublicKey[]) {
108+
constructor(
109+
connection: Connection,
110+
pythProgramKey: PublicKey,
111+
commitment: Commitment = 'finalized',
112+
feedIds?: PublicKey[],
113+
) {
109114
this.connection = connection
110115
this.pythProgramKey = pythProgramKey
111116
this.commitment = commitment
@@ -124,21 +129,20 @@ export class PythConnection {
124129
this.handleAccount(account.pubkey, account.account, true, currentSlot)
125130
}
126131

127-
if(this.feedIds) {
132+
if (this.feedIds) {
128133
// Filter down to only the feeds we want
129134
const rawIDs = this.feedIds.map((feed) => feed.toString())
130135
accounts = accounts.filter((feed) => rawIDs.includes(feed.pubkey.toString()))
131-
for (const account of accounts){
136+
for (const account of accounts) {
132137
this.connection.onAccountChange(
133-
account.pubkey,
138+
account.pubkey,
134139

135-
(accountInfo, context) => {
136-
this.handleAccount(account.pubkey, accountInfo, false, context.slot)
137-
},
138-
this.commitment,
139-
)
140+
(accountInfo, context) => {
141+
this.handleAccount(account.pubkey, accountInfo, false, context.slot)
142+
},
143+
this.commitment,
144+
)
140145
}
141-
142146
} else {
143147
this.connection.onProgramAccountChange(
144148
this.pythProgramKey,

src/__tests__/Anchor.test.ts

+26
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,32 @@ test('Anchor', (done) => {
3131
expect(decoded?.name).toBe('addMapping')
3232
expect(decoded?.data).toStrictEqual({})
3333
})
34+
pythOracle.methods
35+
.addProduct({
36+
asset_type: 'Crypto',
37+
base: 'ETH',
38+
description: 'ETH/USD',
39+
quote_currency: 'USD',
40+
symbol: 'Crypto.ETH/USD',
41+
generic_symbol: 'ETHUSD',
42+
})
43+
.accounts({
44+
fundingAccount: PublicKey.unique(),
45+
productAccount: PublicKey.unique(),
46+
tailMappingAccount: PublicKey.unique(),
47+
})
48+
.instruction()
49+
.then((instruction) => {
50+
const decoded = pythOracleCoder().instruction.decode(instruction.data)
51+
expect(decoded?.name).toBe('addProduct')
52+
expect(decoded?.data.asset_type).toBe('Crypto')
53+
expect(decoded?.data.base).toBe('ETH')
54+
expect(decoded?.data.description).toBe('ETH/USD')
55+
expect(decoded?.data.quote_currency).toBe('USD')
56+
expect(decoded?.data.symbol).toBe('Crypto.ETH/USD')
57+
expect(decoded?.data.generic_symbol).toBe('ETHUSD')
58+
})
59+
3460
pythOracle.methods
3561
.updProduct({
3662
asset_type: 'Crypto',

src/anchor/coder/instructions.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { IdlCoder } from './idl'
1313
import { InstructionCoder } from '@coral-xyz/anchor'
1414
import { Product } from '../..'
1515

16+
const MAX_METADATA_SIZE: number = 464
17+
1618
export type PythIdlInstruction = {
1719
name: string
1820
docs?: string[]
@@ -87,16 +89,16 @@ export class PythOracleInstructionCoder implements InstructionCoder {
8789
throw new Error(`Unknown method: ${methodName}`)
8890
}
8991

90-
/// updProduct has its own format
91-
if (methodName === 'updProduct') {
92+
/// updProduct and addProduct have their own format
93+
if (methodName === 'updProduct' || methodName === 'addProduct') {
9294
let offset = 0
9395
for (const key of Object.keys(ix.productMetadata)) {
9496
offset += buffer.subarray(offset).writeInt8(key.length)
9597
offset += buffer.subarray(offset).write(key)
9698
offset += buffer.subarray(offset).writeInt8(ix.productMetadata[key].length)
9799
offset += buffer.subarray(offset).write(ix.productMetadata[key])
98100
}
99-
if (offset > 464) {
101+
if (offset > MAX_METADATA_SIZE) {
100102
throw new Error('The metadata is too long')
101103
}
102104
const data = buffer.subarray(0, offset)
@@ -134,8 +136,8 @@ export class PythOracleInstructionCoder implements InstructionCoder {
134136
return null
135137
}
136138

137-
/// updProduct has its own format
138-
if (decoder.name === 'updProduct') {
139+
/// updProduct and addProduct have their own format
140+
if (decoder.name === 'updProduct' || decoder.name === 'addProduct') {
139141
const product: Product = {}
140142
let idx = 0
141143
while (idx < data.length) {

src/anchor/idl.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,14 @@
103103
}
104104
}
105105
],
106-
"args": []
106+
"args": [
107+
{
108+
"name": "productMetadata",
109+
"type": {
110+
"defined": "ProductMetadata"
111+
}
112+
}
113+
]
107114
},
108115
{
109116
"name": "updProduct",

src/anchor/program.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,14 @@ export type PythOracle = {
123123
}
124124
},
125125
]
126-
args: []
126+
args: [
127+
{
128+
name: 'productMetadata'
129+
type: {
130+
defined: 'ProductMetadata'
131+
}
132+
},
133+
]
127134
},
128135
{
129136
name: 'updProduct'

src/example_ws_single_feed.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const pythPublicKey = getPythProgramKeyForCluster(PYTHNET_CLUSTER_NAME)
99
// This example shows Crypto.SOL/USD
1010
const feeds = [new PublicKey('H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG')]
1111

12-
const pythConnection = new PythConnection(connection, pythPublicKey, "confirmed", feeds)
12+
const pythConnection = new PythConnection(connection, pythPublicKey, 'confirmed', feeds)
1313
pythConnection.onPriceChangeVerbose((productAccount, priceAccount) => {
1414
// The arguments to the callback include solana account information / the update slot if you need it.
1515
const product = productAccount.accountInfo.data.product

0 commit comments

Comments
 (0)