@pythnetwork/price-pusher 4.1.2 → 5.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 +31 -0
- package/lib/aptos/aptos.d.ts +30 -0
- package/lib/aptos/aptos.js +139 -0
- package/lib/aptos/command.d.ts +17 -0
- package/lib/aptos/command.js +82 -0
- package/lib/common.d.ts +4 -0
- package/lib/common.js +2 -0
- package/lib/evm/evm.js +8 -0
- package/lib/index.js +4 -0
- package/lib/injective/command.d.ts +1 -0
- package/lib/injective/command.js +13 -2
- package/lib/injective/injective.js +5 -5
- package/lib/sui/command.d.ts +20 -0
- package/lib/sui/command.js +107 -0
- package/lib/sui/sui.d.ts +23 -0
- package/lib/sui/sui.js +189 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -71,6 +71,37 @@ npm run start -- injective --grpc-endpoint https://grpc-endpoint.com \
|
|
|
71
71
|
[--pushing-frequency 10] \
|
|
72
72
|
[--polling-frequency 5] \
|
|
73
73
|
|
|
74
|
+
# For Aptos
|
|
75
|
+
npm run start -- aptos --endpoint https://fullnode.testnet.aptoslabs.com/v1 \
|
|
76
|
+
--pyth-contract-address 0x7e783b349d3e89cf5931af376ebeadbfab855b3fa239b7ada8f5a92fbea6b387 --price-service-endpoint "https://xc-testnet.pyth.network" \
|
|
77
|
+
--price-config-file "./price-config.testnet.sample.yaml" \
|
|
78
|
+
--mnemonic-file "path/to/mnemonic.txt" \
|
|
79
|
+
[--pushing-frequency 10] \
|
|
80
|
+
[--polling-frequency 5] \
|
|
81
|
+
|
|
82
|
+
# For Sui
|
|
83
|
+
npm run start -- sui
|
|
84
|
+
--endpoint https://sui-testnet-rpc.allthatnode.com,
|
|
85
|
+
--pyth-package-id 0x975e063f398f720af4f33ec06a927f14ea76ca24f7f8dd544aa62ab9d5d15f44,
|
|
86
|
+
--pyth-state-id 0xd8afde3a48b4ff7212bd6829a150f43f59043221200d63504d981f62bff2e27a,
|
|
87
|
+
--wormhole-package-id 0xcc029e2810f17f9f43f52262f40026a71fbdca40ed3803ad2884994361910b7e,
|
|
88
|
+
--wormhole-state-id 0xebba4cc4d614f7a7cdbe883acc76d1cc767922bc96778e7b68be0d15fce27c02,
|
|
89
|
+
--price-feed-to-price-info-object-table-id 0xf8929174008c662266a1adde78e1e8e33016eb7ad37d379481e860b911e40ed5,
|
|
90
|
+
--price-service-endpoint https://xc-testnet.pyth.network,
|
|
91
|
+
--mnemonic-file ./mnemonic,
|
|
92
|
+
--price-config-file ./price-config.testnet.sample.yaml
|
|
93
|
+
[--pushing-frequency 10] \
|
|
94
|
+
[--polling-frequency 5] \
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
--endpoint https://fullnode.testnet.aptoslabs.com/v1 \
|
|
99
|
+
--pyth-contract-address 0x7e783b349d3e89cf5931af376ebeadbfab855b3fa239b7ada8f5a92fbea6b387 --price-service-endpoint "https://xc-testnet.pyth.network" \
|
|
100
|
+
--price-config-file "./price-config.testnet.sample.yaml" \
|
|
101
|
+
--mnemonic-file "path/to/mnemonic.txt" \
|
|
102
|
+
[--pushing-frequency 10] \
|
|
103
|
+
[--polling-frequency 5] \
|
|
104
|
+
|
|
74
105
|
|
|
75
106
|
# Or, run the price pusher docker image instead of building from the source
|
|
76
107
|
docker run public.ecr.aws/pyth-network/xc-price-pusher:v<version> -- <above-arguments>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ChainPriceListener, IPricePusher, PriceInfo, PriceItem } from "../interface";
|
|
2
|
+
import { DurationInSeconds } from "../utils";
|
|
3
|
+
import { PriceServiceConnection } from "@pythnetwork/price-service-client";
|
|
4
|
+
export declare class AptosPriceListener extends ChainPriceListener {
|
|
5
|
+
private pythModule;
|
|
6
|
+
private endpoint;
|
|
7
|
+
constructor(pythModule: string, endpoint: string, priceItems: PriceItem[], config: {
|
|
8
|
+
pollingFrequency: DurationInSeconds;
|
|
9
|
+
});
|
|
10
|
+
getOnChainPriceInfo(priceId: string): Promise<PriceInfo | undefined>;
|
|
11
|
+
}
|
|
12
|
+
export declare class AptosPricePusher implements IPricePusher {
|
|
13
|
+
private priceServiceConnection;
|
|
14
|
+
private pythContractAddress;
|
|
15
|
+
private endpoint;
|
|
16
|
+
private mnemonic;
|
|
17
|
+
private overrideGasPriceMultiplier;
|
|
18
|
+
private lastPushAttempt;
|
|
19
|
+
private readonly accountHDPath;
|
|
20
|
+
constructor(priceServiceConnection: PriceServiceConnection, pythContractAddress: string, endpoint: string, mnemonic: string, overrideGasPriceMultiplier: number);
|
|
21
|
+
/**
|
|
22
|
+
* Gets price update data which then can be submitted to the Pyth contract to update the prices.
|
|
23
|
+
* This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price ids)
|
|
24
|
+
*
|
|
25
|
+
* @param priceIds Array of hex-encoded price ids.
|
|
26
|
+
* @returns Array of price update data.
|
|
27
|
+
*/
|
|
28
|
+
getPriceFeedsUpdateData(priceIds: string[]): Promise<number[][]>;
|
|
29
|
+
updatePriceFeed(priceIds: string[], pubTimesToPush: number[]): Promise<void>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AptosPricePusher = exports.AptosPriceListener = void 0;
|
|
4
|
+
const interface_1 = require("../interface");
|
|
5
|
+
const aptos_1 = require("aptos");
|
|
6
|
+
class AptosPriceListener extends interface_1.ChainPriceListener {
|
|
7
|
+
pythModule;
|
|
8
|
+
endpoint;
|
|
9
|
+
constructor(pythModule, endpoint, priceItems, config) {
|
|
10
|
+
super("aptos", config.pollingFrequency, priceItems);
|
|
11
|
+
this.pythModule = pythModule;
|
|
12
|
+
this.endpoint = endpoint;
|
|
13
|
+
}
|
|
14
|
+
async getOnChainPriceInfo(priceId) {
|
|
15
|
+
try {
|
|
16
|
+
const client = new aptos_1.AptosClient(this.endpoint);
|
|
17
|
+
const res = await client.getAccountResource(this.pythModule, `${this.pythModule}::state::LatestPriceInfo`);
|
|
18
|
+
// This depends upon the pyth contract storage on Aptos and should not be undefined.
|
|
19
|
+
// If undefined, there has been some change and we would need to update accordingly.
|
|
20
|
+
const handle = res.data.info.handle;
|
|
21
|
+
const priceItemRes = await client.getTableItem(handle, {
|
|
22
|
+
key_type: `${this.pythModule}::price_identifier::PriceIdentifier`,
|
|
23
|
+
value_type: `${this.pythModule}::price_info::PriceInfo`,
|
|
24
|
+
key: {
|
|
25
|
+
bytes: priceId,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
const multiplier = priceItemRes.price_feed.price.price.negative === true ? -1 : 1;
|
|
29
|
+
const price = multiplier * Number(priceItemRes.price_feed.price.price.magnitude);
|
|
30
|
+
console.log(`Polled an Aptos on-chain price for feed ${this.priceIdToAlias.get(priceId)} (${priceId}).`);
|
|
31
|
+
return {
|
|
32
|
+
price: price.toString(),
|
|
33
|
+
conf: priceItemRes.price_feed.price.conf,
|
|
34
|
+
publishTime: Number(priceItemRes.price_feed.price.timestamp),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
console.error(`Polling Aptos on-chain price for ${priceId} failed. Error:`);
|
|
39
|
+
console.error(e);
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.AptosPriceListener = AptosPriceListener;
|
|
45
|
+
class AptosPricePusher {
|
|
46
|
+
priceServiceConnection;
|
|
47
|
+
pythContractAddress;
|
|
48
|
+
endpoint;
|
|
49
|
+
mnemonic;
|
|
50
|
+
overrideGasPriceMultiplier;
|
|
51
|
+
lastPushAttempt;
|
|
52
|
+
accountHDPath = "m/44'/637'/0'/0'/0'";
|
|
53
|
+
constructor(priceServiceConnection, pythContractAddress, endpoint, mnemonic, overrideGasPriceMultiplier) {
|
|
54
|
+
this.priceServiceConnection = priceServiceConnection;
|
|
55
|
+
this.pythContractAddress = pythContractAddress;
|
|
56
|
+
this.endpoint = endpoint;
|
|
57
|
+
this.mnemonic = mnemonic;
|
|
58
|
+
this.overrideGasPriceMultiplier = overrideGasPriceMultiplier;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Gets price update data which then can be submitted to the Pyth contract to update the prices.
|
|
62
|
+
* This will throw an axios error if there is a network problem or the price service returns a non-ok response (e.g: Invalid price ids)
|
|
63
|
+
*
|
|
64
|
+
* @param priceIds Array of hex-encoded price ids.
|
|
65
|
+
* @returns Array of price update data.
|
|
66
|
+
*/
|
|
67
|
+
async getPriceFeedsUpdateData(priceIds) {
|
|
68
|
+
// Fetch the latest price feed update VAAs from the price service
|
|
69
|
+
const latestVaas = await this.priceServiceConnection.getLatestVaas(priceIds);
|
|
70
|
+
return latestVaas.map((vaa) => Array.from(Buffer.from(vaa, "base64")));
|
|
71
|
+
}
|
|
72
|
+
async updatePriceFeed(priceIds, pubTimesToPush) {
|
|
73
|
+
if (priceIds.length === 0) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (priceIds.length !== pubTimesToPush.length)
|
|
77
|
+
throw new Error("Invalid arguments");
|
|
78
|
+
let priceFeedUpdateData;
|
|
79
|
+
try {
|
|
80
|
+
// get the latest VAAs for updatePriceFeed and then push them
|
|
81
|
+
priceFeedUpdateData = await this.getPriceFeedsUpdateData(priceIds);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
console.error("Error fetching the latest vaas to push");
|
|
85
|
+
console.error(e);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const account = aptos_1.AptosAccount.fromDerivePath(this.accountHDPath, this.mnemonic);
|
|
90
|
+
const client = new aptos_1.AptosClient(this.endpoint);
|
|
91
|
+
const rawTx = await client.generateTransaction(account.address(), {
|
|
92
|
+
function: `${this.pythContractAddress}::pyth::update_price_feeds_if_fresh_with_funder`,
|
|
93
|
+
type_arguments: [],
|
|
94
|
+
arguments: [
|
|
95
|
+
priceFeedUpdateData,
|
|
96
|
+
priceIds.map((priceId) => Buffer.from(priceId, "hex")),
|
|
97
|
+
pubTimesToPush,
|
|
98
|
+
],
|
|
99
|
+
});
|
|
100
|
+
const simulation = await client.simulateTransaction(account, rawTx, {
|
|
101
|
+
estimateGasUnitPrice: true,
|
|
102
|
+
estimateMaxGasAmount: true,
|
|
103
|
+
estimatePrioritizedGasUnitPrice: true,
|
|
104
|
+
});
|
|
105
|
+
// Transactions on Aptos can be prioritized by paying a higher gas unit price.
|
|
106
|
+
// We are storing the gas unit price paid for the last transaction.
|
|
107
|
+
// If that transaction is not added to the block, we are increasing the gas unit price
|
|
108
|
+
// by multiplying the old gas unit price with `this.overrideGasPriceMultiplier`.
|
|
109
|
+
// After which we are sending a transaction with the same sequence number as the last
|
|
110
|
+
// transaction. Since they have the same sequence number only one of them will be added to
|
|
111
|
+
// the block and we won't be paying fees twice.
|
|
112
|
+
let gasUnitPrice = Number(simulation[0].gas_unit_price);
|
|
113
|
+
if (this.lastPushAttempt !== undefined &&
|
|
114
|
+
Number(simulation[0].sequence_number) === this.lastPushAttempt.nonce) {
|
|
115
|
+
const newGasUnitPrice = Number(this.lastPushAttempt.gasPrice * this.overrideGasPriceMultiplier);
|
|
116
|
+
if (gasUnitPrice < newGasUnitPrice)
|
|
117
|
+
gasUnitPrice = newGasUnitPrice;
|
|
118
|
+
}
|
|
119
|
+
const gasUsed = Number(simulation[0].gas_used) * 1.5;
|
|
120
|
+
const maxGasAmount = Number(gasUnitPrice * gasUsed);
|
|
121
|
+
const rawTxWithFee = new aptos_1.TxnBuilderTypes.RawTransaction(rawTx.sender, rawTx.sequence_number, rawTx.payload, BigInt(maxGasAmount.toFixed()), BigInt(gasUnitPrice.toFixed()), rawTx.expiration_timestamp_secs, rawTx.chain_id);
|
|
122
|
+
const signedTx = await client.signTransaction(account, rawTxWithFee);
|
|
123
|
+
const pendingTx = await client.submitTransaction(signedTx);
|
|
124
|
+
console.log("Succesfully broadcasted txHash:", pendingTx.hash);
|
|
125
|
+
// Update lastAttempt
|
|
126
|
+
this.lastPushAttempt = {
|
|
127
|
+
nonce: Number(pendingTx.sequence_number),
|
|
128
|
+
gasPrice: gasUnitPrice,
|
|
129
|
+
};
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
console.error("Error executing messages");
|
|
134
|
+
console.log(e);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
exports.AptosPricePusher = AptosPricePusher;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Options } from "yargs";
|
|
2
|
+
declare const _default: {
|
|
3
|
+
command: string;
|
|
4
|
+
describe: string;
|
|
5
|
+
builder: {
|
|
6
|
+
"pushing-frequency": Options;
|
|
7
|
+
"polling-frequency": Options;
|
|
8
|
+
"pyth-contract-address": Options;
|
|
9
|
+
"mnemonic-file": Options;
|
|
10
|
+
"price-service-endpoint": Options;
|
|
11
|
+
"price-config-file": Options;
|
|
12
|
+
endpoint: Options;
|
|
13
|
+
"override-gas-price-multiplier": Options;
|
|
14
|
+
};
|
|
15
|
+
handler: (argv: any) => void;
|
|
16
|
+
};
|
|
17
|
+
export default _default;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
const price_service_client_1 = require("@pythnetwork/price-service-client");
|
|
30
|
+
const options = __importStar(require("../options"));
|
|
31
|
+
const price_config_1 = require("../price-config");
|
|
32
|
+
const fs_1 = __importDefault(require("fs"));
|
|
33
|
+
const pyth_price_listener_1 = require("../pyth-price-listener");
|
|
34
|
+
const controller_1 = require("../controller");
|
|
35
|
+
const aptos_1 = require("./aptos");
|
|
36
|
+
exports.default = {
|
|
37
|
+
command: "aptos",
|
|
38
|
+
describe: "run price pusher for aptos",
|
|
39
|
+
builder: {
|
|
40
|
+
endpoint: {
|
|
41
|
+
description: "RPC endpoint endpoint URL for aptos. The pusher will periodically" +
|
|
42
|
+
"poll for updates. The polling interval is configurable via the " +
|
|
43
|
+
"`polling-frequency` command-line argument.",
|
|
44
|
+
type: "string",
|
|
45
|
+
required: true,
|
|
46
|
+
},
|
|
47
|
+
"override-gas-price-multiplier": {
|
|
48
|
+
description: "Multiply the gas price by this number if the transaction is not landing to override it. Default 2",
|
|
49
|
+
type: "number",
|
|
50
|
+
required: false,
|
|
51
|
+
default: 2,
|
|
52
|
+
},
|
|
53
|
+
...options.priceConfigFile,
|
|
54
|
+
...options.priceServiceEndpoint,
|
|
55
|
+
...options.mnemonicFile,
|
|
56
|
+
...options.pythContractAddress,
|
|
57
|
+
...options.pollingFrequency,
|
|
58
|
+
...options.pushingFrequency,
|
|
59
|
+
},
|
|
60
|
+
handler: function (argv) {
|
|
61
|
+
// FIXME: type checks for this
|
|
62
|
+
const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pythContractAddress, pushingFrequency, pollingFrequency, overrideGasPriceMultiplier, } = argv;
|
|
63
|
+
const priceConfigs = (0, price_config_1.readPriceConfigFile)(priceConfigFile);
|
|
64
|
+
const priceServiceConnection = new price_service_client_1.PriceServiceConnection(priceServiceEndpoint, {
|
|
65
|
+
logger: {
|
|
66
|
+
// Log only warnings and errors from the price service client
|
|
67
|
+
info: () => undefined,
|
|
68
|
+
warn: console.warn,
|
|
69
|
+
error: console.error,
|
|
70
|
+
debug: () => undefined,
|
|
71
|
+
trace: () => undefined,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
const mnemonic = fs_1.default.readFileSync(mnemonicFile, "utf-8").trim();
|
|
75
|
+
const priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
|
|
76
|
+
const pythListener = new pyth_price_listener_1.PythPriceListener(priceServiceConnection, priceItems);
|
|
77
|
+
const aptosListener = new aptos_1.AptosPriceListener(pythContractAddress, endpoint, priceItems, { pollingFrequency });
|
|
78
|
+
const aptosPusher = new aptos_1.AptosPricePusher(priceServiceConnection, pythContractAddress, endpoint, mnemonic, overrideGasPriceMultiplier);
|
|
79
|
+
const controller = new controller_1.Controller(priceConfigs, pythListener, aptosListener, aptosPusher, { pushingFrequency });
|
|
80
|
+
controller.start();
|
|
81
|
+
},
|
|
82
|
+
};
|
package/lib/common.d.ts
ADDED
package/lib/common.js
ADDED
package/lib/evm/evm.js
CHANGED
|
@@ -160,6 +160,14 @@ class EvmPricePusher {
|
|
|
160
160
|
console.log("Multiple users are using the same accounts and nonce is incorrect. Skipping this push.");
|
|
161
161
|
return;
|
|
162
162
|
}
|
|
163
|
+
if (err.message.includes("max fee per gas less than block base fee")) {
|
|
164
|
+
// We just have to handle this error and return.
|
|
165
|
+
// LastPushAttempt was stored with the class
|
|
166
|
+
// Next time the update will be executing, it will check the last attempt
|
|
167
|
+
// and increase the gas price accordingly.
|
|
168
|
+
console.log("The transaction failed with error: max fee per gas less than block base fee ");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
163
171
|
if (err.message.includes("sender doesn't have enough funds to send tx.")) {
|
|
164
172
|
console.error("Payer is out of balance, please top it up.");
|
|
165
173
|
throw err;
|
package/lib/index.js
CHANGED
|
@@ -8,9 +8,13 @@ const yargs_1 = __importDefault(require("yargs"));
|
|
|
8
8
|
const helpers_1 = require("yargs/helpers");
|
|
9
9
|
const command_1 = __importDefault(require("./injective/command"));
|
|
10
10
|
const command_2 = __importDefault(require("./evm/command"));
|
|
11
|
+
const command_3 = __importDefault(require("./aptos/command"));
|
|
12
|
+
const command_4 = __importDefault(require("./sui/command"));
|
|
11
13
|
(0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
12
14
|
.config("config")
|
|
13
15
|
.global("config")
|
|
14
16
|
.command(command_2.default)
|
|
15
17
|
.command(command_1.default)
|
|
18
|
+
.command(command_3.default)
|
|
19
|
+
.command(command_4.default)
|
|
16
20
|
.help().argv;
|
package/lib/injective/command.js
CHANGED
|
@@ -33,6 +33,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
33
33
|
const injective_1 = require("./injective");
|
|
34
34
|
const pyth_price_listener_1 = require("../pyth-price-listener");
|
|
35
35
|
const controller_1 = require("../controller");
|
|
36
|
+
const networks_1 = require("@injectivelabs/networks");
|
|
36
37
|
exports.default = {
|
|
37
38
|
command: "injective",
|
|
38
39
|
describe: "run price pusher for injective",
|
|
@@ -44,6 +45,11 @@ exports.default = {
|
|
|
44
45
|
type: "string",
|
|
45
46
|
required: true,
|
|
46
47
|
},
|
|
48
|
+
network: {
|
|
49
|
+
description: "testnet or mainnet",
|
|
50
|
+
type: "string",
|
|
51
|
+
required: true,
|
|
52
|
+
},
|
|
47
53
|
...options.priceConfigFile,
|
|
48
54
|
...options.priceServiceEndpoint,
|
|
49
55
|
...options.mnemonicFile,
|
|
@@ -53,7 +59,10 @@ exports.default = {
|
|
|
53
59
|
},
|
|
54
60
|
handler: function (argv) {
|
|
55
61
|
// FIXME: type checks for this
|
|
56
|
-
const { grpcEndpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pythContractAddress, pushingFrequency, pollingFrequency, } = argv;
|
|
62
|
+
const { grpcEndpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pythContractAddress, pushingFrequency, pollingFrequency, network, } = argv;
|
|
63
|
+
if (network !== "testnet" && network !== "mainnet") {
|
|
64
|
+
throw new Error("Please specify network. One of [testnet, mainnet]");
|
|
65
|
+
}
|
|
57
66
|
const priceConfigs = (0, price_config_1.readPriceConfigFile)(priceConfigFile);
|
|
58
67
|
const priceServiceConnection = new price_service_client_1.PriceServiceConnection(priceServiceEndpoint, {
|
|
59
68
|
logger: {
|
|
@@ -71,7 +80,9 @@ exports.default = {
|
|
|
71
80
|
const injectiveListener = new injective_1.InjectivePriceListener(pythContractAddress, grpcEndpoint, priceItems, {
|
|
72
81
|
pollingFrequency,
|
|
73
82
|
});
|
|
74
|
-
const injectivePusher = new injective_1.InjectivePricePusher(priceServiceConnection, pythContractAddress, grpcEndpoint, mnemonic
|
|
83
|
+
const injectivePusher = new injective_1.InjectivePricePusher(priceServiceConnection, pythContractAddress, grpcEndpoint, mnemonic, {
|
|
84
|
+
chainId: (0, networks_1.getNetworkInfo)(network).chainId,
|
|
85
|
+
});
|
|
75
86
|
const controller = new controller_1.Controller(priceConfigs, pythListener, injectiveListener, injectivePusher, { pushingFrequency });
|
|
76
87
|
controller.start();
|
|
77
88
|
},
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.InjectivePricePusher = exports.InjectivePriceListener = void 0;
|
|
4
4
|
const interface_1 = require("../interface");
|
|
5
5
|
const sdk_ts_1 = require("@injectivelabs/sdk-ts");
|
|
6
|
-
const
|
|
6
|
+
const DEFAULT_GAS_PRICE = 500000000;
|
|
7
7
|
// this use price without leading 0x
|
|
8
8
|
class InjectivePriceListener extends interface_1.ChainPriceListener {
|
|
9
9
|
pythContractAddress;
|
|
@@ -18,7 +18,7 @@ class InjectivePriceListener extends interface_1.ChainPriceListener {
|
|
|
18
18
|
try {
|
|
19
19
|
const api = new sdk_ts_1.ChainGrpcWasmApi(this.grpcEndpoint);
|
|
20
20
|
const { data } = await api.fetchSmartContractState(this.pythContractAddress, Buffer.from(`{"price_feed":{"id":"${priceId}"}}`).toString("base64"));
|
|
21
|
-
const json = Buffer.from(data
|
|
21
|
+
const json = Buffer.from(data).toString();
|
|
22
22
|
priceQueryResponse = JSON.parse(json);
|
|
23
23
|
}
|
|
24
24
|
catch (e) {
|
|
@@ -49,7 +49,7 @@ class InjectivePricePusher {
|
|
|
49
49
|
this.chainConfig = {
|
|
50
50
|
chainId: chainConfig?.chainId ?? "injective-888",
|
|
51
51
|
gasMultiplier: chainConfig?.gasMultiplier ?? 1.2,
|
|
52
|
-
gasPrice: chainConfig?.gasPrice ??
|
|
52
|
+
gasPrice: chainConfig?.gasPrice ?? DEFAULT_GAS_PRICE,
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
injectiveAddress() {
|
|
@@ -93,7 +93,7 @@ class InjectivePricePusher {
|
|
|
93
93
|
});
|
|
94
94
|
const sig = await this.wallet.sign(Buffer.from(signBytes));
|
|
95
95
|
/** Append Signatures */
|
|
96
|
-
txRaw.
|
|
96
|
+
txRaw.signatures = [sig];
|
|
97
97
|
const txResponse = await txService.broadcast(txRaw);
|
|
98
98
|
return txResponse;
|
|
99
99
|
}
|
|
@@ -129,7 +129,7 @@ class InjectivePricePusher {
|
|
|
129
129
|
vaas: priceFeedUpdateObject.update_price_feeds.data,
|
|
130
130
|
},
|
|
131
131
|
})).toString("base64"));
|
|
132
|
-
const json = Buffer.from(data
|
|
132
|
+
const json = Buffer.from(data).toString();
|
|
133
133
|
updateFeeQueryResponse = JSON.parse(json);
|
|
134
134
|
}
|
|
135
135
|
catch (e) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Options } from "yargs";
|
|
2
|
+
declare const _default: {
|
|
3
|
+
command: string;
|
|
4
|
+
describe: string;
|
|
5
|
+
builder: {
|
|
6
|
+
"pushing-frequency": Options;
|
|
7
|
+
"polling-frequency": Options;
|
|
8
|
+
"mnemonic-file": Options;
|
|
9
|
+
"price-service-endpoint": Options;
|
|
10
|
+
"price-config-file": Options;
|
|
11
|
+
endpoint: Options;
|
|
12
|
+
"pyth-package-id": Options;
|
|
13
|
+
"pyth-state-id": Options;
|
|
14
|
+
"wormhole-package-id": Options;
|
|
15
|
+
"wormhole-state-id": Options;
|
|
16
|
+
"price-feed-to-price-info-object-table-id": Options;
|
|
17
|
+
};
|
|
18
|
+
handler: (argv: any) => void;
|
|
19
|
+
};
|
|
20
|
+
export default _default;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
const price_service_client_1 = require("@pythnetwork/price-service-client");
|
|
30
|
+
const options = __importStar(require("../options"));
|
|
31
|
+
const price_config_1 = require("../price-config");
|
|
32
|
+
const fs_1 = __importDefault(require("fs"));
|
|
33
|
+
const pyth_price_listener_1 = require("../pyth-price-listener");
|
|
34
|
+
const controller_1 = require("../controller");
|
|
35
|
+
const sui_1 = require("./sui");
|
|
36
|
+
exports.default = {
|
|
37
|
+
command: "sui",
|
|
38
|
+
describe: "Run price pusher for sui. Most of the arguments below are" +
|
|
39
|
+
"network specific, so there's one set of values for mainnet and" +
|
|
40
|
+
"another for testnet. See config.sui..sample.json for the " +
|
|
41
|
+
"appropriate values for your network. ",
|
|
42
|
+
builder: {
|
|
43
|
+
endpoint: {
|
|
44
|
+
description: "RPC endpoint URL for sui. The pusher will periodically" +
|
|
45
|
+
"poll for updates. The polling interval is configurable via the " +
|
|
46
|
+
"`polling-frequency` command-line argument.",
|
|
47
|
+
type: "string",
|
|
48
|
+
required: true,
|
|
49
|
+
},
|
|
50
|
+
"pyth-package-id": {
|
|
51
|
+
description: "Pyth Package Id. Can be found here" +
|
|
52
|
+
"https://docs.pyth.network/pythnet-price-feeds/sui",
|
|
53
|
+
type: "string",
|
|
54
|
+
required: true,
|
|
55
|
+
},
|
|
56
|
+
"pyth-state-id": {
|
|
57
|
+
description: "Pyth State Id. Can be found here" +
|
|
58
|
+
"https://docs.pyth.network/pythnet-price-feeds/sui",
|
|
59
|
+
type: "string",
|
|
60
|
+
required: true,
|
|
61
|
+
},
|
|
62
|
+
"wormhole-package-id": {
|
|
63
|
+
description: "Wormhole Package Id. Can be found here" +
|
|
64
|
+
"https://docs.pyth.network/pythnet-price-feeds/sui",
|
|
65
|
+
type: "string",
|
|
66
|
+
required: true,
|
|
67
|
+
},
|
|
68
|
+
"wormhole-state-id": {
|
|
69
|
+
description: "Wormhole State Id. Can be found here" +
|
|
70
|
+
"https://docs.pyth.network/pythnet-price-feeds/sui",
|
|
71
|
+
type: "string",
|
|
72
|
+
required: true,
|
|
73
|
+
},
|
|
74
|
+
"price-feed-to-price-info-object-table-id": {
|
|
75
|
+
description: "This is the id of the table which stored the information related to price data. You can find it here: " +
|
|
76
|
+
"https://docs.pyth.network/pythnet-price-feeds/sui",
|
|
77
|
+
type: "string",
|
|
78
|
+
required: true,
|
|
79
|
+
},
|
|
80
|
+
...options.priceConfigFile,
|
|
81
|
+
...options.priceServiceEndpoint,
|
|
82
|
+
...options.mnemonicFile,
|
|
83
|
+
...options.pollingFrequency,
|
|
84
|
+
...options.pushingFrequency,
|
|
85
|
+
},
|
|
86
|
+
handler: function (argv) {
|
|
87
|
+
const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pushingFrequency, pollingFrequency, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, } = argv;
|
|
88
|
+
const priceConfigs = (0, price_config_1.readPriceConfigFile)(priceConfigFile);
|
|
89
|
+
const priceServiceConnection = new price_service_client_1.PriceServiceConnection(priceServiceEndpoint, {
|
|
90
|
+
logger: {
|
|
91
|
+
// Log only warnings and errors from the price service client
|
|
92
|
+
info: () => undefined,
|
|
93
|
+
warn: console.warn,
|
|
94
|
+
error: console.error,
|
|
95
|
+
debug: () => undefined,
|
|
96
|
+
trace: () => undefined,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
const mnemonic = fs_1.default.readFileSync(mnemonicFile, "utf-8").trim();
|
|
100
|
+
const priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
|
|
101
|
+
const pythListener = new pyth_price_listener_1.PythPriceListener(priceServiceConnection, priceItems);
|
|
102
|
+
const suiListener = new sui_1.SuiPriceListener(pythPackageId, priceFeedToPriceInfoObjectTableId, endpoint, priceItems, { pollingFrequency });
|
|
103
|
+
const suiPusher = new sui_1.SuiPricePusher(priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, endpoint, mnemonic);
|
|
104
|
+
const controller = new controller_1.Controller(priceConfigs, pythListener, suiListener, suiPusher, { pushingFrequency });
|
|
105
|
+
controller.start();
|
|
106
|
+
},
|
|
107
|
+
};
|
package/lib/sui/sui.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ChainPriceListener, IPricePusher, PriceInfo, PriceItem } from "../interface";
|
|
2
|
+
import { DurationInSeconds } from "../utils";
|
|
3
|
+
import { PriceServiceConnection } from "@pythnetwork/price-service-client";
|
|
4
|
+
export declare class SuiPriceListener extends ChainPriceListener {
|
|
5
|
+
private pythPackageId;
|
|
6
|
+
private priceFeedToPriceInfoObjectTableId;
|
|
7
|
+
private endpoint;
|
|
8
|
+
constructor(pythPackageId: string, priceFeedToPriceInfoObjectTableId: string, endpoint: string, priceItems: PriceItem[], config: {
|
|
9
|
+
pollingFrequency: DurationInSeconds;
|
|
10
|
+
});
|
|
11
|
+
getOnChainPriceInfo(priceId: string): Promise<PriceInfo | undefined>;
|
|
12
|
+
}
|
|
13
|
+
export declare class SuiPricePusher implements IPricePusher {
|
|
14
|
+
private priceServiceConnection;
|
|
15
|
+
private pythPackageId;
|
|
16
|
+
private pythStateId;
|
|
17
|
+
private wormholePackageId;
|
|
18
|
+
private wormholeStateId;
|
|
19
|
+
private priceFeedToPriceInfoObjectTableId;
|
|
20
|
+
private readonly signer;
|
|
21
|
+
constructor(priceServiceConnection: PriceServiceConnection, pythPackageId: string, pythStateId: string, wormholePackageId: string, wormholeStateId: string, priceFeedToPriceInfoObjectTableId: string, endpoint: string, mnemonic: string);
|
|
22
|
+
updatePriceFeed(priceIds: string[], pubTimesToPush: number[]): Promise<void>;
|
|
23
|
+
}
|
package/lib/sui/sui.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SuiPricePusher = exports.SuiPriceListener = void 0;
|
|
4
|
+
const interface_1 = require("../interface");
|
|
5
|
+
const sui_js_1 = require("@mysten/sui.js");
|
|
6
|
+
class SuiPriceListener extends interface_1.ChainPriceListener {
|
|
7
|
+
pythPackageId;
|
|
8
|
+
priceFeedToPriceInfoObjectTableId;
|
|
9
|
+
endpoint;
|
|
10
|
+
constructor(pythPackageId, priceFeedToPriceInfoObjectTableId, endpoint, priceItems, config) {
|
|
11
|
+
super("sui", config.pollingFrequency, priceItems);
|
|
12
|
+
this.pythPackageId = pythPackageId;
|
|
13
|
+
this.priceFeedToPriceInfoObjectTableId = priceFeedToPriceInfoObjectTableId;
|
|
14
|
+
this.endpoint = endpoint;
|
|
15
|
+
}
|
|
16
|
+
async getOnChainPriceInfo(priceId) {
|
|
17
|
+
try {
|
|
18
|
+
const provider = new sui_js_1.JsonRpcProvider(new sui_js_1.Connection({ fullnode: this.endpoint }));
|
|
19
|
+
const priceInfoObjectId = await priceIdToPriceInfoObjectId(provider, this.pythPackageId, this.priceFeedToPriceInfoObjectTableId, priceId);
|
|
20
|
+
// Fetching the price info object for the above priceInfoObjectId
|
|
21
|
+
const priceInfoObject = await provider.getObject({
|
|
22
|
+
id: priceInfoObjectId,
|
|
23
|
+
options: { showContent: true },
|
|
24
|
+
});
|
|
25
|
+
if (priceInfoObject.data === undefined ||
|
|
26
|
+
priceInfoObject.data.content === undefined)
|
|
27
|
+
throw new Error("Price not found on chain for price id " + priceId);
|
|
28
|
+
if (priceInfoObject.data.content.dataType !== "moveObject")
|
|
29
|
+
throw new Error("fetched object datatype should be moveObject");
|
|
30
|
+
const { magnitude, negative } = priceInfoObject.data.content.fields.price_info.fields.price_feed.fields
|
|
31
|
+
.price.fields.price.fields;
|
|
32
|
+
const conf = priceInfoObject.data.content.fields.price_info.fields.price_feed.fields
|
|
33
|
+
.price.fields.conf;
|
|
34
|
+
const timestamp = priceInfoObject.data.content.fields.price_info.fields.price_feed.fields
|
|
35
|
+
.price.fields.timestamp;
|
|
36
|
+
return {
|
|
37
|
+
price: negative ? "-" + magnitude : magnitude,
|
|
38
|
+
conf,
|
|
39
|
+
publishTime: Number(timestamp),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
console.error(`Polling Sui on-chain price for ${priceId} failed. Error:`);
|
|
44
|
+
console.error(e);
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.SuiPriceListener = SuiPriceListener;
|
|
50
|
+
class SuiPricePusher {
|
|
51
|
+
priceServiceConnection;
|
|
52
|
+
pythPackageId;
|
|
53
|
+
pythStateId;
|
|
54
|
+
wormholePackageId;
|
|
55
|
+
wormholeStateId;
|
|
56
|
+
priceFeedToPriceInfoObjectTableId;
|
|
57
|
+
signer;
|
|
58
|
+
constructor(priceServiceConnection, pythPackageId, pythStateId, wormholePackageId, wormholeStateId, priceFeedToPriceInfoObjectTableId, endpoint, mnemonic) {
|
|
59
|
+
this.priceServiceConnection = priceServiceConnection;
|
|
60
|
+
this.pythPackageId = pythPackageId;
|
|
61
|
+
this.pythStateId = pythStateId;
|
|
62
|
+
this.wormholePackageId = wormholePackageId;
|
|
63
|
+
this.wormholeStateId = wormholeStateId;
|
|
64
|
+
this.priceFeedToPriceInfoObjectTableId = priceFeedToPriceInfoObjectTableId;
|
|
65
|
+
this.signer = new sui_js_1.RawSigner(sui_js_1.Ed25519Keypair.deriveKeypair(mnemonic), new sui_js_1.JsonRpcProvider(new sui_js_1.Connection({ fullnode: endpoint })));
|
|
66
|
+
}
|
|
67
|
+
async updatePriceFeed(priceIds, pubTimesToPush) {
|
|
68
|
+
if (priceIds.length === 0) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (priceIds.length !== pubTimesToPush.length)
|
|
72
|
+
throw new Error("Invalid arguments");
|
|
73
|
+
const tx = new sui_js_1.TransactionBlock();
|
|
74
|
+
const vaas = await this.priceServiceConnection.getLatestVaas(priceIds);
|
|
75
|
+
// Parse our batch price attestation VAA bytes using Wormhole.
|
|
76
|
+
// Check out the Wormhole cross-chain bridge and generic messaging protocol here:
|
|
77
|
+
// https://github.com/wormhole-foundation/wormhole
|
|
78
|
+
let verified_vaas = [];
|
|
79
|
+
for (const vaa of vaas) {
|
|
80
|
+
const [verified_vaa] = tx.moveCall({
|
|
81
|
+
target: `${this.wormholePackageId}::vaa::parse_and_verify`,
|
|
82
|
+
arguments: [
|
|
83
|
+
tx.object(this.wormholeStateId),
|
|
84
|
+
tx.pure([...Buffer.from(vaa, "base64")]),
|
|
85
|
+
tx.object(sui_js_1.SUI_CLOCK_OBJECT_ID),
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
verified_vaas = verified_vaas.concat(verified_vaa);
|
|
89
|
+
}
|
|
90
|
+
// Create a hot potato vector of price feed updates that will
|
|
91
|
+
// be used to update price feeds.
|
|
92
|
+
let [price_updates_hot_potato] = tx.moveCall({
|
|
93
|
+
target: `${this.pythPackageId}::pyth::create_price_infos_hot_potato`,
|
|
94
|
+
arguments: [
|
|
95
|
+
tx.object(this.pythStateId),
|
|
96
|
+
tx.makeMoveVec({
|
|
97
|
+
type: `${this.wormholePackageId}::vaa::VAA`,
|
|
98
|
+
objects: verified_vaas,
|
|
99
|
+
}),
|
|
100
|
+
tx.object(sui_js_1.SUI_CLOCK_OBJECT_ID),
|
|
101
|
+
],
|
|
102
|
+
});
|
|
103
|
+
// Update each price info object (containing our price feeds of interest)
|
|
104
|
+
// using the hot potato vector.
|
|
105
|
+
for (const priceId of priceIds) {
|
|
106
|
+
let priceInfoObjectId;
|
|
107
|
+
try {
|
|
108
|
+
priceInfoObjectId = await priceIdToPriceInfoObjectId(this.signer.provider, this.pythPackageId, this.priceFeedToPriceInfoObjectTableId, priceId);
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
console.log("Error fetching price info object id for ", priceId);
|
|
112
|
+
console.error(e);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const coin = tx.splitCoins(tx.gas, [tx.pure(1)]);
|
|
116
|
+
[price_updates_hot_potato] = tx.moveCall({
|
|
117
|
+
target: `${this.pythPackageId}::pyth::update_single_price_feed`,
|
|
118
|
+
arguments: [
|
|
119
|
+
tx.object(this.pythStateId),
|
|
120
|
+
price_updates_hot_potato,
|
|
121
|
+
tx.object(priceInfoObjectId),
|
|
122
|
+
coin,
|
|
123
|
+
tx.object(sui_js_1.SUI_CLOCK_OBJECT_ID),
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// Explicitly destroy the hot potato vector, since it can't be dropped
|
|
128
|
+
// automatically.
|
|
129
|
+
tx.moveCall({
|
|
130
|
+
target: `${this.pythPackageId}::hot_potato_vector::destroy`,
|
|
131
|
+
arguments: [price_updates_hot_potato],
|
|
132
|
+
typeArguments: [`${this.pythPackageId}::price_info::PriceInfo`],
|
|
133
|
+
});
|
|
134
|
+
try {
|
|
135
|
+
const result = await this.signer.signAndExecuteTransactionBlock({
|
|
136
|
+
transactionBlock: tx,
|
|
137
|
+
options: {
|
|
138
|
+
showInput: true,
|
|
139
|
+
showEffects: true,
|
|
140
|
+
showEvents: true,
|
|
141
|
+
showObjectChanges: true,
|
|
142
|
+
showBalanceChanges: true,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
console.log("Successfully updated price with transaction digest ", result.digest);
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
console.log("Error when signAndExecuteTransactionBlock");
|
|
149
|
+
if (String(e).includes("GasBalanceTooLow")) {
|
|
150
|
+
console.log("Insufficient Gas Amount. Please top up your account");
|
|
151
|
+
process.exit();
|
|
152
|
+
}
|
|
153
|
+
console.error(e);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
exports.SuiPricePusher = SuiPricePusher;
|
|
159
|
+
// We are calculating stored price info object id for given price id
|
|
160
|
+
// The mapping between which is static. Hence, we are caching it here.
|
|
161
|
+
const CACHE = {};
|
|
162
|
+
// For given priceid, this method will fetch the price info object id
|
|
163
|
+
// where the price information for the corresponding price feed is stored
|
|
164
|
+
async function priceIdToPriceInfoObjectId(provider, pythPackageId, priceFeedToPriceInfoObjectTableId, priceId) {
|
|
165
|
+
// Check if this was fetched before.
|
|
166
|
+
if (CACHE[priceId] !== undefined)
|
|
167
|
+
return CACHE[priceId];
|
|
168
|
+
const storedObjectID = await provider.getDynamicFieldObject({
|
|
169
|
+
parentId: priceFeedToPriceInfoObjectTableId,
|
|
170
|
+
name: {
|
|
171
|
+
type: `${pythPackageId}::price_identifier::PriceIdentifier`,
|
|
172
|
+
value: {
|
|
173
|
+
bytes: "0x" + priceId,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
if (storedObjectID.error !== undefined)
|
|
178
|
+
throw storedObjectID.error;
|
|
179
|
+
if (storedObjectID.data === undefined ||
|
|
180
|
+
storedObjectID.data.content === undefined)
|
|
181
|
+
throw new Error("Price not found on chain for price id " + priceId);
|
|
182
|
+
if (storedObjectID.data.content.dataType !== "moveObject")
|
|
183
|
+
throw new Error("fetched object datatype should be moveObject");
|
|
184
|
+
// This ID points to the price info object for the given price id stored on chain
|
|
185
|
+
const priceInfoObjectId = storedObjectID.data.content.fields.value;
|
|
186
|
+
// cache the price info object id
|
|
187
|
+
CACHE[priceId] = priceInfoObjectId;
|
|
188
|
+
return priceInfoObjectId;
|
|
189
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pythnetwork/price-pusher",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.3.0",
|
|
4
4
|
"description": "Pyth Price Pusher",
|
|
5
5
|
"homepage": "https://pyth.network",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -51,15 +51,17 @@
|
|
|
51
51
|
"typescript": "^4.6.3"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@injectivelabs/sdk-ts": "
|
|
54
|
+
"@injectivelabs/sdk-ts": "1.10.72",
|
|
55
|
+
"@mysten/sui.js": "^0.34.0",
|
|
55
56
|
"@pythnetwork/price-service-client": "*",
|
|
56
57
|
"@pythnetwork/pyth-sdk-solidity": "*",
|
|
57
58
|
"@truffle/hdwallet-provider": "^2.1.3",
|
|
59
|
+
"aptos": "^1.8.5",
|
|
58
60
|
"joi": "^17.6.0",
|
|
59
61
|
"web3": "^1.8.1",
|
|
60
62
|
"web3-eth-contract": "^1.8.1",
|
|
61
63
|
"yaml": "^2.1.1",
|
|
62
64
|
"yargs": "^17.5.1"
|
|
63
65
|
},
|
|
64
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "6cdcf4dffd54017e19168be450df01acd68f07ab"
|
|
65
67
|
}
|