UNPKG

@yubing744/rooch-sdk

Version:
285 lines (284 loc) 7.97 kB
import { DEFAULT_MAX_GAS_AMOUNT } from "../constants"; import { PrivateKeyAuth } from "../auth"; import { BcsSerializer } from "../types/bcs"; import { RoochTransaction, RoochTransactionData, AccountAddress as BCSAccountAddress, Authenticator } from "../generated/runtime/rooch_types/mod"; import { encodeArg, encodeFunctionCall, addressToListTuple, uint8Array2SeqNumber, addressToSeqNumber, encodeStructTypeTag } from "../utils"; import { Ed25519Keypair } from "../utils/keypairs"; const SCOPE_LENGTH = 3; const SCOPE_MODULE_ADDRESSS = 0; const SCOPE_MODULE_NAMES = 1; const SCOPE_FUNCTION_NAMES = 2; class Account { constructor(client, address, authorizer) { this.client = client; this.address = address; this.authorizer = authorizer; } async makeAuth(tsData) { const payload = (() => { const se = new BcsSerializer(); tsData.serialize(se); return se.getBytes(); })(); return this.authorizer.auth(payload); } parseStateToSessionKey(data) { const result = new Array(); for (const state of data.data) { const moveValue = state?.state.decoded_value; if (moveValue) { const val = moveValue.value; result.push({ authentication_key: val.authentication_key, scopes: this.parseScopes(val.scopes), create_time: parseInt(val.create_time), last_active_time: parseInt(val.last_active_time), max_inactive_interval: parseInt(val.max_inactive_interval) }); } } return result; } parseScopes(data) { const result = new Array(); for (const scope of data) { result.push(`${scope.module_name}::${scope.module_address}::${scope.function_name}`); } return result; } async getSequenceNumber() { const resp = await this.client.executeViewFunction({ funcId: "0x3::account::sequence_number", tyArgs: [], args: [ { type: "Address", value: this.address } ] }); if (resp && resp.return_values) { return resp.return_values[0].decoded_value; } return 0; } /** * Get account address */ getAddress() { return this.address; } /** * Run move function by current account * * @param funcId FunctionId the function like '0x3::empty::empty' * @param tyArgs Generic parameter list * @param args parameter list * @param opts Call option */ async runFunction(funcId, tyArgs, args, opts) { const number = await this.getSequenceNumber(); const bcsArgs = args.map((arg) => encodeArg(arg)); const scriptFunction = encodeFunctionCall(funcId, tyArgs, bcsArgs); const txData = new RoochTransactionData( new BCSAccountAddress(addressToListTuple(this.address)), BigInt(number), BigInt(this.client.getChainId()), BigInt(opts.maxGasAmount ?? DEFAULT_MAX_GAS_AMOUNT), scriptFunction ); const authResult = await this.makeAuth(txData); const auth = new Authenticator( BigInt(authResult.scheme), uint8Array2SeqNumber(authResult.payload) ); const ts = new RoochTransaction(txData, auth); const payload = (() => { const se = new BcsSerializer(); ts.serialize(se); return se.getBytes(); })(); return this.client.sendRawTransaction(payload); } async createSessionAccount(scope, maxInactiveInterval, opts) { const kp = Ed25519Keypair.generate(); await this.registerSessionKey( kp.getPublicKey().toRoochAddress(), scope, maxInactiveInterval, opts ); const auth = new PrivateKeyAuth(kp); return new Account(this.client, this.address, auth); } async registerSessionKey(authKey, scopes, maxInactiveInterval, opts) { const [scopeModuleAddresss, scopeModuleNames, scopeFunctionNames] = scopes.map((scope) => { const parts = scope.split("::"); if (parts.length !== SCOPE_LENGTH) { throw new Error("invalid scope"); } const scopeModuleAddress = parts[SCOPE_MODULE_NAMES]; const scopeModuleName = parts[SCOPE_MODULE_ADDRESSS]; const scopeFunctionName = parts[SCOPE_FUNCTION_NAMES]; return [scopeModuleAddress, scopeModuleName, scopeFunctionName]; }).reduce( (acc, val) => { acc[0].push(val[SCOPE_MODULE_NAMES]); acc[1].push(val[SCOPE_MODULE_ADDRESSS]); acc[2].push(val[SCOPE_FUNCTION_NAMES]); return acc; }, [[], [], []] ); await this.runFunction( "0x3::session_key::create_session_key_with_multi_scope_entry", [], [ { type: { Vector: "U8" }, value: addressToSeqNumber(authKey) }, { type: { Vector: "Address" }, value: scopeModuleAddresss }, { type: { Vector: "Ascii" }, value: scopeModuleNames }, { type: { Vector: "Ascii" }, value: scopeFunctionNames }, { type: "U64", value: BigInt(maxInactiveInterval) } ], opts || { maxGasAmount: 1e8 } ); } /** * Remove session key * * @param authKey * @param opts */ async removeSessionKey(authKey, opts) { return await this.runFunction( "0x3::session_key::remove_session_key_entry", [], [ { type: { Vector: "U8" }, value: addressToSeqNumber(authKey) } ], opts || { maxGasAmount: 1e8 } ); } /** * Query account's sessionKey * * @param cursor The page cursor * @param limit The page limit */ async querySessionKeys(cursor, limit) { const accessPath = `/resource/${this.address}/0x3::session_key::SessionKeys`; const state = await this.client.getStates(accessPath); if (state) { const stateView = state; const tableId = stateView[0].value; const accessPath2 = `/table/${tableId}`; const pageView = await this.client.listStates({ accessPath: accessPath2, cursor, limit }); return { data: this.parseStateToSessionKey(pageView), nextCursor: pageView.next_cursor, hasNextPage: pageView.has_next_page }; } throw new Error("not found state"); } /** * Check session key whether expired * * @param authKey the auth key */ async isSessionKeyExpired(authKey) { const result = await this.client.executeViewFunction({ funcId: "0x3::session_key::is_expired_session_key", tyArgs: [], args: [ { type: "Address", value: this.address }, { type: { Vector: "U8" }, value: addressToSeqNumber(authKey) } ] }); if (result && result.vm_status !== "Executed") { throw new Error("view 0x3::session_key::is_expired_session_key fail"); } return result.return_values[0].decoded_value; } async gasCoinBalance() { const result = await this.client.executeViewFunction({ funcId: "0x3::gas_coin::balance", tyArgs: [], args: [ { type: "Address", value: this.getAddress() } ] }); if (result && result.vm_status !== "Executed") { throw new Error("view 0x3::gas_coin::balance fail"); } return BigInt(result.return_values[0].decoded_value); } async coinBalance(coinType) { const structType = encodeStructTypeTag(coinType); const result = await this.client.executeViewFunction({ funcId: "0x3::account_coin_store::balance", tyArgs: [structType], args: [ { type: "Address", value: this.getAddress() } ] }); if (result && result.vm_status !== "Executed") { throw new Error("view 0x3::account_coin_store::balance fail"); } return BigInt(result.return_values[0].decoded_value); } } export { Account }; //# sourceMappingURL=account.js.map