@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
JavaScript
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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