UNPKG

@layerzerolabs/lz-sui-sdk-v2

Version:

576 lines (531 loc) 20.8 kB
import { bcs } from '@mysten/sui/bcs' import { SuiClient } from '@mysten/sui/client' import { Transaction, TransactionArgument, TransactionResult } from '@mysten/sui/transactions' import { ArbitrumPriceExtBcs, ModelTypeBcs, PriceBcs } from '../../bcs' import { ModuleManager } from '../../module-manager' import { ArbitrumPriceExt, ModelType, ObjectOptions, Price } from '../../types' import { asAddress, asBool, asObject, asU128, asU32, asU64, executeSimulate } from '../../utils' const MODULE_NAME = 'price_feed' export const PriceFeedErrorCode = { // PriceFeed related errors PRICE_FEED_EInvalidDenominator: 1, PRICE_FEED_ENoPrice: 2, PRICE_FEED_ENotAnOPStack: 3, PRICE_FEED_EOnlyPriceUpdater: 4, PRICE_FEED_EPriceUpdaterCapNotFound: 5, } as const export class PriceFeed { public packageId: string public readonly client: SuiClient private readonly objects: ObjectOptions constructor( packageId: string, client: SuiClient, objects: ObjectOptions, private readonly moduleManager: ModuleManager ) { this.packageId = packageId this.client = client this.objects = objects } // === Helper Functions === /** * Create price configuration object * @param tx - The transaction to add the move call to * @param priceRatio - Price ratio value or transaction argument * @param gasPriceInUnit - Gas price in unit or transaction argument * @param gasPerByte - Gas per byte value or transaction argument * @returns Transaction result containing the price object */ createPriceMoveCall( tx: Transaction, priceRatio: bigint | number | string | TransactionArgument, gasPriceInUnit: bigint | number | string | TransactionArgument, gasPerByte: number | TransactionArgument ): TransactionResult { return tx.moveCall({ target: this.#target('create_price'), arguments: [asU128(tx, priceRatio), asU64(tx, gasPriceInUnit), asU32(tx, gasPerByte)], }) } /** * Create Arbitrum price extension object * @param tx - The transaction to add the move call to * @param gasPerL2Tx - Gas per L2 transaction or transaction argument * @param gasPerL1CallDataByte - Gas per L1 call data byte or transaction argument * @returns Transaction result containing the Arbitrum price extension object */ createArbitrumPriceExtMoveCall( tx: Transaction, gasPerL2Tx: bigint | number | string | TransactionArgument, gasPerL1CallDataByte: number | TransactionArgument ): TransactionResult { return tx.moveCall({ target: this.#target('create_arbitrum_price_ext'), arguments: [asU64(tx, gasPerL2Tx), asU32(tx, gasPerL1CallDataByte)], }) } /** * Get price model type move call result * @param tx - The transaction to add the move call to * @param modelType - The price model type enum value * @returns Transaction result containing the model type */ getModelTypeMoveCall(tx: Transaction, modelType: ModelType): TransactionResult { switch (modelType) { case ModelType.DEFAULT: return tx.moveCall({ target: this.#target('model_type_default'), arguments: [], }) case ModelType.ARB_STACK: return tx.moveCall({ target: this.#target('model_type_arbitrum'), arguments: [], }) case ModelType.OP_STACK: return tx.moveCall({ target: this.#target('model_type_optimism'), arguments: [], }) default: throw new Error(`Invalid model type: ${JSON.stringify(modelType)}`) } } // === Set Functions === /** * Set price updater role for an address (admin only) * Note: This function will automatically create a price updater capability for new updaters * @param tx - The transaction to add the move call to * @param updater - The updater address or transaction argument * @param active - Whether to activate or deactivate the updater role or transaction argument */ setPriceUpdaterMoveCall( tx: Transaction, updater: string | TransactionArgument, active: boolean | TransactionArgument ): void { tx.moveCall({ target: this.#target('set_price_updater'), arguments: [ tx.object(this.objects.priceFeed), tx.object(this.objects.priceFeedOwnerCap), asAddress(tx, updater), asBool(tx, active), ], }) } /** * Set price ratio denominator for price calculations (admin only) * Note: denominator must be greater than 0, otherwise the transaction will fail * @param tx - The transaction to add the move call to * @param denominator - The price ratio denominator value or transaction argument (must be > 0) */ setPriceRatioDenominatorMoveCall( tx: Transaction, denominator: bigint | number | string | TransactionArgument ): void { tx.moveCall({ target: this.#target('set_price_ratio_denominator'), arguments: [ tx.object(this.objects.priceFeed), tx.object(this.objects.priceFeedOwnerCap), asU128(tx, denominator), ], }) } /** * Set Arbitrum compression percentage (admin only) * @param tx - The transaction to add the move call to * @param compressionPercent - The compression percentage for Arbitrum or transaction argument */ setArbitrumCompressionPercentMoveCall( tx: Transaction, compressionPercent: bigint | number | string | TransactionArgument ): void { tx.moveCall({ target: this.#target('set_arbitrum_compression_percent'), arguments: [ tx.object(this.objects.priceFeed), tx.object(this.objects.priceFeedOwnerCap), asU128(tx, compressionPercent), ], }) } /** * Set price model type for a destination EID (admin only) * @param tx - The transaction to add the move call to * @param dstEid - Destination endpoint ID or transaction argument * @param modelType - The price model type to set */ setEidToModelTypeMoveCall(tx: Transaction, dstEid: number | TransactionArgument, modelType: ModelType): void { const modelTypeCall = this.getModelTypeMoveCall(tx, modelType) tx.moveCall({ target: this.#target('set_eid_to_model_type'), arguments: [ tx.object(this.objects.priceFeed), tx.object(this.objects.priceFeedOwnerCap), asU32(tx, dstEid), modelTypeCall, ], }) } /** * Set price for a destination EID (price updater capability required) * @param tx - The transaction to add the move call to * @param updaterCap - The price updater capability object or transaction argument * @param dstEid - Destination endpoint ID or transaction argument * @param price - The price configuration to set */ setPriceMoveCall( tx: Transaction, updaterCap: string | TransactionArgument, dstEid: number | TransactionArgument, price: Price ): void { const priceCall = this.createPriceMoveCall(tx, price.priceRatio, price.gasPriceInUnit, price.gasPerByte) tx.moveCall({ target: this.#target('set_price'), arguments: [tx.object(this.objects.priceFeed), asObject(tx, updaterCap), asU32(tx, dstEid), priceCall], }) } /** * Set price for Arbitrum with additional extension parameters (price updater capability required) * @param tx - The transaction to add the move call to * @param updaterCap - The price updater capability object or transaction argument * @param dstEid - Destination endpoint ID * @param price - The base price configuration * @param arbitrumPriceExt - Additional Arbitrum-specific price parameters */ setPriceForArbitrumMoveCall( tx: Transaction, updaterCap: string | TransactionArgument, dstEid: number | TransactionArgument, price: Price, arbitrumPriceExt: ArbitrumPriceExt ): void { const priceCall = this.createPriceMoveCall(tx, price.priceRatio, price.gasPriceInUnit, price.gasPerByte) const arbitrumPriceExtCall = this.createArbitrumPriceExtMoveCall( tx, arbitrumPriceExt.gasPerL2Tx, arbitrumPriceExt.gasPerL1CallDataByte ) tx.moveCall({ target: this.#target('set_price_for_arbitrum'), arguments: [ tx.object(this.objects.priceFeed), asObject(tx, updaterCap), asU32(tx, dstEid), priceCall, arbitrumPriceExtCall, ], }) } /** * Set native token price in USD (price updater capability required) * @param tx - The transaction to add the move call to * @param updaterCap - The price updater capability object or transaction argument * @param nativeTokenPriceUsd - The native token price in USD */ setNativeTokenPriceUsdMoveCall( tx: Transaction, updaterCap: string | TransactionArgument, nativeTokenPriceUsd: bigint | number | string | TransactionArgument ): void { tx.moveCall({ target: this.#target('set_native_token_price_usd'), arguments: [tx.object(this.objects.priceFeed), asObject(tx, updaterCap), asU128(tx, nativeTokenPriceUsd)], }) } // === Witness Functions === /** * Create a LayerZero witness for PriceFeed package whitelist registration * @param tx - The transaction to add the move call to * @returns Transaction result containing the LayerZero witness */ createLayerZeroWitnessMoveCall(tx: Transaction): TransactionResult { return tx.moveCall({ target: `${this.packageId}::price_feed_witness::new`, arguments: [], }) } // === Worker Function === /** * Estimate fee by endpoint ID using a call result * @param tx - The transaction to add the move call to * @param call - The call transaction result containing fee parameters */ estimateFeeByEidMoveCall(tx: Transaction, call: TransactionResult): void { tx.moveCall({ target: this.#target('estimate_fee_by_eid'), arguments: [tx.object(this.objects.priceFeed), call], }) } // === View Functions === /** * Get owner capability address * @param tx - The transaction to add the move call to * @returns Transaction result containing the owner capability address */ getOwnerCapMoveCall(tx: Transaction): TransactionResult { return tx.moveCall({ target: this.#target('get_owner_cap'), arguments: [tx.object(this.objects.priceFeed)], }) } /** * Get price updater capability address for a specific updater * @param tx - The transaction to add the move call to * @param updater - The updater address to get capability for * @returns Transaction result containing the price updater capability address */ getPriceUpdaterCapMoveCall(tx: Transaction, updater: string | TransactionArgument): TransactionResult { return tx.moveCall({ target: this.#target('get_price_updater_cap'), arguments: [tx.object(this.objects.priceFeed), asAddress(tx, updater)], }) } /** * Get the owner capability address of this PriceFeed * @returns Promise<string> - The owner capability address */ async ownerCap(): Promise<string> { return executeSimulate( this.client, (tx) => { this.getOwnerCapMoveCall(tx) }, (result) => bcs.Address.parse(result[0].value) ) } /** * Get price updater capability address for a specific updater * @param updater - The updater address to get capability for * @returns Promise<string> - The price updater capability address */ async priceUpdaterCap(updater: string): Promise<string> { return executeSimulate( this.client, (tx) => { this.getPriceUpdaterCapMoveCall(tx, updater) }, (result) => bcs.Address.parse(result[0].value) ) } /** * Check if an address is a price updater * @param tx - The transaction to add the move call to * @param updater - The updater address to check * @returns Transaction result containing the price updater status */ isPriceUpdaterMoveCall(tx: Transaction, updater: string | TransactionArgument): TransactionResult { return tx.moveCall({ target: this.#target('is_price_updater'), arguments: [tx.object(this.objects.priceFeed), asAddress(tx, updater)], }) } /** * Check if an address is a price updater * @param updater - The updater address to check * @returns Promise<boolean> - True if the address is a price updater */ async isPriceUpdater(updater: string): Promise<boolean> { return executeSimulate( this.client, (tx) => { this.isPriceUpdaterMoveCall(tx, updater) }, (result) => bcs.Bool.parse(result[0].value) ) } /** * Get price ratio denominator * @param tx - The transaction to add the move call to * @returns Transaction result containing the price ratio denominator */ priceRatioDenominatorMoveCall(tx: Transaction): TransactionResult { return tx.moveCall({ target: this.#target('get_price_ratio_denominator'), arguments: [tx.object(this.objects.priceFeed)], }) } /** * Get price ratio denominator * @returns Promise<bigint> - The price ratio denominator value */ async priceRatioDenominator(): Promise<bigint> { return executeSimulate( this.client, (tx) => { this.priceRatioDenominatorMoveCall(tx) }, (result) => BigInt(bcs.U128.parse(result[0].value)) ) } /** * Get Arbitrum compression percentage * @param tx - The transaction to add the move call to * @returns Transaction result containing the compression percentage */ arbitrumCompressionPercentMoveCall(tx: Transaction): TransactionResult { return tx.moveCall({ target: this.#target('get_arbitrum_compression_percent'), arguments: [tx.object(this.objects.priceFeed)], }) } /** * Get Arbitrum compression percentage * @returns Promise<bigint> - The compression percentage value */ async arbitrumCompressionPercent(): Promise<bigint> { return executeSimulate( this.client, (tx) => { this.arbitrumCompressionPercentMoveCall(tx) }, (result) => BigInt(bcs.U128.parse(result[0].value)) ) } /** * Get model type for a destination EID * @param tx - The transaction to add the move call to * @param dstEid - Destination endpoint ID * @returns Transaction result containing the model type */ modelTypeMoveCall(tx: Transaction, dstEid: number | TransactionArgument): TransactionResult { return tx.moveCall({ target: this.#target('get_model_type'), arguments: [tx.object(this.objects.priceFeed), asU32(tx, dstEid)], }) } /** * Get model type for a destination EID * @param dstEid - Destination endpoint ID * @returns Promise<PriceModelType> - The price model type */ async modelType(dstEid: number): Promise<ModelType> { return executeSimulate( this.client, (tx) => { this.modelTypeMoveCall(tx, dstEid) }, (result) => { const enumValue = ModelTypeBcs.parse(result[0].value) as { $kind: string } switch (enumValue.$kind) { case 'DEFAULT': return ModelType.DEFAULT case 'ARB_STACK': return ModelType.ARB_STACK case 'OP_STACK': return ModelType.OP_STACK default: throw new Error(`Invalid model type: ${JSON.stringify(enumValue)}`) } } ) } /** * Get native token price in USD * @param tx - The transaction to add the move call to * @returns Transaction result containing the native token price in USD */ nativeTokenPriceUsdMoveCall(tx: Transaction): TransactionResult { return tx.moveCall({ target: this.#target('native_token_price_usd'), arguments: [tx.object(this.objects.priceFeed)], }) } /** * Get native token price in USD * @returns Promise<bigint> - The native token price in USD */ async nativeTokenPriceUsd(): Promise<bigint> { return executeSimulate( this.client, (tx) => { this.nativeTokenPriceUsdMoveCall(tx) }, (result) => BigInt(bcs.U128.parse(result[0].value)) ) } /** * Get Arbitrum price extension parameters * @param tx - The transaction to add the move call to * @returns Transaction result containing Arbitrum price extension */ arbitrumPriceExtMoveCall(tx: Transaction): TransactionResult { return tx.moveCall({ target: this.#target('arbitrum_price_ext'), arguments: [tx.object(this.objects.priceFeed)], }) } /** * Get Arbitrum price extension parameters * @returns Promise<ArbitrumPriceExt> - The Arbitrum price extension configuration */ async arbitrumPriceExt(): Promise<ArbitrumPriceExt> { return executeSimulate( this.client, (tx) => { this.arbitrumPriceExtMoveCall(tx) }, (result) => { const parsed = ArbitrumPriceExtBcs.parse(result[0].value) as { gas_per_l2_tx: string | number | bigint gas_per_l1_call_data_byte: number } return { gasPerL2Tx: BigInt(parsed.gas_per_l2_tx), gasPerL1CallDataByte: parsed.gas_per_l1_call_data_byte, } } ) } /** * Get price for a specific destination EID * @param tx - The transaction to add the move call to * @param dstEid - Destination endpoint ID * @returns Transaction result containing the price configuration */ priceMoveCall(tx: Transaction, dstEid: number | TransactionArgument): TransactionResult { return tx.moveCall({ target: this.#target('get_price'), arguments: [tx.object(this.objects.priceFeed), asU32(tx, dstEid)], }) } /** * Get price for a specific destination EID * @param dstEid - Destination endpoint ID * @returns Promise<Price> - The price configuration for the destination */ async price(dstEid: number): Promise<Price> { return executeSimulate( this.client, (tx) => { this.priceMoveCall(tx, dstEid) }, (result) => { const parsed = PriceBcs.parse(result[0].value) as { price_ratio: string | number | bigint gas_price_in_unit: string | number | bigint gas_per_byte: number } return { priceRatio: BigInt(parsed.price_ratio), gasPriceInUnit: BigInt(parsed.gas_price_in_unit), gasPerByte: parsed.gas_per_byte, } } ) } /** * Generate the full target path for move calls * @param name - The function name to call * @param module_name - The module name (defaults to MODULE_NAME) * @returns The full module path for the move call * @private */ #target(name: string, module_name = MODULE_NAME): string { return `${this.packageId}::${module_name}::${name}` } }