55import { Buffer } from "node:buffer" ;
66
77import { bcs } from "@mysten/sui/bcs" ;
8- import { SuiClient } from "@mysten/sui/client" ;
8+ import type { ClientWithCoreApi } from "@mysten/sui/client" ;
99import { Transaction } from "@mysten/sui/transactions" ;
1010import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui/utils" ;
1111import type { HexString } from "@pythnetwork/hermes-client" ;
@@ -24,7 +24,7 @@ export class SuiPythClient {
2424 private priceFeedObjectIdCache = new Map < HexString , ObjectId > ( ) ;
2525 private baseUpdateFee : number | undefined ;
2626 constructor (
27- public provider : SuiClient ,
27+ public provider : ClientWithCoreApi ,
2828 public pythStateId : ObjectId ,
2929 public wormholeStateId : ObjectId ,
3030 ) {
@@ -34,18 +34,14 @@ export class SuiPythClient {
3434
3535 async getBaseUpdateFee ( ) : Promise < number > {
3636 if ( this . baseUpdateFee === undefined ) {
37- const result = await this . provider . getObject ( {
38- id : this . pythStateId ,
39- options : { showContent : true } ,
37+ const result = await this . provider . core . getObject ( {
38+ objectId : this . pythStateId ,
39+ include : { json : true } ,
4040 } ) ;
41- if (
42- ! result . data ?. content ||
43- result . data . content . dataType !== "moveObject"
44- )
41+ const json = result . object . json as Record < string , unknown > | null ;
42+ if ( ! json )
4543 throw new Error ( "Unable to fetch pyth state object" ) ;
46- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
47- // @ts -ignore
48- this . baseUpdateFee = result . data . content . fields . base_update_fee as number ;
44+ this . baseUpdateFee = Number ( json . base_update_fee ) ;
4945 }
5046
5147 return this . baseUpdateFee ;
@@ -58,27 +54,23 @@ export class SuiPythClient {
5854 * @returns package id
5955 */
6056 async getPackageId ( objectId : ObjectId ) : Promise < ObjectId > {
61- const state = await this . provider
62- . getObject ( {
63- id : objectId ,
64- options : {
65- showContent : true ,
66- } ,
67- } )
68- . then ( ( result ) => {
69- if ( result . data ?. content ?. dataType == "moveObject" ) {
70- return result . data . content . fields ;
71- }
72- console . log ( result . data ?. content ) ;
73-
74- throw new Error ( `Cannot fetch package id for object ${ objectId } ` ) ;
75- } ) ;
57+ const result = await this . provider . core . getObject ( {
58+ objectId,
59+ include : { json : true } ,
60+ } ) ;
61+ const json = result . object . json as Record < string , unknown > | null ;
62+ if ( ! json ) {
63+ throw new Error ( `Cannot fetch package id for object ${ objectId } ` ) ;
64+ }
7665
77- if ( "upgrade_cap" in state ) {
78- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
79- // @ts -ignore
80- // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
81- return state . upgrade_cap . fields . package ;
66+ if ( "upgrade_cap" in json ) {
67+ const upgradeCap = json . upgrade_cap as Record < string , unknown > ;
68+ // JSON-RPC wraps nested objects in { type, fields }, gRPC may not.
69+ const fields = ( upgradeCap . fields ?? upgradeCap ) as Record <
70+ string ,
71+ unknown
72+ > ;
73+ return fields . package as string ;
8274 }
8375
8476 throw new Error ( "upgrade_cap not found" ) ;
@@ -299,28 +291,34 @@ export class SuiPythClient {
299291 const normalizedFeedId = feedId . replace ( "0x" , "" ) ;
300292 if ( ! this . priceFeedObjectIdCache . has ( normalizedFeedId ) ) {
301293 const { id : tableId , fieldType } = await this . getPriceTableInfo ( ) ;
302- const result = await this . provider . getDynamicFieldObject ( {
303- parentId : tableId ,
304- name : {
305- type : `${ fieldType } ::price_identifier::PriceIdentifier` ,
306- value : {
307- bytes : [ ...Buffer . from ( normalizedFeedId , "hex" ) ] ,
294+ // BCS-encode the PriceIdentifier struct name.
295+ // PriceIdentifier has a single field `bytes: vector<u8>`, so its BCS
296+ // encoding is the same as just encoding the inner vector<u8>.
297+ const nameBcs = bcs
298+ . vector ( bcs . U8 )
299+ . serialize ( [ ...Buffer . from ( normalizedFeedId , "hex" ) ] )
300+ . toBytes ( ) ;
301+ try {
302+ const result = await this . provider . core . getDynamicObjectField ( {
303+ parentId : tableId ,
304+ name : {
305+ type : `${ fieldType } ::price_identifier::PriceIdentifier` ,
306+ bcs : nameBcs ,
308307 } ,
309- } ,
310- } ) ;
311- if ( ! result . data ?. content ) {
308+ include : { json : true } ,
309+ } ) ;
310+ const json = result . object . json as Record < string , unknown > | null ;
311+ if ( ! json ) {
312+ return undefined ;
313+ }
314+ this . priceFeedObjectIdCache . set (
315+ normalizedFeedId ,
316+ json . value as string ,
317+ ) ;
318+ } catch {
319+ // Dynamic field not found
312320 return undefined ;
313321 }
314- if ( result . data . content . dataType !== "moveObject" ) {
315- throw new Error ( "Price feed type mismatch" ) ;
316- }
317- this . priceFeedObjectIdCache . set (
318- normalizedFeedId ,
319- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
320- // @ts -ignore
321- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
322- result . data . content . fields . value ,
323- ) ;
324322 }
325323 return this . priceFeedObjectIdCache . get ( normalizedFeedId ) ;
326324 }
@@ -331,24 +329,29 @@ export class SuiPythClient {
331329 */
332330 async getPriceTableInfo ( ) : Promise < { id : ObjectId ; fieldType : ObjectId } > {
333331 if ( this . priceTableInfo === undefined ) {
334- const result = await this . provider . getDynamicFieldObject ( {
332+ // BCS-encode the "price_info" name as vector<u8>
333+ const nameBcs = bcs
334+ . vector ( bcs . U8 )
335+ . serialize ( [ ...Buffer . from ( "price_info" ) ] )
336+ . toBytes ( ) ;
337+ const result = await this . provider . core . getDynamicObjectField ( {
335338 parentId : this . pythStateId ,
336339 name : {
337340 type : "vector<u8>" ,
338- value : "price_info" ,
341+ bcs : nameBcs ,
339342 } ,
340343 } ) ;
341- if ( ! result . data ? .type ) {
344+ if ( ! result . object . type ) {
342345 throw new Error (
343346 "Price Table not found, contract may not be initialized" ,
344347 ) ;
345348 }
346- let type = result . data . type . replace ( "0x2::table::Table<" , "" ) ;
349+ let type = result . object . type . replace ( "0x2::table::Table<" , "" ) ;
347350 type = type . replace (
348351 "::price_identifier::PriceIdentifier, 0x2::object::ID>" ,
349352 "" ,
350353 ) ;
351- this . priceTableInfo = { id : result . data . objectId , fieldType : type } ;
354+ this . priceTableInfo = { id : result . object . objectId , fieldType : type } ;
352355 }
353356 return this . priceTableInfo ;
354357 }
0 commit comments