UNPKG

@ethereum-sourcify/contract-call-decoder

Version:

Library to decode Ethereum smart contract calls into human-readable descriptions using ABI and NatSpec

129 lines 11.6 kB
import radspec from '@blossom-labs/rosette-radspec'; import { decode as decodeBytecode } from '@ethereum-sourcify/bytecode-utils'; import { Interface, } from '@ethersproject/abi'; import { extractCustomFields, getValueFromDecodedFunctionData } from './utils'; require('isomorphic-fetch'); export var MetadataSources; (function (MetadataSources) { MetadataSources[MetadataSources["Sourcify"] = 0] = "Sourcify"; MetadataSources[MetadataSources["BytecodeMetadata"] = 1] = "BytecodeMetadata"; })(MetadataSources || (MetadataSources = {})); const defaultGetMetadataOptions = { source: MetadataSources.Sourcify, sourcifyProvider: 'https://repo.sourcify.dev', ipfsGateway: 'https://ipfs.io', }; export async function getMetadataFromAddress(options) { options = { ...defaultGetMetadataOptions, ...options }; let contractMetadataJSON; if (options.source === MetadataSources.Sourcify) { if (!options.chainId) { throw new Error('Missing chainId while using "MetadataSources.Sourcify"'); } if (!options.address) { throw new Error('Missing address while using "MetadataSources.Sourcify"'); } const sourcifyUrl = `${options.sourcifyProvider}/contracts/full_match/${options.chainId}/${options.address}/metadata.json`; try { const req = await fetch(sourcifyUrl); contractMetadataJSON = await req.json(); } catch (e) { throw new Error(`The contract is not available on "${sourcifyUrl}"`); } } else if (options.source === MetadataSources.BytecodeMetadata) { if (!options.rpcProvider) { throw new Error(`Missing rpcProvider while using "MetadataSources.BytecodeMetadata"`); } const bytecode = (await options?.rpcProvider?.request({ method: 'eth_getCode', params: [options.address, 'latest'], })); if (!bytecode || bytecode === '0x') { throw new Error(`Bytecode not found while using "MetadataSources.BytecodeMetadata"`); } const { ipfs: metadataIpfsCid } = decodeBytecode(bytecode); try { const req = await fetch(`${options.ipfsGateway}/ipfs/${metadataIpfsCid}`); contractMetadataJSON = await req.json(); } catch (e) { console.log(e); throw new Error(`Cannot fetch metadata from ipfs while using "MetadataSources.BytecodeMetadata"`); } } return contractMetadataJSON; } export const evaluate = async function (expression, abi, transaction, provider) { return await radspec(expression, abi, transaction, provider); }; export const findSelectorAndAbiItemFromSignatureHash = (functionSignatureHash, abi) => { try { const interf = new Interface(abi); const selector = Object.keys(interf.functions).find((selector) => { return interf.getSighash(selector) === functionSignatureHash; }); if (!selector) { return false; } return { selector, abi: interf.functions[selector], }; } catch (e) { return false; } }; export const decodeContractCall = async (tx, options = {}) => { const getMetadataOptions = { ...defaultGetMetadataOptions, ...options, address: tx.to, chainId: options.chainId || tx.chainId, }; const metadata = await getMetadataFromAddress(getMetadataOptions); const functionSignatureHash = tx.data.slice(0, 10); const selectorAndAbi = findSelectorAndAbiItemFromSignatureHash(functionSignatureHash, metadata.output.abi); if (!selectorAndAbi) { throw new Error(`Cannot find the function selector in the provided ABI`); } const { selector, abi } = selectorAndAbi; let radspecEvaluatedNotice; if (metadata.output?.userdoc?.methods[selector]?.notice) { radspecEvaluatedNotice = await evaluate(metadata.output.userdoc.methods[selector].notice, metadata.output.abi, tx, getMetadataOptions.rpcProvider); } let radspecEvaluatedDetails; if (metadata.output?.devdoc?.methods[selector]?.details) { radspecEvaluatedDetails = await evaluate(metadata.output?.devdoc?.methods[selector]?.details, metadata.output.abi, tx, getMetadataOptions.rpcProvider); } const iface = new Interface(metadata.output.abi); const decodedParams = iface .decodeFunctionData(selector, tx.data) .map((param) => { return getValueFromDecodedFunctionData(param); }); const devdoc = metadata.output.devdoc; const customFieldsContract = extractCustomFields(devdoc); const customFieldsMethod = extractCustomFields(devdoc.methods[selector]); return { contract: { author: devdoc.author, title: devdoc.title, details: devdoc.details, custom: customFieldsContract, }, method: { selector, abi: abi, details: radspecEvaluatedDetails, params: devdoc.methods[selector]?.params, returns: devdoc.methods[selector]?.returns, notice: radspecEvaluatedNotice, decodedParams, custom: customFieldsMethod, }, }; }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29udHJhY3RDYWxsRGVjb2Rlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvQ29udHJhY3RDYWxsRGVjb2Rlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLE9BQU8sTUFBTSwrQkFBK0IsQ0FBQztBQUVwRCxPQUFPLEVBQUUsTUFBTSxJQUFJLGNBQWMsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQzdFLE9BQU8sRUFHTCxTQUFTLEdBRVYsTUFBTSxvQkFBb0IsQ0FBQztBQUk1QixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsK0JBQStCLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFNL0UsT0FBTyxDQUFDLGtCQUFrQixDQUFDLENBQUM7QUFFNUIsTUFBTSxDQUFOLElBQVksZUFHWDtBQUhELFdBQVksZUFBZTtJQUN6Qiw2REFBUSxDQUFBO0lBQ1IsNkVBQWdCLENBQUE7QUFDbEIsQ0FBQyxFQUhXLGVBQWUsS0FBZixlQUFlLFFBRzFCO0FBV0QsTUFBTSx5QkFBeUIsR0FBdUI7SUFDcEQsTUFBTSxFQUFFLGVBQWUsQ0FBQyxRQUFRO0lBQ2hDLGdCQUFnQixFQUFFLDJCQUEyQjtJQUM3QyxXQUFXLEVBQUUsaUJBQWlCO0NBQy9CLENBQUM7QUFFRixNQUFNLENBQUMsS0FBSyxVQUFVLHNCQUFzQixDQUFDLE9BQTJCO0lBQ3RFLE9BQU8sR0FBRyxFQUFFLEdBQUcseUJBQXlCLEVBQUUsR0FBRyxPQUFPLEVBQUUsQ0FBQztJQUN2RCxJQUFJLG9CQUFvQixDQUFDO0lBQ3pCLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxlQUFlLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDaEQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNyQixNQUFNLElBQUksS0FBSyxDQUFDLHdEQUF3RCxDQUFDLENBQUM7UUFDNUUsQ0FBQztRQUNELElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDckIs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