@pythnetwork/price-pusher 6.8.0 → 7.0.0
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.
- package/README.md +26 -14
- package/lib/aptos/aptos.d.ts +5 -2
- package/lib/aptos/aptos.d.ts.map +1 -1
- package/lib/aptos/aptos.js +31 -31
- package/lib/aptos/command.d.ts +3 -0
- package/lib/aptos/command.d.ts.map +1 -1
- package/lib/aptos/command.js +12 -14
- package/lib/controller.d.ts +3 -1
- package/lib/controller.d.ts.map +1 -1
- package/lib/controller.js +11 -4
- package/lib/evm/command.d.ts +3 -0
- package/lib/evm/command.d.ts.map +1 -1
- package/lib/evm/command.js +14 -16
- package/lib/evm/custom-gas-station.d.ts +4 -2
- package/lib/evm/custom-gas-station.d.ts.map +1 -1
- package/lib/evm/custom-gas-station.js +7 -6
- package/lib/evm/evm.d.ts +7 -3
- package/lib/evm/evm.d.ts.map +1 -1
- package/lib/evm/evm.js +25 -24
- package/lib/injective/command.d.ts +3 -0
- package/lib/injective/command.d.ts.map +1 -1
- package/lib/injective/command.js +11 -13
- package/lib/injective/injective.d.ts +5 -2
- package/lib/injective/injective.d.ts.map +1 -1
- package/lib/injective/injective.js +24 -23
- package/lib/interface.d.ts +1 -2
- package/lib/interface.d.ts.map +1 -1
- package/lib/interface.js +1 -4
- package/lib/near/command.d.ts +3 -0
- package/lib/near/command.d.ts.map +1 -1
- package/lib/near/command.js +14 -13
- package/lib/near/near.d.ts +5 -2
- package/lib/near/near.d.ts.map +1 -1
- package/lib/near/near.js +19 -17
- package/lib/options.d.ts +9 -0
- package/lib/options.d.ts.map +1 -1
- package/lib/options.js +28 -1
- package/lib/price-config.d.ts +2 -1
- package/lib/price-config.d.ts.map +1 -1
- package/lib/price-config.js +4 -6
- package/lib/pyth-price-listener.d.ts +4 -1
- package/lib/pyth-price-listener.d.ts.map +1 -1
- package/lib/pyth-price-listener.js +14 -2
- package/lib/solana/command.d.ts +5 -1
- package/lib/solana/command.d.ts.map +1 -1
- package/lib/solana/command.js +16 -18
- package/lib/solana/solana.d.ts +11 -5
- package/lib/solana/solana.d.ts.map +1 -1
- package/lib/solana/solana.js +41 -18
- package/lib/sui/command.d.ts +3 -0
- package/lib/sui/command.d.ts.map +1 -1
- package/lib/sui/command.js +12 -14
- package/lib/sui/sui.d.ts +6 -7
- package/lib/sui/sui.d.ts.map +1 -1
- package/lib/sui/sui.js +42 -63
- 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
|
-
|
|
16
|
-
|
|
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 (
|
|
50
|
-
|
|
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,22 +72,16 @@ exports.SuiPriceListener = SuiPriceListener;
|
|
|
71
72
|
class SuiPricePusher {
|
|
72
73
|
signer;
|
|
73
74
|
provider;
|
|
75
|
+
logger;
|
|
74
76
|
priceServiceConnection;
|
|
75
|
-
pythPackageId;
|
|
76
|
-
pythStateId;
|
|
77
|
-
wormholePackageId;
|
|
78
|
-
wormholeStateId;
|
|
79
77
|
gasBudget;
|
|
80
78
|
gasPool;
|
|
81
79
|
pythClient;
|
|
82
|
-
constructor(signer, provider,
|
|
80
|
+
constructor(signer, provider, logger, priceServiceConnection, gasBudget, gasPool, pythClient) {
|
|
83
81
|
this.signer = signer;
|
|
84
82
|
this.provider = provider;
|
|
83
|
+
this.logger = logger;
|
|
85
84
|
this.priceServiceConnection = priceServiceConnection;
|
|
86
|
-
this.pythPackageId = pythPackageId;
|
|
87
|
-
this.pythStateId = pythStateId;
|
|
88
|
-
this.wormholePackageId = wormholePackageId;
|
|
89
|
-
this.wormholeStateId = wormholeStateId;
|
|
90
85
|
this.gasBudget = gasBudget;
|
|
91
86
|
this.gasPool = gasPool;
|
|
92
87
|
this.pythClient = pythClient;
|
|
@@ -123,16 +118,14 @@ class SuiPricePusher {
|
|
|
123
118
|
* Create a price pusher with a pool of `numGasObjects` gas coins that will be used to send transactions.
|
|
124
119
|
* The gas coins of the wallet for the provided keypair will be merged and then evenly split into `numGasObjects`.
|
|
125
120
|
*/
|
|
126
|
-
static async createWithAutomaticGasPool(priceServiceConnection, pythStateId, wormholeStateId, endpoint, keypair, gasBudget, numGasObjects, ignoreGasObjects) {
|
|
121
|
+
static async createWithAutomaticGasPool(priceServiceConnection, logger, pythStateId, wormholeStateId, endpoint, keypair, gasBudget, numGasObjects, ignoreGasObjects) {
|
|
127
122
|
if (numGasObjects > MAX_NUM_OBJECTS_IN_ARGUMENT) {
|
|
128
123
|
throw new Error(`numGasObjects cannot be greater than ${MAX_NUM_OBJECTS_IN_ARGUMENT} until we implement split chunking`);
|
|
129
124
|
}
|
|
130
125
|
const provider = new client_1.SuiClient({ url: endpoint });
|
|
131
|
-
const
|
|
132
|
-
const wormholePackageId = await SuiPricePusher.getPackageId(provider, wormholeStateId);
|
|
133
|
-
const gasPool = await SuiPricePusher.initializeGasPool(keypair, provider, numGasObjects, ignoreGasObjects);
|
|
126
|
+
const gasPool = await SuiPricePusher.initializeGasPool(keypair, provider, numGasObjects, ignoreGasObjects, logger);
|
|
134
127
|
const pythClient = new pyth_sui_js_1.SuiPythClient(provider, pythStateId, wormholeStateId);
|
|
135
|
-
return new SuiPricePusher(keypair, provider,
|
|
128
|
+
return new SuiPricePusher(keypair, provider, logger, priceServiceConnection, gasBudget, gasPool, pythClient);
|
|
136
129
|
}
|
|
137
130
|
async updatePriceFeed(priceIds, pubTimesToPush) {
|
|
138
131
|
if (priceIds.length === 0) {
|
|
@@ -141,7 +134,7 @@ class SuiPricePusher {
|
|
|
141
134
|
if (priceIds.length !== pubTimesToPush.length)
|
|
142
135
|
throw new Error("Invalid arguments");
|
|
143
136
|
if (this.gasPool.length === 0) {
|
|
144
|
-
|
|
137
|
+
this.logger.warn("Skipping update: no available gas coin.");
|
|
145
138
|
return;
|
|
146
139
|
}
|
|
147
140
|
// 3 price feeds per transaction is the optimal number for gas cost.
|
|
@@ -167,7 +160,7 @@ class SuiPricePusher {
|
|
|
167
160
|
async sendTransactionBlock(tx) {
|
|
168
161
|
const gasObject = this.gasPool.shift();
|
|
169
162
|
if (gasObject === undefined) {
|
|
170
|
-
|
|
163
|
+
this.logger.warn("No available gas coin. Skipping push.");
|
|
171
164
|
return;
|
|
172
165
|
}
|
|
173
166
|
let nextGasObject = undefined;
|
|
@@ -184,24 +177,20 @@ class SuiPricePusher {
|
|
|
184
177
|
nextGasObject = result.effects?.mutated
|
|
185
178
|
?.map((obj) => obj.reference)
|
|
186
179
|
.find((ref) => ref.objectId === gasObject.objectId);
|
|
187
|
-
|
|
180
|
+
this.logger.info({ hash: result.digest }, "Successfully updated price with transaction digest");
|
|
188
181
|
}
|
|
189
|
-
catch (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
182
|
+
catch (err) {
|
|
183
|
+
if (String(err).includes("Balance of gas object") ||
|
|
184
|
+
String(err).includes("GasBalanceTooLow")) {
|
|
185
|
+
this.logger.error(err, "Insufficient gas balance");
|
|
193
186
|
// If the error is caused by insufficient gas, we should panic
|
|
194
|
-
throw
|
|
187
|
+
throw err;
|
|
195
188
|
}
|
|
196
189
|
else {
|
|
190
|
+
this.logger.error(err, "Failed to update price. Trying to refresh gas object references.");
|
|
197
191
|
// Refresh the coin object here in case the error is caused by an object version mismatch.
|
|
198
192
|
nextGasObject = await SuiPricePusher.tryRefreshObjectReference(this.provider, gasObject);
|
|
199
193
|
}
|
|
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
194
|
}
|
|
206
195
|
if (nextGasObject !== undefined) {
|
|
207
196
|
this.gasPool.push(nextGasObject);
|
|
@@ -211,13 +200,12 @@ class SuiPricePusher {
|
|
|
211
200
|
// split them equally into numGasObjects.
|
|
212
201
|
// ignoreGasObjects is a list of gas objects that will be ignored during the
|
|
213
202
|
// merging -- use this to store any locked objects on initialization.
|
|
214
|
-
static async initializeGasPool(signer, provider, numGasObjects, ignoreGasObjects) {
|
|
215
|
-
const signerAddress =
|
|
203
|
+
static async initializeGasPool(signer, provider, numGasObjects, ignoreGasObjects, logger) {
|
|
204
|
+
const signerAddress = signer.toSuiAddress();
|
|
216
205
|
if (ignoreGasObjects.length > 0) {
|
|
217
|
-
|
|
218
|
-
console.log(ignoreGasObjects);
|
|
206
|
+
logger.info({ ignoreGasObjects }, "Ignoring some gas objects for coin merging");
|
|
219
207
|
}
|
|
220
|
-
const consolidatedCoin = await SuiPricePusher.mergeGasCoinsIntoOne(signer, provider, signerAddress, ignoreGasObjects);
|
|
208
|
+
const consolidatedCoin = await SuiPricePusher.mergeGasCoinsIntoOne(signer, provider, signerAddress, ignoreGasObjects, logger);
|
|
221
209
|
const coinResult = await provider.getObject({
|
|
222
210
|
id: consolidatedCoin.objectId,
|
|
223
211
|
options: { showContent: true },
|
|
@@ -234,28 +222,22 @@ class SuiPricePusher {
|
|
|
234
222
|
throw new Error("Bad coin object");
|
|
235
223
|
const splitAmount = (BigInt(balance) - BigInt(GAS_FEE_FOR_SPLIT)) / BigInt(numGasObjects);
|
|
236
224
|
const gasPool = await SuiPricePusher.splitGasCoinEqually(signer, provider, signerAddress, Number(splitAmount), numGasObjects, consolidatedCoin);
|
|
237
|
-
|
|
225
|
+
logger.info({ gasPool }, "Gas pool is filled with coins");
|
|
238
226
|
return gasPool;
|
|
239
227
|
}
|
|
240
228
|
// Attempt to refresh the version of the provided object reference to point to the current version
|
|
241
|
-
// of the object.
|
|
242
|
-
// be retrieved.
|
|
229
|
+
// of the object. Throws an error if the object cannot be refreshed.
|
|
243
230
|
static async tryRefreshObjectReference(provider, ref) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
return ref;
|
|
255
|
-
}
|
|
231
|
+
const objectResponse = await provider.getObject({ id: ref.objectId });
|
|
232
|
+
if (objectResponse.data !== undefined) {
|
|
233
|
+
return {
|
|
234
|
+
digest: objectResponse.data.digest,
|
|
235
|
+
objectId: objectResponse.data.objectId,
|
|
236
|
+
version: objectResponse.data.version,
|
|
237
|
+
};
|
|
256
238
|
}
|
|
257
|
-
|
|
258
|
-
|
|
239
|
+
else {
|
|
240
|
+
throw new Error("Failed to refresh object reference");
|
|
259
241
|
}
|
|
260
242
|
}
|
|
261
243
|
static async getAllGasCoins(provider, owner) {
|
|
@@ -303,7 +285,7 @@ class SuiPricePusher {
|
|
|
303
285
|
}
|
|
304
286
|
return newCoins;
|
|
305
287
|
}
|
|
306
|
-
static async mergeGasCoinsIntoOne(signer, provider, owner, initialLockedAddresses) {
|
|
288
|
+
static async mergeGasCoinsIntoOne(signer, provider, owner, initialLockedAddresses, logger) {
|
|
307
289
|
const gasCoins = await SuiPricePusher.getAllGasCoins(provider, owner);
|
|
308
290
|
// skip merging if there is only one coin
|
|
309
291
|
if (gasCoins.length === 1) {
|
|
@@ -329,13 +311,10 @@ class SuiPricePusher {
|
|
|
329
311
|
options: { showEffects: true },
|
|
330
312
|
});
|
|
331
313
|
}
|
|
332
|
-
catch (
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
console.log(JSON.stringify(e));
|
|
337
|
-
if (String(e).includes("quorum of validators because of locked objects. Retried a conflicting transaction")) {
|
|
338
|
-
Object.values(e.data).forEach((lockedObjects) => {
|
|
314
|
+
catch (err) {
|
|
315
|
+
logger.error(err, "Merge transaction failed with error");
|
|
316
|
+
if (String(err).includes("quorum of validators because of locked objects. Retried a conflicting transaction")) {
|
|
317
|
+
Object.values(err.data).forEach((lockedObjects) => {
|
|
339
318
|
lockedObjects.forEach((lockedObject) => {
|
|
340
319
|
lockedAddresses.add(lockedObject[0]);
|
|
341
320
|
});
|
|
@@ -344,7 +323,7 @@ class SuiPricePusher {
|
|
|
344
323
|
i--;
|
|
345
324
|
continue;
|
|
346
325
|
}
|
|
347
|
-
throw
|
|
326
|
+
throw err;
|
|
348
327
|
}
|
|
349
328
|
const error = mergeResult?.effects?.status.error;
|
|
350
329
|
if (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pythnetwork/price-pusher",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
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": "
|
|
30
|
-
"preversion": "
|
|
31
|
-
"version": "
|
|
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": "^
|
|
46
|
-
"@typescript-eslint/parser": "^
|
|
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/
|
|
58
|
-
"@pythnetwork/pyth-
|
|
59
|
-
"@pythnetwork/pyth-
|
|
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": "
|
|
80
|
+
"gitHead": "05dc9c8e06b9643bb365db494e67cac843d7b90a"
|
|
71
81
|
}
|