import { VM } from "./vm"; import { TableView } from "./table"; import { API, ABI, Name, NameType, PermissionLevel, PermissionLevelType, Serializer, Transaction, ABIDef, TransactionHeader, TimePoint } from "@greymass/eosio"; import { bnToBigInt, nameToBigInt } from "./bn"; import { Blockchain } from "./blockchain"; import { generatePermissions, addInlinePermission } from "./utils"; import BN from "bn.js"; export type AccountArgs = Omit, 'name'|'abi'|'wasm'> & { name: NameType, abi?: ABIDef | Promise; wasm?: Uint8Array | Promise; enableInline?: boolean; privileged?: boolean; } function isPromise(promise: any) { return !!promise && typeof promise.then === 'function' } export class Account { readonly name: Name; readonly bc: Blockchain; readonly creationTime: TimePoint; readonly actions: { [key: string]: (actionData?: any[] | object) => { send: (authorization?: string | PermissionLevelType | PermissionLevelType[], options?: Partial) => Promise } } = {}; readonly tables: { [key: string]: (scope?: bigint | BN) => TableView } = {}; // Privileged accounts aren't constrained by the normal permission checks public privileged: boolean = false; public permissions: API.v1.AccountPermission[] = []; public wasm?: Uint8Array; public codeSequence: number = 0; public abi?: ABI; public vm?: VM; constructor (args: AccountArgs) { this.name = Name.from(args.name) this.bc = args.bc this.creationTime = this.bc.timestamp // Permissions this.permissions = args.permissions || generatePermissions(this.name) if (args.enableInline) { addInlinePermission(this.name, this.permissions) } if (args.abi && args.wasm) { if (isPromise(args.abi) || isPromise(args.wasm)) { Promise .all([args.abi, args.wasm]) .then(([abi, wasm]) => this.setContract(abi, wasm)) } else { this.setContract(args.abi as ABIDef, args.wasm as Uint8Array) } } if (args.privileged) { this.privileged = true; } } get isContract () { return !!this.abi && !!this.wasm } toBigInt () { return nameToBigInt(this.name) } public async recreateVm () { if (this.wasm) { this.vm = VM.from(this.wasm, this.bc); await this.vm.ready } } setContract (abi: ABIDef, wasm: Uint8Array) { this.abi = ABI.from(abi) this.wasm = wasm this.codeSequence++; this.buildActions() this.buildTables() } setPermissions (permissions: API.v1.AccountPermission[]) { this.permissions = permissions } buildActions () { this.abi.actions.forEach((action) => { const resolved = this.abi.resolveType(action.name.toString()); this.actions[resolved.name] = (actionData: any[] | object) => { const data: Record = {}; if (Array.isArray(actionData)) { actionData.forEach((arg, i) => data[resolved.fields[i].name] = arg); } else { for (const field of resolved.fields) { if (!field.type.isOptional && !actionData.hasOwnProperty(field.name)) { throw new Error(`Missing field ${field.name} on action ${action.name}`); } if (actionData.hasOwnProperty(field.name)) { data[field.name] = actionData[field.name] } } } const serializedData = Serializer.encode({ abi: this.abi, type: action.name as string, object: data, }).array; return { send: async (authorization?: string | PermissionLevelType | PermissionLevelType[], options?: Partial) => { // .send() if (!authorization) { authorization = `${this.name}@active`; } // .send("account") else if ( typeof authorization == "string" && !authorization.includes("@") ) { authorization += '@active'; } await this.bc.applyTransaction(Transaction.from({ actions: [{ account: this.name, name: Name.from(action.name), data: serializedData, authorization: (Array.isArray(authorization) ? authorization : [authorization]).map(_ => PermissionLevel.from(_)) }], expiration: 0, ref_block_num: 0, ref_block_prefix: 0, ...(options || {}) }), data) } } } }); } buildTables () { this.abi.tables.forEach((table) => { const resolved = this.abi.resolveType(table.name as string); this.tables[resolved.name] = (scope: bigint | BN = nameToBigInt(this.name)): TableView => { if (BN.isBN(scope)) { scope = bnToBigInt(scope) } let tab = this.bc.store.findTable(nameToBigInt(this.name), scope, nameToBigInt(Name.from(resolved.name))); if (!tab) { tab = this.bc.store.createTable(nameToBigInt(this.name), scope, nameToBigInt(Name.from(resolved.name)), nameToBigInt(this.name)) } return new TableView(tab, this.abi, this.bc); } }); } }