UNPKG

@yubing744/rooch-sdk

Version:
260 lines (230 loc) 7.88 kB
// Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 import { hexlify } from '@ethersproject/bytes' import { fromHexString } from './hex' import { ROOCH_ADDRESS_LENGTH } from '../constants' import { AccountAddress, FunctionId, TypeTag, StructTag, Arg } from '../types' import * as rooch_types from '../types/bcs' import { bytes as Bytes, Seq, Tuple, ListTuple, uint8, U256, BcsSerializer, Serializable, } from '../types/bcs' import { parseFunctionId, normalizeRoochAddress, structTagToObjectID } from './encode' export function encodeFunctionCall( functionId: FunctionId, tyArgs: TypeTag[], args: Bytes[], ): rooch_types.MoveActionVariantFunction { const funcId = parseFunctionId(functionId) const functionCall = new rooch_types.FunctionCall( new rooch_types.FunctionId( new rooch_types.ModuleId( addressToSCS(funcId.address), new rooch_types.Identifier(funcId.module), ), new rooch_types.Identifier(funcId.functionName), ), tyArgs.map((t) => typeTagToSCS(t)), bytesArrayToSeqSeq(args), ) return new rooch_types.MoveActionVariantFunction(functionCall) } export function typeTagToSCS(ty: TypeTag): rooch_types.TypeTag { if (ty === 'Bool') { return new rooch_types.TypeTagVariantbool() } if (ty === 'U8') { return new rooch_types.TypeTagVariantu8() } if (ty === 'U16') { return new rooch_types.TypeTagVariantu16() } if (ty === 'U32') { return new rooch_types.TypeTagVariantu32() } if (ty === 'U64') { return new rooch_types.TypeTagVariantu64() } if (ty === 'U128') { return new rooch_types.TypeTagVariantu128() } if (ty === 'U256') { return new rooch_types.TypeTagVariantu256() } if (ty === 'Address') { return new rooch_types.TypeTagVariantaddress() } if (ty === 'Signer') { return new rooch_types.TypeTagVariantsigner() } if ((ty as { Vector: TypeTag }).Vector) { return new rooch_types.TypeTagVariantvector(typeTagToSCS((ty as { Vector: TypeTag }).Vector)) } if ((ty as { Struct: StructTag }).Struct) { return new rooch_types.TypeTagVariantstruct( structTagToSCS((ty as { Struct: StructTag }).Struct), ) } throw new Error(`invalid type tag: ${ty}`) } export function structTagToSCS(data: StructTag): rooch_types.StructTag { return new rooch_types.StructTag( addressToSCS(data.address), new rooch_types.Identifier(data.module), new rooch_types.Identifier(data.name), data.type_params ? data.type_params.map((t) => typeTagToSCS(t)) : [], ) } export function addressToSCS(addr: AccountAddress): rooch_types.AccountAddress { // AccountAddress should be 16 bytes, in hex, it's 16 * 2. const bytes = fromHexString(addr, 16 * 2) const data: [number][] = [] for (let i = 0; i < bytes.length; i++) { data.push([bytes[i]]) } return new rooch_types.AccountAddress(data) } export function encodeStructTypeTags(typeArgsString: string[]): TypeTag[] { return typeArgsString.map((str) => encodeStructTypeTag(str)) } export function encodeStructTypeTag(str: string): TypeTag { const arr = str.split('<') const arr1 = arr[0].split('::') const address = arr1[0] const module = arr1[1] const name = arr1[2] const params = arr[1] ? arr[1].replace('>', '').split(',') : [] // eslint-disable-next-line @typescript-eslint/naming-convention const type_params: TypeTag[] = [] if (params.length > 0) { params.forEach((param: string) => { type_params.push(encodeStructTypeTag(param.trim())) }) } const result: TypeTag = { Struct: { address, module, name, type_params, }, } return result } function bytesToSeq(byteArray: Bytes): Seq<number> { return Array.from(byteArray) } function stringToSeq(str: string): Seq<number> { const seq = new Array<number>() for (let i = 0; i < str.length; i++) { seq.push(str.charCodeAt(i)) } return seq } function bytesArrayToSeqSeq(input: Bytes[]): Seq<Seq<number>> { return input.map((byteArray) => bytesToSeq(byteArray)) } export function addressToListTuple(ethAddress: string): ListTuple<[uint8]> { // Remove '0x' prefix const cleanedEthAddress = ethAddress.startsWith('0x') ? ethAddress.slice(2) : ethAddress // Check if the address is valid if (cleanedEthAddress.length !== ROOCH_ADDRESS_LENGTH) { throw new Error('Invalid Rooch address') } // Convert to list of tuples const listTuple: ListTuple<[uint8]> = [] for (let i = 0; i < cleanedEthAddress.length; i += 2) { const byte = parseInt(cleanedEthAddress.slice(i, i + 2), 16) listTuple.push([byte] as Tuple<[uint8]>) } return listTuple } export function addressToSeqNumber(ethAddress: string): Seq<number> { // Remove '0x' prefix const cleanedEthAddress = ethAddress.startsWith('0x') ? ethAddress.slice(2) : ethAddress // Convert to list of tuples const seqNumber: Seq<number> = [] for (let i = 0; i < cleanedEthAddress.length; i += 2) { const byte = parseInt(cleanedEthAddress.slice(i, i + 2), 16) seqNumber.push(byte) } return seqNumber } function serializeValue(value: any, type: TypeTag, se: BcsSerializer) { if (type === 'Bool') { se.serializeBool(value) } else if (type === 'U8') { se.serializeU8(value) } else if (type === 'U16') { se.serializeU16(value) } else if (type === 'U32') { se.serializeU32(value) } else if (type === 'U64') { se.serializeU64(value) } else if (type === 'U128') { se.serializeU128(value) } else if (type === 'U256') { const u256 = new U256(value) u256.serialize(se) } else if (type === 'Address') { const list = addressToListTuple(normalizeRoochAddress(value as string)) const accountAddress = new rooch_types.AccountAddress(list) accountAddress.serialize(se) } else if (type === 'Ascii') { const bytes = stringToSeq(value as string) const moveAsciiString = new rooch_types.MoveAsciiString(bytes) moveAsciiString.serialize(se) } else if (type === 'String') { const bytes = stringToSeq(value as string) const moveString = new rooch_types.MoveString(bytes) moveString.serialize(se) } else if ((type as { Vector: TypeTag }).Vector) { const vectorValues = value as any[] se.serializeLen(vectorValues.length) for (let item of vectorValues) { serializeValue(item, (type as { Vector: TypeTag }).Vector, se) } } else if ((type as { Struct: StructTag }).Struct) { const serializable = value as Serializable serializable.serialize(se) } else if (type === 'ObjectID') { const list = addressToListTuple(normalizeRoochAddress(value as string)) const accountAddress = new rooch_types.AccountAddress(list) accountAddress.serialize(se) } else if (type === 'Object') { const objectId = structTagToObjectID(value as StructTag) const list = addressToListTuple(normalizeRoochAddress(objectId)) const accountAddress = new rooch_types.AccountAddress(list) accountAddress.serialize(se) } else if (type === 'Raw') { const vectorValues = value as Uint8Array se.serializeLen(vectorValues.length) for (let item of vectorValues) { se.serializeU8(item) } } } export function encodeArg(arg: Arg): Bytes { const se = new BcsSerializer() serializeValue(arg.value, arg.type, se) return se.getBytes() } export const encodeMoveCallData = (funcId: FunctionId, tyArgs: TypeTag[], args: Arg[]) => { const bcsArgs = args?.map((arg) => encodeArg(arg)) const scriptFunction = encodeFunctionCall(funcId, tyArgs, bcsArgs) const payloadInHex = (() => { const se = new BcsSerializer() scriptFunction.serialize(se) return se.getBytes() })() return payloadInHex } export const encodeMoveCallDataWithETH = (funcId: FunctionId, tyArgs: TypeTag[], args: Arg[]) => { return hexlify(encodeMoveCallData(funcId, tyArgs, args)) }