UNPKG

autrace

Version:
170 lines 6.32 kB
export class AccountUpdateAnalyzer { constructor() { this.currentDepth = 0; this.parentStack = []; // Stack to track parent AUs this.tokenMap = new Map(); this.auRelationships = new Map(); this.tokenMap = new Map(); } reset() { this.auRelationships = new Map(); this.currentDepth = 0; this.parentStack = []; this.tokenMap = new Map(); } processAccountUpdate(au) { const auId = au.id.toString(); //const callDepth = au.body.callDepth || 0; let callDepth = 0; if (au.metadata?.callDepth !== undefined) { callDepth = au.metadata.callDepth; } else if (au.body.callDepth !== undefined) { callDepth = au.body.callDepth; } // Extract failure information if present const failed = au.metadata?.failed || false; const failureReason = au.metadata?.failureReason; const tokenId = au.metadata?.tokenId || au.body.tokenId || 'default'; if (!this.tokenMap.has(tokenId)) { this.tokenMap.set(tokenId, []); } this.tokenMap.get(tokenId).push(auId); // Manage parent stack based on call depth while (this.currentDepth >= callDepth && this.parentStack.length > 0) { this.parentStack.pop(); this.currentDepth--; } const parentId = this.parentStack.length > 0 ? this.parentStack[this.parentStack.length - 1] : undefined; const methodInfo = this.extractMethodInfo(au); // Get label, handle failure info let label = au.label || 'Unnamed Update'; if (failed) { label = `[FAILED] ${label}`; } // Create relationship entry const relationship = { id: auId, label: label, parentId, children: [], depth: callDepth, method: this.extractMethodInfo(au), stateChanges: this.extractStateChanges(au), tokenId: tokenId, failed: failed, failureReason: failureReason }; // Update parent's children array if (parentId) { const parentRelationship = this.auRelationships.get(parentId); if (parentRelationship) { parentRelationship.children.push(auId); } } this.auRelationships.set(auId, relationship); // Update stack for next iterations if (!failed) { // Only use non-failed operations as potential parents this.parentStack.push(auId); this.currentDepth = callDepth; } } extractMethodInfo(au) { // First check blockchain format if (au.lazyAuthorization?.methodName) { return { contract: au.label || 'Unknown', name: au.lazyAuthorization.methodName }; } if (!au.label) return undefined; const parts = au.label.split('.'); if (parts.length >= 2) { return { contract: parts[0], name: parts[1].replace('()', '') }; } return undefined; } extractStateChanges(au) { // Check blockchain format first if (au.metadata?.stateUpdates) { return au.metadata.stateUpdates.map((update) => ({ field: update.type || 'unknown', value: update.value })); } const changes = []; if (au.body?.update?.appState) { au.body.update.appState.forEach((state, index) => { if (state) { changes.push({ field: `appState[${index}]`, value: state }); } }); } return changes.length > 0 ? changes : undefined; } processTokenRelationships() { // For each token, identify operations that should be related for (const [tokenId, operations] of this.tokenMap.entries()) { if (operations.length <= 1) continue; // Find operations without parents that can be related const orphans = operations.filter(id => { const rel = this.auRelationships.get(id); return rel && !rel.parentId && !rel.failed; }); if (orphans.length <= 1) continue; // Use first operation as a root for others const rootId = orphans[0]; if (!rootId) continue; const rootRel = this.auRelationships.get(rootId); if (!rootRel) continue; // Connect other orphans to the root for (let i = 1; i < orphans.length; i++) { const orphanId = orphans[i]; const orphanRel = this.auRelationships.get(orphanId); if (orphanRel && !orphanRel.parentId) { orphanRel.parentId = rootId; rootRel.children.push(orphanId); } } } } getRelationships() { this.processTokenRelationships(); return this.auRelationships; } getHierarchicalView() { this.processTokenRelationships(); // Get root level AUs (those without parents) const roots = Array.from(this.auRelationships.values()) .filter(r => !r.parentId); // Recursively build tree const buildTree = (auRelationship) => { return { id: auRelationship.id, label: auRelationship.label, method: auRelationship.method, stateChanges: auRelationship.stateChanges, tokenId: auRelationship.tokenId, failed: auRelationship.failed, failureReason: auRelationship.failureReason, children: auRelationship.children.map(childId => { const child = this.auRelationships.get(childId); return child ? buildTree(child) : null; }).filter(x => x) }; }; return roots.map(root => buildTree(root)); } } //# sourceMappingURL=AccountUpdateAnalyzer.js.map