@mysten/sui
Version:
Sui TypeScript API(Work in Progress)
351 lines (350 loc) • 13.1 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
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 json_rpc_resolver_exports = {};
__export(json_rpc_resolver_exports, {
getClient: () => getClient,
resolveTransactionData: () => resolveTransactionData
});
module.exports = __toCommonJS(json_rpc_resolver_exports);
var import_valibot = require("valibot");
var import_bcs = require("../bcs/index.js");
var import_utils = require("../utils/index.js");
var import_internal = require("./data/internal.js");
var import_Inputs = require("./Inputs.js");
var import_serializer = require("./serializer.js");
var import_utils2 = require("@mysten/utils");
const MAX_OBJECTS_PER_FETCH = 50;
const GAS_SAFE_OVERHEAD = 1000n;
const MAX_GAS = 5e10;
async function resolveTransactionData(transactionData, options, next) {
await normalizeInputs(transactionData, options);
await resolveObjectReferences(transactionData, options);
if (!options.onlyTransactionKind) {
await setGasPrice(transactionData, options);
await setGasBudget(transactionData, options);
await setGasPayment(transactionData, options);
}
await validate(transactionData);
return await next();
}
async function setGasPrice(transactionData, options) {
if (!transactionData.gasConfig.price) {
transactionData.gasConfig.price = String(await getClient(options).getReferenceGasPrice());
}
}
async function setGasBudget(transactionData, options) {
if (transactionData.gasConfig.budget) {
return;
}
const dryRunResult = await getClient(options).dryRunTransactionBlock({
transactionBlock: transactionData.build({
overrides: {
gasData: {
budget: String(MAX_GAS),
payment: []
}
}
})
});
if (dryRunResult.effects.status.status !== "success") {
throw new Error(
`Dry run failed, could not automatically determine a budget: ${dryRunResult.effects.status.error}`,
{ cause: dryRunResult }
);
}
const safeOverhead = GAS_SAFE_OVERHEAD * BigInt(transactionData.gasConfig.price || 1n);
const baseComputationCostWithOverhead = BigInt(dryRunResult.effects.gasUsed.computationCost) + safeOverhead;
const gasBudget = baseComputationCostWithOverhead + BigInt(dryRunResult.effects.gasUsed.storageCost) - BigInt(dryRunResult.effects.gasUsed.storageRebate);
transactionData.gasConfig.budget = String(
gasBudget > baseComputationCostWithOverhead ? gasBudget : baseComputationCostWithOverhead
);
}
async function setGasPayment(transactionData, options) {
if (!transactionData.gasConfig.payment) {
const coins = await getClient(options).getCoins({
owner: transactionData.gasConfig.owner || transactionData.sender,
coinType: import_utils.SUI_TYPE_ARG
});
const paymentCoins = coins.data.filter((coin) => {
const matchingInput = transactionData.inputs.find((input) => {
if (input.Object?.ImmOrOwnedObject) {
return coin.coinObjectId === input.Object.ImmOrOwnedObject.objectId;
}
return false;
});
return !matchingInput;
}).map((coin) => ({
objectId: coin.coinObjectId,
digest: coin.digest,
version: coin.version
}));
if (!paymentCoins.length) {
throw new Error("No valid gas coins found for the transaction.");
}
transactionData.gasConfig.payment = paymentCoins.map((payment) => (0, import_valibot.parse)(import_internal.ObjectRef, payment));
}
}
async function resolveObjectReferences(transactionData, options) {
const objectsToResolve = transactionData.inputs.filter((input) => {
return input.UnresolvedObject && !(input.UnresolvedObject.version || input.UnresolvedObject?.initialSharedVersion);
});
const dedupedIds = [
...new Set(
objectsToResolve.map((input) => (0, import_utils.normalizeSuiObjectId)(input.UnresolvedObject.objectId))
)
];
const objectChunks = dedupedIds.length ? (0, import_utils2.chunk)(dedupedIds, MAX_OBJECTS_PER_FETCH) : [];
const resolved = (await Promise.all(
objectChunks.map(
(chunk2) => getClient(options).multiGetObjects({
ids: chunk2,
options: { showOwner: true }
})
)
)).flat();
const responsesById = new Map(
dedupedIds.map((id, index) => {
return [id, resolved[index]];
})
);
const invalidObjects = Array.from(responsesById).filter(([_, obj]) => obj.error).map(([_, obj]) => JSON.stringify(obj.error));
if (invalidObjects.length) {
throw new Error(`The following input objects are invalid: ${invalidObjects.join(", ")}`);
}
const objects = resolved.map((object) => {
if (object.error || !object.data) {
throw new Error(`Failed to fetch object: ${object.error}`);
}
const owner = object.data.owner;
const initialSharedVersion = owner && typeof owner === "object" && "Shared" in owner ? owner.Shared.initial_shared_version : null;
return {
objectId: object.data.objectId,
digest: object.data.digest,
version: object.data.version,
initialSharedVersion
};
});
const objectsById = new Map(
dedupedIds.map((id, index) => {
return [id, objects[index]];
})
);
for (const [index, input] of transactionData.inputs.entries()) {
if (!input.UnresolvedObject) {
continue;
}
let updated;
const id = (0, import_utils.normalizeSuiAddress)(input.UnresolvedObject.objectId);
const object = objectsById.get(id);
if (input.UnresolvedObject.initialSharedVersion ?? object?.initialSharedVersion) {
updated = import_Inputs.Inputs.SharedObjectRef({
objectId: id,
initialSharedVersion: input.UnresolvedObject.initialSharedVersion || object?.initialSharedVersion,
mutable: isUsedAsMutable(transactionData, index)
});
} else if (isUsedAsReceiving(transactionData, index)) {
updated = import_Inputs.Inputs.ReceivingRef(
{
objectId: id,
digest: input.UnresolvedObject.digest ?? object?.digest,
version: input.UnresolvedObject.version ?? object?.version
}
);
}
transactionData.inputs[transactionData.inputs.indexOf(input)] = updated ?? import_Inputs.Inputs.ObjectRef({
objectId: id,
digest: input.UnresolvedObject.digest ?? object?.digest,
version: input.UnresolvedObject.version ?? object?.version
});
}
}
async function normalizeInputs(transactionData, options) {
const { inputs, commands } = transactionData;
const moveCallsToResolve = [];
const moveFunctionsToResolve = /* @__PURE__ */ new Set();
commands.forEach((command) => {
if (command.MoveCall) {
if (command.MoveCall._argumentTypes) {
return;
}
const inputs2 = command.MoveCall.arguments.map((arg) => {
if (arg.$kind === "Input") {
return transactionData.inputs[arg.Input];
}
return null;
});
const needsResolution = inputs2.some(
(input) => input?.UnresolvedPure || input?.UnresolvedObject
);
if (needsResolution) {
const functionName = `${command.MoveCall.package}::${command.MoveCall.module}::${command.MoveCall.function}`;
moveFunctionsToResolve.add(functionName);
moveCallsToResolve.push(command.MoveCall);
}
}
switch (command.$kind) {
case "SplitCoins":
command.SplitCoins.amounts.forEach((amount) => {
normalizeRawArgument(amount, import_bcs.bcs.U64, transactionData);
});
break;
case "TransferObjects":
normalizeRawArgument(command.TransferObjects.address, import_bcs.bcs.Address, transactionData);
break;
}
});
const moveFunctionParameters = /* @__PURE__ */ new Map();
if (moveFunctionsToResolve.size > 0) {
const client = getClient(options);
await Promise.all(
[...moveFunctionsToResolve].map(async (functionName) => {
const [packageId, moduleId, functionId] = functionName.split("::");
const def = await client.getNormalizedMoveFunction({
package: packageId,
module: moduleId,
function: functionId
});
moveFunctionParameters.set(
functionName,
def.parameters.map((param) => (0, import_serializer.normalizedTypeToMoveTypeSignature)(param))
);
})
);
}
if (moveCallsToResolve.length) {
await Promise.all(
moveCallsToResolve.map(async (moveCall) => {
const parameters = moveFunctionParameters.get(
`${moveCall.package}::${moveCall.module}::${moveCall.function}`
);
if (!parameters) {
return;
}
const hasTxContext = parameters.length > 0 && (0, import_serializer.isTxContext)(parameters.at(-1));
const params = hasTxContext ? parameters.slice(0, parameters.length - 1) : parameters;
moveCall._argumentTypes = params;
})
);
}
commands.forEach((command) => {
if (!command.MoveCall) {
return;
}
const moveCall = command.MoveCall;
const fnName = `${moveCall.package}::${moveCall.module}::${moveCall.function}`;
const params = moveCall._argumentTypes;
if (!params) {
return;
}
if (params.length !== command.MoveCall.arguments.length) {
throw new Error(`Incorrect number of arguments for ${fnName}`);
}
params.forEach((param, i) => {
const arg = moveCall.arguments[i];
if (arg.$kind !== "Input") return;
const input = inputs[arg.Input];
if (!input.UnresolvedPure && !input.UnresolvedObject) {
return;
}
const inputValue = input.UnresolvedPure?.value ?? input.UnresolvedObject?.objectId;
const schema = (0, import_serializer.getPureBcsSchema)(param.body);
if (schema) {
arg.type = "pure";
inputs[inputs.indexOf(input)] = import_Inputs.Inputs.Pure(schema.serialize(inputValue));
return;
}
if (typeof inputValue !== "string") {
throw new Error(
`Expect the argument to be an object id string, got ${JSON.stringify(
inputValue,
null,
2
)}`
);
}
arg.type = "object";
const unresolvedObject = input.UnresolvedPure ? {
$kind: "UnresolvedObject",
UnresolvedObject: {
objectId: inputValue
}
} : input;
inputs[arg.Input] = unresolvedObject;
});
});
}
function validate(transactionData) {
transactionData.inputs.forEach((input, index) => {
if (input.$kind !== "Object" && input.$kind !== "Pure") {
throw new Error(
`Input at index ${index} has not been resolved. Expected a Pure or Object input, but found ${JSON.stringify(
input
)}`
);
}
});
}
function normalizeRawArgument(arg, schema, transactionData) {
if (arg.$kind !== "Input") {
return;
}
const input = transactionData.inputs[arg.Input];
if (input.$kind !== "UnresolvedPure") {
return;
}
transactionData.inputs[arg.Input] = import_Inputs.Inputs.Pure(schema.serialize(input.UnresolvedPure.value));
}
function isUsedAsMutable(transactionData, index) {
let usedAsMutable = false;
transactionData.getInputUses(index, (arg, tx) => {
if (tx.MoveCall && tx.MoveCall._argumentTypes) {
const argIndex = tx.MoveCall.arguments.indexOf(arg);
usedAsMutable = tx.MoveCall._argumentTypes[argIndex].ref !== "&" || usedAsMutable;
}
if (tx.$kind === "MakeMoveVec" || tx.$kind === "MergeCoins" || tx.$kind === "SplitCoins") {
usedAsMutable = true;
}
});
return usedAsMutable;
}
function isUsedAsReceiving(transactionData, index) {
let usedAsReceiving = false;
transactionData.getInputUses(index, (arg, tx) => {
if (tx.MoveCall && tx.MoveCall._argumentTypes) {
const argIndex = tx.MoveCall.arguments.indexOf(arg);
usedAsReceiving = isReceivingType(tx.MoveCall._argumentTypes[argIndex]) || usedAsReceiving;
}
});
return usedAsReceiving;
}
function isReceivingType(type) {
if (typeof type.body !== "object" || !("datatype" in type.body)) {
return false;
}
return type.body.datatype.package === "0x2" && type.body.datatype.module === "transfer" && type.body.datatype.type === "Receiving";
}
function getClient(options) {
if (!options.client) {
throw new Error(
`No sui client passed to Transaction#build, but transaction data was not sufficient to build offline.`
);
}
return options.client;
}
//# sourceMappingURL=json-rpc-resolver.js.map
;