@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.
- package/lib/evm/command.d.ts +1 -0
- package/lib/evm/command.d.ts.map +1 -1
- package/lib/evm/command.js +13 -3
- package/lib/evm/evm.d.ts +2 -1
- package/lib/evm/evm.d.ts.map +1 -1
- package/lib/evm/evm.js +4 -2
- package/lib/sui/command.d.ts +0 -4
- package/lib/sui/command.d.ts.map +1 -1
- package/lib/sui/command.js +3 -27
- package/lib/sui/sui.d.ts +16 -10
- package/lib/sui/sui.d.ts.map +1 -1
- package/lib/sui/sui.js +60 -141
- package/package.json +4 -3
package/lib/evm/command.d.ts
CHANGED
package/lib/evm/command.d.ts.map
CHANGED
|
@@ -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
|
|
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"}
|
package/lib/evm/command.js
CHANGED
|
@@ -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
|
|
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
|
}
|
package/lib/evm/evm.d.ts.map
CHANGED
|
@@ -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;
|
|
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}`);
|
package/lib/sui/command.d.ts
CHANGED
|
@@ -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
|
};
|
package/lib/sui/command.d.ts.map
CHANGED
|
@@ -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
|
|
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"}
|
package/lib/sui/command.js
CHANGED
|
@@ -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,
|
|
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(
|
|
128
|
-
const suiPusher = await sui_1.SuiPricePusher.createWithAutomaticGasPool(priceServiceConnection,
|
|
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
|
|
7
|
-
private
|
|
8
|
-
|
|
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
|
-
|
|
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,
|
|
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. */
|
package/lib/sui/sui.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
12
|
-
|
|
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.
|
|
17
|
-
this.
|
|
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
|
|
23
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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.
|
|
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.
|
|
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": "
|
|
67
|
+
"gitHead": "2b51cffdc7e782995e7c267d2fc3b0a8a24e1c60"
|
|
67
68
|
}
|