@pythnetwork/price-pusher 5.4.10 → 5.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,7 @@ declare const _default: {
13
13
  "custom-gas-station": Options;
14
14
  "tx-speed": Options;
15
15
  "override-gas-price-multiplier": Options;
16
+ "override-gas-price-multiplier-cap": Options;
16
17
  };
17
18
  handler: (argv: any) => void;
18
19
  };
@@ -1 +1 @@
1
- {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/evm/command.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;oBA4CL,GAAG;;AAxC9B,wBA+GE"}
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/evm/command.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;;oBAuDL,GAAG;;AAnD9B,wBA4HE"}
@@ -58,11 +58,21 @@ exports.default = {
58
58
  required: false,
59
59
  },
60
60
  "override-gas-price-multiplier": {
61
- description: "Multiply the gas price by this number if the transaction is not landing to override it. Default to 1.1",
61
+ description: "Multiply the previous gas price by this number if the transaction is not landing to override. " +
62
+ "Please note that the gas price can grow exponentially on consecutive failures; " +
63
+ "to set a cap on the multiplier, use the `override-gas-price-multiplier-cap` option." +
64
+ "Default to 1.1",
62
65
  type: "number",
63
66
  required: false,
64
67
  default: 1.1,
65
68
  },
69
+ "override-gas-price-multiplier-cap": {
70
+ description: "Maximum gas price multiplier to use in override compared to the RPC returned " +
71
+ "gas price. Default to 5",
72
+ type: "number",
73
+ required: false,
74
+ default: 5,
75
+ },
66
76
  ...options.priceConfigFile,
67
77
  ...options.priceServiceEndpoint,
68
78
  ...options.mnemonicFile,
@@ -72,7 +82,7 @@ exports.default = {
72
82
  },
73
83
  handler: function (argv) {
74
84
  // FIXME: type checks for this
75
- const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pythContractAddress, pushingFrequency, pollingFrequency, customGasStation, txSpeed, overrideGasPriceMultiplier, } = argv;
85
+ const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pythContractAddress, pushingFrequency, pollingFrequency, customGasStation, txSpeed, overrideGasPriceMultiplier, overrideGasPriceMultiplierCap, } = argv;
76
86
  const priceConfigs = (0, price_config_1.readPriceConfigFile)(priceConfigFile);
