@pythnetwork/price-pusher 6.7.0 → 7.0.0-alpha

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.
Files changed (57) hide show
  1. package/README.md +26 -14
  2. package/lib/aptos/aptos.d.ts +5 -2
  3. package/lib/aptos/aptos.d.ts.map +1 -1
  4. package/lib/aptos/aptos.js +19 -19
  5. package/lib/aptos/command.d.ts +3 -0
  6. package/lib/aptos/command.d.ts.map +1 -1
  7. package/lib/aptos/command.js +12 -14
  8. package/lib/controller.d.ts +3 -1
  9. package/lib/controller.d.ts.map +1 -1
  10. package/lib/controller.js +11 -4
  11. package/lib/evm/command.d.ts +3 -0
  12. package/lib/evm/command.d.ts.map +1 -1
  13. package/lib/evm/command.js +14 -16
  14. package/lib/evm/custom-gas-station.d.ts +4 -2
  15. package/lib/evm/custom-gas-station.d.ts.map +1 -1
  16. package/lib/evm/custom-gas-station.js +7 -6
  17. package/lib/evm/evm.d.ts +7 -3
  18. package/lib/evm/evm.d.ts.map +1 -1
  19. package/lib/evm/evm.js +25 -24
  20. package/lib/index.js +3 -0
  21. package/lib/injective/command.d.ts +3 -0
  22. package/lib/injective/command.d.ts.map +1 -1
  23. package/lib/injective/command.js +11 -13
  24. package/lib/injective/injective.d.ts +5 -2
  25. package/lib/injective/injective.d.ts.map +1 -1
  26. package/lib/injective/injective.js +22 -22
  27. package/lib/interface.d.ts +1 -2
  28. package/lib/interface.d.ts.map +1 -1
  29. package/lib/interface.js +1 -4
  30. package/lib/near/command.d.ts +3 -0
  31. package/lib/near/command.d.ts.map +1 -1
  32. package/lib/near/command.js +14 -13
  33. package/lib/near/near.d.ts +5 -2
  34. package/lib/near/near.d.ts.map +1 -1
  35. package/lib/near/near.js +19 -17
  36. package/lib/options.d.ts +9 -0
  37. package/lib/options.d.ts.map +1 -1
  38. package/lib/options.js +28 -1
  39. package/lib/price-config.d.ts +2 -1
  40. package/lib/price-config.d.ts.map +1 -1
  41. package/lib/price-config.js +4 -6
  42. package/lib/pyth-price-listener.d.ts +3 -1
  43. package/lib/pyth-price-listener.d.ts.map +1 -1
  44. package/lib/pyth-price-listener.js +4 -2
  45. package/lib/solana/command.d.ts +5 -1
  46. package/lib/solana/command.d.ts.map +1 -1
  47. package/lib/solana/command.js +16 -18
  48. package/lib/solana/solana.d.ts +9 -5
  49. package/lib/solana/solana.d.ts.map +1 -1
  50. package/lib/solana/solana.js +42 -39
  51. package/lib/sui/command.d.ts +4 -0
  52. package/lib/sui/command.d.ts.map +1 -1
  53. package/lib/sui/command.js +18 -14
  54. package/lib/sui/sui.d.ts +6 -3
  55. package/lib/sui/sui.d.ts.map +1 -1
  56. package/lib/sui/sui.js +36 -30
  57. package/package.json +21 -11
