1. /* Sourced from 0x.js */
    2. import { BigNumber } from '@0xproject/utils';
    3. import * as _ from 'lodash';
    4. import * as Web3 from 'web3';
    5. import * as SolidityCoder from 'web3/lib/solidity/coder';
    6. import { AbiType, ContractEventArgs, DecodedLogArgs, LogWithDecodedArgs, RawLog, SolidityTypes } from '../types';
    7. // abi解码器
    8. export class AbiDecoder {
    9. private _savedABIs: Web3.AbiDefinition[] = [];
    10. private _methodIds: { [signatureHash: string]: Web3.EventAbi } = {};
    11. // 填充0
    12. private static _padZeros(address: string) {
    13. let formatted = address;
    14. if (_.startsWith(formatted, '0x')) {
    15. formatted = formatted.slice(2);
    16. }
    17. formatted = _.padStart(formatted, 40, '0');
    18. return `0x${formatted}`;
    19. }
    20. constructor(abiArrays: Web3.AbiDefinition[][]) {
    21. _.map(abiArrays, this._addABI.bind(this));
    22. }
    23. // 此方法只能解码 0x 和 ERC20 智能合约的日志
    24. // 尝试解码日志或 Noop
    25. public tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
    26. log: Web3.LogEntry,
    27. ): LogWithDecodedArgs<ArgsType> | RawLog {
    28. const methodId = log.topics[0];
    29. const event = this._methodIds[methodId];
    30. if (_.isUndefined(event)) {
    31. return log;
    32. }
    33. const logData = log.data;
    34. const decodedParams: DecodedLogArgs = {};
    35. let dataIndex = 0;
    36. let topicsIndex = 1;
    37. const nonIndexedInputs = _.filter(event.inputs, input => !input.indexed);
    38. const dataTypes = _.map(nonIndexedInputs, input => input.type);
    39. const decodedData = SolidityCoder.decodeParams(dataTypes, logData.slice('0x'.length));
    40. _.map(event.inputs, (param: Web3.EventParameter) => {
    41. // 索引参数存储在主题中。解码数据中的非索引项
    42. let value = param.indexed ? log.topics[topicsIndex++] : decodedData[dataIndex++];
    43. if (param.type === SolidityTypes.Address) {
    44. value = AbiDecoder._padZeros(new BigNumber(value).toString(16));
    45. } else if (
    46. param.type === SolidityTypes.Uint256 ||
    47. param.type === SolidityTypes.Uint8 ||
    48. param.type === SolidityTypes.Uint
    49. ) {
    50. value = new BigNumber(value);
    51. }
    52. decodedParams[param.name] = value;
    53. });
    54. return {
    55. ...log,
    56. event: event.name,
    57. args: decodedParams,
    58. };
    59. }
    60. private _addABI(abiArray: Web3.AbiDefinition[]): void {
    61. _.map(abiArray, (abi: Web3.AbiDefinition) => {
    62. if (abi.type === AbiType.Event) {
    63. const signature = `${abi.name}(${_.map(abi.inputs, input => input.type).join(',')})`;
    64. const signatureHash = new Web3().sha3(signature);
    65. this._methodIds[signatureHash] = abi;
    66. }
    67. });
    68. this._savedABIs = this._savedABIs.concat(abiArray);
    69. }
    70. }