77
87
  const priceServiceConnection = new price_service_client_1.PriceServiceConnection(priceServiceEndpoint, {
78
88
  logger: {
@@ -95,7 +105,7 @@ exports.default = {
95
105
  pollingFrequency,
96
106
  });
97
107
  const gasStation = (0, custom_gas_station_1.getCustomGasStation)(customGasStation, txSpeed);
98
- const evmPusher = new evm_1.EvmPricePusher(priceServiceConnection, pythContractFactory, overrideGasPriceMultiplier, gasStation);
108
+ const evmPusher = new evm_1.EvmPricePusher(priceServiceConnection, pythContractFactory, overrideGasPriceMultiplier, overrideGasPriceMultiplierCap, gasStation);
99
109
  const controller = new controller_1.Controller(priceConfigs, pythListener, evmListener, evmPusher, { pushingFrequency });
100
110
  controller.start();
101
111
  },
package/lib/evm/evm.d.ts CHANGED
@@ -18,12 +18,13 @@ export declare class EvmPriceListener extends ChainPriceListener {
18
18
  export declare class EvmPricePusher implements IPricePusher {
19
19
  private connection;
20
20
  private overrideGasPriceMultiplier;
21
+ private overrideGasPriceMultiplierCap;
21
22
  private customGasStation?;
22
23
  private pythContract;
23
24
  private web3;
24
25
  private pusherAddress;
25
26
  private lastPushAttempt;
26
- constructor(connection: PriceServiceConnection, pythContractFactory: PythContractFactory, overrideGasPriceMultiplier: number, customGasStation?: CustomGasStation);
27
+ constructor(connection: PriceServiceConnection, pythContractFactory: PythContractFactory, overrideGasPriceMultiplier: number, overrideGasPriceMultiplierCap: number, customGasStation?: CustomGasStation);
27
28
  updatePriceFeed(priceIds: string[], pubTimesToPush: UnixTimestamp[]): Promise<void>;
28
29
  private getPriceFeedsUpdateData;
29
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"evm.d.ts","sourceRoot":"","sources":["../../src/evm/evm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAgB,iBAAiB,EAAmB,MAAM,UAAU,CAAC;AAE5E,OAAO,gBAAgB,MAAM,4BAA4B,CAAC;AAG1D,OAAO,EACL,sBAAsB,EACtB,SAAS,EACT,aAAa,EACd,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAIxD,qBAAa,gBAAiB,SAAQ,kBAAkB;IACtD,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,YAAY,CAAW;gBAG7B,mBAAmB,EAAE,mBAAmB,EACxC,UAAU,EAAE,SAAS,EAAE,EACvB,MAAM,EAAE;QACN,gBAAgB,EAAE,iBAAiB,CAAC;KACrC;IAUG,KAAK;YAeG,iBAAiB;IAc/B,OAAO,CAAC,iBAAiB;IAsBnB,mBAAmB,CACvB,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;CAwBlC;AAED,qBAAa,cAAe,YAAW,YAAY;IAQ/C,OAAO,CAAC,UAAU;IAElB,OAAO,CAAC,0BAA0B;IATpC,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,YAAY,CAAW;IAC/B,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,eAAe,CAA0B;gBAGvC,UAAU,EAAE,sBAAsB,EAC1C,mBAAmB,EAAE,mBAAmB,EAChC,0BAA0B,EAAE,MAAM,EAC1C,gBAAgB,CAAC,EAAE,gBAAgB;IAa/B,eAAe,CACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,cAAc,EAAE,aAAa,EAAE,GAC9B,OAAO,CAAC,IAAI,CAAC;YA6IF,uBAAuB;CAQtC;AAED,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,mBAAmB;gBAFnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,mBAAmB,EAAE,MAAM;IAGrC;;;;;;OAMG;IACH,2BAA2B,IAAI,QAAQ;IAcvC;;;;;OAKG;IACH,kBAAkB,IAAI,QAAQ;IAS9B,oBAAoB,IAAI,OAAO;IAI/B,kBAAkB;IA0BlB,uBAAuB;CAQxB"}
1
+ {"version":3,"file":"evm.d.ts","sourceRoot":"","sources":["../../src/evm/evm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,YAAY,EACZ,SAAS,EACT,kBAAkB,EAClB,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAgB,iBAAiB,EAAmB,MAAM,UAAU,CAAC;AAE5E,OAAO,gBAAgB,MAAM,4BAA4B,CAAC;AAG1D,OAAO,EACL,sBAAsB,EACtB,SAAS,EACT,aAAa,EACd,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAIxD,qBAAa,gBAAiB,SAAQ,kBAAkB;IACtD,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,YAAY,CAAW;gBAG7B,mBAAmB,EAAE,mBAAmB,EACxC,UAAU,EAAE,SAAS,EAAE,EACvB,MAAM,EAAE;QACN,gBAAgB,EAAE,iBAAiB,CAAC;KACrC;IAUG,KAAK;YAeG,iBAAiB;IAc/B,OAAO,CAAC,iBAAiB;IAsBnB,mBAAmB,CACvB,OAAO,EAAE,SAAS,GACjB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;CAwBlC;AAED,qBAAa,cAAe,YAAW,YAAY;IAQ/C,OAAO,CAAC,UAAU;IAElB,OAAO,CAAC,0BAA0B;IAClC,OAAO,CAAC,6BAA6B;IAVvC,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,YAAY,CAAW;IAC/B,OAAO,CAAC,IAAI,CAAO;IACnB,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,eAAe,CAA0B;gBAGvC,UAAU,EAAE,sBAAsB,EAC1C,mBAAmB,EAAE,mBAAmB,EAChC,0BAA0B,EAAE,MAAM,EAClC,6BAA6B,EAAE,MAAM,EAC7C,gBAAgB,CAAC,EAAE,gBAAgB;IAa/B,eAAe,CACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,cAAc,EAAE,aAAa,EAAE,GAC9B,OAAO,CAAC,IAAI,CAAC;YAgJF,uBAAuB;CAQtC;AAED,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,mBAAmB;gBAFnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,mBAAmB,EAAE,MAAM;IAGrC;;;;;;OAMG;IACH,2BAA2B,IAAI,QAAQ;IAcvC;;;;;OAKG;IACH,kBAAkB,IAAI,QAAQ;IAS9B,oBAAoB,IAAI,OAAO;IAI/B,kBAAkB;IA0BlB,uBAAuB;CAQxB"}
package/lib/evm/evm.js CHANGED
@@ -80,14 +80,16 @@ exports.EvmPriceListener = EvmPriceListener;
80
80
  class EvmPricePusher {
81
81
  connection;
82
82
  overrideGasPriceMultiplier;
83
+ overrideGasPriceMultiplierCap;
83
84
  customGasStation;
84
85
  pythContract;
85
86
  web3;
86
87
  pusherAddress;
87
88
  lastPushAttempt;
88
- constructor(connection, pythContractFactory, overrideGasPriceMultiplier, customGasStation) {
89
+ constructor(connection, pythContractFactory, overrideGasPriceMultiplier, overrideGasPriceMultiplierCap, customGasStation) {
89
90
  this.connection = connection;
90
91
  this.overrideGasPriceMultiplier = overrideGasPriceMultiplier;
92
+ this.overrideGasPriceMultiplierCap = overrideGasPriceMultiplierCap;
91
93
  this.customGasStation = customGasStation;
92
94
  this.pythContract = pythContractFactory.createPythContractWithPayer();
93
95
  this.web3 = new web3_1.default(pythContractFactory.createWeb3PayerProvider());
@@ -135,7 +137,7 @@ class EvmPricePusher {
135
137
  }
136
138
  }
137
139
  if (gasPriceToOverride !== undefined && gasPriceToOverride > gasPrice) {
138
- gasPrice = gasPriceToOverride;
140
+ gasPrice = Math.min(gasPriceToOverride, gasPrice * this.overrideGasPriceMultiplierCap);
139
141
  }
140
142
  const txNonce = lastExecutedNonce + 1;
141
143
  console.log(`Using gas price: ${gasPrice} and nonce: ${txNonce}`);
@@ -9,12 +9,8 @@ declare const _default: {
9
9
  "price-service-endpoint": Options;
10
10
  "price-config-file": Options;
11
11
  endpoint: Options;
12
- "pyth-package-id": Options;
13
12
  "pyth-state-id": Options;
14
- "wormhole-package-id": Options;
15
13
  "wormhole-state-id": Options;
16
- "price-feed-to-price-info-object-table-id": Options;
17
- "max-vaas-per-ptb": Options;
18
14
  "num-gas-objects": Options;
19
15
  "gas-budget": Options;
20
16
  };
@@ -1 +1 @@
1
- {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/sui/command.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;;;;;oBAgFC,GAAG;;AA5EpC,wBA8JE"}
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/sui/command.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;;;;;;;;;;;;;;;;oBAoDC,GAAG;;AAhDpC,wBA0HE"}
@@ -48,42 +48,18 @@ exports.default = {
48
48
  type: "string",
49
49
  required: true,
50
50
  },
51
- "pyth-package-id": {
52
- description: "Pyth Package Id. Can be found here" +
53
- "https://docs.pyth.network/documentation/pythnet-price-feeds/sui",
54
- type: "string",
55
- required: true,
56
- },
57
51
  "pyth-state-id": {
58
52
  description: "Pyth State Id. Can be found here" +
59
53
  "https://docs.pyth.network/documentation/pythnet-price-feeds/sui",
60
54
  type: "string",
61
55
  required: true,
62
56
  },
63
- "wormhole-package-id": {
64
- description: "Wormhole Package Id. Can be found here" +
65
- "https://docs.pyth.network/documentation/pythnet-price-feeds/sui",
66
- type: "string",
67
- required: true,
68
- },
69
57
  "wormhole-state-id": {
70
58
  description: "Wormhole State Id. Can be found here" +
71
59
  "https://docs.pyth.network/documentation/pythnet-price-feeds/sui",
72
60
  type: "string",
73
61
  required: true,
74
62
  },
75
- "price-feed-to-price-info-object-table-id": {
76
- description: "This is the id of the table which stored the information related to price data. You can find it here: " +
77
- "https://docs.pyth.network/documentation/pythnet-price-feeds/sui",
78
- type: "string",
79
- required: true,
80
- },
81
- "max-vaas-per-ptb": {
82
- description: "Maximum number of VAAs that can be included in a single PTB.",
83
- type: "number",
84
- required: true,
85
- default: 1,
86
- },
87
63
  "num-gas-objects": {
88
64
  description: "Number of gas objects in the pool.",
89
65
  type: "number",
@@ -103,7 +79,7 @@ exports.default = {
103
79
  ...options.pushingFrequency,
104
80
  },
105
81
  handler: async function (argv) {
106
- const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pushingFrequency, pollingFrequency, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, maxVaasPerPtb, numGasObjects, gasBudget, } = argv;
82
+ const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pushingFrequency, pollingFrequency, pythStateId, wormholeStateId, numGasObjects, gasBudget, } = argv;
107
83
  const priceConfigs = (0, price_config_1.readPriceConfigFile)(priceConfigFile);
108
84
  const priceServiceConnection = new price_service_client_1.PriceServiceConnection(priceServiceEndpoint, {
109
85
  logger: {
@@ -124,8 +100,8 @@ exports.default = {
124
100
  .toSuiAddress()}`);
125
101
  const priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
126
102
  const pythListener = new pyth_price_listener_1.PythPriceListener(priceServiceConnection, priceItems);
127
- const suiListener = new sui_1.SuiPriceListener(pythPackageId, priceFeedToPriceInfoObjectTableId, endpoint, priceItems, { pollingFrequency });
128
- const suiPusher = await sui_1.SuiPricePusher.createWithAutomaticGasPool(priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, maxVaasPerPtb, endpoint, mnemonic, gasBudget, numGasObjects);
103
+ const suiListener = new sui_1.SuiPriceListener(pythStateId, wormholeStateId, endpoint, priceItems, { pollingFrequency });
104
+ const suiPusher = await sui_1.SuiPricePusher.createWithAutomaticGasPool(priceServiceConnection, pythStateId, wormholeStateId, endpoint, mnemonic, gasBudget, numGasObjects);
129
105
  const controller = new controller_1.Controller(priceConfigs, pythListener, suiListener, suiPusher, { pushingFrequency });
130
106
  controller.start();
131
107
  },
package/lib/sui/sui.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { ChainPriceListener, IPricePusher, PriceInfo, PriceItem } from "../interface";
2
2
  import { DurationInSeconds } from "../utils";
3
3
  import { PriceServiceConnection } from "@pythnetwork/price-service-client";
4
- import { RawSigner, SuiObjectRef } from "@mysten/sui.js";
4
+ import { JsonRpcProvider, RawSigner, SuiObjectRef, ObjectId } from "@mysten/sui.js";
5
+ import { SuiPythClient } from "@pythnetwork/pyth-sui-js";
5
6
  export declare class SuiPriceListener extends ChainPriceListener {
6
- private pythPackageId;
7
- private priceFeedToPriceInfoObjectTableId;
8
- private endpoint;
9
- constructor(pythPackageId: string, priceFeedToPriceInfoObjectTableId: string, endpoint: string, priceItems: PriceItem[], config: {
7
+ private pythClient;
8
+ private provider;
9
+ constructor(pythStateId: ObjectId, wormholeStateId: ObjectId, endpoint: string, priceItems: PriceItem[], config: {
10
10
  pollingFrequency: DurationInSeconds;
11
11
  });
12
12
  getOnChainPriceInfo(priceId: string): Promise<PriceInfo | undefined>;
@@ -32,18 +32,24 @@ export declare class SuiPricePusher implements IPricePusher {
32
32
  private pythStateId;
33
33
  private wormholePackageId;
34
34
  private wormholeStateId;
35
- private priceFeedToPriceInfoObjectTableId;
36
- private maxVaasPerPtb;
37
35
  private gasBudget;
38
36
  private gasPool;
39
- constructor(signer: RawSigner, priceServiceConnection: PriceServiceConnection, pythPackageId: string, pythStateId: string, wormholePackageId: string, wormholeStateId: string, priceFeedToPriceInfoObjectTableId: string, maxVaasPerPtb: number, endpoint: string, mnemonic: string, gasBudget: number, gasPool: SuiObjectRef[]);
37
+ private pythClient;
38
+ constructor(signer: RawSigner, priceServiceConnection: PriceServiceConnection, pythPackageId: string, pythStateId: string, wormholePackageId: string, wormholeStateId: string, endpoint: string, mnemonic: string, gasBudget: number, gasPool: SuiObjectRef[], pythClient: SuiPythClient);
39
+ /**
40
+ * getPackageId returns the latest package id that the object belongs to. Use this to
41
+ * fetch the latest package id for a given object id and handle package upgrades automatically.
42
+ * @param provider
43
+ * @param objectId
44
+ * @returns package id
45
+ */
46
+ static getPackageId(provider: JsonRpcProvider, objectId: ObjectId): Promise<ObjectId>;
40
47
  /**
41
48
  * Create a price pusher with a pool of `numGasObjects` gas coins that will be used to send transactions.
42
49
  * The gas coins of the wallet for the provided mnemonic will be merged and then evenly split into `numGasObjects`.
43
50
  */
44
- static createWithAutomaticGasPool(priceServiceConnection: PriceServiceConnection, pythPackageId: string, pythStateId: string, wormholePackageId: string, wormholeStateId: string, priceFeedToPriceInfoObjectTableId: string, maxVaasPerPtb: number, endpoint: string, mnemonic: string, gasBudget: number, numGasObjects: number): Promise<SuiPricePusher>;
51
+ static createWithAutomaticGasPool(priceServiceConnection: PriceServiceConnection, pythStateId: string, wormholeStateId: string, endpoint: string, mnemonic: string, gasBudget: number, numGasObjects: number): Promise<SuiPricePusher>;
45
52
  updatePriceFeed(priceIds: string[], pubTimesToPush: number[]): Promise<void>;
46
- private createPriceUpdateTransaction;
47
53
  /** Send every transaction in txs in parallel, returning when all transactions have completed. */
48
54
  private sendTransactionBlocks;
49
55
  /** Send a single transaction block using a gas coin from the pool. */
@@ -1 +1 @@
1
- {"version":3,"file":"sui.d.ts","sourceRoot":"","sources":["../../src/sui/sui.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAIL,SAAS,EAIT,YAAY,EAKb,MAAM,gBAAgB,CAAC;AAMxB,qBAAa,gBAAiB,SAAQ,kBAAkB;IAEpD,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,iCAAiC;IACzC,OAAO,CAAC,QAAQ;gBAFR,aAAa,EAAE,MAAM,EACrB,iCAAiC,EAAE,MAAM,EACzC,QAAQ,EAAE,MAAM,EACxB,UAAU,EAAE,SAAS,EAAE,EACvB,MAAM,EAAE;QACN,gBAAgB,EAAE,iBAAiB,CAAC;KACrC;IAKG,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;CAmD3E;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,cAAe,YAAW,YAAY;IAE/C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,sBAAsB;IAC9B,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,iBAAiB;IACzB,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,iCAAiC;IACzC,OAAO,CAAC,aAAa;IAGrB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,OAAO;gBAXE,MAAM,EAAE,SAAS,EAC1B,sBAAsB,EAAE,sBAAsB,EAC9C,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,EACvB,iCAAiC,EAAE,MAAM,EACzC,aAAa,EAAE,MAAM,EAC7B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EACR,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EAAE;IAGjC;;;OAGG;WACU,0BAA0B,CACrC,sBAAsB,EAAE,sBAAsB,EAC9C,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,EACvB,iCAAiC,EAAE,MAAM,EACzC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,cAAc,CAAC;IAiCpB,eAAe,CACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,cAAc,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC;YAsDF,4BAA4B;IA2E1C,iGAAiG;YACnF,qBAAqB;IAMnC,sEAAsE;YACxD,oBAAoB;mBAsDb,iBAAiB;mBAuCjB,yBAAyB;mBAoBzB,cAAc;mBAiCd,mBAAmB;mBAsCnB,oBAAoB;CA+D1C"}
1
+ {"version":3,"file":"sui.d.ts","sourceRoot":"","sources":["../../src/sui/sui.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EACL,eAAe,EAGf,SAAS,EAGT,YAAY,EAKZ,QAAQ,EACT,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAOzD,qBAAa,gBAAiB,SAAQ,kBAAkB;IACtD,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,QAAQ,CAAkB;gBAGhC,WAAW,EAAE,QAAQ,EACrB,eAAe,EAAE,QAAQ,EACzB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,SAAS,EAAE,EACvB,MAAM,EAAE;QACN,gBAAgB,EAAE,iBAAiB,CAAC;KACrC;IAWG,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;CA+C3E;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,cAAe,YAAW,YAAY;IAE/C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,sBAAsB;IAC9B,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,iBAAiB;IACzB,OAAO,CAAC,eAAe;IAGvB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,UAAU;gBAVD,MAAM,EAAE,SAAS,EAC1B,sBAAsB,EAAE,sBAAsB,EAC9C,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,EAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EACR,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,aAAa;IAGnC;;;;;;OAMG;WACU,YAAY,CACvB,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,QAAQ,CAAC;IAuBpB;;;OAGG;WACU,0BAA0B,CACrC,sBAAsB,EAAE,sBAAsB,EAC9C,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,cAAc,CAAC;IAiDpB,eAAe,CACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,cAAc,EAAE,MAAM,EAAE,GACvB,OAAO,CAAC,IAAI,CAAC;IA0ChB,iGAAiG;YACnF,qBAAqB;IAMnC,sEAAsE;YACxD,oBAAoB;mBAsDb,iBAAiB;mBAuCjB,yBAAyB;mBAoBzB,cAAc;mBAiCd,mBAAmB;mBAsCnB,oBAAoB;CA+D1C"}
package/lib/sui/sui.js CHANGED
@@ -3,26 +3,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SuiPricePusher = exports.SuiPriceListener = void 0;
4
4
  const interface_1 = require("../interface");
5
5
  const sui_js_1 = require("@mysten/sui.js");
6
+ const pyth_sui_js_1 = require("@pythnetwork/pyth-sui-js");
6
7
  const GAS_FEE_FOR_SPLIT = 2_000_000_000;
7
8
  // TODO: read this from on chain config
8
9
  const MAX_NUM_GAS_OBJECTS_IN_PTB = 256;
9
10
  const MAX_NUM_OBJECTS_IN_ARGUMENT = 510;
10
11
  class SuiPriceListener extends interface_1.ChainPriceListener {
11
- pythPackageId;
12
- priceFeedToPriceInfoObjectTableId;
13
- endpoint;
14
- constructor(pythPackageId, priceFeedToPriceInfoObjectTableId, endpoint, priceItems, config) {
12
+ pythClient;
13
+ provider;
14
+ constructor(pythStateId, wormholeStateId, endpoint, priceItems, config) {
15
15
  super("sui", config.pollingFrequency, priceItems);
16
- this.pythPackageId = pythPackageId;
17
- this.priceFeedToPriceInfoObjectTableId = priceFeedToPriceInfoObjectTableId;
18
- this.endpoint = endpoint;
16
+ this.provider = new sui_js_1.JsonRpcProvider(new sui_js_1.Connection({ fullnode: endpoint }));
17
+ this.pythClient = new pyth_sui_js_1.SuiPythClient(this.provider, pythStateId, wormholeStateId);
19
18
  }
20
19
  async getOnChainPriceInfo(priceId) {
21
20
  try {
22
- const provider = new sui_js_1.JsonRpcProvider(new sui_js_1.Connection({ fullnode: this.endpoint }));
23
- const priceInfoObjectId = await priceIdToPriceInfoObjectId(provider, this.pythPackageId, this.priceFeedToPriceInfoObjectTableId, priceId);
21
+ const priceInfoObjectId = await this.pythClient.getPriceFeedObjectId(priceId);
22
+ if (priceInfoObjectId === undefined) {
23
+ throw new Error("Price not found on chain for price id " + priceId);
24
+ }
24
25
  // Fetching the price info object for the above priceInfoObjectId
25
- const priceInfoObject = await provider.getObject({
26
+ const priceInfoObject = await this.provider.getObject({
26
27
  id: priceInfoObjectId,
27
28
  options: { showContent: true },
28
29
  });
@@ -72,33 +73,61 @@ class SuiPricePusher {
72
73
  pythStateId;
73
74
  wormholePackageId;
74
75
  wormholeStateId;
75
- priceFeedToPriceInfoObjectTableId;
76
- maxVaasPerPtb;
77
76
  gasBudget;
78
77
  gasPool;
79
- constructor(signer, priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, maxVaasPerPtb, endpoint, mnemonic, gasBudget, gasPool) {
78
+ pythClient;
79
+ constructor(signer, priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, endpoint, mnemonic, gasBudget, gasPool, pythClient) {
80
80
  this.signer = signer;
81
81
  this.priceServiceConnection = priceServiceConnection;
82
82
  this.pythPackageId = pythPackageId;
83
83
  this.pythStateId = pythStateId;
84
84
  this.wormholePackageId = wormholePackageId;
85
85
  this.wormholeStateId = wormholeStateId;
86
- this.priceFeedToPriceInfoObjectTableId = priceFeedToPriceInfoObjectTableId;
87
- this.maxVaasPerPtb = maxVaasPerPtb;
88
86
  this.gasBudget = gasBudget;
89
87
  this.gasPool = gasPool;
88
+ this.pythClient = pythClient;
89
+ }
90
+ /**
91
+ * getPackageId returns the latest package id that the object belongs to. Use this to
92
+ * fetch the latest package id for a given object id and handle package upgrades automatically.
93
+ * @param provider
94
+ * @param objectId
95
+ * @returns package id
96
+ */
97
+ static async getPackageId(provider, objectId) {
98
+ const state = await provider
99
+ .getObject({
100
+ id: objectId,
101
+ options: {
102
+ showContent: true,
103
+ },
104
+ })
105
+ .then((result) => {
106
+ if (result.data?.content?.dataType == "moveObject") {
107
+ return result.data.content.fields;
108
+ }
109
+ throw new Error("not move object");
110
+ });
111
+ if ("upgrade_cap" in state) {
112
+ return state.upgrade_cap.fields.package;
113
+ }
114
+ throw new Error("upgrade_cap not found");
90
115
  }
91
116
  /**
92
117
  * Create a price pusher with a pool of `numGasObjects` gas coins that will be used to send transactions.
93
118
  * The gas coins of the wallet for the provided mnemonic will be merged and then evenly split into `numGasObjects`.
94
119
  */
95
- static async createWithAutomaticGasPool(priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, maxVaasPerPtb, endpoint, mnemonic, gasBudget, numGasObjects) {
120
+ static async createWithAutomaticGasPool(priceServiceConnection, pythStateId, wormholeStateId, endpoint, mnemonic, gasBudget, numGasObjects) {
96
121
  if (numGasObjects > MAX_NUM_OBJECTS_IN_ARGUMENT) {
97
122
  throw new Error(`numGasObjects cannot be greater than ${MAX_NUM_OBJECTS_IN_ARGUMENT} until we implement split chunking`);
98
123
  }
99
- const signer = new sui_js_1.RawSigner(sui_js_1.Ed25519Keypair.deriveKeypair(mnemonic), new sui_js_1.JsonRpcProvider(new sui_js_1.Connection({ fullnode: endpoint })));
124
+ const provider = new sui_js_1.JsonRpcProvider(new sui_js_1.Connection({ fullnode: endpoint }));
125
+ const signer = new sui_js_1.RawSigner(sui_js_1.Ed25519Keypair.deriveKeypair(mnemonic), provider);
126
+ const pythPackageId = await SuiPricePusher.getPackageId(provider, pythStateId);
127
+ const wormholePackageId = await SuiPricePusher.getPackageId(provider, wormholeStateId);
100
128
  const gasPool = await SuiPricePusher.initializeGasPool(signer, numGasObjects);
101
- return new SuiPricePusher(signer, priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, maxVaasPerPtb, endpoint, mnemonic, gasBudget, gasPool);
129
+ const pythClient = new pyth_sui_js_1.SuiPythClient(provider, pythStateId, wormholeStateId);
130
+ return new SuiPricePusher(signer, priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, endpoint, mnemonic, gasBudget, gasPool, pythClient);
102
131
  }
103
132
  async updatePriceFeed(priceIds, pubTimesToPush) {
104
133
  if (priceIds.length === 0) {
@@ -110,99 +139,20 @@ class SuiPricePusher {
110
139
  console.warn("Skipping update: no available gas coin.");
111
140
  return;
112
141
  }
113
- const priceFeeds = await this.priceServiceConnection.getLatestPriceFeeds(priceIds);
114
- if (priceFeeds === undefined) {
115
- console.warn("Failed to fetch price updates. Skipping push.");
116
- return;
117
- }
118
- const vaaToPriceFeedIds = new Map();
119
- for (const priceFeed of priceFeeds) {
120
- // The ! will succeed as long as the priceServiceConnection is configured to return binary vaa data (which it is).
121
- const vaa = priceFeed.getVAA();
122
- if (!vaaToPriceFeedIds.has(vaa)) {
123
- vaaToPriceFeedIds.set(vaa, []);
124
- }
125
- vaaToPriceFeedIds.get(vaa).push(priceFeed.id);
126
- }
127
- const txs = [];
128
- let currentBatchVaas = [];
129
- let currentBatchPriceFeedIds = [];
130
- for (const [vaa, priceFeedIds] of vaaToPriceFeedIds.entries()) {
131
- currentBatchVaas.push(vaa);
132
- currentBatchPriceFeedIds.push(...priceFeedIds);
133
- if (currentBatchVaas.length >= this.maxVaasPerPtb) {
134
- const tx = await this.createPriceUpdateTransaction(currentBatchVaas, currentBatchPriceFeedIds);
135
- if (tx !== undefined) {
136
- txs.push(tx);
137
- }
138
- currentBatchVaas = [];
139
- currentBatchPriceFeedIds = [];
142
+ // 3 price feeds per transaction is the optimal number for gas cost.
143
+ const priceIdChunks = chunkArray(priceIds, 3);
144
+ const txBlocks = [];
145
+ await Promise.all(priceIdChunks.map(async (priceIdChunk) => {
146
+ const vaas = await this.priceServiceConnection.getLatestVaas(priceIdChunk);
147
+ if (vaas.length !== 1) {
148
+ throw new Error(`Expected a single VAA for all priceIds ${priceIdChunk} but received ${vaas.length} VAAs: ${vaas}`);
140
149
  }
141
- }
142
- await this.sendTransactionBlocks(txs);
143
- }
144
- async createPriceUpdateTransaction(vaas, priceIds) {
145
- const tx = new sui_js_1.TransactionBlock();
146
- // Parse our batch price attestation VAA bytes using Wormhole.
147
- // Check out the Wormhole cross-chain bridge and generic messaging protocol here:
148
- // https://github.com/wormhole-foundation/wormhole
149
- let verified_vaas = [];
150
- for (const vaa of vaas) {
151
- const [verified_vaa] = tx.moveCall({
152
- target: `${this.wormholePackageId}::vaa::parse_and_verify`,
153
- arguments: [
154
- tx.object(this.wormholeStateId),
155
- tx.pure([...Buffer.from(vaa, "base64")]),
156
- tx.object(sui_js_1.SUI_CLOCK_OBJECT_ID),
157
- ],
158
- });
159
- verified_vaas = verified_vaas.concat(verified_vaa);
160
- }
161
- // Create a hot potato vector of price feed updates that will
162
- // be used to update price feeds.
163
- let [price_updates_hot_potato] = tx.moveCall({
164
- target: `${this.pythPackageId}::pyth::create_price_infos_hot_potato`,
165
- arguments: [
166
- tx.object(this.pythStateId),
167
- tx.makeMoveVec({
168
- type: `${this.wormholePackageId}::vaa::VAA`,
169
- objects: verified_vaas,
170
- }),
171
- tx.object(sui_js_1.SUI_CLOCK_OBJECT_ID),
172
- ],
173
- });
174
- // Update each price info object (containing our price feeds of interest)
175
- // using the hot potato vector.
176
- for (const priceId of priceIds) {
177
- let priceInfoObjectId;
178
- try {
179
- priceInfoObjectId = await priceIdToPriceInfoObjectId(this.signer.provider, this.pythPackageId, this.priceFeedToPriceInfoObjectTableId, priceId);
180
- }
181
- catch (e) {
182
- console.log("Error fetching price info object id for ", priceId);
183
- console.error(e);
184
- return undefined;
185
- }
186
- const coin = tx.splitCoins(tx.gas, [tx.pure(1)]);
187
- [price_updates_hot_potato] = tx.moveCall({
188
- target: `${this.pythPackageId}::pyth::update_single_price_feed`,
189
- arguments: [
190
- tx.object(this.pythStateId),
191
- price_updates_hot_potato,
192
- tx.object(priceInfoObjectId),
193
- coin,
194
- tx.object(sui_js_1.SUI_CLOCK_OBJECT_ID),
195
- ],
196
- });
197
- }
198
- // Explicitly destroy the hot potato vector, since it can't be dropped
199
- // automatically.
200
- tx.moveCall({
201
- target: `${this.pythPackageId}::hot_potato_vector::destroy`,
202
- arguments: [price_updates_hot_potato],
203
- typeArguments: [`${this.pythPackageId}::price_info::PriceInfo`],
204
- });
205
- return tx;
150
+ const vaa = vaas[0];
151
+ const tx = new sui_js_1.TransactionBlock();
152
+ await this.pythClient.updatePriceFeeds(tx, [Buffer.from(vaa, "base64")], priceIdChunk);
153
+ txBlocks.push(tx);
154
+ }));
155
+ await this.sendTransactionBlocks(txBlocks);
206
156
  }
207
157
  /** Send every transaction in txs in parallel, returning when all transactions have completed. */
208
158
  async sendTransactionBlocks(txs) {
@@ -383,37 +333,6 @@ class SuiPricePusher {
383
333
  }
384
334
  }
385
335
  exports.SuiPricePusher = SuiPricePusher;
386
- // We are calculating stored price info object id for given price id
387
- // The mapping between which is static. Hence, we are caching it here.
388
- const CACHE = {};
389
- // For given priceid, this method will fetch the price info object id
390
- // where the price information for the corresponding price feed is stored
391
- async function priceIdToPriceInfoObjectId(provider, pythPackageId, priceFeedToPriceInfoObjectTableId, priceId) {
392
- // Check if this was fetched before.
393
- if (CACHE[priceId] !== undefined)
394
- return CACHE[priceId];
395
- const storedObjectID = await provider.getDynamicFieldObject({
396
- parentId: priceFeedToPriceInfoObjectTableId,
397
- name: {
398
- type: `${pythPackageId}::price_identifier::PriceIdentifier`,
399
- value: {
400
- bytes: "0x" + priceId,
401
- },
402
- },
403
- });
404
- if (storedObjectID.error !== undefined)
405
- throw storedObjectID.error;
406
- if (storedObjectID.data === undefined ||
407
- storedObjectID.data.content === undefined)
408
- throw new Error("Price not found on chain for price id " + priceId);
409
- if (storedObjectID.data.content.dataType !== "moveObject")
410
- throw new Error("fetched object datatype should be moveObject");
411
- // This ID points to the price info object for the given price id stored on chain
412
- const priceInfoObjectId = storedObjectID.data.content.fields.value;
413
- // cache the price info object id
414
- CACHE[priceId] = priceInfoObjectId;
415
- return priceInfoObjectId;
416
- }
417
336
  function chunkArray(array, size) {
418
337
  const chunked = [];
419
338
  let index = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pythnetwork/price-pusher",
3
- "version": "5.4.10",
3
+ "version": "5.6.3",
4
4
  "description": "Pyth Price Pusher",
5
5
  "homepage": "https://pyth.network",
6
6
  "main": "lib/index.js",
@@ -52,9 +52,10 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@injectivelabs/sdk-ts": "1.10.72",
55
- "@mysten/sui.js": "^0.34.0",
55
+ "@mysten/sui.js": "^0.37.1",
56
56
  "@pythnetwork/price-service-client": "*",
57
57
  "@pythnetwork/pyth-sdk-solidity": "*",
58
+ "@pythnetwork/pyth-sui-js": "*",
58
59
  "@truffle/hdwallet-provider": "^2.1.3",
59
60
  "aptos": "^1.8.5",
60
61
  "joi": "^17.6.0",
@@ -63,5 +64,5 @@
63
64
  "yaml": "^2.1.1",
64
65
  "yargs": "^17.5.1"
65
66
  },
66
- "gitHead": "f41cf822ff404de523c9d4e34bed816e140cfa83"
67
+ "gitHead": "2b51cffdc7e782995e7c267d2fc3b0a8a24e1c60"
67
68
  }