package/lib/sui/sui.js CHANGED
@@ -12,10 +12,12 @@ const MAX_NUM_OBJECTS_IN_ARGUMENT = 510;
12
12
  class SuiPriceListener extends interface_1.ChainPriceListener {
13
13
  pythClient;
14
14
  provider;
15
- constructor(pythStateId, wormholeStateId, endpoint, priceItems, config) {
16
- super("sui", config.pollingFrequency, priceItems);
15
+ logger;
16
+ constructor(pythStateId, wormholeStateId, endpoint, priceItems, logger, config) {
17
+ super(config.pollingFrequency, priceItems);
17
18
  this.provider = new client_1.SuiClient({ url: endpoint });
18
19
  this.pythClient = new pyth_sui_js_1.SuiPythClient(this.provider, pythStateId, wormholeStateId);
20
+ this.logger = logger;
19
21
  }
20
22
  async getOnChainPriceInfo(priceId) {
21
23
  try {
@@ -46,9 +48,8 @@ class SuiPriceListener extends interface_1.ChainPriceListener {
46
48
  publishTime: Number(timestamp),
47
49
  };
48
50
  }
49
- catch (e) {
50
- console.error(`Polling Sui on-chain price for ${priceId} failed. Error:`);
51
- console.error(e);
51
+ catch (err) {
52
+ this.logger.error(err, `Polling Sui on-chain price for ${priceId} failed.`);
52
53
  return undefined;
53
54
  }
54
55
  }
@@ -71,6 +72,7 @@ exports.SuiPriceListener = SuiPriceListener;
71
72
  class SuiPricePusher {
72
73
  signer;
73
74
  provider;
75
+ logger;
74
76
  priceServiceConnection;
75
77
  pythPackageId;
76
78
  pythStateId;
@@ -79,9 +81,10 @@ class SuiPricePusher {
79
81
  gasBudget;
80
82
  gasPool;
81
83
  pythClient;
82
- constructor(signer, provider, priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, endpoint, keypair, gasBudget, gasPool, pythClient) {
84
+ constructor(signer, provider, logger, priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, endpoint, keypair, gasBudget, gasPool, pythClient) {
83
85
  this.signer = signer;
84
86
  this.provider = provider;
87
+ this.logger = logger;
85
88
  this.priceServiceConnection = priceServiceConnection;
86
89
  this.pythPackageId = pythPackageId;
87
90
  this.pythStateId = pythStateId;
@@ -123,16 +126,16 @@ class SuiPricePusher {
123
126
  * Create a price pusher with a pool of `numGasObjects` gas coins that will be used to send transactions.
124
127
  * The gas coins of the wallet for the provided keypair will be merged and then evenly split into `numGasObjects`.
125
128
  */
126
- static async createWithAutomaticGasPool(priceServiceConnection, pythStateId, wormholeStateId, endpoint, keypair, gasBudget, numGasObjects) {
129
+ static async createWithAutomaticGasPool(priceServiceConnection, logger, pythStateId, wormholeStateId, endpoint, keypair, gasBudget, numGasObjects, ignoreGasObjects) {
127
130
  if (numGasObjects > MAX_NUM_OBJECTS_IN_ARGUMENT) {
128
131
  throw new Error(`numGasObjects cannot be greater than ${MAX_NUM_OBJECTS_IN_ARGUMENT} until we implement split chunking`);
129
132
  }
130
133
  const provider = new client_1.SuiClient({ url: endpoint });
131
134
  const pythPackageId = await SuiPricePusher.getPackageId(provider, pythStateId);
132
135
  const wormholePackageId = await SuiPricePusher.getPackageId(provider, wormholeStateId);
133
- const gasPool = await SuiPricePusher.initializeGasPool(keypair, provider, numGasObjects);
136
+ const gasPool = await SuiPricePusher.initializeGasPool(keypair, provider, numGasObjects, ignoreGasObjects, logger);
134
137
  const pythClient = new pyth_sui_js_1.SuiPythClient(provider, pythStateId, wormholeStateId);
135
- return new SuiPricePusher(keypair, provider, priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, endpoint, keypair, gasBudget, gasPool, pythClient);
138
+ return new SuiPricePusher(keypair, provider, logger, priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, endpoint, keypair, gasBudget, gasPool, pythClient);
136
139
  }
137
140
  async updatePriceFeed(priceIds, pubTimesToPush) {
138
141
  if (priceIds.length === 0) {
@@ -141,7 +144,7 @@ class SuiPricePusher {
141
144
  if (priceIds.length !== pubTimesToPush.length)
142
145
  throw new Error("Invalid arguments");
143
146
  if (this.gasPool.length === 0) {
144
- console.warn("Skipping update: no available gas coin.");
147
+ this.logger.warn("Skipping update: no available gas coin.");
145
148
  return;
146
149
  }
147
150
  // 3 price feeds per transaction is the optimal number for gas cost.
@@ -167,7 +170,7 @@ class SuiPricePusher {
167
170
  async sendTransactionBlock(tx) {
168
171
  const gasObject = this.gasPool.shift();
169
172
  if (gasObject === undefined) {
170
- console.warn("No available gas coin. Skipping push.");
173
+ this.logger.warn("No available gas coin. Skipping push.");
171
174
  return;
172
175
  }
173
176
  let nextGasObject = undefined;
@@ -184,24 +187,20 @@ class SuiPricePusher {
184
187
  nextGasObject = result.effects?.mutated
185
188
  ?.map((obj) => obj.reference)
186
189
  .find((ref) => ref.objectId === gasObject.objectId);
187
- console.log("Successfully updated price with transaction digest ", result.digest);
190
+ this.logger.info({ hash: result.digest }, "Successfully updated price with transaction digest");
188
191
  }
189
- catch (e) {
190
- console.log("Error when signAndExecuteTransactionBlock");
191
- if (String(e).includes("Balance of gas object") ||
192
- String(e).includes("GasBalanceTooLow")) {
192
+ catch (err) {
193
+ if (String(err).includes("Balance of gas object") ||
194
+ String(err).includes("GasBalanceTooLow")) {
195
+ this.logger.error(err, "Insufficient gas balance");
193
196
  // If the error is caused by insufficient gas, we should panic
194
- throw e;
197
+ throw err;
195
198
  }
196
199
  else {
200
+ this.logger.error(err, "Failed to update price. Trying to refresh gas object references.");
197
201
  // Refresh the coin object here in case the error is caused by an object version mismatch.
198
202
  nextGasObject = await SuiPricePusher.tryRefreshObjectReference(this.provider, gasObject);
199
203
  }
200
- console.error(e);
201
- if ("data" in e) {
202
- console.error("Error has .data field:");
203
- console.error(JSON.stringify(e.data));
204
- }
205
204
  }
206
205
  if (nextGasObject !== undefined) {
207
206
  this.gasPool.push(nextGasObject);
@@ -209,9 +208,14 @@ class SuiPricePusher {
209
208
  }
210
209
  // This function will smash all coins owned by the signer into one, and then
211
210
  // split them equally into numGasObjects.
212
- static async initializeGasPool(signer, provider, numGasObjects) {
211
+ // ignoreGasObjects is a list of gas objects that will be ignored during the
212
+ // merging -- use this to store any locked objects on initialization.
213
+ static async initializeGasPool(signer, provider, numGasObjects, ignoreGasObjects, logger) {
213
214
  const signerAddress = await signer.toSuiAddress();
214
- const consolidatedCoin = await SuiPricePusher.mergeGasCoinsIntoOne(signer, provider, signerAddress);
215
+ if (ignoreGasObjects.length > 0) {
216
+ logger.info({ ignoreGasObjects }, "Ignoring some gas objects for coin merging");
217
+ }
218
+ const consolidatedCoin = await SuiPricePusher.mergeGasCoinsIntoOne(signer, provider, signerAddress, ignoreGasObjects, logger);
215
219
  const coinResult = await provider.getObject({
216
220
  id: consolidatedCoin.objectId,
217
221
  options: { showContent: true },
@@ -228,7 +232,7 @@ class SuiPricePusher {
228
232
  throw new Error("Bad coin object");
229
233
  const splitAmount = (BigInt(balance) - BigInt(GAS_FEE_FOR_SPLIT)) / BigInt(numGasObjects);
230
234
  const gasPool = await SuiPricePusher.splitGasCoinEqually(signer, provider, signerAddress, Number(splitAmount), numGasObjects, consolidatedCoin);
231
- console.log("Gas pool is filled with coins: ", gasPool);
235
+ logger.info({ gasPool }, "Gas pool is filled with coins");
232
236
  return gasPool;
233
237
  }
234
238
  // Attempt to refresh the version of the provided object reference to point to the current version
@@ -297,7 +301,7 @@ class SuiPricePusher {
297
301
  }
298
302
  return newCoins;
299
303
  }
300
- static async mergeGasCoinsIntoOne(signer, provider, owner) {
304
+ static async mergeGasCoinsIntoOne(signer, provider, owner, initialLockedAddresses, logger) {
301
305
  const gasCoins = await SuiPricePusher.getAllGasCoins(provider, owner);
302
306
  // skip merging if there is only one coin
303
307
  if (gasCoins.length === 1) {
@@ -306,6 +310,7 @@ class SuiPricePusher {
306
310
  const gasCoinsChunks = chunkArray(gasCoins, MAX_NUM_GAS_OBJECTS_IN_PTB - 2);
307
311
  let finalCoin;
308
312
  const lockedAddresses = new Set();
313
+ initialLockedAddresses.forEach((value) => lockedAddresses.add(value));
309
314
  for (let i = 0; i < gasCoinsChunks.length; i++) {
310
315
  const mergeTx = new transactions_1.TransactionBlock();
311
316
  let coins = gasCoinsChunks[i];
@@ -322,9 +327,10 @@ class SuiPricePusher {
322
327
  options: { showEffects: true },
323
328
  });
324
329
  }
325
- catch (e) {
326
- if (String(e).includes("quorum of validators because of locked objects. Retried a conflicting transaction")) {
327
- Object.values(e.data).forEach((lockedObjects) => {
330
+ catch (err) {
331
+ logger.error(err, "Merge transaction failed with error");
332
+ if (String(err).includes("quorum of validators because of locked objects. Retried a conflicting transaction")) {
333
+ Object.values(err.data).forEach((lockedObjects) => {
328
334
  lockedObjects.forEach((lockedObject) => {
329
335
  lockedAddresses.add(lockedObject[0]);
330
336
  });
@@ -333,7 +339,7 @@ class SuiPricePusher {
333
339
  i--;
334
340
  continue;
335
341
  }
336
- throw e;
342
+ throw err;
337
343
  }
338
344
  const error = mergeResult?.effects?.status.error;
339
345
  if (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pythnetwork/price-pusher",
3
- "version": "6.7.0",
3
+ "version": "7.0.0-alpha",
4
4
  "description": "Pyth Price Pusher",
5
5
  "homepage": "https://pyth.network",
6
6
  "main": "lib/index.js",
@@ -26,9 +26,9 @@
26
26
  "lint": "eslint src/",
27
27
  "start": "node lib/index.js",
28
28
  "dev": "ts-node src/index.ts",
29
- "prepublishOnly": "npm run build && npm test && npm run lint",
30
- "preversion": "npm run lint",
31
- "version": "npm run format && git add -A src"
29
+ "prepublishOnly": "pnpm run build && pnpm test && pnpm run lint",
30
+ "preversion": "pnpm run lint",
31
+ "version": "pnpm run format && git add -A src"
32
32
  },
33
33
  "keywords": [
34
34
  "pyth",
@@ -42,30 +42,40 @@
42
42
  "@types/ethereum-protocol": "^1.0.2",
43
43
  "@types/jest": "^27.4.1",
44
44
  "@types/yargs": "^17.0.10",
45
- "@typescript-eslint/eslint-plugin": "^5.20.0",
46
- "@typescript-eslint/parser": "^5.20.0",
45
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
46
+ "@typescript-eslint/parser": "^6.0.0",
47
47
  "eslint": "^8.13.0",
48
48
  "jest": "^29.7.0",
49
+ "pino-pretty": "^11.2.1",
49
50
  "prettier": "^2.6.2",
50
51
  "ts-jest": "^29.1.1",
52
+ "ts-node": "^10.9.1",
51
53
  "typescript": "^5.3.3"
52
54
  },
53
55
  "dependencies": {
56
+ "@coral-xyz/anchor": "^0.30.0",
57
+ "@injectivelabs/networks": "^1.14.6",
54
58
  "@injectivelabs/sdk-ts": "1.10.72",
55
59
  "@mysten/sui.js": "^0.49.1",
56
- "@pythnetwork/price-service-client": "*",
57
- "@pythnetwork/pyth-sdk-solidity": "*",
58
- "@pythnetwork/pyth-solana-receiver": "*",
59
- "@pythnetwork/pyth-sui-js": "*",
60
+ "@pythnetwork/price-service-client": "1.9.0",
61
+ "@pythnetwork/price-service-sdk": "^1.7.1",
62
+ "@pythnetwork/pyth-sdk-solidity": "3.1.0",
63
+ "@pythnetwork/pyth-solana-receiver": "0.8.0",
64
+ "@pythnetwork/pyth-sui-js": "2.0.0",
65
+ "@pythnetwork/solana-utils": "0.4.1",
66
+ "@solana/web3.js": "^1.93.0",
60
67
  "@truffle/hdwallet-provider": "^2.1.3",
68
+ "@types/pino": "^7.0.5",
61
69
  "aptos": "^1.8.5",
62
70
  "jito-ts": "^3.0.1",
63
71
  "joi": "^17.6.0",
64
72
  "near-api-js": "^3.0.2",
73
+ "pino": "^9.2.0",
65
74
  "web3": "^1.8.1",
75
+ "web3-core": "^1.8.1",
66
76
  "web3-eth-contract": "^1.8.1",
67
77
  "yaml": "^2.1.1",
68
78
  "yargs": "^17.5.1"
69
79
  },
70
- "gitHead": "586a4398bd2b1f178ee70a38ff101bd1aec8971f"
80
+ "gitHead": "40a63bf8f80d9f929eeddfb7c4e4f9d0e56d93fb"
71
81
  }