@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/dist/metrics.cjs
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/no-misused-promises */ "use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "PricePusherMetrics", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return PricePusherMetrics;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _express = /*#__PURE__*/ _interop_require_default(require("express"));
|
|
12
|
+
const _promclient = require("prom-client");
|
|
13
|
+
const _priceconfig = require("./price-config.cjs");
|
|
14
|
+
function _interop_require_default(obj) {
|
|
15
|
+
return obj && obj.__esModule ? obj : {
|
|
16
|
+
default: obj
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
class PricePusherMetrics {
|
|
20
|
+
registry;
|
|
21
|
+
server;
|
|
22
|
+
logger;
|
|
23
|
+
// Metrics for price feed updates
|
|
24
|
+
lastPublishedTime;
|
|
25
|
+
priceUpdateAttempts;
|
|
26
|
+
priceFeedsTotal;
|
|
27
|
+
sourceTimestamp;
|
|
28
|
+
configuredTimeDifference;
|
|
29
|
+
sourcePriceValue;
|
|
30
|
+
targetPriceValue;
|
|
31
|
+
// Wallet metrics
|
|
32
|
+
walletBalance;
|
|
33
|
+
constructor(logger){
|
|
34
|
+
this.logger = logger;
|
|
35
|
+
this.registry = new _promclient.Registry();
|
|
36
|
+
this.server = (0, _express.default)();
|
|
37
|
+
// Register the default metrics (memory, CPU, etc.)
|
|
38
|
+
this.registry.setDefaultLabels({
|
|
39
|
+
app: "price_pusher"
|
|
40
|
+
});
|
|
41
|
+
// Create metrics
|
|
42
|
+
this.lastPublishedTime = new _promclient.Gauge({
|
|
43
|
+
name: "pyth_price_last_published_time",
|
|
44
|
+
help: "The last published time of a price feed in unix timestamp",
|
|
45
|
+
labelNames: [
|
|
46
|
+
"price_id",
|
|
47
|
+
"alias"
|
|
48
|
+
],
|
|
49
|
+
registers: [
|
|
50
|
+
this.registry
|
|
51
|
+
]
|
|
52
|
+
});
|
|
53
|
+
this.priceUpdateAttempts = new _promclient.Counter({
|
|
54
|
+
name: "pyth_price_update_attempts_total",
|
|
55
|
+
help: "Total number of price update attempts with their trigger condition and status",
|
|
56
|
+
labelNames: [
|
|
57
|
+
"price_id",
|
|
58
|
+
"alias",
|
|
59
|
+
"trigger",
|
|
60
|
+
"status"
|
|
61
|
+
],
|
|
62
|
+
registers: [
|
|
63
|
+
this.registry
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
this.priceFeedsTotal = new _promclient.Gauge({
|
|
67
|
+
name: "pyth_price_feeds_total",
|
|
68
|
+
help: "Total number of price feeds being monitored",
|
|
69
|
+
registers: [
|
|
70
|
+
this.registry
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
this.sourceTimestamp = new _promclient.Gauge({
|
|
74
|
+
name: "pyth_source_timestamp",
|
|
75
|
+
help: "Latest source chain price publish timestamp",
|
|
76
|
+
labelNames: [
|
|
77
|
+
"price_id",
|
|
78
|
+
"alias"
|
|
79
|
+
],
|
|
80
|
+
registers: [
|
|
81
|
+
this.registry
|
|
82
|
+
]
|
|
83
|
+
});
|
|
84
|
+
this.configuredTimeDifference = new _promclient.Gauge({
|
|
85
|
+
name: "pyth_configured_time_difference",
|
|
86
|
+
help: "Configured time difference threshold between source and target chains",
|
|
87
|
+
labelNames: [
|
|
88
|
+
"price_id",
|
|
89
|
+
"alias"
|
|
90
|
+
],
|
|
91
|
+
registers: [
|
|
92
|
+
this.registry
|
|
93
|
+
]
|
|
94
|
+
});
|
|
95
|
+
this.sourcePriceValue = new _promclient.Gauge({
|
|
96
|
+
name: "pyth_source_price",
|
|
97
|
+
help: "Latest price value from Pyth source",
|
|
98
|
+
labelNames: [
|
|
99
|
+
"price_id",
|
|
100
|
+
"alias"
|
|
101
|
+
],
|
|
102
|
+
registers: [
|
|
103
|
+
this.registry
|
|
104
|
+
]
|
|
105
|
+
});
|
|
106
|
+
this.targetPriceValue = new _promclient.Gauge({
|
|
107
|
+
name: "pyth_target_price",
|
|
108
|
+
help: "Latest price value from target chain",
|
|
109
|
+
labelNames: [
|
|
110
|
+
"price_id",
|
|
111
|
+
"alias"
|
|
112
|
+
],
|
|
113
|
+
registers: [
|
|
114
|
+
this.registry
|
|
115
|
+
]
|
|
116
|
+
});
|
|
117
|
+
// Wallet balance metric
|
|
118
|
+
this.walletBalance = new _promclient.Gauge({
|
|
119
|
+
name: "pyth_wallet_balance",
|
|
120
|
+
help: "Current wallet balance of the price pusher in native token units",
|
|
121
|
+
labelNames: [
|
|
122
|
+
"wallet_address",
|
|
123
|
+
"network"
|
|
124
|
+
],
|
|
125
|
+
registers: [
|
|
126
|
+
this.registry
|
|
127
|
+
]
|
|
128
|
+
});
|
|
129
|
+
// Setup the metrics endpoint
|
|
130
|
+
this.server.get("/metrics", async (_, res)=>{
|
|
131
|
+
res.set("Content-Type", this.registry.contentType);
|
|
132
|
+
res.end(await this.registry.metrics());
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
// Start the metrics server
|
|
136
|
+
start(port) {
|
|
137
|
+
this.server.listen(port, ()=>{
|
|
138
|
+
this.logger.info(`Metrics server started on port ${port}`);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// Record a successful price update
|
|
142
|
+
recordPriceUpdate(priceId, alias, trigger = "yes") {
|
|
143
|
+
this.priceUpdateAttempts.inc({
|
|
144
|
+
price_id: priceId,
|
|
145
|
+
alias,
|
|
146
|
+
trigger: trigger.toLowerCase(),
|
|
147
|
+
status: "success"
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
// Record update condition status (YES/NO/EARLY)
|
|
151
|
+
recordUpdateCondition(priceId, alias, condition) {
|
|
152
|
+
const triggerLabel = _priceconfig.UpdateCondition[condition].toLowerCase();
|
|
153
|
+
// Only record as 'skipped' when the condition is NO
|
|
154
|
+
if (condition === _priceconfig.UpdateCondition.NO) {
|
|
155
|
+
this.priceUpdateAttempts.inc({
|
|
156
|
+
price_id: priceId,
|
|
157
|
+
alias,
|
|
158
|
+
trigger: triggerLabel,
|
|
159
|
+
status: "skipped"
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// YES and EARLY don't increment the counter here - they'll be counted
|
|
163
|
+
// when recordPriceUpdate or recordPriceUpdateError is called
|
|
164
|
+
}
|
|
165
|
+
// Record a price update error
|
|
166
|
+
recordPriceUpdateError(priceId, alias, trigger = "yes") {
|
|
167
|
+
this.priceUpdateAttempts.inc({
|
|
168
|
+
price_id: priceId,
|
|
169
|
+
alias,
|
|
170
|
+
trigger: trigger.toLowerCase(),
|
|
171
|
+
status: "error"
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
// Set the number of price feeds
|
|
175
|
+
setPriceFeedsTotal(count) {
|
|
176
|
+
this.priceFeedsTotal.set(count);
|
|
177
|
+
}
|
|
178
|
+
// Update source, target and configured time difference timestamps
|
|
179
|
+
updateTimestamps(priceId, alias, targetLatestPricePublishTime, sourceLatestPricePublishTime, priceConfigTimeDifference) {
|
|
180
|
+
this.sourceTimestamp.set({
|
|
181
|
+
price_id: priceId,
|
|
182
|
+
alias
|
|
183
|
+
}, sourceLatestPricePublishTime);
|
|
184
|
+
this.lastPublishedTime.set({
|
|
185
|
+
price_id: priceId,
|
|
186
|
+
alias
|
|
187
|
+
}, targetLatestPricePublishTime);
|
|
188
|
+
this.configuredTimeDifference.set({
|
|
189
|
+
price_id: priceId,
|
|
190
|
+
alias
|
|
191
|
+
}, priceConfigTimeDifference);
|
|
192
|
+
}
|
|
193
|
+
// Update price values
|
|
194
|
+
updatePriceValues(priceId, alias, sourcePrice, targetPrice) {
|
|
195
|
+
if (sourcePrice !== undefined) {
|
|
196
|
+
this.sourcePriceValue.set({
|
|
197
|
+
price_id: priceId,
|
|
198
|
+
alias
|
|
199
|
+
}, Number(sourcePrice));
|
|
200
|
+
}
|
|
201
|
+
if (targetPrice !== undefined) {
|
|
202
|
+
this.targetPriceValue.set({
|
|
203
|
+
price_id: priceId,
|
|
204
|
+
alias
|
|
205
|
+
}, Number(targetPrice));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Update wallet balance
|
|
209
|
+
updateWalletBalance(walletAddress, network, balance) {
|
|
210
|
+
// Convert to number for compatibility with prometheus
|
|
211
|
+
const balanceNum = typeof balance === "bigint" ? Number(balance) / 1e18 : balance;
|
|
212
|
+
this.walletBalance.set({
|
|
213
|
+
wallet_address: walletAddress,
|
|
214
|
+
network
|
|
215
|
+
}, balanceNum);
|
|
216
|
+
this.logger.debug(`Updated wallet balance metric: ${walletAddress} = ${balanceNum}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
import type { Logger } from "pino";
|
|
1
2
|
import { Counter, Gauge } from "prom-client";
|
|
2
|
-
import {
|
|
3
|
-
import { UpdateCondition } from "./price-config";
|
|
3
|
+
import { UpdateCondition } from "./price-config.js";
|
|
4
4
|
export declare class PricePusherMetrics {
|
|
5
5
|
private registry;
|
|
6
6
|
private server;
|
|
7
7
|
private logger;
|
|
8
|
-
lastPublishedTime: Gauge
|
|
9
|
-
priceUpdateAttempts: Counter
|
|
10
|
-
priceFeedsTotal: Gauge
|
|
11
|
-
sourceTimestamp: Gauge
|
|
12
|
-
configuredTimeDifference: Gauge
|
|
13
|
-
|
|
8
|
+
lastPublishedTime: Gauge;
|
|
9
|
+
priceUpdateAttempts: Counter;
|
|
10
|
+
priceFeedsTotal: Gauge;
|
|
11
|
+
sourceTimestamp: Gauge;
|
|
12
|
+
configuredTimeDifference: Gauge;
|
|
13
|
+
sourcePriceValue: Gauge;
|
|
14
|
+
targetPriceValue: Gauge;
|
|
15
|
+
walletBalance: Gauge;
|
|
14
16
|
constructor(logger: Logger);
|
|
15
17
|
start(port: number): void;
|
|
16
18
|
recordPriceUpdate(priceId: string, alias: string, trigger?: string): void;
|
|
@@ -18,6 +20,6 @@ export declare class PricePusherMetrics {
|
|
|
18
20
|
recordPriceUpdateError(priceId: string, alias: string, trigger?: string): void;
|
|
19
21
|
setPriceFeedsTotal(count: number): void;
|
|
20
22
|
updateTimestamps(priceId: string, alias: string, targetLatestPricePublishTime: number, sourceLatestPricePublishTime: number, priceConfigTimeDifference: number): void;
|
|
23
|
+
updatePriceValues(priceId: string, alias: string, sourcePrice: string | undefined, targetPrice: string | undefined): void;
|
|
21
24
|
updateWalletBalance(walletAddress: string, network: string, balance: bigint | number): void;
|
|
22
25
|
}
|
|
23
|
-
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/* 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 _hermesclient = require("@pythnetwork/hermes-client");
|
|
12
|
+
const _pino = require("pino");
|
|
13
|
+
const _controller = require("../controller.cjs");
|
|
14
|
+
const _options = /*#__PURE__*/ _interop_require_wildcard(require("../options.cjs"));
|
|
15
|
+
const _priceconfig = require("../price-config.cjs");
|
|
16
|
+
const _pythpricelistener = require("../pyth-price-listener.cjs");
|
|
17
|
+
const _near = require("./near.cjs");
|
|
18
|
+
const _utils = require("../utils.cjs");
|
|
19
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
20
|
+
if (typeof WeakMap !== "function") return null;
|
|
21
|
+
var cacheBabelInterop = new WeakMap();
|
|
22
|
+
var cacheNodeInterop = new WeakMap();
|
|
23
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
24
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
25
|
+
})(nodeInterop);
|
|
26
|
+
}
|
|
27
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
28
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
29
|
+
return obj;
|
|
30
|
+
}
|
|
31
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
32
|
+
return {
|
|
33
|
+
default: obj
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
37
|
+
if (cache && cache.has(obj)) {
|
|
38
|
+
return cache.get(obj);
|
|
39
|
+
}
|
|
40
|
+
var newObj = {
|
|
41
|
+
__proto__: null
|
|
42
|
+
};
|
|
43
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
44
|
+
for(var key in obj){
|
|
45
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
46
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
47
|
+
if (desc && (desc.get || desc.set)) {
|
|
48
|
+
Object.defineProperty(newObj, key, desc);
|
|
49
|
+
} else {
|
|
50
|
+
newObj[key] = obj[key];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
newObj.default = obj;
|
|
55
|
+
if (cache) {
|
|
56
|
+
cache.set(obj, newObj);
|
|
57
|
+
}
|
|
58
|
+
return newObj;
|
|
59
|
+
}
|
|
60
|
+
const _default = {
|
|
61
|
+
command: "near",
|
|
62
|
+
describe: "run price pusher for near",
|
|
63
|
+
builder: {
|
|
64
|
+
"node-url": {
|
|
65
|
+
description: "NEAR RPC API url. used to make JSON RPC calls to interact with NEAR.",
|
|
66
|
+
type: "string",
|
|
67
|
+
required: true
|
|
68
|
+
},
|
|
69
|
+
network: {
|
|
70
|
+
description: "testnet or mainnet.",
|
|
71
|
+
type: "string",
|
|
72
|
+
required: true
|
|
73
|
+
},
|
|
74
|
+
"account-id": {
|
|
75
|
+
description: "payer account identifier.",
|
|
76
|
+
type: "string",
|
|
77
|
+
required: true
|
|
78
|
+
},
|
|
79
|
+
"private-key-path": {
|
|
80
|
+
description: "path to payer private key file.",
|
|
81
|
+
type: "string",
|
|
82
|
+
required: false
|
|
83
|
+
},
|
|
84
|
+
..._options.priceConfigFile,
|
|
85
|
+
..._options.priceServiceEndpoint,
|
|
86
|
+
..._options.pythContractAddress,
|
|
87
|
+
..._options.pollingFrequency,
|
|
88
|
+
..._options.pushingFrequency,
|
|
89
|
+
..._options.logLevel,
|
|
90
|
+
..._options.controllerLogLevel
|
|
91
|
+
},
|
|
92
|
+
handler: async function(argv) {
|
|
93
|
+
// FIXME: type checks for this
|
|
94
|
+
const { nodeUrl, network, accountId, privateKeyPath, priceConfigFile, priceServiceEndpoint, pythContractAddress, pushingFrequency, pollingFrequency, logLevel, controllerLogLevel } = argv;
|
|
95
|
+
const logger = (0, _pino.pino)({
|
|
96
|
+
level: logLevel
|
|
97
|
+
});
|
|
98
|
+
const priceConfigs = (0, _priceconfig.readPriceConfigFile)(priceConfigFile);
|
|
99
|
+
const hermesClient = new _hermesclient.HermesClient(priceServiceEndpoint);
|
|
100
|
+
let priceItems = priceConfigs.map(({ id, alias })=>({
|
|
101
|
+
id,
|
|
102
|
+
alias
|
|
103
|
+
}));
|
|
104
|
+
// Better to filter out invalid price items before creating the pyth listener
|
|
105
|
+
const { existingPriceItems, invalidPriceItems } = await (0, _utils.filterInvalidPriceItems)(hermesClient, priceItems);
|
|
106
|
+
if (invalidPriceItems.length > 0) {
|
|
107
|
+
logger.error(`Invalid price id submitted for: ${invalidPriceItems.map(({ alias })=>alias).join(", ")}`);
|
|
108
|
+
}
|
|
109
|
+
priceItems = existingPriceItems;
|
|
110
|
+
const pythListener = new _pythpricelistener.PythPriceListener(hermesClient, priceItems, logger);
|
|
111
|
+
const nearAccount = new _near.NearAccount(network, accountId, nodeUrl, privateKeyPath, pythContractAddress);
|
|
112
|
+
const nearListener = new _near.NearPriceListener(nearAccount, priceItems, logger.child({
|
|
113
|
+
module: "NearPriceListener"
|
|
114
|
+
}), {
|
|
115
|
+
pollingFrequency
|
|
116
|
+
});
|
|
117
|
+
const nearPusher = new _near.NearPricePusher(nearAccount, hermesClient, logger.child({
|
|
118
|
+
module: "NearPricePusher"
|
|
119
|
+
}));
|
|
120
|
+
const controller = new _controller.Controller(priceConfigs, pythListener, nearListener, nearPusher, logger.child({
|
|
121
|
+
module: "Controller"
|
|
122
|
+
}, {
|
|
123
|
+
level: controllerLogLevel
|
|
124
|
+
}), {
|
|
125
|
+
pushingFrequency
|
|
126
|
+
});
|
|
127
|
+
void controller.start();
|
|
128
|
+
}
|
|
129
|
+
};
|
|
@@ -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;
|
|
@@ -18,4 +18,3 @@ declare const _default: {
|
|
|
18
18
|
handler: (argv: any) => Promise<void>;
|
|
19
19
|
};
|
|
20
20
|
export default _default;
|
|
21
|
-
//# sourceMappingURL=command.d.ts.map
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable unicorn/no-array-reduce */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* 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 NearAccount () {
|
|
13
|
+
return NearAccount;
|
|
14
|
+
},
|
|
15
|
+
get NearPriceListener () {
|
|
16
|
+
return NearPriceListener;
|
|
17
|
+
},
|
|
18
|
+
get NearPricePusher () {
|
|
19
|
+
return NearPricePusher;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
const _nodefs = /*#__PURE__*/ _interop_require_default(require("node:fs"));
|
|
23
|
+
const _nodeos = /*#__PURE__*/ _interop_require_default(require("node:os"));
|
|
24
|
+
const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path"));
|
|
25
|
+
const _nearapijs = require("near-api-js");
|
|
26
|
+
const _key_stores = require("near-api-js/lib/key_stores");
|
|
27
|
+
const _interface = require("../interface.cjs");
|
|
28
|
+
function _interop_require_default(obj) {
|
|
29
|
+
return obj && obj.__esModule ? obj : {
|
|
30
|
+
default: obj
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
class NearPriceListener extends _interface.ChainPriceListener {
|
|
34
|
+
account;
|
|
35
|
+
logger;
|
|
36
|
+
constructor(account, priceItems, logger, config){
|
|
37
|
+
super(config.pollingFrequency, priceItems), this.account = account, this.logger = logger;
|
|
38
|
+
}
|
|
39
|
+
async getOnChainPriceInfo(priceId) {
|
|
40
|
+
try {
|
|
41
|
+
const priceRaw = await this.account.getPriceUnsafe(priceId);
|
|
42
|
+
this.logger.debug(`Polled a NEAR on chain price for feed ${this.priceIdToAlias.get(priceId)} (${priceId}) ${JSON.stringify(priceRaw)}.`);
|
|
43
|
+
return priceRaw ? {
|
|
44
|
+
conf: priceRaw.conf,
|
|
45
|
+
price: priceRaw.price,
|
|
46
|
+
publishTime: priceRaw.publish_time
|
|
47
|
+
} : undefined;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
this.logger.error(error, `Polling on-chain price for ${priceId} failed.:`);
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
class NearPricePusher {
|
|
55
|
+
account;
|
|
56
|
+
hermesClient;
|
|
57
|
+
logger;
|
|
58
|
+
constructor(account, hermesClient, logger){
|
|
59
|
+
this.account = account;
|
|
60
|
+
this.hermesClient = hermesClient;
|
|
61
|
+
this.logger = logger;
|
|
62
|
+
}
|
|
63
|
+
async updatePriceFeed(priceIds, pubTimesToPush) {
|
|
64
|
+
if (priceIds.length === 0) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (priceIds.length !== pubTimesToPush.length) throw new Error("Invalid arguments");
|
|
68
|
+
let priceFeedUpdateData;
|
|
69
|
+
try {
|
|
70
|
+
priceFeedUpdateData = await this.getPriceFeedsUpdateData(priceIds);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
this.logger.error(error, "getPriceFeedsUpdateData failed");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
for (const data of priceFeedUpdateData){
|
|
76
|
+
let updateFee;
|
|
77
|
+
try {
|
|
78
|
+
updateFee = await this.account.getUpdateFeeEstimate(data);
|
|
79
|
+
this.logger.debug(`Update fee: ${updateFee}`);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
this.logger.error(error, "getUpdateFeeEstimate failed");
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const outcome = await this.account.updatePriceFeeds(data, updateFee);
|
|
86
|
+
const failureMessages = [];
|
|
87
|
+
const is_success = Object.values(outcome.receipts_outcome).reduce((is_success, receipt)=>{
|
|
88
|
+
if (Object.prototype.hasOwnProperty.call(receipt.outcome.status, "Failure")) {
|
|
89
|
+
failureMessages.push(receipt.outcome.status);
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return is_success;
|
|
93
|
+
}, true);
|
|
94
|
+
if (is_success) {
|
|
95
|
+
this.logger.info({
|
|
96
|
+
hash: outcome.transaction.hash
|
|
97
|
+
}, "updatePriceFeeds successful.");
|
|
98
|
+
} else {
|
|
99
|
+
this.logger.error({
|
|
100
|
+
failureMessages
|
|
101
|
+
}, "updatePriceFeeds failed");
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
this.logger.error(error, "updatePriceFeeds failed");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async getPriceFeedsUpdateData(priceIds) {
|
|
109
|
+
const response = await this.hermesClient.getLatestPriceUpdates(priceIds, {
|
|
110
|
+
encoding: "base64",
|
|
111
|
+
ignoreInvalidPriceIds: true
|
|
112
|
+
});
|
|
113
|
+
return response.binary.data;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
class NearAccount {
|
|
117
|
+
pythAccountId;
|
|
118
|
+
account;
|
|
119
|
+
constructor(network, accountId, nodeUrl, privateKeyPath, pythAccountId){
|
|
120
|
+
this.pythAccountId = pythAccountId;
|
|
121
|
+
const connection = this.getConnection(network, accountId, nodeUrl, privateKeyPath);
|
|
122
|
+
this.account = new _nearapijs.Account(connection, accountId);
|
|
123
|
+
}
|
|
124
|
+
async getPriceUnsafe(priceId) {
|
|
125
|
+
return await this.account.viewFunction({
|
|
126
|
+
contractId: this.pythAccountId,
|
|
127
|
+
methodName: "get_price_unsafe",
|
|
128
|
+
args: {
|
|
129
|
+
price_identifier: priceId
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
async getUpdateFeeEstimate(data) {
|
|
134
|
+
return await this.account.viewFunction({
|
|
135
|
+
contractId: this.pythAccountId,
|
|
136
|
+
methodName: "get_update_fee_estimate",
|
|
137
|
+
args: {
|
|
138
|
+
data
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
async updatePriceFeeds(data, updateFee) {
|
|
143
|
+
return await this.account.functionCall({
|
|
144
|
+
contractId: this.pythAccountId,
|
|
145
|
+
methodName: "update_price_feeds",
|
|
146
|
+
args: {
|
|
147
|
+
data
|
|
148
|
+
},
|
|
149
|
+
gas: "300000000000000",
|
|
150
|
+
attachedDeposit: updateFee
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
getConnection(network, accountId, nodeUrl, privateKeyPath) {
|
|
154
|
+
const content = _nodefs.default.readFileSync(// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
155
|
+
privateKeyPath || _nodepath.default.join(_nodeos.default.homedir(), ".near-credentials", network, accountId + ".json"));
|
|
156
|
+
const accountInfo = JSON.parse(content.toString());
|
|
157
|
+
let privateKey = accountInfo.private_key;
|
|
158
|
+
if (!privateKey && accountInfo.secret_key) {
|
|
159
|
+
privateKey = accountInfo.secret_key;
|
|
160
|
+
}
|
|
161
|
+
if (accountInfo.account_id && privateKey) {
|
|
162
|
+
const keyPair = _nearapijs.KeyPair.fromString(privateKey);
|
|
163
|
+
const keyStore = new _key_stores.InMemoryKeyStore();
|
|
164
|
+
void keyStore.setKey(network, accountInfo.account_id, keyPair);
|
|
165
|
+
return _nearapijs.Connection.fromConfig({
|
|
166
|
+
networkId: network,
|
|
167
|
+
provider: {
|
|
168
|
+
type: "JsonRpcProvider",
|
|
169
|
+
args: {
|
|
170
|
+
url: nodeUrl
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
signer: {
|
|
174
|
+
type: "InMemorySigner",
|
|
175
|
+
keyStore
|
|
176
|
+
},
|
|
177
|
+
jsvmAccountId: `jsvm.${network}`
|
|
178
|
+
});
|
|
179
|
+
} else {
|
|
180
|
+
throw new Error("Invalid key file!");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { IPricePusher, PriceInfo, ChainPriceListener, PriceItem } from "../interface";
|
|
2
1
|
import { HermesClient } from "@pythnetwork/hermes-client";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import type { FinalExecutionOutcome } from "near-api-js/lib/providers/provider";
|
|
3
|
+
import type { Logger } from "pino";
|
|
4
|
+
import type { IPricePusher, PriceInfo, PriceItem } from "../interface.js";
|
|
5
|
+
import { ChainPriceListener } from "../interface.js";
|
|
6
|
+
import type { DurationInSeconds } from "../utils.js";
|
|
6
7
|
export declare class NearPriceListener extends ChainPriceListener {
|
|
7
8
|
private account;
|
|
8
9
|
private logger;
|
|
@@ -28,4 +29,3 @@ export declare class NearAccount {
|
|
|
28
29
|
updatePriceFeeds(data: string, updateFee: any): Promise<FinalExecutionOutcome>;
|
|
29
30
|
private getConnection;
|
|
30
31
|
}
|
|
31
|
-
//# sourceMappingURL=near.d.ts.map
|