@pythnetwork/price-pusher 10.0.0 → 10.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 -3
- package/{lib/aptos/aptos.js → dist/aptos/aptos.cjs} +80 -76
- package/{lib → dist}/aptos/aptos.d.ts +5 -5
- package/{lib/aptos/balance-tracker.js → dist/aptos/balance-tracker.cjs} +37 -25
- package/{lib → dist}/aptos/balance-tracker.d.ts +9 -9
- package/dist/aptos/command.cjs +161 -0
- package/{lib → dist}/aptos/command.d.ts +1 -2
- package/dist/common.cjs +4 -0
- package/{lib → dist}/common.d.ts +0 -1
- package/{lib/controller.js → dist/controller.cjs} +36 -33
- package/{lib → dist}/controller.d.ts +5 -6
- package/dist/evm/balance-tracker.cjs +58 -0
- package/{lib → dist}/evm/balance-tracker.d.ts +10 -10
- package/dist/evm/command.cjs +205 -0
- package/{lib → dist}/evm/command.d.ts +1 -2
- package/dist/evm/custom-gas-station.cjs +54 -0
- package/{lib → dist}/evm/custom-gas-station.d.ts +1 -2
- package/dist/evm/evm.cjs +287 -0
- package/{lib → dist}/evm/evm.d.ts +8 -7
- package/{lib/evm/pyth-abi.js → dist/evm/pyth-abi.cjs} +181 -160
- package/{lib → dist}/evm/pyth-abi.d.ts +0 -1
- package/dist/evm/pyth-contract.cjs +17 -0
- package/{lib → dist}/evm/pyth-contract.d.ts +3 -4
- package/dist/evm/super-wallet.cjs +90 -0
- package/{lib → dist}/evm/super-wallet.d.ts +1 -2
- package/dist/fuel/command.cjs +135 -0
- package/{lib → dist}/fuel/command.d.ts +1 -2
- package/dist/fuel/fuel.cjs +108 -0
- package/{lib → dist}/fuel/fuel.d.ts +5 -5
- package/dist/index.cjs +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/injective/command.cjs +150 -0
- package/{lib → dist}/injective/command.d.ts +1 -2
- package/{lib/injective/injective.js → dist/injective/injective.cjs} +100 -98
- package/{lib → dist}/injective/injective.d.ts +7 -6
- package/dist/interface.cjs +142 -0
- package/{lib → dist}/interface.d.ts +12 -13
- package/dist/metrics.cjs +218 -0
- package/{lib → dist}/metrics.d.ts +11 -9
- package/dist/near/command.cjs +129 -0
- package/{lib → dist}/near/command.d.ts +1 -2
- package/dist/near/near.cjs +183 -0
- package/{lib → dist}/near/near.d.ts +5 -5
- package/dist/options.cjs +132 -0
- package/{lib → dist}/options.d.ts +1 -2
- package/dist/package.json +1 -0
- package/dist/price-config.cjs +104 -0
- package/{lib → dist}/price-config.d.ts +5 -6
- package/{lib/pyth-price-listener.js → dist/pyth-price-listener.cjs} +30 -24
- package/{lib → dist}/pyth-price-listener.d.ts +4 -4
- package/dist/solana/balance-tracker.cjs +60 -0
- package/{lib → dist}/solana/balance-tracker.d.ts +9 -9
- package/dist/solana/command.cjs +259 -0
- package/{lib → dist}/solana/command.d.ts +2 -3
- package/{lib/solana/solana.js → dist/solana/solana.cjs} +90 -78
- package/{lib → dist}/solana/solana.d.ts +6 -6
- package/dist/sui/balance-tracker.cjs +58 -0
- package/{lib → dist}/sui/balance-tracker.d.ts +9 -9
- package/dist/sui/command.cjs +190 -0
- package/{lib → dist}/sui/command.d.ts +1 -2
- package/{lib/sui/sui.js → dist/sui/sui.cjs} +145 -133
- package/{lib → dist}/sui/sui.d.ts +7 -8
- package/dist/ton/command.cjs +137 -0
- package/{lib → dist}/ton/command.d.ts +1 -2
- package/dist/ton/ton.cjs +103 -0
- package/{lib → dist}/ton/ton.d.ts +7 -6
- package/dist/utils.cjs +102 -0
- package/{lib → dist}/utils.d.ts +4 -4
- package/package.json +161 -20
- package/lib/aptos/aptos.d.ts.map +0 -1
- package/lib/aptos/balance-tracker.d.ts.map +0 -1
- package/lib/aptos/command.d.ts.map +0 -1
- package/lib/aptos/command.js +0 -126
- package/lib/common.d.ts.map +0 -1
- package/lib/common.js +0 -2
- package/lib/controller.d.ts.map +0 -1
- package/lib/evm/balance-tracker.d.ts.map +0 -1
- package/lib/evm/balance-tracker.js +0 -49
- package/lib/evm/command.d.ts.map +0 -1
- package/lib/evm/command.js +0 -178
- package/lib/evm/custom-gas-station.d.ts.map +0 -1
- package/lib/evm/custom-gas-station.js +0 -40
- package/lib/evm/evm.d.ts.map +0 -1
- package/lib/evm/evm.js +0 -270
- package/lib/evm/pyth-abi.d.ts.map +0 -1
- package/lib/evm/pyth-contract.d.ts.map +0 -1
- package/lib/evm/pyth-contract.js +0 -11
- package/lib/evm/super-wallet.d.ts.map +0 -1
- package/lib/evm/super-wallet.js +0 -73
- package/lib/fuel/command.d.ts.map +0 -1
- package/lib/fuel/command.js +0 -98
- package/lib/fuel/fuel.d.ts.map +0 -1
- package/lib/fuel/fuel.js +0 -101
- package/lib/index.d.ts +0 -3
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -34
- package/lib/injective/command.d.ts.map +0 -1
- package/lib/injective/command.js +0 -119
- package/lib/injective/injective.d.ts.map +0 -1
- package/lib/interface.d.ts.map +0 -1
- package/lib/interface.js +0 -122
- package/lib/metrics.d.ts.map +0 -1
- package/lib/metrics.js +0 -129
- package/lib/near/command.d.ts.map +0 -1
- package/lib/near/command.js +0 -103
- package/lib/near/near.d.ts.map +0 -1
- package/lib/near/near.js +0 -168
- package/lib/options.d.ts.map +0 -1
- package/lib/options.js +0 -84
- package/lib/price-config.d.ts.map +0 -1
- package/lib/price-config.js +0 -114
- package/lib/pyth-price-listener.d.ts.map +0 -1
- package/lib/solana/balance-tracker.d.ts.map +0 -1
- package/lib/solana/balance-tracker.js +0 -51
- package/lib/solana/command.d.ts.map +0 -1
- package/lib/solana/command.js +0 -218
- package/lib/solana/solana.d.ts.map +0 -1
- package/lib/sui/balance-tracker.d.ts.map +0 -1
- package/lib/sui/balance-tracker.js +0 -49
- package/lib/sui/command.d.ts.map +0 -1
- package/lib/sui/command.js +0 -160
- package/lib/sui/sui.d.ts.map +0 -1
- package/lib/ton/command.d.ts.map +0 -1
- package/lib/ton/command.js +0 -99
- package/lib/ton/ton.d.ts.map +0 -1
- package/lib/ton/ton.js +0 -97
- package/lib/utils.d.ts.map +0 -1
- package/lib/utils.js +0 -61
package/README.md
CHANGED
|
@@ -211,12 +211,12 @@ and the on-chain Pyth contract and deciding whether to push a new price. You can
|
|
|
211
211
|
|
|
212
212
|
### Example
|
|
213
213
|
|
|
214
|
-
For example, to push `BTC/USD` and `BNB/USD` prices on
|
|
214
|
+
For example, to push `BTC/USD` and `BNB/USD` prices on Sonic blaze testnet, run the following command:
|
|
215
215
|
|
|
216
216
|
```sh
|
|
217
217
|
pnpm run dev evm \
|
|
218
|
-
--endpoint https://
|
|
219
|
-
--pyth-contract-address
|
|
218
|
+
--endpoint https://rpc.blaze.soniclabs.com \
|
|
219
|
+
--pyth-contract-address 0x2880aB155794e7179c9eE2e38200202908C17B43 \
|
|
220
220
|
--price-service-endpoint https://hermes.pyth.network \
|
|
221
221
|
--mnemonic-file "./mnemonic" \
|
|
222
222
|
--price-config-file "./price-config.stable.sample.yaml" \
|
|
@@ -272,6 +272,8 @@ The following metrics are available:
|
|
|
272
272
|
- **pyth_price_last_published_time** (Gauge): The last published time of a price feed in unix timestamp, labeled by price_id and alias
|
|
273
273
|
- **pyth_price_update_attempts_total** (Counter): Total number of price update attempts with their trigger condition and status, labeled by price_id, alias, trigger, and status
|
|
274
274
|
- **pyth_price_feeds_total** (Gauge): Total number of price feeds being monitored
|
|
275
|
+
- **pyth_source_price** (Gauge): Latest price value from Pyth source, labeled by price_id and alias
|
|
276
|
+
- **pyth_target_price** (Gauge): Latest price value from target chain, labeled by price_id and alias
|
|
275
277
|
- **pyth_wallet_balance** (Gauge): Current wallet balance of the price pusher in native token units, labeled by wallet_address and network
|
|
276
278
|
|
|
277
279
|
### Configuration
|
|
@@ -343,6 +345,30 @@ pyth_wallet_balance
|
|
|
343
345
|
pyth_wallet_balance < 0.1
|
|
344
346
|
```
|
|
345
347
|
|
|
348
|
+
7. Monitor current source price values:
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
pyth_source_price
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
8. Monitor current target price values:
|
|
355
|
+
|
|
356
|
+
```
|
|
357
|
+
pyth_target_price
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
9. Compare source vs target price differences:
|
|
361
|
+
|
|
362
|
+
```
|
|
363
|
+
abs(pyth_source_price - pyth_target_price) / pyth_source_price * 100
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
10. Detect significant price deviations (>1%):
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
abs(pyth_source_price - pyth_target_price) / pyth_source_price * 100 > 1
|
|
370
|
+
```
|
|
371
|
+
|
|
346
372
|
### Dashboard
|
|
347
373
|
|
|
348
374
|
The docker-compose setup includes a pre-configured Grafana dashboard (`grafana-dashboard.sample.json`) that provides monitoring of your price pusher operations. The dashboard includes the following panels:
|
|
@@ -353,6 +379,8 @@ The docker-compose setup includes a pre-configured Grafana dashboard (`grafana-d
|
|
|
353
379
|
- **Price Feeds List**: A table listing all configured price feeds with their details.
|
|
354
380
|
- **Successful Updates (Current Range)**: Graph showing the number of successful price updates over the current range with timeline.
|
|
355
381
|
- **Update Conditions Distribution**: Pie chart showing the distribution of update conditions (YES/NO/EARLY) over the selected time range.
|
|
382
|
+
- **Source vs Target Price Values**: Graphs showing current price values from both Pyth source and target chains for comparison.
|
|
383
|
+
- **Price Deviation Monitoring**: Panels to track price differences between source and target chains.
|
|
356
384
|
- **Wallet Balance**: Current balance of your wallet in native token units.
|
|
357
385
|
- **Wallet Balance Over Time**: Graph tracking your wallet balance over time to monitor consumption.
|
|
358
386
|
- **Failed Updates (Current Range)**: Graph showing the number of failed price updates over the current range with timeline.
|
|
@@ -1,20 +1,35 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable unicorn/no-await-expression-member */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ "use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get APTOS_ACCOUNT_HD_PATH () {
|
|
13
|
+
return APTOS_ACCOUNT_HD_PATH;
|
|
14
|
+
},
|
|
15
|
+
get AptosPriceListener () {
|
|
16
|
+
return AptosPriceListener;
|
|
17
|
+
},
|
|
18
|
+
get AptosPricePusher () {
|
|
19
|
+
return AptosPricePusher;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
const _aptos = require("./aptos.cjs");
|
|
23
|
+
const _interface = require("../interface.cjs");
|
|
24
|
+
class AptosPriceListener extends _interface.ChainPriceListener {
|
|
7
25
|
pythModule;
|
|
8
26
|
endpoint;
|
|
9
27
|
logger;
|
|
10
|
-
constructor(pythModule, endpoint, priceItems, logger, config)
|
|
11
|
-
super(config.pollingFrequency, priceItems);
|
|
12
|
-
this.pythModule = pythModule;
|
|
13
|
-
this.endpoint = endpoint;
|
|
14
|
-
this.logger = logger;
|
|
28
|
+
constructor(pythModule, endpoint, priceItems, logger, config){
|
|
29
|
+
super(config.pollingFrequency, priceItems), this.pythModule = pythModule, this.endpoint = endpoint, this.logger = logger;
|
|
15
30
|
}
|
|
16
31
|
async getOnChainPriceInfo(priceId) {
|
|
17
|
-
const client = new
|
|
32
|
+
const client = new _aptos.AptosClient(this.endpoint);
|
|
18
33
|
const res = await client.getAccountResource(this.pythModule, `${this.pythModule}::state::LatestPriceInfo`);
|
|
19
34
|
try {
|
|
20
35
|
// This depends upon the pyth contract storage on Aptos and should not be undefined.
|
|
@@ -24,37 +39,24 @@ class AptosPriceListener extends interface_1.ChainPriceListener {
|
|
|
24
39
|
key_type: `${this.pythModule}::price_identifier::PriceIdentifier`,
|
|
25
40
|
value_type: `${this.pythModule}::price_info::PriceInfo`,
|
|
26
41
|
key: {
|
|
27
|
-
bytes: priceId
|
|
28
|
-
}
|
|
42
|
+
bytes: priceId
|
|
43
|
+
}
|
|
29
44
|
});
|
|
30
45
|
const multiplier = priceItemRes.price_feed.price.price.negative === true ? -1 : 1;
|
|
31
46
|
const price = multiplier * Number(priceItemRes.price_feed.price.price.magnitude);
|
|
32
|
-
this.logger.debug(`Polled an Aptos on-chain price for feed ${this.priceIdToAlias.get(priceId)} (${priceId}).`);
|
|
47
|
+
this.logger.debug(`Polled an Aptos on-chain price for feed ${this.priceIdToAlias.get(priceId) ?? ""} (${priceId}).`);
|
|
33
48
|
return {
|
|
34
49
|
price: price.toString(),
|
|
35
50
|
conf: priceItemRes.price_feed.price.conf,
|
|
36
|
-
publishTime: Number(priceItemRes.price_feed.price.timestamp)
|
|
51
|
+
publishTime: Number(priceItemRes.price_feed.price.timestamp)
|
|
37
52
|
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
this.logger.error(err, `Polling Aptos on-chain price for ${priceId} failed.`);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
this.logger.error(error, `Polling Aptos on-chain price for ${priceId} failed.`);
|
|
41
55
|
return undefined;
|
|
42
56
|
}
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
|
-
|
|
46
|
-
// Derivation path for aptos accounts
|
|
47
|
-
exports.APTOS_ACCOUNT_HD_PATH = "m/44'/637'/0'/0'/0'";
|
|
48
|
-
/**
|
|
49
|
-
* The `AptosPricePusher` is designed for high-throughput of price updates.
|
|
50
|
-
* Achieving this property requires sacrificing some nice-to-have features of other
|
|
51
|
-
* pusher implementations that can reduce cost when running multiple pushers. Specifically,
|
|
52
|
-
* this implementation does not use `update_price_feeds_if_necssary` and simulate the transaction
|
|
53
|
-
* before submission.
|
|
54
|
-
*
|
|
55
|
-
* If multiple instances of this pusher are running in parallel, both of them will
|
|
56
|
-
* land all of their pushed updates on-chain.
|
|
57
|
-
*/
|
|
59
|
+
const APTOS_ACCOUNT_HD_PATH = "m/44'/637'/0'/0'/0'";
|
|
58
60
|
class AptosPricePusher {
|
|
59
61
|
hermesClient;
|
|
60
62
|
logger;
|
|
@@ -66,7 +68,8 @@ class AptosPricePusher {
|
|
|
66
68
|
lastSequenceNumber;
|
|
67
69
|
// If true, we are trying to fetch the most recent sequence number from the blockchain.
|
|
68
70
|
sequenceNumberLocked;
|
|
69
|
-
constructor(hermesClient, logger, pythContractAddress, endpoint, mnemonic,
|
|
71
|
+
constructor(hermesClient, logger, pythContractAddress, endpoint, mnemonic, // @ts-expect-error - TODO: this class member is unused. remove this exception when it is
|
|
72
|
+
overrideGasPriceMultiplier){
|
|
70
73
|
this.hermesClient = hermesClient;
|
|
71
74
|
this.logger = logger;
|
|
72
75
|
this.pythContractAddress = pythContractAddress;
|
|
@@ -76,57 +79,59 @@ class AptosPricePusher {
|
|
|
76
79
|
this.sequenceNumberLocked = false;
|
|
77
80
|
}
|
|
78
81
|
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
async getPriceFeedsUpdateData(priceIds) {
|
|
82
|
+
* Gets price update data which then can be submitted to the Pyth contract to update the prices.
|
|
83
|
+
* 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)
|
|
84
|
+
*
|
|
85
|
+
* @param priceIds - Array of hex-encoded price ids.
|
|
86
|
+
* @returns Array of price update data.
|
|
87
|
+
*/ async getPriceFeedsUpdateData(priceIds) {
|
|
86
88
|
const response = await this.hermesClient.getLatestPriceUpdates(priceIds, {
|
|
87
89
|
encoding: "base64",
|
|
88
|
-
ignoreInvalidPriceIds: true
|
|
90
|
+
ignoreInvalidPriceIds: true
|
|
89
91
|
});
|
|
90
|
-
return response.binary.data.map((data)
|
|
92
|
+
return response.binary.data.map((data)=>[
|
|
93
|
+
...Buffer.from(data, "base64")
|
|
94
|
+
]);
|
|
91
95
|
}
|
|
92
96
|
async updatePriceFeed(priceIds, pubTimesToPush) {
|
|
93
97
|
if (priceIds.length === 0) {
|
|
94
98
|
return;
|
|
95
99
|
}
|
|
96
|
-
if (priceIds.length !== pubTimesToPush.length)
|
|
97
|
-
throw new Error("Invalid arguments");
|
|
100
|
+
if (priceIds.length !== pubTimesToPush.length) throw new Error("Invalid arguments");
|
|
98
101
|
let priceFeedUpdateData;
|
|
99
102
|
try {
|
|
100
103
|
// get the latest VAAs for updatePriceFeed and then push them
|
|
101
104
|
priceFeedUpdateData = await this.getPriceFeedsUpdateData(priceIds);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
this.logger.error(err, "Error fetching the latest vaas to push.");
|
|
105
|
+
} catch (error) {
|
|
106
|
+
this.logger.error(error, "Error fetching the latest vaas to push.");
|
|
105
107
|
return;
|
|
106
108
|
}
|
|
107
|
-
const account =
|
|
108
|
-
const client = new
|
|
109
|
+
const account = _aptos.AptosAccount.fromDerivePath(APTOS_ACCOUNT_HD_PATH, this.mnemonic);
|
|
110
|
+
const client = new _aptos.AptosClient(this.endpoint);
|
|
109
111
|
const sequenceNumber = await this.tryGetNextSequenceNumber(client, account);
|
|
110
112
|
const rawTx = await client.generateTransaction(account.address(), {
|
|
111
113
|
function: `${this.pythContractAddress}::pyth::update_price_feeds_with_funder`,
|
|
112
114
|
type_arguments: [],
|
|
113
|
-
arguments: [
|
|
115
|
+
arguments: [
|
|
116
|
+
priceFeedUpdateData
|
|
117
|
+
]
|
|
114
118
|
}, {
|
|
115
|
-
sequence_number: sequenceNumber.toFixed()
|
|
119
|
+
sequence_number: sequenceNumber.toFixed(0)
|
|
116
120
|
});
|
|
117
121
|
try {
|
|
118
122
|
const signedTx = await client.signTransaction(account, rawTx);
|
|
119
123
|
const pendingTx = await client.submitTransaction(signedTx);
|
|
120
|
-
this.logger.debug({
|
|
124
|
+
this.logger.debug({
|
|
125
|
+
hash: pendingTx.hash
|
|
126
|
+
}, "Successfully broadcasted tx.");
|
|
121
127
|
// Sometimes broadcasted txs don't make it on-chain and they cause our sequence number
|
|
122
128
|
// to go out of sync. Missing transactions are rare and we don't want this check to block
|
|
123
129
|
// the next price update. So we use spawn a promise without awaiting on it to wait for the
|
|
124
130
|
// transaction to be confirmed and if it fails, it resets the sequence number and return.
|
|
125
|
-
this.waitForTransactionConfirmation(client, pendingTx.hash);
|
|
131
|
+
void this.waitForTransactionConfirmation(client, pendingTx.hash);
|
|
126
132
|
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
this.logger.error(err, "Error executing messages");
|
|
133
|
+
} catch (error) {
|
|
134
|
+
this.logger.error(error, "Error executing messages");
|
|
130
135
|
// Reset the sequence number to re-sync it (in case that was the issue)
|
|
131
136
|
this.lastSequenceNumber = undefined;
|
|
132
137
|
return;
|
|
@@ -137,12 +142,16 @@ class AptosPricePusher {
|
|
|
137
142
|
try {
|
|
138
143
|
await client.waitForTransaction(txHash, {
|
|
139
144
|
checkSuccess: true,
|
|
140
|
-
timeoutSecs: 10
|
|
145
|
+
timeoutSecs: 10
|
|
141
146
|
});
|
|
142
|
-
this.logger.info({
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
147
|
+
this.logger.info({
|
|
148
|
+
hash: txHash
|
|
149
|
+
}, `Transaction confirmed.`);
|
|
150
|
+
} catch (error) {
|
|
151
|
+
this.logger.error({
|
|
152
|
+
err: error,
|
|
153
|
+
hash: txHash
|
|
154
|
+
}, `Transaction failed to confirm.`);
|
|
146
155
|
this.lastSequenceNumber = undefined;
|
|
147
156
|
}
|
|
148
157
|
}
|
|
@@ -150,32 +159,27 @@ class AptosPricePusher {
|
|
|
150
159
|
// to predict the next sequence number if possible; if not, it fetches the number from
|
|
151
160
|
// the blockchain itself (and caches it for later).
|
|
152
161
|
async tryGetNextSequenceNumber(client, account) {
|
|
153
|
-
if (this.lastSequenceNumber
|
|
154
|
-
this.lastSequenceNumber += 1;
|
|
155
|
-
return this.lastSequenceNumber;
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
162
|
+
if (this.lastSequenceNumber === undefined) {
|
|
158
163
|
// Fetch from the blockchain if we don't have the local cache.
|
|
159
164
|
// Note that this is locked so that only 1 fetch occurs regardless of how many updates
|
|
160
165
|
// happen during that fetch.
|
|
161
|
-
if (
|
|
166
|
+
if (this.sequenceNumberLocked) {
|
|
167
|
+
throw new Error("Waiting for sequence number in another thread.");
|
|
168
|
+
} else {
|
|
162
169
|
try {
|
|
163
170
|
this.sequenceNumberLocked = true;
|
|
164
171
|
this.lastSequenceNumber = Number((await client.getAccount(account.address())).sequence_number);
|
|
165
172
|
this.logger.debug(`Fetched account sequence number: ${this.lastSequenceNumber}`);
|
|
166
173
|
return this.lastSequenceNumber;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
finally {
|
|
174
|
+
} catch (error) {
|
|
175
|
+
throw new Error(`Failed to retrieve sequence number ${error}`);
|
|
176
|
+
} finally{
|
|
172
177
|
this.sequenceNumberLocked = false;
|
|
173
178
|
}
|
|
174
179
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
180
|
+
} else {
|
|
181
|
+
this.lastSequenceNumber += 1;
|
|
182
|
+
return this.lastSequenceNumber;
|
|
178
183
|
}
|
|
179
184
|
}
|
|
180
185
|
}
|
|
181
|
-
exports.AptosPricePusher = AptosPricePusher;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { ChainPriceListener, IPricePusher, PriceInfo, PriceItem } from "../interface";
|
|
2
|
-
import { DurationInSeconds } from "../utils";
|
|
3
1
|
import { HermesClient } from "@pythnetwork/hermes-client";
|
|
4
|
-
import { Logger } from "pino";
|
|
2
|
+
import type { Logger } from "pino";
|
|
3
|
+
import type { IPricePusher, PriceInfo, PriceItem } from "../interface.js";
|
|
4
|
+
import { ChainPriceListener } from "../interface.js";
|
|
5
|
+
import type { DurationInSeconds } from "../utils.js";
|
|
5
6
|
export declare class AptosPriceListener extends ChainPriceListener {
|
|
6
7
|
private pythModule;
|
|
7
8
|
private endpoint;
|
|
@@ -36,7 +37,7 @@ export declare class AptosPricePusher implements IPricePusher {
|
|
|
36
37
|
* Gets price update data which then can be submitted to the Pyth contract to update the prices.
|
|
37
38
|
* 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)
|
|
38
39
|
*
|
|
39
|
-
* @param priceIds Array of hex-encoded price ids.
|
|
40
|
+
* @param priceIds - Array of hex-encoded price ids.
|
|
40
41
|
* @returns Array of price update data.
|
|
41
42
|
*/
|
|
42
43
|
getPriceFeedsUpdateData(priceIds: string[]): Promise<number[][]>;
|
|
@@ -44,4 +45,3 @@ export declare class AptosPricePusher implements IPricePusher {
|
|
|
44
45
|
private waitForTransactionConfirmation;
|
|
45
46
|
private tryGetNextSequenceNumber;
|
|
46
47
|
}
|
|
47
|
-
//# sourceMappingURL=aptos.d.ts.map
|
|
@@ -1,35 +1,50 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get AptosBalanceTracker () {
|
|
13
|
+
return AptosBalanceTracker;
|
|
14
|
+
},
|
|
15
|
+
get createAptosBalanceTracker () {
|
|
16
|
+
return createAptosBalanceTracker;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const _tssdk = require("@aptos-labs/ts-sdk");
|
|
20
|
+
const _interface = require("../interface.cjs");
|
|
21
|
+
class AptosBalanceTracker extends _interface.BaseBalanceTracker {
|
|
11
22
|
client;
|
|
12
23
|
aptosAddress;
|
|
13
24
|
decimals;
|
|
14
|
-
constructor(config)
|
|
25
|
+
constructor(config){
|
|
15
26
|
super({
|
|
16
27
|
...config,
|
|
17
|
-
logger: config.logger.child({
|
|
28
|
+
logger: config.logger.child({
|
|
29
|
+
module: "AptosBalanceTracker"
|
|
30
|
+
})
|
|
18
31
|
});
|
|
19
|
-
this.client = new
|
|
32
|
+
this.client = new _tssdk.Aptos(new _tssdk.AptosConfig({
|
|
33
|
+
network: _tssdk.Network.CUSTOM,
|
|
34
|
+
fullnode: config.endpoint
|
|
35
|
+
}));
|
|
20
36
|
this.aptosAddress = config.address;
|
|
21
37
|
// APT has 8 decimal places by default
|
|
22
38
|
this.decimals = config.decimals ?? 8;
|
|
23
39
|
}
|
|
24
40
|
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
async updateBalance() {
|
|
41
|
+
* Aptos-specific implementation of balance update
|
|
42
|
+
* Fetches the native APT balance for the configured address
|
|
43
|
+
*/ async updateBalance() {
|
|
29
44
|
try {
|
|
30
45
|
// Get account resource to check the balance
|
|
31
46
|
const accountAPTAmount = await this.client.getAccountAPTAmount({
|
|
32
|
-
accountAddress: this.aptosAddress
|
|
47
|
+
accountAddress: this.aptosAddress
|
|
33
48
|
});
|
|
34
49
|
// Convert the amount to a bigint
|
|
35
50
|
const balance = BigInt(accountAPTAmount);
|
|
@@ -38,16 +53,13 @@ class AptosBalanceTracker extends interface_1.BaseBalanceTracker {
|
|
|
38
53
|
// Update metrics with the new balance
|
|
39
54
|
this.metrics.updateWalletBalance(this.address, this.network, normalizedBalance);
|
|
40
55
|
this.logger.debug(`Updated Aptos wallet balance: ${this.address} = ${normalizedBalance.toString()} APT (raw: ${balance.toString()})`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
} catch (error) {
|
|
57
|
+
this.logger.error({
|
|
58
|
+
error
|
|
59
|
+
}, "Error fetching Aptos wallet balance for metrics");
|
|
44
60
|
}
|
|
45
61
|
}
|
|
46
62
|
}
|
|
47
|
-
exports.AptosBalanceTracker = AptosBalanceTracker;
|
|
48
|
-
/**
|
|
49
|
-
* Factory function to create a balance tracker for Aptos chain
|
|
50
|
-
*/
|
|
51
63
|
function createAptosBalanceTracker(params) {
|
|
52
64
|
return new AptosBalanceTracker({
|
|
53
65
|
endpoint: params.endpoint,
|
|
@@ -56,6 +68,6 @@ function createAptosBalanceTracker(params) {
|
|
|
56
68
|
updateInterval: params.updateInterval,
|
|
57
69
|
metrics: params.metrics,
|
|
58
70
|
logger: params.logger,
|
|
59
|
-
decimals: params.decimals
|
|
71
|
+
decimals: params.decimals
|
|
60
72
|
});
|
|
61
73
|
}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import type { Logger } from "pino";
|
|
2
|
+
import type { BaseBalanceTrackerConfig, IBalanceTracker } from "../interface.js";
|
|
3
|
+
import { BaseBalanceTracker } from "../interface.js";
|
|
4
|
+
import { PricePusherMetrics } from "../metrics.js";
|
|
5
|
+
import type { DurationInSeconds } from "../utils.js";
|
|
5
6
|
/**
|
|
6
7
|
* Aptos-specific configuration for balance tracker
|
|
7
8
|
*/
|
|
8
|
-
export
|
|
9
|
+
export type AptosBalanceTrackerConfig = {
|
|
9
10
|
/** Aptos node endpoint URL */
|
|
10
11
|
endpoint: string;
|
|
11
12
|
/** Aptos account address */
|
|
12
13
|
address: string;
|
|
13
14
|
/** Optional decimal places for APT token (default: 8) */
|
|
14
15
|
decimals?: number;
|
|
15
|
-
}
|
|
16
|
+
} & BaseBalanceTrackerConfig;
|
|
16
17
|
/**
|
|
17
18
|
* Aptos-specific implementation of the balance tracker
|
|
18
19
|
*/
|
|
@@ -30,7 +31,7 @@ export declare class AptosBalanceTracker extends BaseBalanceTracker {
|
|
|
30
31
|
/**
|
|
31
32
|
* Parameters for creating an Aptos balance tracker
|
|
32
33
|
*/
|
|
33
|
-
export
|
|
34
|
+
export type CreateAptosBalanceTrackerParams = {
|
|
34
35
|
endpoint: string;
|
|
35
36
|
address: string;
|
|
36
37
|
network: string;
|
|
@@ -38,9 +39,8 @@ export interface CreateAptosBalanceTrackerParams {
|
|
|
38
39
|
metrics: PricePusherMetrics;
|
|
39
40
|
logger: Logger;
|
|
40
41
|
decimals?: number;
|
|
41
|
-
}
|
|
42
|
+
};
|
|
42
43
|
/**
|
|
43
44
|
* Factory function to create a balance tracker for Aptos chain
|
|
44
45
|
*/
|
|
45
46
|
export declare function createAptosBalanceTracker(params: CreateAptosBalanceTrackerParams): IBalanceTracker;
|
|
46
|
-
//# sourceMappingURL=balance-tracker.d.ts.map
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-explicit-any */ "use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "default", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return _default;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _nodefs = /*#__PURE__*/ _interop_require_default(require("node:fs"));
|
|
12
|
+
const _hermesclient = require("@pythnetwork/hermes-client");
|
|
13
|
+
const _aptos = require("./aptos.cjs");
|
|
14
|
+
const _pino = /*#__PURE__*/ _interop_require_default(require("pino"));
|
|
15
|
+
const _controller = require("../controller.cjs");
|
|
16
|
+
const _metrics = require("../metrics.cjs");
|
|
17
|
+
const _options = /*#__PURE__*/ _interop_require_wildcard(require("../options.cjs"));
|
|
18
|
+
const _priceconfig = require("../price-config.cjs");
|
|
19
|
+
const _pythpricelistener = require("../pyth-price-listener.cjs");
|
|
20
|
+
const _aptos1 = require("./aptos.cjs");
|
|
21
|
+
const _utils = require("../utils.cjs");
|
|
22
|
+
const _balancetracker = require("./balance-tracker.cjs");
|
|
23
|
+
function _interop_require_default(obj) {
|
|
24
|
+
return obj && obj.__esModule ? obj : {
|
|
25
|
+
default: obj
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
29
|
+
if (typeof WeakMap !== "function") return null;
|
|
30
|
+
var cacheBabelInterop = new WeakMap();
|
|
31
|
+
var cacheNodeInterop = new WeakMap();
|
|
32
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
33
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
34
|
+
})(nodeInterop);
|
|
35
|
+
}
|
|
36
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
37
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
38
|
+
return obj;
|
|
39
|
+
}
|
|
40
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
41
|
+
return {
|
|
42
|
+
default: obj
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
46
|
+
if (cache && cache.has(obj)) {
|
|
47
|
+
return cache.get(obj);
|
|
48
|
+
}
|
|
49
|
+
var newObj = {
|
|
50
|
+
__proto__: null
|
|
51
|
+
};
|
|
52
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
53
|
+
for(var key in obj){
|
|
54
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
55
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
56
|
+
if (desc && (desc.get || desc.set)) {
|
|
57
|
+
Object.defineProperty(newObj, key, desc);
|
|
58
|
+
} else {
|
|
59
|
+
newObj[key] = obj[key];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
newObj.default = obj;
|
|
64
|
+
if (cache) {
|
|
65
|
+
cache.set(obj, newObj);
|
|
66
|
+
}
|
|
67
|
+
return newObj;
|
|
68
|
+
}
|
|
69
|
+
const _default = {
|
|
70
|
+
command: "aptos",
|
|
71
|
+
describe: "run price pusher for aptos",
|
|
72
|
+
builder: {
|
|
73
|
+
endpoint: {
|
|
74
|
+
description: "RPC endpoint endpoint URL for aptos. The pusher will periodically" + "poll for updates. The polling interval is configurable via the " + "`polling-frequency` command-line argument.",
|
|
75
|
+
type: "string",
|
|
76
|
+
required: true
|
|
77
|
+
},
|
|
78
|
+
"override-gas-price-multiplier": {
|
|
79
|
+
description: "Multiply the gas price by this number if the transaction is not landing to override it. Default 2",
|
|
80
|
+
type: "number",
|
|
81
|
+
required: false,
|
|
82
|
+
default: 2
|
|
83
|
+
},
|
|
84
|
+
..._options.priceConfigFile,
|
|
85
|
+
..._options.priceServiceEndpoint,
|
|
86
|
+
..._options.mnemonicFile,
|
|
87
|
+
..._options.pythContractAddress,
|
|
88
|
+
..._options.pollingFrequency,
|
|
89
|
+
..._options.pushingFrequency,
|
|
90
|
+
..._options.logLevel,
|
|
91
|
+
..._options.controllerLogLevel,
|
|
92
|
+
..._options.enableMetrics,
|
|
93
|
+
..._options.metricsPort
|
|
94
|
+
},
|
|
95
|
+
handler: async function(argv) {
|
|
96
|
+
// FIXME: type checks for this
|
|
97
|
+
const { endpoint, priceConfigFile, priceServiceEndpoint, mnemonicFile, pythContractAddress, pushingFrequency, pollingFrequency, overrideGasPriceMultiplier, logLevel, controllerLogLevel, enableMetrics, metricsPort } = argv;
|
|
98
|
+
const logger = (0, _pino.default)({
|
|
99
|
+
level: logLevel
|
|
100
|
+
});
|
|
101
|
+
const priceConfigs = (0, _priceconfig.readPriceConfigFile)(priceConfigFile);
|
|
102
|
+
const hermesClient = new _hermesclient.HermesClient(priceServiceEndpoint);
|
|
103
|
+
// Initialize metrics if enabled
|
|
104
|
+
let metrics;
|
|
105
|
+
if (enableMetrics) {
|
|
106
|
+
metrics = new _metrics.PricePusherMetrics(logger.child({
|
|
107
|
+
module: "Metrics"
|
|
108
|
+
}));
|
|
109
|
+
metrics.start(metricsPort);
|
|
110
|
+
logger.info(`Metrics server started on port ${metricsPort}`);
|
|
111
|
+
}
|
|
112
|
+
const mnemonic = _nodefs.default.readFileSync(mnemonicFile, "utf8").trim();
|
|
113
|
+
const account = _aptos.AptosAccount.fromDerivePath(_aptos1.APTOS_ACCOUNT_HD_PATH, mnemonic);
|
|
114
|
+
logger.info(`Pushing from account address: ${account.address()}`);
|
|
115
|
+
let priceItems = priceConfigs.map(({ id, alias })=>({
|
|
116
|
+
id,
|
|
117
|
+
alias
|
|
118
|
+
}));
|
|
119
|
+
// Better to filter out invalid price items before creating the pyth listener
|
|
120
|
+
const { existingPriceItems, invalidPriceItems } = await (0, _utils.filterInvalidPriceItems)(hermesClient, priceItems);
|
|
121
|
+
if (invalidPriceItems.length > 0) {
|
|
122
|
+
logger.error(`Invalid price id submitted for: ${invalidPriceItems.map(({ alias })=>alias).join(", ")}`);
|
|
123
|
+
}
|
|
124
|
+
priceItems = existingPriceItems;
|
|
125
|
+
const pythListener = new _pythpricelistener.PythPriceListener(hermesClient, priceItems, logger.child({
|
|
126
|
+
module: "PythPriceListener"
|
|
127
|
+
}));
|
|
128
|
+
const aptosListener = new _aptos1.AptosPriceListener(pythContractAddress, endpoint, priceItems, logger.child({
|
|
129
|
+
module: "AptosPriceListener"
|
|
130
|
+
}), {
|
|
131
|
+
pollingFrequency
|
|
132
|
+
});
|
|
133
|
+
const aptosPusher = new _aptos1.AptosPricePusher(hermesClient, logger.child({
|
|
134
|
+
module: "AptosPricePusher"
|
|
135
|
+
}), pythContractAddress, endpoint, mnemonic, overrideGasPriceMultiplier);
|
|
136
|
+
const controller = new _controller.Controller(priceConfigs, pythListener, aptosListener, aptosPusher, logger.child({
|
|
137
|
+
module: "Controller"
|
|
138
|
+
}, {
|
|
139
|
+
level: controllerLogLevel
|
|
140
|
+
}), {
|
|
141
|
+
pushingFrequency,
|
|
142
|
+
metrics: metrics
|
|
143
|
+
});
|
|
144
|
+
// Create and start the balance tracker if metrics are enabled
|
|
145
|
+
if (metrics) {
|
|
146
|
+
const balanceTracker = (0, _balancetracker.createAptosBalanceTracker)({
|
|
147
|
+
address: account.address().toString(),
|
|
148
|
+
endpoint,
|
|
149
|
+
network: "aptos",
|
|
150
|
+
updateInterval: pushingFrequency,
|
|
151
|
+
metrics,
|
|
152
|
+
logger: logger.child({
|
|
153
|
+
module: "AptosBalanceTracker"
|
|
154
|
+
})
|
|
155
|
+
});
|
|
156
|
+
// Start the balance tracker
|
|
157
|
+
await balanceTracker.start();
|
|
158
|
+
}
|
|
159
|
+
void controller.start();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Options } from "yargs";
|
|
1
|
+
import type { Options } from "yargs";
|
|
2
2
|
declare const _default: {
|
|
3
3
|
command: string;
|
|
4
4
|
describe: string;
|
|
@@ -19,4 +19,3 @@ declare const _default: {
|
|
|
19
19
|
handler: (argv: any) => Promise<void>;
|
|
20
20
|
};
|
|
21
21
|
export default _default;
|
|
22
|
-
//# sourceMappingURL=command.d.ts.map
|
package/dist/common.cjs
ADDED
package/{lib → dist}/common.d.ts
RENAMED