viem
Version:
131 lines • 5.66 kB
JavaScript
import { AbiItemAmbiguityError, } from '../../errors/abi.js';
import { isHex } from '../../utils/data/isHex.js';
import { isAddress } from '../address/isAddress.js';
import { toEventSelector } from '../hash/toEventSelector.js';
import { toFunctionSelector, } from '../hash/toFunctionSelector.js';
export function getAbiItem(parameters) {
const { abi, args = [], name } = parameters;
const isSelector = isHex(name, { strict: false });
const abiItems = abi.filter((abiItem) => {
if (isSelector) {
if (abiItem.type === 'function')
return toFunctionSelector(abiItem) === name;
if (abiItem.type === 'event')
return toEventSelector(abiItem) === name;
return false;
}
return 'name' in abiItem && abiItem.name === name;
});
if (abiItems.length === 0)
return undefined;
if (abiItems.length === 1)
return abiItems[0];
let matchedAbiItem = undefined;
for (const abiItem of abiItems) {
if (!('inputs' in abiItem))
continue;
if (!args || args.length === 0) {
if (!abiItem.inputs || abiItem.inputs.length === 0)
return abiItem;
continue;
}
if (!abiItem.inputs)
continue;
if (abiItem.inputs.length === 0)
continue;
if (abiItem.inputs.length !== args.length)
continue;
const matched = args.every((arg, index) => {
const abiParameter = 'inputs' in abiItem && abiItem.inputs[index];
if (!abiParameter)
return false;
return isArgOfType(arg, abiParameter);
});
if (matched) {
// Check for ambiguity against already matched parameters (e.g. `address` vs `bytes20`).
if (matchedAbiItem &&
'inputs' in matchedAbiItem &&
matchedAbiItem.inputs) {
const ambiguousTypes = getAmbiguousTypes(abiItem.inputs, matchedAbiItem.inputs, args);
if (ambiguousTypes)
throw new AbiItemAmbiguityError({
abiItem,
type: ambiguousTypes[0],
}, {
abiItem: matchedAbiItem,
type: ambiguousTypes[1],
});
}
matchedAbiItem = abiItem;
}
}
if (matchedAbiItem)
return matchedAbiItem;
return abiItems[0];
}
/** @internal */
export function isArgOfType(arg, abiParameter) {
const argType = typeof arg;
const abiParameterType = abiParameter.type;
switch (abiParameterType) {
case 'address':
return isAddress(arg, { strict: false });
case 'bool':
return argType === 'boolean';
case 'function':
return argType === 'string';
case 'string':
return argType === 'string';
default: {
if (abiParameterType === 'tuple' && 'components' in abiParameter)
return Object.values(abiParameter.components).every((component, index) => {
return isArgOfType(Object.values(arg)[index], component);
});
// `(u)int<M>`: (un)signed integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`
// https://regexr.com/6v8hp
if (/^u?int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)?$/.test(abiParameterType))
return argType === 'number' || argType === 'bigint';
// `bytes<M>`: binary type of `M` bytes, `0 < M <= 32`
// https://regexr.com/6va55
if (/^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/.test(abiParameterType))
return argType === 'string' || arg instanceof Uint8Array;
// fixed-length (`<type>[M]`) and dynamic (`<type>[]`) arrays
// https://regexr.com/6va6i
if (/[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/.test(abiParameterType)) {
return (Array.isArray(arg) &&
arg.every((x) => isArgOfType(x, {
...abiParameter,
// Pop off `[]` or `[M]` from end of type
type: abiParameterType.replace(/(\[[0-9]{0,}\])$/, ''),
})));
}
return false;
}
}
}
/** @internal */
export function getAmbiguousTypes(sourceParameters, targetParameters, args) {
for (const parameterIndex in sourceParameters) {
const sourceParameter = sourceParameters[parameterIndex];
const targetParameter = targetParameters[parameterIndex];
if (sourceParameter.type === 'tuple' &&
targetParameter.type === 'tuple' &&
'components' in sourceParameter &&
'components' in targetParameter)
return getAmbiguousTypes(sourceParameter.components, targetParameter.components, args[parameterIndex]);
const types = [sourceParameter.type, targetParameter.type];
const ambiguous = (() => {
if (types.includes('address') && types.includes('bytes20'))
return true;
if (types.includes('address') && types.includes('string'))
return isAddress(args[parameterIndex], { strict: false });
if (types.includes('address') && types.includes('bytes'))
return isAddress(args[parameterIndex], { strict: false });
return false;
})();
if (ambiguous)
return types;
}
return;
}
//# sourceMappingURL=getAbiItem.js.map