@wormhole-foundation/sdk-sui
Version:
SDK for Sui chains, used in conjunction with @wormhole-foundation/sdk
199 lines • 7.7 kB
JavaScript
import { Transaction } from "@mysten/sui/transactions";
import { isValidSuiAddress, normalizeSuiAddress, normalizeSuiObjectId } from "@mysten/sui/utils";
import { encoding } from "@wormhole-foundation/sdk-connect";
import { getFieldsFromObjectResponse, isMoveStructObject, isMoveStructStruct, isSameType, } from "./types.js";
import { getPackageIdFromType, normalizeSuiType } from "./address.js";
export const UPGRADE_CAP_TYPE = "0x2::package::UpgradeCap";
//
// Utils to fetch data from Sui
//
export const getOriginalPackageId = async (provider, stateObjectId) => {
const { data, error } = await provider.getObject({
id: stateObjectId,
options: { showContent: true },
});
if (error)
throw new Error("Error getting object: " + error);
if (!data || !isMoveStructStruct(data.content))
throw new Error(`Cannot get oject for state id ${stateObjectId}: ` + data);
return getPackageIdFromType(data.content.type);
};
export const getObjectFields = async (provider, objectId) => {
if (!isValidSuiAddress(objectId)) {
throw new Error(`Invalid object ID: ${objectId}`);
}
const res = await provider.getObject({
id: objectId,
options: {
showContent: true,
},
});
return getFieldsFromObjectResponse(res);
};
export const getPackageId = async (provider, objectId) => {
let currentPackage;
let nextCursor;
do {
const dynamicFields = await provider.getDynamicFields({
parentId: objectId,
cursor: nextCursor,
});
currentPackage = dynamicFields.data.find((field) => field.name.type.endsWith("CurrentPackage"));
nextCursor = dynamicFields.hasNextPage ? dynamicFields.nextCursor : null;
} while (nextCursor && !currentPackage);
if (!currentPackage)
throw new Error("CurrentPackage not found");
const obj = await provider.getObject({
id: currentPackage.objectId,
options: {
showContent: true,
},
});
const fields = getFieldsFromObjectResponse(obj);
if (!fields || !isMoveStructObject(fields))
throw new Error("Unable to get fields from object response");
if (!("value" in fields) || !isMoveStructStruct(fields["value"]))
throw new Error("Unable to get package id");
return fields["value"].fields["package"];
};
export const getOldestEmitterCapObjectId = async (provider, coreBridgePackageId, owner) => {
let oldestVersion = null;
let oldestObjectId = null;
let response = null;
let nextCursor;
do {
response = await provider.getOwnedObjects({
owner,
filter: {
StructType: `${coreBridgePackageId}::emitter::EmitterCap`,
},
options: {
showContent: true,
},
cursor: nextCursor,
});
if (!response || !response.data) {
throw Error("Failed to get owned objects");
}
for (const objectResponse of response.data) {
if (!objectResponse.data)
continue;
const { version, objectId } = objectResponse.data;
if (oldestVersion === null || version < oldestVersion) {
oldestVersion = version;
oldestObjectId = objectId;
}
}
nextCursor = response.hasNextPage ? response.nextCursor : undefined;
} while (nextCursor);
return oldestObjectId;
};
export const getOwnedObjectId = async (provider, owner, type) => {
// Upgrade caps are a special case
if (isSameType(type, UPGRADE_CAP_TYPE)) {
throw new Error("`getOwnedObjectId` should not be used to get the object ID of an `UpgradeCap`. Use `getUpgradeCapObjectId` instead.");
}
try {
const res = await provider.getOwnedObjects({
owner,
filter: { StructType: type },
options: {
showContent: true,
},
});
if (!res || !res.data) {
throw new Error("Failed to get owned objects");
}
const objects = res.data.filter((o) => o.data?.objectId);
if (!objects || objects.length === 0)
return null;
if (objects.length === 1) {
return objects[0].data?.objectId ?? null;
}
else {
const objectsStr = JSON.stringify(objects, null, 2);
throw new Error(`Found multiple objects owned by ${owner} of type ${type}. This may mean that we've received an unexpected response from the Sui RPC and \`worm\` logic needs to be updated to handle this. Objects: ${objectsStr}`);
}
}
catch (error) {
// Handle 504 error by using findOwnedObjectByType method
const is504HttpError = `${error}`.includes("504 Gateway Time-out");
if (error && is504HttpError) {
return getOwnedObjectIdPaginated(provider, owner, type);
}
else {
throw error;
}
}
};
export const getOwnedObjectIdPaginated = async (provider, owner, type, cursor) => {
const res = await provider.getOwnedObjects({
owner,
filter: undefined, // Filter must be undefined to avoid 504 responses
cursor: cursor || undefined,
options: {
showType: true,
},
});
if (!res || !res.data) {
throw new Error("Could not fetch owned object id");
}
const object = res.data.find((d) => isSameType(d.data?.type || "", type));
if (!object && res.hasNextPage) {
return getOwnedObjectIdPaginated(provider, owner, type, res.nextCursor);
}
else if (!object && !res.hasNextPage) {
return null;
}
else {
return object?.data?.objectId ?? null;
}
};
export const getUpgradeCapObjectId = async (provider, owner, packageId) => {
const res = await provider.getOwnedObjects({
owner,
filter: { StructType: normalizeSuiType(UPGRADE_CAP_TYPE) },
options: {
showContent: true,
},
});
if (!res || !res.data) {
throw new Error("Failed to get upgrade caps");
}
const objects = res.data.filter((o) => {
const fields = getFieldsFromObjectResponse(o);
return (isMoveStructStruct(fields) &&
normalizeSuiAddress(fields.fields["package"]) === normalizeSuiAddress(packageId));
});
if (!objects || objects.length === 0)
return null;
if (objects.length === 1) {
// We've found the object we're looking for
return objects[0].data?.objectId ?? null;
}
else {
const objectsStr = JSON.stringify(objects, null, 2);
throw new Error(`Found multiple upgrade capabilities owned by ${owner} from package ${packageId}. Objects: ${objectsStr}`);
}
};
// Create a TransactionBlock to publish a package
export const publishPackage = async (buildOutput, signerAddress) => {
const tx = new Transaction();
const [upgradeCap] = tx.publish({
modules: buildOutput.modules.map((m) => Array.from(encoding.b64.decode(m))),
dependencies: buildOutput.dependencies.map((d) => normalizeSuiObjectId(d)),
});
// Transfer upgrade capability to recipient
tx.transferObjects([upgradeCap], tx.pure.address(signerAddress));
return tx;
};
export const newEmitterCap = (coreBridgePackageId, coreBridgeStateObjectId, owner) => {
const tx = new Transaction();
const [emitterCap] = tx.moveCall({
target: `${coreBridgePackageId}::emitter::new`,
arguments: [tx.object(coreBridgeStateObjectId)],
});
tx.transferObjects([emitterCap], tx.pure.address(owner));
return tx;
};
//# sourceMappingURL=utils.js.map