UNPKG

@mysten/suins

Version:
269 lines (268 loc) 11 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __typeError = (msg) => { throw TypeError(msg); }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); var pyth_exports = {}; __export(pyth_exports, { SuiPriceServiceConnection: () => SuiPriceServiceConnection, SuiPythClient: () => SuiPythClient }); module.exports = __toCommonJS(pyth_exports); var import_bcs = require("@mysten/sui/bcs"); var import_transactions = require("@mysten/sui/transactions"); var import_utils = require("@mysten/sui/utils"); var import_PriceServiceConnection = require("./PriceServiceConnection.js"); var import_pyth_helpers = require("./pyth-helpers.js"); var _pythPackageId, _wormholePackageId, _priceFeedObjectIdCache, _priceTableInfo, _baseUpdateFee, _SuiPythClient_instances, fetchPriceFeedObjectId_fn, fetchPriceTableInfo_fn, fetchWormholePackageId_fn, fetchPythPackageId_fn, getPackageId_fn, fetchBaseUpdateFee_fn; const MAX_ARGUMENT_SIZE = 16 * 1024; class SuiPriceServiceConnection extends import_PriceServiceConnection.PriceServiceConnection { /** * Fetch price feed update data. * * @param priceIds Array of hex-encoded price IDs. * @returns Array of buffers containing the price update data. */ async getPriceFeedsUpdateData(priceIds) { const latestVaas = await this.getLatestVaas(priceIds); return latestVaas.map((vaa) => (0, import_utils.fromBase64)(vaa)); } } class SuiPythClient { constructor(provider, pythStateId, wormholeStateId) { __privateAdd(this, _SuiPythClient_instances); __privateAdd(this, _pythPackageId); __privateAdd(this, _wormholePackageId); __privateAdd(this, _priceFeedObjectIdCache, /* @__PURE__ */ new Map()); __privateAdd(this, _priceTableInfo); __privateAdd(this, _baseUpdateFee); this.provider = provider; this.pythStateId = pythStateId; this.wormholeStateId = wormholeStateId; } /** * Verifies the VAAs using the Wormhole contract. * * @param vaas Array of VAA buffers to verify. * @param tx Transaction block to add commands to. * @returns Array of verified VAAs. */ async verifyVaas(vaas, tx) { const wormholePackageId = await this.getWormholePackageId(); const verifiedVaas = []; for (const vaa of vaas) { const [verifiedVaa] = tx.moveCall({ target: `${wormholePackageId}::vaa::parse_and_verify`, arguments: [tx.object(this.wormholeStateId), tx.pure.vector("u8", vaa), tx.object.clock()] }); verifiedVaas.push(verifiedVaa); } return verifiedVaas; } /** * Adds the necessary commands for updating the Pyth price feeds to the transaction block. * * @param tx Transaction block to add commands to. * @param updates Array of price feed updates received from the price service. * @param feedIds Array of feed IDs to update (in hex format). */ async updatePriceFeeds(tx, updates, feedIds) { const packageId = await this.getPythPackageId(); let priceUpdatesHotPotato; if (updates.length > 1) { throw new Error( "SDK does not support sending multiple accumulator messages in a single transaction" ); } const vaa = (0, import_pyth_helpers.extractVaaBytesFromAccumulatorMessage)(updates[0]); const verifiedVaas = await this.verifyVaas([vaa], tx); [priceUpdatesHotPotato] = tx.moveCall({ target: `${packageId}::pyth::create_authenticated_price_infos_using_accumulator`, arguments: [ tx.object(this.pythStateId), tx.pure( import_bcs.bcs.vector(import_bcs.bcs.U8).serialize(Array.from(updates[0]), { maxSize: MAX_ARGUMENT_SIZE }).toBytes() ), verifiedVaas[0], tx.object.clock() ] }); const priceInfoObjects = []; const baseUpdateFee = await this.getBaseUpdateFee(); for (const feedId of feedIds) { const priceInfoObjectId = await this.getPriceFeedObjectId(feedId); if (!priceInfoObjectId) { throw new Error(`Price feed ${feedId} not found, please create it first`); } priceInfoObjects.push(priceInfoObjectId); [priceUpdatesHotPotato] = tx.moveCall({ target: `${packageId}::pyth::update_single_price_feed`, arguments: [ tx.object(this.pythStateId), priceUpdatesHotPotato, tx.object(priceInfoObjectId), (0, import_transactions.coinWithBalance)({ balance: baseUpdateFee }), tx.object.clock() ] }); } tx.moveCall({ target: `${packageId}::hot_potato_vector::destroy`, arguments: [priceUpdatesHotPotato], typeArguments: [`${packageId}::price_info::PriceInfo`] }); return priceInfoObjects; } /** * Get the price feed object ID for a given feed ID, caching the promise. * @param feedId */ getPriceFeedObjectId(feedId) { if (!__privateGet(this, _priceFeedObjectIdCache).has(feedId)) { __privateGet(this, _priceFeedObjectIdCache).set( feedId, __privateMethod(this, _SuiPythClient_instances, fetchPriceFeedObjectId_fn).call(this, feedId).catch((err) => { __privateGet(this, _priceFeedObjectIdCache).delete(feedId); throw err; }) ); } return __privateGet(this, _priceFeedObjectIdCache).get(feedId); } /** * Fetches the price table object ID for the current state ID, caching the promise. * @returns Price table object ID and field type */ getPriceTableInfo() { if (!__privateGet(this, _priceTableInfo)) { const promise = __privateMethod(this, _SuiPythClient_instances, fetchPriceTableInfo_fn).call(this).catch((err) => { __privateSet(this, _priceTableInfo, void 0); throw err; }); __privateSet(this, _priceTableInfo, promise); } return __privateGet(this, _priceTableInfo); } /** * Fetches the package ID for the Wormhole contract, with caching. */ getWormholePackageId() { if (!__privateGet(this, _wormholePackageId)) { __privateSet(this, _wormholePackageId, __privateMethod(this, _SuiPythClient_instances, fetchWormholePackageId_fn).call(this)); } return __privateGet(this, _wormholePackageId); } /** * Fetches the package ID for the Pyth contract, with caching. */ getPythPackageId() { if (!__privateGet(this, _pythPackageId)) { __privateSet(this, _pythPackageId, __privateMethod(this, _SuiPythClient_instances, fetchPythPackageId_fn).call(this)); } return __privateGet(this, _pythPackageId); } /** * Returns the cached base update fee, fetching it if necessary. */ getBaseUpdateFee() { if (!__privateGet(this, _baseUpdateFee)) { __privateSet(this, _baseUpdateFee, __privateMethod(this, _SuiPythClient_instances, fetchBaseUpdateFee_fn).call(this)); } return __privateGet(this, _baseUpdateFee); } } _pythPackageId = new WeakMap(); _wormholePackageId = new WeakMap(); _priceFeedObjectIdCache = new WeakMap(); _priceTableInfo = new WeakMap(); _baseUpdateFee = new WeakMap(); _SuiPythClient_instances = new WeakSet(); fetchPriceFeedObjectId_fn = async function(feedId) { const { id: tableId, fieldType } = await this.getPriceTableInfo(); const result = await this.provider.getDynamicFieldObject({ parentId: tableId, name: { type: `${fieldType}::price_identifier::PriceIdentifier`, value: { bytes: Array.from((0, import_utils.fromHex)(feedId)) } } }); if (!result.data || !result.data.content) { throw new Error(`Price feed object ID for feed ID ${feedId} not found.`); } if (result.data.content.dataType !== "moveObject") { throw new Error("Price feed type mismatch"); } return result.data.content.fields.value; }; fetchPriceTableInfo_fn = async function() { const result = await this.provider.getDynamicFieldObject({ parentId: this.pythStateId, name: { type: "vector<u8>", value: "price_info" } }); if (!result.data || !result.data.type) { throw new Error("Price Table not found, contract may not be initialized"); } const priceIdentifier = (0, import_utils.parseStructTag)(result.data.type).typeParams[0]; if (typeof priceIdentifier === "object" && priceIdentifier !== null && priceIdentifier.name === "PriceIdentifier" && "address" in priceIdentifier) { return { id: result.data.objectId, fieldType: priceIdentifier.address }; } else { throw new Error("fieldType not found"); } }; fetchWormholePackageId_fn = async function() { return await __privateMethod(this, _SuiPythClient_instances, getPackageId_fn).call(this, this.wormholeStateId); }; fetchPythPackageId_fn = async function() { return await __privateMethod(this, _SuiPythClient_instances, getPackageId_fn).call(this, this.pythStateId); }; getPackageId_fn = async function(objectId) { const result = await this.provider.getObject({ id: objectId, options: { showContent: true } }); if (result.data?.content?.dataType === "moveObject" && "upgrade_cap" in result.data.content.fields) { const fields = result.data.content.fields; return fields.upgrade_cap.fields.package; } throw new Error(`Cannot fetch package ID for object ${objectId}`); }; fetchBaseUpdateFee_fn = async function() { const result = await this.provider.getObject({ id: this.pythStateId, options: { showContent: true } }); if (!result.data || result.data.content?.dataType !== "moveObject") { throw new Error("Unable to fetch Pyth state object"); } const fields = result.data.content.fields; return fields.base_update_fee; }; //# sourceMappingURL=pyth.js.map