@typus/typus-sdk 1.9.3 → 1.10.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.
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
2
13
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
14
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
15
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -36,81 +47,259 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
36
47
  }
37
48
  };
38
49
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.createPythClient = createPythClient;
40
- exports.updatePyth = updatePyth;
41
- exports.updateOracleWithPythUsd = updateOracleWithPythUsd;
42
- exports.updatePriceFeeds = updatePriceFeeds;
43
- var pyth_sui_js_1 = require("@pythnetwork/pyth-sui-js");
44
- var constant_1 = require("./constant");
50
+ exports.PythLazerSuiClient = void 0;
45
51
  var constants_1 = require("../../../src/constants");
46
- function createPythClient(provider, network) {
47
- var client = new pyth_sui_js_1.SuiPythClient(provider, constant_1.pythStateId[network], constant_1.wormholeStateId[network]);
48
- var connection = network == "MAINNET"
49
- ? new pyth_sui_js_1.SuiPriceServiceConnection("https://hermes.pyth.network")
50
- : new pyth_sui_js_1.SuiPriceServiceConnection("https://hermes-beta.pyth.network");
51
- var pythClient = { network: network, client: client, connection: connection };
52
- return pythClient;
53
- }
54
- /**
55
- * @returns priceInfoObjectIds
56
- */
57
- function updatePyth(pythClient, tx, tokens, suiCoin) {
58
- return __awaiter(this, void 0, void 0, function () {
59
- var _priceIDs, priceFeedUpdateData, priceInfoObjectIds;
60
- return __generator(this, function (_a) {
61
- switch (_a.label) {
62
- case 0:
63
- _priceIDs = tokens.map(function (token) { return constant_1.priceIDs[pythClient.network][token]; });
64
- return [4 /*yield*/, pythClient.connection.getPriceFeedsUpdateData(_priceIDs)];
65
- case 1:
66
- priceFeedUpdateData = _a.sent();
67
- return [4 /*yield*/, updatePriceFeeds(pythClient, tx, priceFeedUpdateData, _priceIDs, suiCoin)];
68
- case 2:
69
- priceInfoObjectIds = _a.sent();
70
- return [2 /*return*/, priceInfoObjectIds];
71
- }
72
- });
73
- });
74
- }
75
- function updateOracleWithPythUsd(pythClient, tx, oraclePackage, baseToken) {
76
- tx.moveCall({
77
- target: "".concat(oraclePackage, "::oracle::update_with_pyth_usd"),
78
- typeArguments: [],
79
- arguments: [
80
- tx.object(constants_1.oracle[pythClient.network][baseToken]),
81
- tx.object(constant_1.pythStateId[pythClient.network]),
82
- tx.object(constant_1.priceInfoObjectIds[pythClient.network][baseToken]),
83
- tx.object("0x6"),
84
- ],
52
+ var constant_1 = require("./constant");
53
+ var DEFAULT_PROPERTIES = ["price", "emaPrice", "exponent", "feedUpdateTimestamp"];
54
+ // The mainnet oracle reads `fixed_rate@1000ms`, and that channel is only served
55
+ // by the Lazer *v2* Move API (`..._v2` entries) — the v1 entries are pinned to
56
+ // the 200ms channel. Changing one without the other aborts on-chain.
57
+ var DEFAULT_CHANNEL = "fixed_rate@1000ms";
58
+ var DEFAULT_MOVE_API = "v2";
59
+ // Signed (`leEcdsa`) payloads come from the legacy Lazer host, not the Pro `/v1`
60
+ // API — the Pro OpenAPI exposes no signed-payload route at all.
61
+ var DEFAULT_REST_ENDPOINT = "https://pyth-lazer-0.dourolabs.app";
62
+ /** Browser-safe: hex -> bytes without Node's Buffer. */
63
+ var hexToBytes = function (hex) {
64
+ var clean = hex.startsWith("0x") ? hex.slice(2) : hex;
65
+ if (clean.length % 2 !== 0) {
66
+ throw new Error("Pyth Lazer leEcdsa payload is not valid hex (odd length)");
67
+ }
68
+ var out = new Uint8Array(clean.length / 2);
69
+ for (var i = 0; i < out.length; i++) {
70
+ out[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
71
+ }
72
+ return out;
73
+ };
74
+ var PythLazerSuiClient = /** @class */ (function () {
75
+ function PythLazerSuiClient(opts) {
76
+ var _a, _b, _c, _d;
77
+ this.lazer = opts.lazer;
78
+ this.getAccessToken = opts.getAccessToken;
79
+ // A missing price source is only fatal when a price is actually fetched
80
+ // — plenty of callers construct a client purely for read-only views.
81
+ this.restEndpoint = ((_a = opts.restEndpoint) !== null && _a !== void 0 ? _a : DEFAULT_REST_ENDPOINT).replace(/\/+$/, "");
82
+ this.channel = (_b = opts.channel) !== null && _b !== void 0 ? _b : DEFAULT_CHANNEL;
83
+ this.moveApiVersion = (_c = opts.moveApiVersion) !== null && _c !== void 0 ? _c : DEFAULT_MOVE_API;
84
+ this.sui = opts.sui;
85
+ this.network = opts.network;
86
+ this.lazerPackage = opts.lazerPackage;
87
+ this.oraclePackage = opts.oraclePackage;
88
+ this.oracleV2Id = opts.oracleV2Id;
89
+ this.stateObjectId = (_d = opts.stateObjectId) !== null && _d !== void 0 ? _d : constant_1.lazerStateId[opts.network];
90
+ if (!this.stateObjectId) {
91
+ throw new Error("No Pyth Lazer state object id mapped for ".concat(opts.network));
92
+ }
93
+ }
94
+ Object.defineProperty(PythLazerSuiClient.prototype, "v", {
95
+ /** `_v2` suffix on the Lazer/oracle entries when on the v2 Move API. */
96
+ get: function () {
97
+ return this.moveApiVersion === "v2" ? "_v2" : "";
98
+ },
99
+ enumerable: false,
100
+ configurable: true
85
101
  });
86
- }
87
- /**
88
- * Adds the necessary commands for updating the pyth price feeds to the transaction block.
89
- * @param tx transaction block to add commands to
90
- * @param updates array of price feed updates received from the price service
91
- * @param feedIds array of feed ids to update (in hex format)
92
- */
93
- function updatePriceFeeds(pythClient, tx, updates, feedIds, suiCoin) {
94
- return __awaiter(this, void 0, void 0, function () {
95
- var packageId, priceUpdatesHotPotato, baseUpdateFee, coins;
96
- return __generator(this, function (_a) {
97
- switch (_a.label) {
98
- case 0: return [4 /*yield*/, pythClient.client.getPythPackageId()];
99
- case 1:
100
- packageId = _a.sent();
101
- return [4 /*yield*/, pythClient.client.verifyVaasAndGetHotPotato(tx, updates, packageId)];
102
- case 2:
103
- priceUpdatesHotPotato = _a.sent();
104
- return [4 /*yield*/, pythClient.client.getBaseUpdateFee()];
105
- case 3:
106
- baseUpdateFee = _a.sent();
107
- coins = tx.splitCoins(suiCoin !== null && suiCoin !== void 0 ? suiCoin : tx.gas, feedIds.map(function () { return tx.pure.u64(baseUpdateFee); }));
108
- if (suiCoin) {
109
- tx.moveCall({ target: "0x2::coin::destroy_zero", arguments: [suiCoin], typeArguments: [constants_1.tokenType.MAINNET.SUI] });
110
- }
111
- return [4 /*yield*/, pythClient.client.executePriceFeedUpdates(tx, packageId, feedIds, priceUpdatesHotPotato, coins)];
112
- case 4: return [2 /*return*/, _a.sent()];
102
+ /**
103
+ * Fetches a signed `leEcdsa` update over REST using a short-lived token.
104
+ * Mirrors typus-rust `lazer_ingestion::rest_client`.
105
+ */
106
+ PythLazerSuiClient.prototype.fetchUpdateOverRest = function (priceFeedIds, timestamp) {
107
+ return __awaiter(this, void 0, void 0, function () {
108
+ var token, path, res, _a, _b, _c;
109
+ return __generator(this, function (_d) {
110
+ switch (_d.label) {
111
+ case 0: return [4 /*yield*/, this.getAccessToken()];
112
+ case 1:
113
+ token = _d.sent();
114
+ path = timestamp ? "v1/price" : "v1/latest_price";
115
+ return [4 /*yield*/, fetch("".concat(this.restEndpoint, "/").concat(path), {
116
+ method: "POST",
117
+ headers: { Authorization: "Bearer ".concat(token), "Content-Type": "application/json" },
118
+ body: JSON.stringify(__assign({ priceFeedIds: priceFeedIds, properties: DEFAULT_PROPERTIES, formats: ["leEcdsa"], jsonBinaryEncoding: "hex", channel: this.channel }, (timestamp ? { timestamp: timestamp * 1000000 } : {}))),
119
+ })];
120
+ case 2:
121
+ res = _d.sent();
122
+ if (!!res.ok) return [3 /*break*/, 4];
123
+ _a = Error.bind;
124
+ _c = (_b = "Pyth Lazer ".concat(path, " ").concat(res.status, ": ")).concat;
125
+ return [4 /*yield*/, res.text()];
126
+ case 3: throw new (_a.apply(Error, [void 0, _c.apply(_b, [_d.sent()])]))();
127
+ case 4: return [4 /*yield*/, res.json()];
128
+ case 5: return [2 /*return*/, (_d.sent())];
129
+ }
130
+ });
131
+ });
132
+ };
133
+ PythLazerSuiClient.prototype.resolveFeedIds = function (tokens) {
134
+ return tokens.map(function (token) {
135
+ var id = constant_1.feedIds[token];
136
+ if (id == null) {
137
+ throw new Error("No Pyth Lazer feed id mapped for token ".concat(token));
113
138
  }
139
+ return id;
114
140
  });
115
- });
116
- }
141
+ };
142
+ PythLazerSuiClient.prototype.resolveTypeArgument = function (token, override) {
143
+ var typeArg = override !== null && override !== void 0 ? override : constants_1.tokenType[this.network][token];
144
+ if (!typeArg) {
145
+ throw new Error("No coin type mapped for ".concat(token, " on ").concat(this.network));
146
+ }
147
+ return typeArg;
148
+ };
149
+ /**
150
+ * Fetches a Pyth Lazer `leEcdsa` update covering every requested token, adds
151
+ * `pyth_lazer::parse_and_verify_le_ecdsa_update` to the PTB, and returns the
152
+ * verified `Update` argument for use in subsequent move calls.
153
+ */
154
+ PythLazerSuiClient.prototype.updatePythLazer = function (tx, tokens, timestamp) {
155
+ return __awaiter(this, void 0, void 0, function () {
156
+ var priceFeedIds, update, hex, u8array;
157
+ var _a;
158
+ return __generator(this, function (_b) {
159
+ switch (_b.label) {
160
+ case 0:
161
+ priceFeedIds = this.resolveFeedIds(tokens);
162
+ if (!this.lazer && !this.getAccessToken) {
163
+ throw new Error("PythLazerSuiClient needs either `lazer` (WS) or `getAccessToken` (REST) to fetch prices");
164
+ }
165
+ if (!this.getAccessToken) return [3 /*break*/, 2];
166
+ return [4 /*yield*/, this.fetchUpdateOverRest(priceFeedIds, timestamp)];
167
+ case 1:
168
+ update = _b.sent();
169
+ return [3 /*break*/, 6];
170
+ case 2:
171
+ if (!timestamp) return [3 /*break*/, 4];
172
+ return [4 /*yield*/, this.lazer.getPrice({
173
+ priceFeedIds: priceFeedIds,
174
+ properties: DEFAULT_PROPERTIES,
175
+ formats: ["leEcdsa"],
176
+ jsonBinaryEncoding: "hex",
177
+ channel: this.channel,
178
+ timestamp: timestamp * 1000000, // seconds -> nanoseconds
179
+ })];
180
+ case 3:
181
+ update = _b.sent();
182
+ return [3 /*break*/, 6];
183
+ case 4: return [4 /*yield*/, this.lazer.getLatestPrice({
184
+ priceFeedIds: priceFeedIds,
185
+ properties: DEFAULT_PROPERTIES,
186
+ formats: ["leEcdsa"],
187
+ jsonBinaryEncoding: "hex",
188
+ channel: this.channel,
189
+ })];
190
+ case 5:
191
+ update = _b.sent();
192
+ _b.label = 6;
193
+ case 6:
194
+ hex = (_a = update.leEcdsa) === null || _a === void 0 ? void 0 : _a.data;
195
+ if (!hex) {
196
+ throw new Error("Pyth Lazer getLatestPrice returned no leEcdsa payload");
197
+ }
198
+ u8array = hexToBytes(hex);
199
+ return [2 /*return*/, tx.moveCall({
200
+ target: "".concat(this.lazerPackage, "::pyth_lazer::parse_and_verify_le_ecdsa_update").concat(this.v),
201
+ arguments: [tx.object(this.stateObjectId), tx.object.clock(), tx.pure.vector("u8", u8array)],
202
+ })];
203
+ }
204
+ });
205
+ });
206
+ };
207
+ /**
208
+ * Calls `oracle_v2::update_latest_price(oracle_v2, update, clock)`. A single
209
+ * call updates every feed in the verified update.
210
+ */
211
+ PythLazerSuiClient.prototype.updateOracleV2WithPythLazer = function (tx, tokens) {
212
+ return __awaiter(this, void 0, void 0, function () {
213
+ var verifiedUpdate;
214
+ return __generator(this, function (_a) {
215
+ switch (_a.label) {
216
+ case 0: return [4 /*yield*/, this.updatePythLazer(tx, tokens)];
217
+ case 1:
218
+ verifiedUpdate = _a.sent();
219
+ tx.moveCall({
220
+ target: "".concat(this.oraclePackage, "::oracle_v2::update_latest_price").concat(this.v),
221
+ typeArguments: [],
222
+ arguments: [tx.object(this.oracleV2Id), verifiedUpdate, tx.object("0x6")],
223
+ });
224
+ return [2 /*return*/];
225
+ }
226
+ });
227
+ });
228
+ };
229
+ /**
230
+ * Calls `oracle_v2::update_settle_price(oracle_v2, update, timestamp, clock)`.
231
+ * Authority-gated on-chain. `timestamp` is in seconds.
232
+ */
233
+ PythLazerSuiClient.prototype.updateOracleV2SettlePriceWithPythLazer = function (tx, tokens, timestamp) {
234
+ return __awaiter(this, void 0, void 0, function () {
235
+ var verifiedUpdate;
236
+ return __generator(this, function (_a) {
237
+ switch (_a.label) {
238
+ case 0: return [4 /*yield*/, this.updatePythLazer(tx, tokens, timestamp)];
239
+ case 1:
240
+ verifiedUpdate = _a.sent();
241
+ tx.moveCall({
242
+ target: "".concat(this.oraclePackage, "::oracle_v2::update_settle_price").concat(this.v),
243
+ typeArguments: [],
244
+ arguments: [tx.object(this.oracleV2Id), verifiedUpdate, tx.pure.u64(timestamp), tx.object("0x6")],
245
+ });
246
+ return [2 /*return*/];
247
+ }
248
+ });
249
+ });
250
+ };
251
+ /**
252
+ * Calls `oracle_v2::add_latest_price_new_token<TOKEN>(oracle_v2, feed_id, update, clock)`.
253
+ * One-shot initializer for a brand-new token in the oracle's `feed_id_map`.
254
+ */
255
+ PythLazerSuiClient.prototype.addLatestPriceNewToken = function (tx, token, tokenTypeArgument) {
256
+ return __awaiter(this, void 0, void 0, function () {
257
+ var feedId, typeArg, verifiedUpdate;
258
+ return __generator(this, function (_a) {
259
+ switch (_a.label) {
260
+ case 0:
261
+ feedId = this.resolveFeedIds([token])[0];
262
+ typeArg = this.resolveTypeArgument(token, tokenTypeArgument);
263
+ return [4 /*yield*/, this.updatePythLazer(tx, [token])];
264
+ case 1:
265
+ verifiedUpdate = _a.sent();
266
+ tx.moveCall({
267
+ target: "".concat(this.oraclePackage, "::oracle_v2::add_latest_price_new_token").concat(this.v),
268
+ typeArguments: [typeArg],
269
+ arguments: [tx.object(this.oracleV2Id), tx.pure.u32(feedId), verifiedUpdate, tx.object("0x6")],
270
+ });
271
+ return [2 /*return*/];
272
+ }
273
+ });
274
+ });
275
+ };
276
+ /**
277
+ * Calls `oracle_v2::add_settle_price_new_token<TOKEN>(oracle_v2, feed_id, update, timestamp, clock)`.
278
+ * `timestamp` (seconds) is forwarded to the Lazer fetch when supplied and
279
+ * passed on-chain; defaults to now, matching the freshness window.
280
+ */
281
+ PythLazerSuiClient.prototype.addSettlePriceNewToken = function (tx, token, timestamp, tokenTypeArgument) {
282
+ return __awaiter(this, void 0, void 0, function () {
283
+ var feedId, typeArg, ts, verifiedUpdate;
284
+ return __generator(this, function (_a) {
285
+ switch (_a.label) {
286
+ case 0:
287
+ feedId = this.resolveFeedIds([token])[0];
288
+ typeArg = this.resolveTypeArgument(token, tokenTypeArgument);
289
+ ts = timestamp !== null && timestamp !== void 0 ? timestamp : Math.floor(Date.now() / 1000);
290
+ return [4 /*yield*/, this.updatePythLazer(tx, [token], timestamp)];
291
+ case 1:
292
+ verifiedUpdate = _a.sent();
293
+ tx.moveCall({
294
+ target: "".concat(this.oraclePackage, "::oracle_v2::add_settle_price_new_token").concat(this.v),
295
+ typeArguments: [typeArg],
296
+ arguments: [tx.object(this.oracleV2Id), tx.pure.u32(feedId), verifiedUpdate, tx.pure.u64(ts), tx.object("0x6")],
297
+ });
298
+ return [2 /*return*/];
299
+ }
300
+ });
301
+ });
302
+ };
303
+ return PythLazerSuiClient;
304
+ }());
305
+ exports.PythLazerSuiClient = PythLazerSuiClient;
package/package.json CHANGED
@@ -2,12 +2,13 @@
2
2
  "name": "@typus/typus-sdk",
3
3
  "author": "Typus",
4
4
  "description": "typus sdk",
5
- "version": "1.9.3",
5
+ "version": "1.10.0",
6
6
  "dependencies": {
7
7
  "@mysten/bcs": "2.0.3",
8
8
  "@mysten/kiosk": "1.2.0",
9
9
  "@mysten/sui": "2.13.2",
10
- "@pythnetwork/pyth-sui-js": "2.3.0",
10
+ "@pythnetwork/pyth-lazer-sdk": "6.2.2",
11
+ "@pythnetwork/pyth-lazer-sui-js": "0.3.0",
11
12
  "@shinami/clients": "0.10.0",
12
13
  "bignumber.js": "^9.1.2",
13
14
  "camelcase-keys-deep": "^0.1.0",
@@ -51,4 +52,4 @@
51
52
  "url": "https://github.com/Typus-Lab/typus-sdk/issues"
52
53
  },
53
54
  "homepage": "https://github.com/Typus-Lab/typus-sdk#readme"
54
- }
55
+ }