@mysten/suins
Version:
269 lines (268 loc) • 11 kB
JavaScript
"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