UNPKG

@mysten/sui

Version:

Sui TypeScript API(Work in Progress)

603 lines (602 loc) 24.2 kB
var __typeError = (msg) => { throw TypeError(msg); }; 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 _serializationPlugins, _buildPlugins, _intentResolvers, _inputSection, _commandSection, _availableResults, _pendingPromises, _added, _data, _Transaction_instances, fork_fn, addCommand_fn, addInput_fn, normalizeTransactionArgument_fn, resolveArgument_fn, prepareBuild_fn, runPlugins_fn, waitForPendingTasks_fn, sortCommandsAndInputs_fn; import { fromBase64, isSerializedBcs } from "@mysten/bcs"; import { is, parse } from "valibot"; import { normalizeSuiAddress } from "../utils/sui-types.js"; import { Commands } from "./Commands.js"; import { Argument, NormalizedCallArg, ObjectRef, TransactionExpiration } from "./data/internal.js"; import { serializeV1TransactionData } from "./data/v1.js"; import { SerializedTransactionDataV2 } from "./data/v2.js"; import { Inputs } from "./Inputs.js"; import { resolveTransactionData } from "./json-rpc-resolver.js"; import { createObjectMethods } from "./object.js"; import { createPure } from "./pure.js"; import { TransactionDataBuilder } from "./TransactionData.js"; import { getIdFromCallArg } from "./utils.js"; function createTransactionResult(index, length = Infinity) { const baseResult = { $kind: "Result", Result: index }; const nestedResults = []; const nestedResultFor = (resultIndex) => nestedResults[resultIndex] ?? (nestedResults[resultIndex] = { $kind: "NestedResult", NestedResult: [index, resultIndex] }); return new Proxy(baseResult, { set() { throw new Error( "The transaction result is a proxy, and does not support setting properties directly" ); }, // TODO: Instead of making this return a concrete argument, we should ideally // make it reference-based (so that this gets resolved at build-time), which // allows re-ordering transactions. get(target, property) { if (property in target) { return Reflect.get(target, property); } if (property === Symbol.iterator) { return function* () { let i = 0; while (i < length) { yield nestedResultFor(i); i++; } }; } if (typeof property === "symbol") return; const resultIndex = parseInt(property, 10); if (Number.isNaN(resultIndex) || resultIndex < 0) return; return nestedResultFor(resultIndex); } }); } const TRANSACTION_BRAND = Symbol.for("@mysten/transaction"); function isTransaction(obj) { return !!obj && typeof obj === "object" && obj[TRANSACTION_BRAND] === true; } const modulePluginRegistry = { buildPlugins: /* @__PURE__ */ new Map(), serializationPlugins: /* @__PURE__ */ new Map() }; const TRANSACTION_REGISTRY_KEY = Symbol.for("@mysten/transaction/registry"); function getGlobalPluginRegistry() { try { const target = globalThis; if (!target[TRANSACTION_REGISTRY_KEY]) { target[TRANSACTION_REGISTRY_KEY] = modulePluginRegistry; } return target[TRANSACTION_REGISTRY_KEY]; } catch (e) { return modulePluginRegistry; } } const _Transaction = class _Transaction { constructor() { __privateAdd(this, _Transaction_instances); __privateAdd(this, _serializationPlugins); __privateAdd(this, _buildPlugins); __privateAdd(this, _intentResolvers, /* @__PURE__ */ new Map()); __privateAdd(this, _inputSection, []); __privateAdd(this, _commandSection, []); __privateAdd(this, _availableResults, /* @__PURE__ */ new Set()); __privateAdd(this, _pendingPromises, /* @__PURE__ */ new Set()); __privateAdd(this, _added, /* @__PURE__ */ new Map()); __privateAdd(this, _data); /** * Add a new object input to the transaction. */ this.object = createObjectMethods( (value) => { if (typeof value === "function") { return this.object(this.add(value)); } if (typeof value === "object" && is(Argument, value)) { return value; } const id = getIdFromCallArg(value); const inserted = __privateGet(this, _data).inputs.find((i) => id === getIdFromCallArg(i)); if (inserted?.Object?.SharedObject && typeof value === "object" && value.Object?.SharedObject) { inserted.Object.SharedObject.mutable = inserted.Object.SharedObject.mutable || value.Object.SharedObject.mutable; } return inserted ? { $kind: "Input", Input: __privateGet(this, _data).inputs.indexOf(inserted), type: "object" } : __privateMethod(this, _Transaction_instances, addInput_fn).call(this, "object", typeof value === "string" ? { $kind: "UnresolvedObject", UnresolvedObject: { objectId: normalizeSuiAddress(value) } } : value); } ); const globalPlugins = getGlobalPluginRegistry(); __privateSet(this, _data, new TransactionDataBuilder()); __privateSet(this, _buildPlugins, [...globalPlugins.buildPlugins.values()]); __privateSet(this, _serializationPlugins, [...globalPlugins.serializationPlugins.values()]); } /** * Converts from a serialize transaction kind (built with `build({ onlyTransactionKind: true })`) to a `Transaction` class. * Supports either a byte array, or base64-encoded bytes. */ static fromKind(serialized) { const tx = new _Transaction(); __privateSet(tx, _data, TransactionDataBuilder.fromKindBytes( typeof serialized === "string" ? fromBase64(serialized) : serialized )); __privateSet(tx, _inputSection, __privateGet(tx, _data).inputs); __privateSet(tx, _commandSection, __privateGet(tx, _data).commands); return tx; } /** * Converts from a serialized transaction format to a `Transaction` class. * There are two supported serialized formats: * - A string returned from `Transaction#serialize`. The serialized format must be compatible, or it will throw an error. * - A byte array (or base64-encoded bytes) containing BCS transaction data. */ static from(transaction) { const newTransaction = new _Transaction(); if (isTransaction(transaction)) { __privateSet(newTransaction, _data, new TransactionDataBuilder(transaction.getData())); } else if (typeof transaction !== "string" || !transaction.startsWith("{")) { __privateSet(newTransaction, _data, TransactionDataBuilder.fromBytes( typeof transaction === "string" ? fromBase64(transaction) : transaction )); } else { __privateSet(newTransaction, _data, TransactionDataBuilder.restore(JSON.parse(transaction))); } __privateSet(newTransaction, _inputSection, __privateGet(newTransaction, _data).inputs); __privateSet(newTransaction, _commandSection, __privateGet(newTransaction, _data).commands); return newTransaction; } static registerGlobalSerializationPlugin(stepOrStep, step) { getGlobalPluginRegistry().serializationPlugins.set( stepOrStep, step ?? stepOrStep ); } static unregisterGlobalSerializationPlugin(name) { getGlobalPluginRegistry().serializationPlugins.delete(name); } static registerGlobalBuildPlugin(stepOrStep, step) { getGlobalPluginRegistry().buildPlugins.set( stepOrStep, step ?? stepOrStep ); } static unregisterGlobalBuildPlugin(name) { getGlobalPluginRegistry().buildPlugins.delete(name); } addSerializationPlugin(step) { __privateGet(this, _serializationPlugins).push(step); } addBuildPlugin(step) { __privateGet(this, _buildPlugins).push(step); } addIntentResolver(intent, resolver) { if (__privateGet(this, _intentResolvers).has(intent) && __privateGet(this, _intentResolvers).get(intent) !== resolver) { throw new Error(`Intent resolver for ${intent} already exists`); } __privateGet(this, _intentResolvers).set(intent, resolver); } setSender(sender) { __privateGet(this, _data).sender = sender; } /** * Sets the sender only if it has not already been set. * This is useful for sponsored transaction flows where the sender may not be the same as the signer address. */ setSenderIfNotSet(sender) { if (!__privateGet(this, _data).sender) { __privateGet(this, _data).sender = sender; } } setExpiration(expiration) { __privateGet(this, _data).expiration = expiration ? parse(TransactionExpiration, expiration) : null; } setGasPrice(price) { __privateGet(this, _data).gasConfig.price = String(price); } setGasBudget(budget) { __privateGet(this, _data).gasConfig.budget = String(budget); } setGasBudgetIfNotSet(budget) { if (__privateGet(this, _data).gasData.budget == null) { __privateGet(this, _data).gasConfig.budget = String(budget); } } setGasOwner(owner) { __privateGet(this, _data).gasConfig.owner = owner; } setGasPayment(payments) { __privateGet(this, _data).gasConfig.payment = payments.map((payment) => parse(ObjectRef, payment)); } /** @deprecated Use `getData()` instead. */ get blockData() { return serializeV1TransactionData(__privateGet(this, _data).snapshot()); } /** Get a snapshot of the transaction data, in JSON form: */ getData() { return __privateGet(this, _data).snapshot(); } // Used to brand transaction classes so that they can be identified, even between multiple copies // of the builder. get [TRANSACTION_BRAND]() { return true; } // Temporary workaround for the wallet interface accidentally serializing transactions via postMessage get pure() { Object.defineProperty(this, "pure", { enumerable: false, value: createPure((value) => { if (isSerializedBcs(value)) { return __privateMethod(this, _Transaction_instances, addInput_fn).call(this, "pure", { $kind: "Pure", Pure: { bytes: value.toBase64() } }); } return __privateMethod(this, _Transaction_instances, addInput_fn).call(this, "pure", is(NormalizedCallArg, value) ? parse(NormalizedCallArg, value) : value instanceof Uint8Array ? Inputs.Pure(value) : { $kind: "UnresolvedPure", UnresolvedPure: { value } }); }) }); return this.pure; } /** Returns an argument for the gas coin, to be used in a transaction. */ get gas() { return { $kind: "GasCoin", GasCoin: true }; } /** * Add a new object input to the transaction using the fully-resolved object reference. * If you only have an object ID, use `builder.object(id)` instead. */ objectRef(...args) { return this.object(Inputs.ObjectRef(...args)); } /** * Add a new receiving input to the transaction using the fully-resolved object reference. * If you only have an object ID, use `builder.object(id)` instead. */ receivingRef(...args) { return this.object(Inputs.ReceivingRef(...args)); } /** * Add a new shared object input to the transaction using the fully-resolved shared object reference. * If you only have an object ID, use `builder.object(id)` instead. */ sharedObjectRef(...args) { return this.object(Inputs.SharedObjectRef(...args)); } add(command) { if (typeof command === "function") { if (__privateGet(this, _added).has(command)) { return __privateGet(this, _added).get(command); } const fork = __privateMethod(this, _Transaction_instances, fork_fn).call(this); const result = command(fork); if (!(result && typeof result === "object" && "then" in result)) { __privateSet(this, _availableResults, __privateGet(fork, _availableResults)); __privateGet(this, _added).set(command, result); return result; } const placeholder = __privateMethod(this, _Transaction_instances, addCommand_fn).call(this, { $kind: "$Intent", $Intent: { name: "AsyncTransactionThunk", inputs: {}, data: { result: null } } }); __privateGet(this, _pendingPromises).add( Promise.resolve(result).then((result2) => { placeholder.$Intent.data.result = result2; }) ); const txResult = createTransactionResult(__privateGet(this, _data).commands.length - 1); __privateGet(this, _added).set(command, txResult); return txResult; } else { __privateMethod(this, _Transaction_instances, addCommand_fn).call(this, command); } return createTransactionResult(__privateGet(this, _data).commands.length - 1); } // Method shorthands: splitCoins(coin, amounts) { const command = Commands.SplitCoins( typeof coin === "string" ? this.object(coin) : __privateMethod(this, _Transaction_instances, resolveArgument_fn).call(this, coin), amounts.map( (amount) => typeof amount === "number" || typeof amount === "bigint" || typeof amount === "string" ? this.pure.u64(amount) : __privateMethod(this, _Transaction_instances, normalizeTransactionArgument_fn).call(this, amount) ) ); __privateMethod(this, _Transaction_instances, addCommand_fn).call(this, command); return createTransactionResult(__privateGet(this, _data).commands.length - 1, amounts.length); } mergeCoins(destination, sources) { return this.add( Commands.MergeCoins( this.object(destination), sources.map((src) => this.object(src)) ) ); } publish({ modules, dependencies }) { return this.add( Commands.Publish({ modules, dependencies }) ); } upgrade({ modules, dependencies, package: packageId, ticket }) { return this.add( Commands.Upgrade({ modules, dependencies, package: packageId, ticket: this.object(ticket) }) ); } moveCall({ arguments: args, ...input }) { return this.add( Commands.MoveCall({ ...input, arguments: args?.map((arg) => __privateMethod(this, _Transaction_instances, normalizeTransactionArgument_fn).call(this, arg)) }) ); } transferObjects(objects, address) { return this.add( Commands.TransferObjects( objects.map((obj) => this.object(obj)), typeof address === "string" ? this.pure.address(address) : __privateMethod(this, _Transaction_instances, normalizeTransactionArgument_fn).call(this, address) ) ); } makeMoveVec({ type, elements }) { return this.add( Commands.MakeMoveVec({ type, elements: elements.map((obj) => this.object(obj)) }) ); } /** * @deprecated Use toJSON instead. * For synchronous serialization, you can use `getData()` * */ serialize() { return JSON.stringify(serializeV1TransactionData(__privateGet(this, _data).snapshot())); } async toJSON(options = {}) { await this.prepareForSerialization(options); return JSON.stringify( parse(SerializedTransactionDataV2, __privateGet(this, _data).snapshot()), (_key, value) => typeof value === "bigint" ? value.toString() : value, 2 ); } /** Build the transaction to BCS bytes, and sign it with the provided keypair. */ async sign(options) { const { signer, ...buildOptions } = options; const bytes = await this.build(buildOptions); return signer.signTransaction(bytes); } /** Build the transaction to BCS bytes. */ async build(options = {}) { await this.prepareForSerialization(options); await __privateMethod(this, _Transaction_instances, prepareBuild_fn).call(this, options); return __privateGet(this, _data).build({ onlyTransactionKind: options.onlyTransactionKind }); } /** Derive transaction digest */ async getDigest(options = {}) { await __privateMethod(this, _Transaction_instances, prepareBuild_fn).call(this, options); return __privateGet(this, _data).getDigest(); } async prepareForSerialization(options) { await __privateMethod(this, _Transaction_instances, waitForPendingTasks_fn).call(this); __privateMethod(this, _Transaction_instances, sortCommandsAndInputs_fn).call(this); const intents = /* @__PURE__ */ new Set(); for (const command of __privateGet(this, _data).commands) { if (command.$Intent) { intents.add(command.$Intent.name); } } const steps = [...__privateGet(this, _serializationPlugins)]; for (const intent of intents) { if (options.supportedIntents?.includes(intent)) { continue; } if (!__privateGet(this, _intentResolvers).has(intent)) { throw new Error(`Missing intent resolver for ${intent}`); } steps.push(__privateGet(this, _intentResolvers).get(intent)); } await __privateMethod(this, _Transaction_instances, runPlugins_fn).call(this, steps, options); } }; _serializationPlugins = new WeakMap(); _buildPlugins = new WeakMap(); _intentResolvers = new WeakMap(); _inputSection = new WeakMap(); _commandSection = new WeakMap(); _availableResults = new WeakMap(); _pendingPromises = new WeakMap(); _added = new WeakMap(); _data = new WeakMap(); _Transaction_instances = new WeakSet(); fork_fn = function() { const fork = new _Transaction(); __privateSet(fork, _data, __privateGet(this, _data)); __privateSet(fork, _serializationPlugins, __privateGet(this, _serializationPlugins)); __privateSet(fork, _buildPlugins, __privateGet(this, _buildPlugins)); __privateSet(fork, _intentResolvers, __privateGet(this, _intentResolvers)); __privateSet(fork, _pendingPromises, __privateGet(this, _pendingPromises)); __privateSet(fork, _availableResults, new Set(__privateGet(this, _availableResults))); __privateSet(fork, _added, __privateGet(this, _added)); __privateGet(this, _inputSection).push(__privateGet(fork, _inputSection)); __privateGet(this, _commandSection).push(__privateGet(fork, _commandSection)); return fork; }; addCommand_fn = function(command) { const resultIndex = __privateGet(this, _data).commands.length; __privateGet(this, _commandSection).push(command); __privateGet(this, _availableResults).add(resultIndex); __privateGet(this, _data).commands.push(command); __privateGet(this, _data).mapCommandArguments(resultIndex, (arg) => { if (arg.$kind === "Result" && !__privateGet(this, _availableResults).has(arg.Result)) { throw new Error( `Result { Result: ${arg.Result} } is not available to use the current transaction` ); } if (arg.$kind === "NestedResult" && !__privateGet(this, _availableResults).has(arg.NestedResult[0])) { throw new Error( `Result { NestedResult: [${arg.NestedResult[0]}, ${arg.NestedResult[1]}] } is not available to use the current transaction` ); } if (arg.$kind === "Input" && arg.Input >= __privateGet(this, _data).inputs.length) { throw new Error( `Input { Input: ${arg.Input} } references an input that does not exist in the current transaction` ); } return arg; }); return command; }; addInput_fn = function(type, input) { __privateGet(this, _inputSection).push(input); return __privateGet(this, _data).addInput(type, input); }; normalizeTransactionArgument_fn = function(arg) { if (isSerializedBcs(arg)) { return this.pure(arg); } return __privateMethod(this, _Transaction_instances, resolveArgument_fn).call(this, arg); }; resolveArgument_fn = function(arg) { if (typeof arg === "function") { const resolved = this.add(arg); if (typeof resolved === "function") { return __privateMethod(this, _Transaction_instances, resolveArgument_fn).call(this, resolved); } return parse(Argument, resolved); } return parse(Argument, arg); }; prepareBuild_fn = async function(options) { if (!options.onlyTransactionKind && !__privateGet(this, _data).sender) { throw new Error("Missing transaction sender"); } await __privateMethod(this, _Transaction_instances, runPlugins_fn).call(this, [...__privateGet(this, _buildPlugins), resolveTransactionData], options); }; runPlugins_fn = async function(plugins, options) { const createNext = (i) => { if (i >= plugins.length) { return () => { }; } const plugin = plugins[i]; return async () => { const next = createNext(i + 1); let calledNext = false; let nextResolved = false; await plugin(__privateGet(this, _data), options, async () => { if (calledNext) { throw new Error(`next() was call multiple times in TransactionPlugin ${i}`); } calledNext = true; await next(); nextResolved = true; }); if (!calledNext) { throw new Error(`next() was not called in TransactionPlugin ${i}`); } if (!nextResolved) { throw new Error(`next() was not awaited in TransactionPlugin ${i}`); } }; }; await createNext(0)(); __privateSet(this, _inputSection, __privateGet(this, _data).inputs); __privateSet(this, _commandSection, __privateGet(this, _data).commands); }; waitForPendingTasks_fn = async function() { while (__privateGet(this, _pendingPromises).size > 0) { const newPromise = Promise.all(__privateGet(this, _pendingPromises)); __privateGet(this, _pendingPromises).clear(); __privateGet(this, _pendingPromises).add(newPromise); await newPromise; __privateGet(this, _pendingPromises).delete(newPromise); } }; sortCommandsAndInputs_fn = function() { const unorderedCommands = __privateGet(this, _data).commands; const unorderedInputs = __privateGet(this, _data).inputs; const orderedCommands = __privateGet(this, _commandSection).flat(Infinity); const orderedInputs = __privateGet(this, _inputSection).flat(Infinity); if (orderedCommands.length !== unorderedCommands.length) { throw new Error("Unexpected number of commands found in transaction data"); } if (orderedInputs.length !== unorderedInputs.length) { throw new Error("Unexpected number of inputs found in transaction data"); } const filteredCommands = orderedCommands.filter( (cmd) => cmd.$Intent?.name !== "AsyncTransactionThunk" ); __privateGet(this, _data).commands = filteredCommands; __privateGet(this, _data).inputs = orderedInputs; __privateSet(this, _commandSection, filteredCommands); __privateSet(this, _inputSection, orderedInputs); function getOriginalIndex(index) { const command = unorderedCommands[index]; if (command.$Intent?.name === "AsyncTransactionThunk") { const result = command.$Intent.data.result; if (result == null) { throw new Error("AsyncTransactionThunk has not been resolved"); } return getOriginalIndex(result.Result); } const updated = filteredCommands.indexOf(command); if (updated === -1) { throw new Error("Unable to find original index for command"); } return updated; } __privateGet(this, _data).mapArguments((arg) => { if (arg.$kind === "Input") { const updated = orderedInputs.indexOf(unorderedInputs[arg.Input]); if (updated === -1) { throw new Error("Input has not been resolved"); } return { ...arg, Input: updated }; } else if (arg.$kind === "Result") { const updated = getOriginalIndex(arg.Result); return { ...arg, Result: updated }; } else if (arg.$kind === "NestedResult") { const updated = getOriginalIndex(arg.NestedResult[0]); return { ...arg, NestedResult: [updated, arg.NestedResult[1]] }; } return arg; }); }; let Transaction = _Transaction; export { Transaction, isTransaction }; //# sourceMappingURL=Transaction.js.map