UNPKG

ox

Version:

Ethereum Standard Library

333 lines 12.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FilterTypeNotSupportedError = exports.SelectorTopicMismatchError = exports.TopicsMismatchError = exports.DataMismatchError = exports.InputNotFoundError = exports.ArgsMismatchError = void 0; exports.assertArgs = assertArgs; exports.decode = decode; exports.encode = encode; exports.format = format; exports.from = from; exports.fromAbi = fromAbi; exports.getSelector = getSelector; const abitype = require("abitype"); const AbiItem = require("./AbiItem.js"); const AbiParameters = require("./AbiParameters.js"); const Address = require("./Address.js"); const Bytes = require("./Bytes.js"); const Errors = require("./Errors.js"); const Hash = require("./Hash.js"); const Hex = require("./Hex.js"); const Cursor = require("./internal/cursor.js"); const errors_js_1 = require("./internal/errors.js"); function assertArgs(abiEvent, args, matchArgs) { if (!args || !matchArgs) throw new ArgsMismatchError({ abiEvent, expected: args, given: matchArgs, }); function isEqual(input, value, arg) { if (input.type === 'address') return Address.isEqual(value, arg); if (input.type === 'string') return Hash.keccak256(Bytes.fromString(value)) === arg; if (input.type === 'bytes') return Hash.keccak256(value) === arg; return value === arg; } if (Array.isArray(args) && Array.isArray(matchArgs)) { for (const [index, value] of matchArgs.entries()) { if (value === null || value === undefined) continue; const input = abiEvent.inputs[index]; if (!input) throw new InputNotFoundError({ abiEvent, name: `${index}`, }); const value_ = Array.isArray(value) ? value : [value]; let equal = false; for (const value of value_) { if (isEqual(input, value, args[index])) equal = true; } if (!equal) throw new ArgsMismatchError({ abiEvent, expected: args, given: matchArgs, }); } } if (typeof args === 'object' && !Array.isArray(args) && typeof matchArgs === 'object' && !Array.isArray(matchArgs)) for (const [key, value] of Object.entries(matchArgs)) { if (value === null || value === undefined) continue; const input = abiEvent.inputs.find((input) => input.name === key); if (!input) throw new InputNotFoundError({ abiEvent, name: key }); const value_ = Array.isArray(value) ? value : [value]; let equal = false; for (const value of value_) { if (isEqual(input, value, args[key])) equal = true; } if (!equal) throw new ArgsMismatchError({ abiEvent, expected: args, given: matchArgs, }); } } function decode(abiEvent, log) { const { data, topics } = log; const [selector_, ...argTopics] = topics; const selector = getSelector(abiEvent); if (selector_ !== selector) throw new SelectorTopicMismatchError({ abiEvent, actual: selector_, expected: selector, }); const { inputs } = abiEvent; const isUnnamed = inputs?.every((x) => !('name' in x && x.name)); let args = isUnnamed ? [] : {}; const indexedInputs = inputs.filter((x) => 'indexed' in x && x.indexed); for (let i = 0; i < indexedInputs.length; i++) { const param = indexedInputs[i]; const topic = argTopics[i]; if (!topic) throw new TopicsMismatchError({ abiEvent, param: param, }); args[isUnnamed ? i : param.name || i] = (() => { if (param.type === 'string' || param.type === 'bytes' || param.type === 'tuple' || param.type.match(/^(.*)\[(\d+)?\]$/)) return topic; const decoded = AbiParameters.decode([param], topic) || []; return decoded[0]; })(); } const nonIndexedInputs = inputs.filter((x) => !('indexed' in x && x.indexed)); if (nonIndexedInputs.length > 0) { if (data && data !== '0x') { try { const decodedData = AbiParameters.decode(nonIndexedInputs, data); if (decodedData) { if (isUnnamed) args = [...args, ...decodedData]; else { for (let i = 0; i < nonIndexedInputs.length; i++) { const index = inputs.indexOf(nonIndexedInputs[i]); args[nonIndexedInputs[i].name || index] = decodedData[i]; } } } } catch (err) { if (err instanceof AbiParameters.DataSizeTooSmallError || err instanceof Cursor.PositionOutOfBoundsError) throw new DataMismatchError({ abiEvent, data: data, parameters: nonIndexedInputs, size: Hex.size(data), }); throw err; } } else { throw new DataMismatchError({ abiEvent, data: '0x', parameters: nonIndexedInputs, size: 0, }); } } return Object.values(args).length > 0 ? args : undefined; } function encode(abiEvent, ...[args]) { let topics = []; if (args && abiEvent.inputs) { const indexedInputs = abiEvent.inputs.filter((param) => 'indexed' in param && param.indexed); const args_ = Array.isArray(args) ? args : Object.values(args).length > 0 ? indexedInputs?.map((x, i) => args[x.name ?? i]) ?? [] : []; if (args_.length > 0) { const encode = (param, value) => { if (param.type === 'string') return Hash.keccak256(Hex.fromString(value)); if (param.type === 'bytes') return Hash.keccak256(value); if (param.type === 'tuple' || param.type.match(/^(.*)\[(\d+)?\]$/)) throw new FilterTypeNotSupportedError(param.type); return AbiParameters.encode([param], [value]); }; topics = indexedInputs?.map((param, i) => { if (Array.isArray(args_[i])) return args_[i].map((_, j) => encode(param, args_[i][j])); return typeof args_[i] !== 'undefined' && args_[i] !== null ? encode(param, args_[i]) : null; }) ?? []; } } const selector = (() => { if (abiEvent.hash) return abiEvent.hash; return getSelector(abiEvent); })(); return { topics: [selector, ...topics] }; } function format(abiEvent) { return abitype.formatAbiItem(abiEvent); } function from(abiEvent, options = {}) { return AbiItem.from(abiEvent, options); } function fromAbi(abi, name, options) { const item = AbiItem.fromAbi(abi, name, options); if (item.type !== 'event') throw new AbiItem.NotFoundError({ name, type: 'event' }); return item; } function getSelector(abiItem) { return AbiItem.getSignatureHash(abiItem); } class ArgsMismatchError extends Errors.BaseError { constructor({ abiEvent, expected, given, }) { super('Given arguments do not match the expected arguments.', { metaMessages: [ `Event: ${format(abiEvent)}`, `Expected Arguments: ${!expected ? 'None' : ''}`, expected ? (0, errors_js_1.prettyPrint)(expected) : undefined, `Given Arguments: ${!given ? 'None' : ''}`, given ? (0, errors_js_1.prettyPrint)(given) : undefined, ], }); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'AbiEvent.ArgsMismatchError' }); } } exports.ArgsMismatchError = ArgsMismatchError; class InputNotFoundError extends Errors.BaseError { constructor({ abiEvent, name, }) { super(`Parameter "${name}" not found on \`${format(abiEvent)}\`.`); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'AbiEvent.InputNotFoundError' }); } } exports.InputNotFoundError = InputNotFoundError; class DataMismatchError extends Errors.BaseError { constructor({ abiEvent, data, parameters, size, }) { super([ `Data size of ${size} bytes is too small for non-indexed event parameters.`, ].join('\n'), { metaMessages: [ `Non-indexed Parameters: (${AbiParameters.format(parameters)})`, `Data: ${data} (${size} bytes)`, ], }); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'AbiEvent.DataMismatchError' }); Object.defineProperty(this, "abiEvent", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "data", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "parameters", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "size", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.abiEvent = abiEvent; this.data = data; this.parameters = parameters; this.size = size; } } exports.DataMismatchError = DataMismatchError; class TopicsMismatchError extends Errors.BaseError { constructor({ abiEvent, param, }) { super([ `Expected a topic for indexed event parameter${param.name ? ` "${param.name}"` : ''} for "${format(abiEvent)}".`, ].join('\n')); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'AbiEvent.TopicsMismatchError' }); Object.defineProperty(this, "abiEvent", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.abiEvent = abiEvent; } } exports.TopicsMismatchError = TopicsMismatchError; class SelectorTopicMismatchError extends Errors.BaseError { constructor({ abiEvent, actual, expected, }) { super(`topics[0]="${actual}" does not match the expected topics[0]="${expected}".`, { metaMessages: [`Event: ${format(abiEvent)}`, `Selector: ${expected}`], }); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'AbiEvent.SelectorTopicMismatchError' }); } } exports.SelectorTopicMismatchError = SelectorTopicMismatchError; class FilterTypeNotSupportedError extends Errors.BaseError { constructor(type) { super(`Filter type "${type}" is not supported.`); Object.defineProperty(this, "name", { enumerable: true, configurable: true, writable: true, value: 'AbiEvent.FilterTypeNotSupportedError' }); } } exports.FilterTypeNotSupportedError = FilterTypeNotSupportedError; //# sourceMappingURL=AbiEvent.js.map