UNPKG

@mysten/suins

Version:
235 lines (201 loc) 5.96 kB
import { bcs, BcsType, TypeTag, TypeTagSerializer, BcsStruct, BcsEnum, BcsTuple, } from '@mysten/sui/bcs'; import { normalizeSuiAddress } from '@mysten/sui/utils'; import { TransactionArgument, isArgument } from '@mysten/sui/transactions'; import { ClientWithCoreApi, SuiClientTypes } from '@mysten/sui/client'; const MOVE_STDLIB_ADDRESS = normalizeSuiAddress('0x1'); const SUI_FRAMEWORK_ADDRESS = normalizeSuiAddress('0x2'); export type RawTransactionArgument<T> = T | TransactionArgument; export interface GetOptions< Include extends Omit<SuiClientTypes.ObjectInclude, 'content'> = {}, > extends SuiClientTypes.GetObjectOptions<Include> { client: ClientWithCoreApi; } export interface GetManyOptions< Include extends Omit<SuiClientTypes.ObjectInclude, 'content'> = {}, > extends SuiClientTypes.GetObjectsOptions<Include> { client: ClientWithCoreApi; } export function getPureBcsSchema(typeTag: string | TypeTag): BcsType<any> | null { const parsedTag = typeof typeTag === 'string' ? TypeTagSerializer.parseFromStr(typeTag) : typeTag; if ('u8' in parsedTag) { return bcs.U8; } else if ('u16' in parsedTag) { return bcs.U16; } else if ('u32' in parsedTag) { return bcs.U32; } else if ('u64' in parsedTag) { return bcs.U64; } else if ('u128' in parsedTag) { return bcs.U128; } else if ('u256' in parsedTag) { return bcs.U256; } else if ('address' in parsedTag) { return bcs.Address; } else if ('bool' in parsedTag) { return bcs.Bool; } else if ('vector' in parsedTag) { const type = getPureBcsSchema(parsedTag.vector); return type ? bcs.vector(type) : null; } else if ('struct' in parsedTag) { const structTag = parsedTag.struct; const pkg = normalizeSuiAddress(structTag.address); if (pkg === MOVE_STDLIB_ADDRESS) { if ( (structTag.module === 'ascii' || structTag.module === 'string') && structTag.name === 'String' ) { return bcs.String; } if (structTag.module === 'option' && structTag.name === 'Option') { const type = getPureBcsSchema(structTag.typeParams[0]); return type ? bcs.option(type) : null; } } if ( pkg === SUI_FRAMEWORK_ADDRESS && structTag.module === 'object' && (structTag.name === 'ID' || structTag.name === 'UID') ) { return bcs.Address; } } return null; } export function normalizeMoveArguments( args: unknown[] | object, argTypes: readonly (string | null)[], parameterNames?: string[], ) { const argLen = Array.isArray(args) ? args.length : Object.keys(args).length; if (parameterNames && argLen !== parameterNames.length) { throw new Error( `Invalid number of arguments, expected ${parameterNames.length}, got ${argLen}`, ); } const normalizedArgs: TransactionArgument[] = []; let index = 0; for (const [i, argType] of argTypes.entries()) { if (argType === '0x2::clock::Clock') { normalizedArgs.push((tx) => tx.object.clock()); continue; } if (argType === '0x2::random::Random') { normalizedArgs.push((tx) => tx.object.random()); continue; } if (argType === '0x2::deny_list::DenyList') { normalizedArgs.push((tx) => tx.object.denyList()); continue; } if (argType === '0x3::sui_system::SuiSystemState') { normalizedArgs.push((tx) => tx.object.system()); continue; } let arg; if (Array.isArray(args)) { if (index >= args.length) { throw new Error( `Invalid number of arguments, expected at least ${index + 1}, got ${args.length}`, ); } arg = args[index]; } else { if (!parameterNames) { throw new Error(`Expected arguments to be passed as an array`); } const name = parameterNames[index]; arg = args[name as keyof typeof args]; if (arg === undefined) { throw new Error(`Parameter ${name} is required`); } } index += 1; if (typeof arg === 'function' || isArgument(arg)) { normalizedArgs.push(arg as TransactionArgument); continue; } const type = argTypes[i]; const bcsType = type === null ? null : getPureBcsSchema(type); if (bcsType) { const bytes = bcsType.serialize(arg as never); normalizedArgs.push((tx) => tx.pure(bytes)); continue; } else if (typeof arg === 'string') { normalizedArgs.push((tx) => tx.object(arg)); continue; } throw new Error(`Invalid argument ${stringify(arg)} for type ${type}`); } return normalizedArgs; } export class MoveStruct< T extends Record<string, BcsType<any>>, const Name extends string = string, > extends BcsStruct<T, Name> { async get<Include extends Omit<SuiClientTypes.ObjectInclude, 'content' | 'json'> = {}>({ objectId, ...options }: GetOptions<Include>): Promise< SuiClientTypes.Object<Include & { content: true; json: true }> & { json: BcsStruct<T>['$inferType']; } > { const [res] = await this.getMany<Include>({ ...options, objectIds: [objectId], }); return res; } async getMany<Include extends Omit<SuiClientTypes.ObjectInclude, 'content' | 'json'> = {}>({ client, ...options }: GetManyOptions<Include>): Promise< Array< SuiClientTypes.Object<Include & { content: true; json: true }> & { json: BcsStruct<T>['$inferType']; } > > { const response = (await client.core.getObjects({ ...options, include: { ...options.include, content: true, }, })) as SuiClientTypes.GetObjectsResponse<Include & { content: true }>; return response.objects.map((obj) => { if (obj instanceof Error) { throw obj; } return { ...obj, json: this.parse(obj.content), }; }); } } export class MoveEnum< T extends Record<string, BcsType<any> | null>, const Name extends string, > extends BcsEnum<T, Name> {} export class MoveTuple< const T extends readonly BcsType<any>[], const Name extends string, > extends BcsTuple<T, Name> {} function stringify(val: unknown) { if (typeof val === 'object') { return JSON.stringify(val, (val: unknown) => val); } if (typeof val === 'bigint') { return val.toString(); } return val; }