UNPKG

@0xobelisk/rooch-client

Version:
474 lines (428 loc) 13 kB
import { getRoochNodeUrl, Args, TypeTag, AnnotatedFunctionResultView, Secp256k1Keypair, ExecuteTransactionResponseView, ModuleABIView, BalanceInfoView, Transaction, GetStatesParams, ObjectStateView, ListStatesParams, PaginatedStateKVViews, GetEventsByEventHandleParams, PaginatedEventViews, address, TypeArgs, } from '@roochnetwork/rooch-sdk'; import { RoochAccountManager } from './libs/roochAccountManager'; import { RoochInteractor } from './libs/roochInteractor'; import { RoochContractFactory } from './libs/roochContractFactory'; import { DubheParams, DerivePathParams, ContractQuery, ContractTx, MapModuleFuncQuery, MapModuleFuncTx, MoveModuleFuncType, } from './types'; export function isUndefined(value?: unknown): value is undefined { return value === undefined; } export function withMeta<T extends { meta: MoveModuleFuncType }>( meta: MoveModuleFuncType, creator: Omit<T, 'meta'> ): T { (creator as T).meta = meta; return creator as T; } function createQuery( meta: MoveModuleFuncType, fn: ( params?: Args[], typeArguments?: TypeTag[] ) => Promise<AnnotatedFunctionResultView> ): ContractQuery { return withMeta( meta, async ({ params, typeArguments, }: { params?: Args[]; typeArguments?: TypeTag[]; } = {}): Promise<AnnotatedFunctionResultView> => { const result = await fn(params, typeArguments); return result; } ); } function createTx( meta: MoveModuleFuncType, fn: ( tx: Transaction, signer?: Secp256k1Keypair, params?: Args[], typeArguments?: TypeTag[], isRaw?: boolean ) => Promise<ExecuteTransactionResponseView> ): ContractTx { return withMeta( meta, async ({ tx, signer, params, typeArguments, isRaw, }: { tx: Transaction; signer?: Secp256k1Keypair; params?: Args[]; typeArguments?: TypeTag[]; isRaw?: boolean; }): Promise<ExecuteTransactionResponseView> => { // const result = await fn(signer, params, typeArguments, isRaw); const result = await fn(tx, signer, params, typeArguments, isRaw); return result; } ); } /** * @class Dubhe * @description This class is used to aggregate the tools that used to interact with SUI network. */ export class Dubhe { public accountManager: RoochAccountManager; public roochInteractor: RoochInteractor; public contractFactory: RoochContractFactory; public packageId: string | undefined; public metadata: ModuleABIView[] | undefined; readonly #query: MapModuleFuncQuery = {}; readonly #tx: MapModuleFuncTx = {}; /** * Support the following ways to init the DubheClient: * 1. mnemonics * 2. secretKey (base64 or hex) * If none of them is provided, will generate a random mnemonics with 24 words. * * @param mnemonics, 12 or 24 mnemonics words, separated by space * @param secretKey, base64 or hex string, when mnemonics is provided, secretKey will be ignored * @param networkType, 'testnet' | 'mainnet' | 'devnet' | 'localnet', default is 'devnet' * @param fullnodeUrl, the fullnode url, default is the preconfig fullnode url for the given network type * @param packageId */ constructor({ mnemonics, secretKey, networkType, fullnodeUrls, packageId, metadata, }: DubheParams = {}) { // Init the account manager this.accountManager = new RoochAccountManager({ mnemonics, secretKey }); // Init the rpc provider fullnodeUrls = fullnodeUrls || [getRoochNodeUrl(networkType ?? 'mainnet')]; this.roochInteractor = new RoochInteractor(fullnodeUrls); this.packageId = packageId; if (metadata !== undefined) { this.metadata = metadata as ModuleABIView[]; Object.values(metadata as ModuleABIView[]).forEach((metadataRes) => { let contractAddress = metadataRes.address; let moduleName = metadataRes.name; Object.values(metadataRes.functions).forEach((value) => { const meta: MoveModuleFuncType = { contractAddress, moduleName, funcName: value.name, isEntry: value.is_entry, typeParams: value.type_params, params: value.params, return: value.return, }; // if (value.is_view) { if (isUndefined(this.#query[moduleName])) { this.#query[moduleName] = {}; } if (isUndefined(this.#query[moduleName][value.name])) { this.#query[moduleName][value.name] = createQuery( meta, (p, type_p) => this.#read(meta, p, type_p) ); } // } // if (value.is_entry) { if (isUndefined(this.#tx[moduleName])) { this.#tx[moduleName] = {}; } if (isUndefined(this.#tx[moduleName][value.name])) { this.#tx[moduleName][value.name] = createTx( meta, (tx, s, p, type_p, isRaw) => this.#exec(meta, tx, s, p, type_p, isRaw) ); } // } }); }); } this.contractFactory = new RoochContractFactory({ packageId, metadata, }); } public get query(): MapModuleFuncQuery { return this.#query; } public get tx(): MapModuleFuncTx { return this.#tx; } #exec = async ( meta: MoveModuleFuncType, tx: Transaction, signer?: Secp256k1Keypair, params?: Args[], typeArguments?: TypeTag[], isRaw?: boolean ) => { if (isRaw === true) { return tx.callFunction({ target: `${this.contractFactory.packageId}::${meta.moduleName}::${meta.funcName}`, args: params, typeArgs: typeArguments, }); } tx.callFunction({ target: `${this.contractFactory.packageId}::${meta.moduleName}::${meta.funcName}`, args: params, typeArgs: typeArguments, }); return await this.signAndExecuteTransaction(tx, signer); }; #read = async ( meta: MoveModuleFuncType, params?: Args[], typeArguments?: TypeTag[] ) => { return this.roochInteractor.executeViewFunction( meta.contractAddress, meta.moduleName, meta.funcName, typeArguments, params ); }; /** * if derivePathParams is not provided or mnemonics is empty, it will return the keypair. * else: * it will generate signer from the mnemonic with the given derivePathParams. * @param derivePathParams, such as { accountIndex: 2, isExternal: false, addressIndex: 10 }, comply with the BIP44 standard */ getKeypair(derivePathParams?: DerivePathParams) { return this.accountManager.getKeyPair(derivePathParams); } /** * if derivePathParams is not provided or mnemonics is empty, it will return the currentSigner. * else: * it will generate signer from the mnemonic with the given derivePathParams. * @param derivePathParams, such as { accountIndex: 2, isExternal: false, addressIndex: 10 }, comply with the BIP44 standard */ getSigner(derivePathParams?: DerivePathParams) { const keyPair = this.accountManager.getKeyPair(derivePathParams); return keyPair; } /** * @description Switch the current account with the given derivePathParams * @param derivePathParams, such as { accountIndex: 2, isExternal: false, addressIndex: 10 }, comply with the BIP44 standard */ switchAccount(derivePathParams: DerivePathParams) { this.accountManager.switchAccount(derivePathParams); } /** * @description Get the address of the account for the given derivePathParams * @param derivePathParams, such as { accountIndex: 2, isExternal: false, addressIndex: 10 }, comply with the BIP44 standard */ getBech32Address(derivePathParams?: DerivePathParams) { return this.accountManager.getAddress(derivePathParams).toBech32Address(); } getRoochAddress(derivePathParams?: DerivePathParams) { return this.accountManager.getAddress(derivePathParams); } getHexAddress(derivePathParams?: DerivePathParams) { return this.accountManager.getAddress(derivePathParams).toHexAddress(); } getBitcoinAddress(derivePathParams?: DerivePathParams) { return this.accountManager.getBitcoinAddress(derivePathParams); } currentAddress() { return this.accountManager.currentAddress; } client() { return this.roochInteractor.currentClient; } getPackageId() { return this.contractFactory.packageId; } getMetadata(): ModuleABIView[] | undefined { return this.contractFactory.metadata; } getNetwork() { return this.roochInteractor.network; } getFullNodeUrl() { return this.roochInteractor.currentFullNode; } async getRpcApiVersion(): Promise<string | undefined> { return this.roochInteractor.getRpcApiVersion(); } async getChainId(): Promise<bigint> { return this.roochInteractor.getChainId(); } async getBalance( accountAddress?: string, coinType?: string, outputOnly?: boolean ): Promise<BalanceInfoView | string> { try { if (accountAddress === undefined) { accountAddress = this.getBech32Address(); } if (coinType === undefined) { coinType = '0x3::gas_coin::RGas'; } const resource = await this.roochInteractor.getBalance( accountAddress, coinType ); return outputOnly ? resource : resource.balance; } catch (err) { const errorMessage = err instanceof Error ? err.message : String(err); throw new Error( `Failed to get ${coinType} balance for address ${accountAddress}: ${errorMessage}` ); } } async signAndExecuteTransaction( transaction: Transaction, signer?: Secp256k1Keypair, derivePathParams?: DerivePathParams ) { if (signer === undefined) { signer = this.getSigner(derivePathParams); } return this.roochInteractor.signAndExecuteTransaction(transaction, signer, { withOutput: true, }); } async signAndSendTransaction( tx: Transaction, derivePathParams?: DerivePathParams ) { const sender = this.getSigner(derivePathParams); return this.signAndExecuteTransaction(tx, sender); } async createSession( sessionArgs: { appName: string; appUrl: string; scopes: string[]; }, signer?: Secp256k1Keypair, derivePathParams?: DerivePathParams ) { if (signer === undefined) { signer = this.getSigner(derivePathParams); } return this.roochInteractor.createSession(sessionArgs, signer); } async moveCall(callParams: { target: string; params?: Args[]; typeArguments?: TypeTag[]; signer?: Secp256k1Keypair; derivePathParams?: DerivePathParams; }) { const { target, params = [], typeArguments = [], signer, derivePathParams, } = callParams; const effectiveSigner = signer ?? this.getSigner(derivePathParams); console.log('effectiveSigner', effectiveSigner.getRoochAddress().toStr()); const tx = new Transaction(); tx.callFunction({ target, args: params, typeArgs: typeArguments, }); return this.signAndExecuteTransaction( tx, effectiveSigner, derivePathParams ); } async publishPackage(callParams: { packageBytes: Uint8Array; signer?: Secp256k1Keypair; derivePathParams?: DerivePathParams; }) { const { packageBytes, signer, derivePathParams } = callParams; return this.moveCall({ target: `0x2::module_store::publish_package_entry`, params: [Args.vec('u8', Array.from(packageBytes))], signer, derivePathParams, }); } async getStates(params: GetStatesParams): Promise<ObjectStateView[]> { return this.roochInteractor.getStates(params); } async listStates(params: ListStatesParams): Promise<PaginatedStateKVViews> { return this.roochInteractor.listStates(params); } async getEvents( input: GetEventsByEventHandleParams ): Promise<PaginatedEventViews> { return this.roochInteractor.getEvents(input); } async transfer( input: { recipient: address; amount: number | bigint; coinType: TypeArgs; }, signer?: Secp256k1Keypair, derivePathParams?: DerivePathParams ): Promise<ExecuteTransactionResponseView> { if (signer === undefined) { signer = this.getSigner(derivePathParams); } return this.roochInteractor.transfer({ ...input, signer, }); } async transferObject( input: { recipient: address; objectId: string; objectType: TypeArgs; }, signer?: Secp256k1Keypair, derivePathParams?: DerivePathParams ): Promise<ExecuteTransactionResponseView> { if (signer === undefined) { signer = this.getSigner(derivePathParams); } return this.roochInteractor.transferObject({ ...input, signer, }); } }