@pythnetwork/price-pusher 10.4.0 → 11.3.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 +25 -6
- package/dist/common.cjs +0 -3
- package/dist/common.d.ts +0 -4
- package/dist/controller.cjs +10 -5
- package/dist/evm/command.cjs +49 -3
- package/dist/evm/command.d.ts +5 -0
- package/dist/evm/evm.cjs +155 -51
- package/dist/evm/evm.d.ts +11 -5
- package/dist/evm/gas-price.cjs +114 -0
- package/dist/evm/gas-price.d.ts +33 -0
- package/dist/metrics.cjs +46 -24
- package/dist/metrics.d.ts +10 -5
- package/dist/price-config.cjs +2 -2
- package/dist/pyth-price-listener.cjs +3 -1
- package/dist/solana/solana.cjs +5 -5
- package/dist/sui/balance-tracker.cjs +2 -2
- package/dist/sui/balance-tracker.d.ts +4 -4
- package/dist/sui/command.cjs +44 -10
- package/dist/sui/command.d.ts +3 -0
- package/dist/sui/sui.cjs +149 -96
- package/dist/sui/sui.d.ts +29 -12
- package/package.json +14 -7
package/dist/sui/sui.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* biome-ignore-all lint/style/noNonNullAssertion: pre-existing; gas-pool code asserts on Sui RPC results */ /* biome-ignore-all lint/suspicious/noExplicitAny: pre-existing; untyped Sui RPC error/result shapes */ /* biome-ignore-all lint/complexity/noForEach: pre-existing */ /* biome-ignore-all lint/suspicious/useAwait: pre-existing */ "use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
@@ -14,25 +14,91 @@ _export(exports, {
|
|
|
14
14
|
},
|
|
15
15
|
get SuiPricePusher () {
|
|
16
16
|
return SuiPricePusher;
|
|
17
|
+
},
|
|
18
|
+
get createSuiProvider () {
|
|
19
|
+
return createSuiProvider;
|
|
17
20
|
}
|
|
18
21
|
});
|
|
19
|
-
const
|
|
22
|
+
const _grpcjs = require("@grpc/grpc-js");
|
|
23
|
+
const _grpc = require("@mysten/sui/grpc");
|
|
24
|
+
const _jsonRpc = require("@mysten/sui/jsonRpc");
|
|
20
25
|
const _transactions = require("@mysten/sui/transactions");
|
|
26
|
+
const _grpctransport = require("@protobuf-ts/grpc-transport");
|
|
21
27
|
const _pythsuijs = require("@pythnetwork/pyth-sui-js");
|
|
22
28
|
const _interface = require("../interface.cjs");
|
|
23
29
|
const GAS_FEE_FOR_SPLIT = 2_000_000_000;
|
|
24
30
|
// TODO: read this from on chain config
|
|
25
31
|
const MAX_NUM_GAS_OBJECTS_IN_PTB = 256;
|
|
26
32
|
const MAX_NUM_OBJECTS_IN_ARGUMENT = 510;
|
|
33
|
+
/**
|
|
34
|
+
* Turn a gRPC endpoint into a `@grpc/grpc-js` `host:port` plus channel
|
|
35
|
+
* credentials. Third-party Sui gRPC endpoints (QuikNode `:9000`, Ankr /
|
|
36
|
+
* BlockVision `:443`) serve **native gRPC over HTTP/2**, so the host must be
|
|
37
|
+
* a bare `host:port` with no scheme or path. A `http://` prefix selects an
|
|
38
|
+
* insecure channel (local devnet); anything else uses TLS.
|
|
39
|
+
*/ function parseGrpcEndpoint(url) {
|
|
40
|
+
let host = url.trim();
|
|
41
|
+
let insecure = false;
|
|
42
|
+
if (host.startsWith("https://")) {
|
|
43
|
+
host = host.slice("https://".length);
|
|
44
|
+
} else if (host.startsWith("http://")) {
|
|
45
|
+
host = host.slice("http://".length);
|
|
46
|
+
insecure = true;
|
|
47
|
+
}
|
|
48
|
+
// Drop any path/token segment — native gRPC authenticates via metadata, not URL.
|
|
49
|
+
host = host.replace(/\/.*$/, "");
|
|
50
|
+
return {
|
|
51
|
+
credentials: insecure ? _grpcjs.ChannelCredentials.createInsecure() : _grpcjs.ChannelCredentials.createSsl(),
|
|
52
|
+
host
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function createSuiProvider(endpointType, network, url, grpcMetadata) {
|
|
56
|
+
switch(endpointType){
|
|
57
|
+
case "grpc":
|
|
58
|
+
{
|
|
59
|
+
const { host, credentials } = parseGrpcEndpoint(url);
|
|
60
|
+
const transport = new _grpctransport.GrpcTransport({
|
|
61
|
+
channelCredentials: credentials,
|
|
62
|
+
host,
|
|
63
|
+
...grpcMetadata && {
|
|
64
|
+
meta: grpcMetadata
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return new _grpc.SuiGrpcClient({
|
|
68
|
+
network,
|
|
69
|
+
transport
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
case "json-rpc":
|
|
73
|
+
{
|
|
74
|
+
return new _jsonRpc.SuiJsonRpcClient({
|
|
75
|
+
network,
|
|
76
|
+
url
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
default:
|
|
80
|
+
{
|
|
81
|
+
throw new Error(`Unknown Sui endpoint type: ${endpointType}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Unwrap the executed transaction from the `.core` execution result union. */ function getExecutedTransaction(result) {
|
|
86
|
+
return result.$kind === "Transaction" ? result.Transaction : result.FailedTransaction;
|
|
87
|
+
}
|
|
88
|
+
/** Build an owned-object reference from a `.core` effects changed object. */ function changedObjectToRef(obj) {
|
|
89
|
+
return {
|
|
90
|
+
digest: obj.outputDigest,
|
|
91
|
+
objectId: obj.objectId,
|
|
92
|
+
version: obj.outputVersion
|
|
93
|
+
};
|
|
94
|
+
}
|
|
27
95
|
class SuiPriceListener extends _interface.ChainPriceListener {
|
|
28
96
|
pythClient;
|
|
29
97
|
provider;
|
|
30
98
|
logger;
|
|
31
|
-
constructor(pythStateId, wormholeStateId, endpoint, priceItems, logger, config){
|
|
99
|
+
constructor(pythStateId, wormholeStateId, endpoint, endpointType, network, priceItems, logger, config, grpcMetadata){
|
|
32
100
|
super(config.pollingFrequency, priceItems);
|
|
33
|
-
this.provider =
|
|
34
|
-
url: endpoint
|
|
35
|
-
});
|
|
101
|
+
this.provider = createSuiProvider(endpointType, network, endpoint, grpcMetadata);
|
|
36
102
|
this.pythClient = new _pythsuijs.SuiPythClient(this.provider, pythStateId, wormholeStateId);
|
|
37
103
|
this.logger = logger;
|
|
38
104
|
}
|
|
@@ -43,24 +109,23 @@ class SuiPriceListener extends _interface.ChainPriceListener {
|
|
|
43
109
|
throw new Error("Price not found on chain for price id " + priceId);
|
|
44
110
|
}
|
|
45
111
|
// Fetching the price info object for the above priceInfoObjectId
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
112
|
+
const { object } = await this.provider.core.getObject({
|
|
113
|
+
include: {
|
|
114
|
+
json: true
|
|
115
|
+
},
|
|
116
|
+
objectId: priceInfoObjectId
|
|
51
117
|
});
|
|
52
|
-
if (!
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const timestamp = priceInfo.timestamp;
|
|
118
|
+
if (!object.json) throw new Error("Price not found on chain for price id " + priceId);
|
|
119
|
+
// PriceInfoObject -> price_info -> price_feed -> price (the `Price` struct,
|
|
120
|
+
// holding the signed `price` magnitude, `conf`, and `timestamp`).
|
|
121
|
+
const priceFields = (0, _pythsuijs.getStructFields)((0, _pythsuijs.getStructFields)((0, _pythsuijs.getStructFields)(object.json.price_info).price_feed).price);
|
|
122
|
+
const magnitudeFields = (0, _pythsuijs.getStructFields)(priceFields.price);
|
|
123
|
+
const magnitude = magnitudeFields.magnitude;
|
|
124
|
+
const negative = magnitudeFields.negative;
|
|
60
125
|
return {
|
|
61
|
-
conf,
|
|
126
|
+
conf: priceFields.conf,
|
|
62
127
|
price: negative ? `-${magnitude}` : magnitude,
|
|
63
|
-
publishTime: Number(timestamp)
|
|
128
|
+
publishTime: Number(priceFields.timestamp)
|
|
64
129
|
};
|
|
65
130
|
} catch (error) {
|
|
66
131
|
this.logger.error(error, `Polling Sui on-chain price for ${priceId} failed.`);
|
|
@@ -86,38 +151,13 @@ class SuiPricePusher {
|
|
|
86
151
|
this.pythClient = pythClient;
|
|
87
152
|
}
|
|
88
153
|
/**
|
|
89
|
-
* getPackageId returns the latest package id that the object belongs to. Use this to
|
|
90
|
-
* fetch the latest package id for a given object id and handle package upgrades automatically.
|
|
91
|
-
* @returns package id
|
|
92
|
-
*/ static async getPackageId(provider, objectId) {
|
|
93
|
-
const state = await provider.getObject({
|
|
94
|
-
id: objectId,
|
|
95
|
-
options: {
|
|
96
|
-
showContent: true
|
|
97
|
-
}
|
|
98
|
-
}).then((result)=>{
|
|
99
|
-
if (result.data?.content?.dataType == "moveObject") {
|
|
100
|
-
return result.data.content.fields;
|
|
101
|
-
}
|
|
102
|
-
throw new Error("not move object");
|
|
103
|
-
});
|
|
104
|
-
if ("upgrade_cap" in state) {
|
|
105
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
106
|
-
// @ts-ignore
|
|
107
|
-
return state.upgrade_cap.fields.package;
|
|
108
|
-
}
|
|
109
|
-
throw new Error("upgrade_cap not found");
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
154
|
* Create a price pusher with a pool of `numGasObjects` gas coins that will be used to send transactions.
|
|
113
155
|
* The gas coins of the wallet for the provided keypair will be merged and then evenly split into `numGasObjects`.
|
|
114
|
-
*/ static async createWithAutomaticGasPool(hermesClient, logger, pythStateId, wormholeStateId, endpoint, keypair, gasBudget, numGasObjects, ignoreGasObjects) {
|
|
156
|
+
*/ static async createWithAutomaticGasPool(hermesClient, logger, pythStateId, wormholeStateId, endpoint, endpointType, network, keypair, gasBudget, numGasObjects, ignoreGasObjects, grpcMetadata) {
|
|
115
157
|
if (numGasObjects > MAX_NUM_OBJECTS_IN_ARGUMENT) {
|
|
116
158
|
throw new Error(`numGasObjects cannot be greater than ${MAX_NUM_OBJECTS_IN_ARGUMENT} until we implement split chunking`);
|
|
117
159
|
}
|
|
118
|
-
const provider =
|
|
119
|
-
url: endpoint
|
|
120
|
-
});
|
|
160
|
+
const provider = createSuiProvider(endpointType, network, endpoint, grpcMetadata);
|
|
121
161
|
const gasPool = await SuiPricePusher.initializeGasPool(keypair, provider, numGasObjects, ignoreGasObjects, logger);
|
|
122
162
|
const pythClient = new _pythsuijs.SuiPythClient(provider, pythStateId, wormholeStateId);
|
|
123
163
|
return new SuiPricePusher(keypair, provider, logger, hermesClient, gasBudget, gasPool, pythClient);
|
|
@@ -166,16 +206,27 @@ class SuiPricePusher {
|
|
|
166
206
|
gasObject
|
|
167
207
|
]);
|
|
168
208
|
tx.setGasBudget(this.gasBudget);
|
|
169
|
-
const result = await this.provider.signAndExecuteTransaction({
|
|
170
|
-
|
|
171
|
-
|
|
209
|
+
const result = await this.provider.core.signAndExecuteTransaction({
|
|
210
|
+
include: {
|
|
211
|
+
effects: true
|
|
172
212
|
},
|
|
173
213
|
signer: this.signer,
|
|
174
214
|
transaction: tx
|
|
175
215
|
});
|
|
176
|
-
|
|
216
|
+
const executed = getExecutedTransaction(result);
|
|
217
|
+
// The `.core` API returns a `FailedTransaction` for on-chain execution
|
|
218
|
+
// failures rather than throwing, so check the status explicitly — otherwise
|
|
219
|
+
// a failed push would emit the success log below and silently miss updates.
|
|
220
|
+
if (executed.effects.status.error) {
|
|
221
|
+
throw new Error(`Transaction ${executed.digest} failed on-chain: ${JSON.stringify(executed.effects.status.error)}`);
|
|
222
|
+
}
|
|
223
|
+
const gasObjectChange = executed.effects.gasObject;
|
|
224
|
+
nextGasObject = gasObjectChange && gasObjectChange.objectId === gasObject.objectId ? changedObjectToRef(gasObjectChange) : undefined;
|
|
225
|
+
// Keep at INFO: the bundled Grafana "Tx Hash" panel scrapes this message
|
|
226
|
+
// from Loki and extracts {{.hash}}; debug is not emitted under the default
|
|
227
|
+
// log level, which would hide successful Sui hashes from the dashboard.
|
|
177
228
|
this.logger.info({
|
|
178
|
-
hash:
|
|
229
|
+
hash: executed.digest
|
|
179
230
|
}, "Successfully updated price with transaction digest");
|
|
180
231
|
} catch (error) {
|
|
181
232
|
if (String(error).includes("Balance of gas object") || String(error).includes("GasBalanceTooLow")) {
|
|
@@ -204,18 +255,14 @@ class SuiPricePusher {
|
|
|
204
255
|
}, "Ignoring some gas objects for coin merging");
|
|
205
256
|
}
|
|
206
257
|
const consolidatedCoin = await SuiPricePusher.mergeGasCoinsIntoOne(signer, provider, signerAddress, ignoreGasObjects, logger);
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
258
|
+
const { object } = await provider.core.getObject({
|
|
259
|
+
include: {
|
|
260
|
+
json: true
|
|
261
|
+
},
|
|
262
|
+
objectId: consolidatedCoin.objectId
|
|
212
263
|
});
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
216
|
-
// @ts-ignore
|
|
217
|
-
balance = coinResult.data.content.fields.balance;
|
|
218
|
-
} else throw new Error("Bad coin object");
|
|
264
|
+
if (!object.json) throw new Error("Bad coin object");
|
|
265
|
+
const balance = object.json.balance;
|
|
219
266
|
const splitAmount = (BigInt(balance) - BigInt(GAS_FEE_FOR_SPLIT)) / BigInt(numGasObjects);
|
|
220
267
|
const gasPool = await SuiPricePusher.splitGasCoinEqually(signer, provider, signerAddress, Number(splitAmount), numGasObjects, consolidatedCoin);
|
|
221
268
|
logger.info({
|
|
@@ -226,40 +273,36 @@ class SuiPricePusher {
|
|
|
226
273
|
// Attempt to refresh the version of the provided object reference to point to the current version
|
|
227
274
|
// of the object. Throws an error if the object cannot be refreshed.
|
|
228
275
|
static async tryRefreshObjectReference(provider, ref) {
|
|
229
|
-
const
|
|
230
|
-
|
|
276
|
+
const { object } = await provider.core.getObject({
|
|
277
|
+
objectId: ref.objectId
|
|
231
278
|
});
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
objectId: objectResponse.data.objectId,
|
|
238
|
-
version: objectResponse.data.version
|
|
239
|
-
};
|
|
240
|
-
}
|
|
279
|
+
return {
|
|
280
|
+
digest: object.digest,
|
|
281
|
+
objectId: object.objectId,
|
|
282
|
+
version: object.version
|
|
283
|
+
};
|
|
241
284
|
}
|
|
242
285
|
static async getAllGasCoins(provider, owner) {
|
|
243
286
|
let hasNextPage = true;
|
|
244
|
-
let cursor;
|
|
287
|
+
let cursor = null;
|
|
245
288
|
const coins = new Set([]);
|
|
246
289
|
let numCoins = 0;
|
|
247
290
|
while(hasNextPage){
|
|
248
|
-
const paginatedCoins = await provider.
|
|
291
|
+
const paginatedCoins = await provider.core.listCoins({
|
|
249
292
|
cursor,
|
|
250
293
|
owner
|
|
251
294
|
});
|
|
252
|
-
numCoins += paginatedCoins.
|
|
253
|
-
for (const c of paginatedCoins.
|
|
295
|
+
numCoins += paginatedCoins.objects.length;
|
|
296
|
+
for (const c of paginatedCoins.objects)coins.add(JSON.stringify({
|
|
254
297
|
digest: c.digest,
|
|
255
|
-
objectId: c.
|
|
298
|
+
objectId: c.objectId,
|
|
256
299
|
version: c.version
|
|
257
300
|
}));
|
|
258
301
|
hasNextPage = paginatedCoins.hasNextPage;
|
|
259
|
-
cursor = paginatedCoins.
|
|
302
|
+
cursor = paginatedCoins.cursor;
|
|
260
303
|
}
|
|
261
304
|
if (numCoins !== coins.size) {
|
|
262
|
-
throw new Error("Unexpected
|
|
305
|
+
throw new Error("Unexpected listCoins result: duplicate coins found");
|
|
263
306
|
}
|
|
264
307
|
return [
|
|
265
308
|
...coins
|
|
@@ -277,18 +320,18 @@ class SuiPricePusher {
|
|
|
277
320
|
tx.setGasPayment([
|
|
278
321
|
gasCoin
|
|
279
322
|
]);
|
|
280
|
-
const result = await provider.signAndExecuteTransaction({
|
|
281
|
-
|
|
282
|
-
|
|
323
|
+
const result = await provider.core.signAndExecuteTransaction({
|
|
324
|
+
include: {
|
|
325
|
+
effects: true
|
|
283
326
|
},
|
|
284
327
|
signer,
|
|
285
328
|
transaction: tx
|
|
286
329
|
});
|
|
287
|
-
const
|
|
288
|
-
if (error) {
|
|
289
|
-
throw new Error(`Failed to initialize gas pool: ${error}. Try re-running the script`);
|
|
330
|
+
const effects = getExecutedTransaction(result).effects;
|
|
331
|
+
if (effects.status.error) {
|
|
332
|
+
throw new Error(`Failed to initialize gas pool: ${JSON.stringify(effects.status.error)}. Try re-running the script`);
|
|
290
333
|
}
|
|
291
|
-
const newCoins =
|
|
334
|
+
const newCoins = effects.changedObjects.filter((obj)=>obj.idOperation === "Created").map(changedObjectToRef);
|
|
292
335
|
if (newCoins.length !== numGasObjects) {
|
|
293
336
|
throw new Error(`Failed to initialize gas pool. Expected ${numGasObjects}, got: ${JSON.stringify(newCoins)}`);
|
|
294
337
|
}
|
|
@@ -317,9 +360,9 @@ class SuiPricePusher {
|
|
|
317
360
|
mergeTx.setGasPayment(coins);
|
|
318
361
|
let mergeResult;
|
|
319
362
|
try {
|
|
320
|
-
mergeResult = await provider.signAndExecuteTransaction({
|
|
321
|
-
|
|
322
|
-
|
|
363
|
+
mergeResult = await provider.core.signAndExecuteTransaction({
|
|
364
|
+
include: {
|
|
365
|
+
effects: true
|
|
323
366
|
},
|
|
324
367
|
signer,
|
|
325
368
|
transaction: mergeTx
|
|
@@ -340,11 +383,21 @@ class SuiPricePusher {
|
|
|
340
383
|
}
|
|
341
384
|
throw error_;
|
|
342
385
|
}
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
386
|
+
const executed = getExecutedTransaction(mergeResult);
|
|
387
|
+
const effects = executed.effects;
|
|
388
|
+
if (effects.status.error) {
|
|
389
|
+
throw new Error(`Failed to merge coins when initializing gas pool: ${JSON.stringify(effects.status.error)}. Try re-running the script`);
|
|
346
390
|
}
|
|
347
|
-
finalCoin =
|
|
391
|
+
finalCoin = changedObjectToRef(effects.gasObject);
|
|
392
|
+
// Block until this merge is observable before the next transaction spends
|
|
393
|
+
// its output (the next chunk's gas, or the subsequent split). gRPC
|
|
394
|
+
// endpoints are load-balanced across fullnodes at different checkpoints,
|
|
395
|
+
// so without this the follow-up tx can be simulated against a backend
|
|
396
|
+
// that has not yet applied this merge and fail with a version conflict.
|
|
397
|
+
// The legacy JSON-RPC path got this for free via WaitForLocalExecution.
|
|
398
|
+
await provider.core.waitForTransaction({
|
|
399
|
+
digest: executed.digest
|
|
400
|
+
});
|
|
348
401
|
}
|
|
349
402
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
350
403
|
return finalCoin;
|
package/dist/sui/sui.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { ClientWithCoreApi } from "@mysten/sui/client";
|
|
2
|
+
import type { SuiObjectRef } from "@mysten/sui/jsonRpc";
|
|
3
3
|
import type { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
|
|
4
4
|
import type { HermesClient } from "@pythnetwork/hermes-client";
|
|
5
5
|
import { SuiPythClient } from "@pythnetwork/pyth-sui-js";
|
|
@@ -8,13 +8,36 @@ import type { IPricePusher, PriceInfo, PriceItem } from "../interface.js";
|
|
|
8
8
|
import { ChainPriceListener } from "../interface.js";
|
|
9
9
|
import type { DurationInSeconds } from "../utils.js";
|
|
10
10
|
type ObjectId = string;
|
|
11
|
+
/**
|
|
12
|
+
* Sui transport selector. `json-rpc` is the legacy default; `grpc` migrates to
|
|
13
|
+
* the transport Sui Foundation is replacing JSON-RPC with (public JSON-RPC
|
|
14
|
+
* endpoints are turned off in July 2026, removed entirely by mid-Oct 2026).
|
|
15
|
+
*/
|
|
16
|
+
export type SuiEndpointType = "json-rpc" | "grpc";
|
|
17
|
+
/** Sui network label passed to the `@mysten/sui` v2 clients. */
|
|
18
|
+
export type SuiNetwork = "mainnet" | "testnet" | "devnet" | "localnet";
|
|
19
|
+
/** gRPC request metadata (e.g. `{ "x-token": "<secret>" }`) sent on every call. */
|
|
20
|
+
export type SuiGrpcMetadata = Record<string, string>;
|
|
21
|
+
/**
|
|
22
|
+
* Both the `@mysten/sui` v2 JSON-RPC client (`SuiJsonRpcClient`) and the
|
|
23
|
+
* experimental gRPC client (`SuiGrpcClient`) expose the unified `.core` API.
|
|
24
|
+
* The pusher reads and writes exclusively through `.core` so the same driver
|
|
25
|
+
* works over either transport.
|
|
26
|
+
*
|
|
27
|
+
* `SuiGrpcClient` defaults to a grpc-web (HTTP/1.1) transport, which the Sui
|
|
28
|
+
* providers' native-gRPC (HTTP/2) endpoints reject; it also drops the `meta`
|
|
29
|
+
* option on that default path, so credentials never reach the wire. We
|
|
30
|
+
* therefore build an explicit `@protobuf-ts/grpc-transport` (native gRPC over
|
|
31
|
+
* `@grpc/grpc-js`) and pass the auth header through its `meta`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createSuiProvider(endpointType: SuiEndpointType, network: SuiNetwork, url: string, grpcMetadata?: SuiGrpcMetadata): ClientWithCoreApi;
|
|
11
34
|
export declare class SuiPriceListener extends ChainPriceListener {
|
|
12
35
|
private pythClient;
|
|
13
36
|
private provider;
|
|
14
37
|
private logger;
|
|
15
|
-
constructor(pythStateId: ObjectId, wormholeStateId: ObjectId, endpoint: string, priceItems: PriceItem[], logger: Logger, config: {
|
|
38
|
+
constructor(pythStateId: ObjectId, wormholeStateId: ObjectId, endpoint: string, endpointType: SuiEndpointType, network: SuiNetwork, priceItems: PriceItem[], logger: Logger, config: {
|
|
16
39
|
pollingFrequency: DurationInSeconds;
|
|
17
|
-
});
|
|
40
|
+
}, grpcMetadata?: SuiGrpcMetadata);
|
|
18
41
|
getOnChainPriceInfo(priceId: string): Promise<PriceInfo | undefined>;
|
|
19
42
|
}
|
|
20
43
|
/**
|
|
@@ -39,18 +62,12 @@ export declare class SuiPricePusher implements IPricePusher {
|
|
|
39
62
|
private gasBudget;
|
|
40
63
|
private gasPool;
|
|
41
64
|
private pythClient;
|
|
42
|
-
constructor(signer: Ed25519Keypair, provider:
|
|
43
|
-
/**
|
|
44
|
-
* getPackageId returns the latest package id that the object belongs to. Use this to
|
|
45
|
-
* fetch the latest package id for a given object id and handle package upgrades automatically.
|
|
46
|
-
* @returns package id
|
|
47
|
-
*/
|
|
48
|
-
static getPackageId(provider: SuiClient, objectId: ObjectId): Promise<ObjectId>;
|
|
65
|
+
constructor(signer: Ed25519Keypair, provider: ClientWithCoreApi, logger: Logger, hermesClient: HermesClient, gasBudget: number, gasPool: SuiObjectRef[], pythClient: SuiPythClient);
|
|
49
66
|
/**
|
|
50
67
|
* Create a price pusher with a pool of `numGasObjects` gas coins that will be used to send transactions.
|
|
51
68
|
* The gas coins of the wallet for the provided keypair will be merged and then evenly split into `numGasObjects`.
|
|
52
69
|
*/
|
|
53
|
-
static createWithAutomaticGasPool(hermesClient: HermesClient, logger: Logger, pythStateId: string, wormholeStateId: string, endpoint: string, keypair: Ed25519Keypair, gasBudget: number, numGasObjects: number, ignoreGasObjects: string[]): Promise<SuiPricePusher>;
|
|
70
|
+
static createWithAutomaticGasPool(hermesClient: HermesClient, logger: Logger, pythStateId: string, wormholeStateId: string, endpoint: string, endpointType: SuiEndpointType, network: SuiNetwork, keypair: Ed25519Keypair, gasBudget: number, numGasObjects: number, ignoreGasObjects: string[], grpcMetadata?: SuiGrpcMetadata): Promise<SuiPricePusher>;
|
|
54
71
|
updatePriceFeed(priceIds: string[], pubTimesToPush: number[]): Promise<void>;
|
|
55
72
|
/** Send every transaction in txs in parallel, returning when all transactions have completed. */
|
|
56
73
|
private sendTransactionBlocks;
|
package/package.json
CHANGED
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@aptos-labs/ts-sdk": "^1.39.0",
|
|
7
7
|
"@coral-xyz/anchor": "^0.30.0",
|
|
8
|
+
"@grpc/grpc-js": "^1.13.2",
|
|
8
9
|
"@injectivelabs/networks": "1.14.47",
|
|
9
10
|
"@injectivelabs/sdk-ts": "1.14.50",
|
|
10
11
|
"@injectivelabs/utils": "^1.14.48",
|
|
11
|
-
"@mysten/sui": "^
|
|
12
|
+
"@mysten/sui": "^2.7.0",
|
|
13
|
+
"@protobuf-ts/grpc-transport": "^2.11.1",
|
|
12
14
|
"@solana/web3.js": "^1.93.0",
|
|
13
15
|
"@ton/core": "^0.59.0",
|
|
14
16
|
"@ton/crypto": "^3.3.0",
|
|
@@ -25,14 +27,14 @@
|
|
|
25
27
|
"viem": "^2.19.4",
|
|
26
28
|
"yaml": "^2.1.1",
|
|
27
29
|
"yargs": "^17.5.1",
|
|
28
|
-
"@pythnetwork/hermes-client": "3.1.0",
|
|
29
30
|
"@pythnetwork/price-service-sdk": "^1.9.0",
|
|
30
31
|
"@pythnetwork/pyth-fuel-js": "1.1.0",
|
|
32
|
+
"@pythnetwork/hermes-client": "3.1.0",
|
|
31
33
|
"@pythnetwork/pyth-solana-receiver": "0.16.0",
|
|
32
|
-
"@pythnetwork/pyth-
|
|
33
|
-
"@pythnetwork/pyth-
|
|
34
|
+
"@pythnetwork/pyth-sui-js": "4.0.0",
|
|
35
|
+
"@pythnetwork/pyth-ton-js": "0.4.0",
|
|
34
36
|
"@pythnetwork/solana-utils": "0.6.0",
|
|
35
|
-
"@pythnetwork/pyth-
|
|
37
|
+
"@pythnetwork/pyth-sdk-solidity": "4.3.1"
|
|
36
38
|
},
|
|
37
39
|
"description": "Pyth Price Pusher",
|
|
38
40
|
"devDependencies": {
|
|
@@ -89,6 +91,10 @@
|
|
|
89
91
|
"default": "./dist/evm/evm.cjs",
|
|
90
92
|
"types": "./dist/evm/evm.d.ts"
|
|
91
93
|
},
|
|
94
|
+
"./evm/gas-price": {
|
|
95
|
+
"default": "./dist/evm/gas-price.cjs",
|
|
96
|
+
"types": "./dist/evm/gas-price.d.ts"
|
|
97
|
+
},
|
|
92
98
|
"./evm/pyth-abi": {
|
|
93
99
|
"default": "./dist/evm/pyth-abi.cjs",
|
|
94
100
|
"types": "./dist/evm/pyth-abi.d.ts"
|
|
@@ -211,12 +217,13 @@
|
|
|
211
217
|
},
|
|
212
218
|
"type": "module",
|
|
213
219
|
"types": "./dist/index.d.ts",
|
|
214
|
-
"version": "
|
|
220
|
+
"version": "11.3.0",
|
|
215
221
|
"scripts": {
|
|
216
222
|
"build": "ts-duality --noEsm",
|
|
217
223
|
"clean": "rm -rf ./dist",
|
|
218
224
|
"dev": "ts-node src/index.ts",
|
|
219
225
|
"start": "node dist/index.cjs",
|
|
220
|
-
"test:types": "tsc"
|
|
226
|
+
"test:types": "tsc",
|
|
227
|
+
"test:unit": "test-unit"
|
|
221
228
|
}
|
|
222
229
|
}
|