UNPKG

wowok_agent

Version:

Create, collaborate, and transact on your own terms with the AI-driven web3 collaboration protocol.

160 lines (137 loc) 6.54 kB
import { Protocol, Entity, Resource, TxbAddress, array_unique, TagName, ResourceObject, PassportObject, Errors, ERROR, Permission, PermissionIndexType, GuardParser, Passport, WitnessFill, CallResponse, TransactionBlock } from 'wowok'; import { query_permission } from '../permission'; import { Account } from '../account'; import { ObjectBase, queryTableItem_Personal, raw2type} from '../objects'; export interface Namedbject { name?: string; tags?: string[]; } export interface AddressMark { address: TxbAddress; name?: string; tags: string[]; } export interface ResponseData extends ObjectBase { change:'created' | 'mutated' | string; } export interface GuardInfo_forCall { guard: string[]; witness: WitnessFill[]; } export type CallResult = GuardInfo_forCall | CallResponse | undefined; export function ResponseData(response: CallResponse | undefined) : ResponseData[] { const res : ResponseData[] = []; response?.objectChanges?.forEach(v => { const type_raw: string | undefined = (v as any)?.objectType; const type = raw2type(type_raw); if (type) { res.push({type:type, type_raw:type_raw, object:(v as any)?.objectId, version:(v as any)?.version, owner:(v as any)?.owner, change:v.type }) } }) return res; } export class CallBase { // operation implementation for a call resouceObject:ResourceObject | undefined; protected async operate(txb:TransactionBlock, passport?:PassportObject, account?:string) {}; constructor () {} // return WitnessFill to resolve filling witness, and than 'call_with_witness' to complete the call; // return ResponseData when the call has completed; // throw an exception when errors. async call(account?:string) : Promise<CallResult> { return undefined }; async call_with_witness (info: GuardInfo_forCall, account?:string) : Promise<CallResponse | undefined> { if (info.guard.length > 0) { // prepare passport const p: GuardParser | undefined = await GuardParser.Create([...info.guard]); if (p) { const query = await p.done(info.witness); if (query) { const txb = new TransactionBlock(); const passport = new Passport(txb, query!); await this.operate(txb, passport?.get_object(), account) passport.destroy(); return await this.sign_and_commit(txb, account); } } else { ERROR(Errors.Fail, 'guard verify') } } } protected async check_permission_and_call (permission:string, permIndex: PermissionIndexType[], guards_needed: string[], checkOwner?:boolean, checkAdmin?:boolean, account?:string) : Promise<CallResult> { var guards : string[] = []; if (permIndex.length > 0 || checkOwner) { const addr = await Account.Instance().get_address(account); if (!addr) ERROR(Errors.InvalidParam, 'check_permission_and_call: account invalid'); const p = await query_permission({permission_object:permission, address:addr!}); if (checkOwner && !p.owner) ERROR(Errors.noPermission, 'owner'); if (checkAdmin && !p.admin) ERROR(Errors.noPermission, 'admin'); permIndex.forEach(v => { const r = Permission.HasPermission(p, v); if (!r?.has) ERROR(Errors.noPermission, v); if (r?.guard) guards.push(r.guard) }) } if (guards_needed.length > 0) { guards = guards.concat(guards_needed); } if (guards.length > 0) { // prepare passport const p: GuardParser | undefined = await GuardParser.Create([...guards]); const futures = p ? p.future_fills() : []; if (!p) ERROR(Errors.Fail, 'guard parse') if (p && futures.length === 0) { const query = await p.done(); if (query) { const txb = new TransactionBlock(); const passport = new Passport(txb, query!); await this.operate(txb, passport?.get_object(), account) passport.destroy(); return await this.sign_and_commit(txb, account); } } return {guard:[...guards], witness:p!.future_fills()}; } else { // no passport needed return await this.exec() } } protected async exec (account?:string) : Promise<CallResponse> { const txb = new TransactionBlock(); await this.operate(txb, undefined, account); return await this.sign_and_commit(txb, account); } protected async new_with_mark(txb:TransactionBlock, object:TxbAddress, named_new?:Namedbject, account?:string, innerTags:string[]=[TagName.Launch]) { const tags = named_new?.tags ? array_unique([...named_new.tags, ...innerTags]) : array_unique([...innerTags]); if (!this.resouceObject) { const addr = await Account.Instance().get_address(account); if (addr) { const r = await queryTableItem_Personal({address:addr}); //@ use cache if (!r?.mark_object) { this.resouceObject = Entity.From(txb).create_resource2(); // new Resource.From(txb, this.resouceObject).add(object, tags, named_new?.name); } else { Resource.From(txb, r.mark_object).add(object, tags, named_new?.name); } } else { ERROR(Errors.InvalidParam, 'account - ' + account) } } else { Resource.From(txb, this.resouceObject).add(object, tags, named_new?.name); } } protected async sign_and_commit(txb: TransactionBlock, account?: string) : Promise<CallResponse> { const pair = await Account.Instance().get_pair(account, true); if (!pair) ERROR(Errors.Fail, 'account invalid') if (this.resouceObject) { Resource.From(txb, this.resouceObject).launch(); //@ resource launch, if created. this.resouceObject = undefined; } return await Protocol.Client().signAndExecuteTransaction({ transaction: txb, signer: pair!, options:{showObjectChanges:true}, }); } }