Skip to content

Commit 862053c

Browse files
authored
Merge pull request #55 from pyth-network/guibescos/upd_product_decoder
Add updProduct encoder/decoder
2 parents 166e0b6 + 6ac5beb commit 862053c

File tree

5 files changed

+91
-12
lines changed

5 files changed

+91
-12
lines changed

jestconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
"transform": {
33
"^.+\\.(t|j)sx?$": "ts-jest"
44
},
5-
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
5+
"testRegex": "./__tests__/Anchor.test.ts",
66
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"]
77
}

src/__tests__/Anchor.test.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,18 @@ test('Anchor', (done) => {
3232
expect(decoded?.data).toStrictEqual({})
3333
})
3434
pythOracle.methods
35-
.updProduct()
35+
.updProduct({ asset_type: 'Crypto', base: 'BTC', description : "BTC/USD", quote_currency :"USD", symbol : "Crypto.BTC/USD", generic_symbol : "BTCUSD" })
3636
.accounts({ fundingAccount: PublicKey.unique(), productAccount: PublicKey.unique() })
3737
.instruction()
3838
.then((instruction) => {
39-
expect(instruction.data).toStrictEqual(Buffer.from([2, 0, 0, 0, 3, 0, 0, 0]))
4039
const decoded = pythOracleCoder().instruction.decode(instruction.data)
4140
expect(decoded?.name).toBe('updProduct')
42-
expect(decoded?.data).toStrictEqual({})
41+
expect(decoded?.data.asset_type).toBe("Crypto")
42+
expect(decoded?.data.base).toBe("BTC")
43+
expect(decoded?.data.description).toBe("BTC/USD")
44+
expect(decoded?.data.quote_currency).toBe("USD")
45+
expect(decoded?.data.symbol).toBe("Crypto.BTC/USD")
46+
expect(decoded?.data.generic_symbol).toBe("BTCUSD")
4347
})
4448

4549
pythOracle.methods

src/anchor/coder/instructions.ts

+49-6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { PublicKey } from '@solana/web3.js'
1111
import { Idl, IdlField, IdlType, IdlAccountItem } from '@coral-xyz/anchor/dist/cjs/idl'
1212
import { IdlCoder } from './idl'
1313
import { InstructionCoder } from '@coral-xyz/anchor'
14+
import { Product } from '../..'
1415

1516
export type PythIdlInstruction = {
1617
name: string
@@ -79,14 +80,32 @@ export class PythOracleInstructionCoder implements InstructionCoder {
7980
public encode(ixName: string, ix: any): Buffer {
8081
const buffer = Buffer.alloc(1000) // TODO: use a tighter buffer.
8182
const methodName = camelCase(ixName)
83+
8284
const layout = this.ixLayout.get(methodName)
8385
const discriminator = this.ixDiscriminator.get(methodName)
8486
if (!layout || !discriminator) {
8587
throw new Error(`Unknown method: ${methodName}`)
8688
}
87-
const len = layout.encode(ix, buffer)
88-
const data = buffer.subarray(0, len)
89-
return Buffer.concat([discriminator, data])
89+
90+
/// updProduct has its own format
91+
if (methodName === 'updProduct') {
92+
let offset = 0
93+
for (const key of Object.keys(ix.productMetadata)) {
94+
offset += buffer.subarray(offset).writeInt8(key.length)
95+
offset += buffer.subarray(offset).write(key)
96+
offset += buffer.subarray(offset).writeInt8(ix.productMetadata[key].length)
97+
offset += buffer.subarray(offset).write(ix.productMetadata[key])
98+
}
99+
if (offset > 464) {
100+
throw new Error('The metadata is too long')
101+
}
102+
const data = buffer.subarray(0, offset)
103+
return Buffer.concat([discriminator, data])
104+
} else {
105+
const len = layout.encode(ix, buffer)
106+
const data = buffer.subarray(0, len)
107+
return Buffer.concat([discriminator, data])
108+
}
90109
}
91110

92111
private static parseIxLayout(idl: Idl): Map<string, Layout> {
@@ -114,9 +133,33 @@ export class PythOracleInstructionCoder implements InstructionCoder {
114133
if (!decoder) {
115134
return null
116135
}
117-
return {
118-
data: decoder.layout.decode(data),
119-
name: decoder.name,
136+
137+
/// updProduct has its own format
138+
if (decoder.name === 'updProduct') {
139+
const product: Product = {}
140+
let idx = 0
141+
while (idx < data.length) {
142+
const keyLength = data[idx]
143+
idx++
144+
if (keyLength) {
145+
const key = data.slice(idx, idx + keyLength).toString()
146+
idx += keyLength
147+
const valueLength = data[idx]
148+
idx++
149+
const value = data.slice(idx, idx + valueLength).toString()
150+
idx += valueLength
151+
product[key] = value
152+
}
153+
}
154+
return {
155+
data: product,
156+
name: decoder.name,
157+
}
158+
} else {
159+
return {
160+
data: decoder.layout.decode(data),
161+
name: decoder.name,
162+
}
120163
}
121164
}
122165
}

src/anchor/idl.json

+17-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,14 @@
134134
}
135135
}
136136
],
137-
"args": []
137+
"args": [
138+
{
139+
"name": "productMetadata",
140+
"type": {
141+
"defined": "ProductMetadata"
142+
}
143+
}
144+
]
138145
},
139146
{
140147
"name": "addPrice",
@@ -585,5 +592,14 @@
585592
}
586593
]
587594
}
595+
],
596+
"types": [
597+
{
598+
"name": "ProductMetadata",
599+
"type": {
600+
"kind": "struct",
601+
"fields": []
602+
}
603+
}
588604
]
589605
}

src/anchor/program.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,14 @@ export type PythOracle = {
154154
}
155155
},
156156
]
157-
args: []
157+
args: [
158+
{
159+
name: 'productMetadata'
160+
type: {
161+
defined: 'ProductMetadata'
162+
}
163+
},
164+
]
158165
},
159166
{
160167
name: 'addPrice'
@@ -606,4 +613,13 @@ export type PythOracle = {
606613
]
607614
},
608615
]
616+
types: [
617+
{
618+
name: 'ProductMetadata'
619+
type: {
620+
kind: 'struct'
621+
fields: []
622+
}
623+
},
624+
]
609625
}

0 commit comments

Comments
 (0)