UNPKG

@layr-labs/hourglass-performer

Version:

TypeScript SDK for building Hourglass AVS performers

281 lines 9.34 kB
"use strict"; // ABI encoding/decoding utilities for Solidity integration Object.defineProperty(exports, "__esModule", { value: true }); exports.PayloadAutoDecoder = exports.SolidityTypeUtils = exports.AbiCodec = void 0; const ethers_1 = require("ethers"); const protobuf_1 = require("../types/protobuf"); /** * ABI codec for encoding/decoding Solidity function calls */ class AbiCodec { constructor(abi) { this.functionInfo = new Map(); this.abiCoder = ethers_1.AbiCoder.defaultAbiCoder(); this.contractInterface = new ethers_1.Interface(abi); this.buildFunctionInfo(); } /** * Build function information from ABI */ buildFunctionInfo() { for (const fragment of this.contractInterface.fragments) { if (fragment.type === 'function') { const funcFragment = fragment; const info = { name: funcFragment.name, signature: funcFragment.format('minimal'), inputs: funcFragment.inputs, outputs: funcFragment.outputs, selector: funcFragment.selector, }; this.functionInfo.set(funcFragment.name, info); } } } /** * Get function information by name */ getFunctionInfo(functionName) { return this.functionInfo.get(functionName); } /** * Get all available functions */ getAllFunctions() { return Array.from(this.functionInfo.values()); } /** * Decode function call data */ decodeFunctionCall(functionName, data) { const funcInfo = this.functionInfo.get(functionName); if (!funcInfo) { throw new Error(`Function ${functionName} not found in ABI`); } try { // Convert bytes to hex string for ethers const hexData = (0, protobuf_1.bytesToHex)(data); // Decode using contract interface const result = this.contractInterface.decodeFunctionData(functionName, hexData); // Convert result to plain object with parameter names const decoded = {}; for (let i = 0; i < funcInfo.inputs.length; i++) { const param = funcInfo.inputs[i]; decoded[param?.name || `param${i}`] = result[i]; } return decoded; } catch (error) { throw new Error(`Failed to decode function call: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Encode function call data */ encodeFunctionCall(functionName, params) { const funcInfo = this.functionInfo.get(functionName); if (!funcInfo) { throw new Error(`Function ${functionName} not found in ABI`); } try { const encoded = this.contractInterface.encodeFunctionData(functionName, params); return (0, protobuf_1.hexToBytes)(encoded); } catch (error) { throw new Error(`Failed to encode function call: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Decode function result */ decodeFunctionResult(functionName, data) { const funcInfo = this.functionInfo.get(functionName); if (!funcInfo) { throw new Error(`Function ${functionName} not found in ABI`); } try { const hexData = (0, protobuf_1.bytesToHex)(data); const result = this.contractInterface.decodeFunctionResult(functionName, hexData); // Convert result to plain object with parameter names if (funcInfo.outputs.length === 1) { return result[0]; } else { const decoded = {}; for (let i = 0; i < funcInfo.outputs.length; i++) { const param = funcInfo.outputs[i]; decoded[param?.name || `result${i}`] = result[i]; } return decoded; } } catch (error) { throw new Error(`Failed to decode function result: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Encode function result */ encodeFunctionResult(functionName, result) { const funcInfo = this.functionInfo.get(functionName); if (!funcInfo) { throw new Error(`Function ${functionName} not found in ABI`); } try { // Handle single vs multiple return values let values; if (funcInfo.outputs.length === 1) { values = [result]; } else { values = []; for (let i = 0; i < funcInfo.outputs.length; i++) { const param = funcInfo.outputs[i]; const key = param?.name || `result${i}`; values.push(result[key]); } } const encoded = this.abiCoder.encode(funcInfo.outputs.map(p => p.format()), values); return (0, protobuf_1.hexToBytes)(encoded); } catch (error) { throw new Error(`Failed to encode function result: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Detect if data looks like ABI-encoded function call */ static isAbiEncoded(data) { // ABI-encoded function calls start with 4-byte function selector return data.length >= 4; } /** * Try to detect function from encoded data */ detectFunction(data) { if (data.length < 4) return null; const selector = (0, protobuf_1.bytesToHex)(data.slice(0, 4)); for (const [name, info] of this.functionInfo) { if (info.selector === selector) { return name; } } return null; } } exports.AbiCodec = AbiCodec; /** * Utility functions for common Solidity types */ class SolidityTypeUtils { /** * Convert JavaScript value to Solidity type */ static toSolidityType(value, solidityType) { switch (solidityType) { case 'address': return value.toLowerCase(); case 'bool': return Boolean(value); case 'string': return String(value); case 'bytes': case 'bytes32': return value instanceof Uint8Array ? (0, protobuf_1.bytesToHex)(value) : value; default: if (solidityType.startsWith('uint') || solidityType.startsWith('int')) { return BigInt(value); } return value; } } /** * Convert Solidity type to JavaScript value */ static fromSolidityType(value, solidityType) { switch (solidityType) { case 'address': return value.toLowerCase(); case 'bool': return Boolean(value); case 'string': return String(value); case 'bytes': case 'bytes32': return typeof value === 'string' ? (0, protobuf_1.hexToBytes)(value) : value; default: if (solidityType.startsWith('uint') || solidityType.startsWith('int')) { return typeof value === 'bigint' ? value : BigInt(value); } return value; } } /** * Validate value against Solidity type */ static validateType(value, solidityType) { try { this.toSolidityType(value, solidityType); return true; } catch { return false; } } } exports.SolidityTypeUtils = SolidityTypeUtils; /** * Auto-detect payload format and decode accordingly */ class PayloadAutoDecoder { /** * Attempt to decode payload using multiple strategies */ static decode(payload) { // Try ABI decoding first if (AbiCodec.isAbiEncoded(payload)) { return { format: 'abi', data: payload, confidence: 0.8, }; } // Try JSON decoding try { const text = new TextDecoder().decode(payload); const json = JSON.parse(text); return { format: 'json', data: json, confidence: 0.9, }; } catch { // Not JSON } // Try string decoding try { const text = new TextDecoder().decode(payload); // Check if it's valid UTF-8 and printable if (text.length > 0 && !/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/.test(text)) { return { format: 'string', data: text, confidence: 0.7, }; } } catch { // Not valid UTF-8 } // Default to raw bytes return { format: 'raw', data: payload, confidence: 0.5, }; } } exports.PayloadAutoDecoder = PayloadAutoDecoder; //# sourceMappingURL=abiUtils.js.map