UNPKG

@roochnetwork/rooch-sdk

Version:
203 lines (176 loc) 5.57 kB
// Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 import { bcs, BcsType, BcsTypeOptions } from '@mysten/bcs' import { address, Bytes } from '../types/index.js' import { bytes, CoderType, fromHEX, toHEX } from '../utils/index.js' import { normalizeRoochAddress, ROOCH_ADDRESS_LENGTH, convertToRoochAddressBytes, isValidAddress, } from '../address/index.js' import { Serializer } from './serializer.js' import type { BcsTypeTag as TypeTagType } from './types.js' type Merge<T> = T extends infer U ? { [K in keyof U]: U[K] } : never type EnumKindTransform<T> = T extends infer U ? Merge<(U[keyof U] extends null | boolean ? object : U[keyof U]) & { kind: keyof U }> : never function enumKind<T extends object, Input extends object>(type: BcsType<T, Input>) { return type.transform({ input: ({ kind, ...val }: EnumKindTransform<Input>) => ({ [kind]: val, }) as Input, output: (val) => { const key = Object.keys(val)[0] as keyof T return { kind: key, ...val[key] } as EnumKindTransform<T> }, }) } // TODO: support parse export const raw = <T, Input>( type: BcsType<T, Input>, options?: BcsTypeOptions<T[], Iterable<Input> & { length: number }>, ): BcsType<T[], Iterable<Input> & { length: number }> => { return new BcsType<T[], Iterable<Input> & { length: number }>({ name: `vector<${type.name}>`, read: (reader) => { const result: T[] = [] for (let i = 0; i < length; i++) { result[i] = type.read(reader) } return result }, write: (value, writer) => { for (const item of value) { type.write(item, writer) } }, ...options, validate: (value) => { options?.validate?.(value) if (!('length' in value)) { throw new TypeError(`Expected array, found ${typeof value}`) } }, }) } export const RawBytes = (coder: CoderType = 'hex') => { return raw(bcs.u8()).transform({ input: (input: string | Bytes) => (typeof input === 'string' ? bytes(coder, input) : input), output: (input) => input, }) } export const Vector = (coder: CoderType = 'hex') => { return bcs.vector(bcs.u8()).transform({ input: (input: string | Bytes) => (typeof input === 'string' ? bytes(coder, input) : input), output: (input) => new Uint8Array(input), }) } export const Address = bcs.bytes(ROOCH_ADDRESS_LENGTH).transform({ validate: (input) => { if (!isValidAddress(input)) { throw new Error(`Invalid address ${input}`) } }, input: (input: address) => convertToRoochAddressBytes(input), output: (val) => normalizeRoochAddress(toHEX(val)), }) export const MultiChainAddress = bcs.struct('MultiChainAddress', { multiChainId: bcs.u64().transform({ input: (input: number | bigint) => (typeof typeof input === 'number' ? BigInt(input) : input), output: (input) => BigInt(input), }), rawAddress: bcs.vector(bcs.u8()), }) export const ObjectId = bcs.vector(Address).transform({ input: (input: string | Bytes[]) => { if (typeof input === 'string') { const normalizeId = normalizeRoochAddress(input) let bytes = fromHEX(normalizeId) let addresses: Uint8Array[] = [] for (let i = 0; i < bytes.length; i += ROOCH_ADDRESS_LENGTH) { let chunk = bytes.slice(i, i + ROOCH_ADDRESS_LENGTH) if (chunk.length !== ROOCH_ADDRESS_LENGTH) { throw new Error('Invalid chunk size') } addresses.push(chunk) } return addresses } return input }, output: (val) => { return val.join('') }, }) const InnerTypeTag: BcsType<TypeTagType, TypeTagType> = bcs.enum('TypeTag', { bool: null, u8: null, u64: null, u128: null, address: null, signer: null, vector: bcs.lazy(() => InnerTypeTag), struct: bcs.lazy(() => StructTag), u16: null, u32: null, u256: null, }) as BcsType<TypeTagType> export const StructTag = bcs.struct('StructTag', { address: Address, module: bcs.string(), name: bcs.string(), typeParams: bcs.vector(InnerTypeTag), }) export const TypeTag = InnerTypeTag.transform({ input: (typeTag: string | TypeTagType) => typeof typeTag === 'string' ? Serializer.typeTagParseFromStr(typeTag, true) : typeTag, output: (typeTag: TypeTagType) => Serializer.tagToString(typeTag), }) export const BitcoinAuthPayload = bcs.struct('AuthPayload', { signature: Vector(), messagePrefix: Vector('utf8'), messageInfo: Vector('utf8'), publicKey: Vector('hex'), fromAddress: Vector('utf8'), }) export const ModuleId = bcs.struct('ModuleId', { address: Address, name: bcs.string(), }) export const FunctionId = bcs.struct('FunctionId', { moduleId: ModuleId, name: bcs.string(), }) export const ScriptCall = bcs.struct('ScriptCall', { code: RawBytes(), args: bcs.vector(bcs.u8()), typeArgs: bcs.vector(TypeTag), }) export const CallFunction = bcs.struct('FunctionCall', { functionId: FunctionId, typeArgs: bcs.vector(TypeTag), args: bcs.vector(bcs.vector(bcs.u8())), }) export const MoveAction = enumKind( bcs.enum('MoveAction', { ScriptCall, CallFunction, }), ) export const RoochTransactionData = bcs.struct('RoochTransactionData', { sender: Address, sequenceNumber: bcs.u64(), chainId: bcs.u64(), maxGas: bcs.u64(), action: MoveAction, }) export const Authenticator = bcs.struct('Authenticator', { authValidatorId: bcs.u64(), payload: bcs.vector(bcs.u8()), }) export const RoochTransaction = bcs.struct('RoochTransaction', { data: raw(bcs.u8()), auth: raw(bcs.u8()), })