Fixed log decoding. Dynamic structures were not decoding properly. Now uses AbiEncoder from utils package.

This commit is contained in:
Greg Hysen 2020-01-14 12:25:24 -08:00
parent ebd08d9c63
commit b3c3ec16e5
8 changed files with 84 additions and 50 deletions

View File

@ -17,6 +17,7 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "@0x/contracts-utils/contracts/src/LibAddress.sol"; import "@0x/contracts-utils/contracts/src/LibAddress.sol";

View File

@ -1,4 +1,23 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/LibSafeMath.sol"; import "@0x/contracts-utils/contracts/src/LibSafeMath.sol";
import "./ERC1155.sol"; import "./ERC1155.sol";

View File

@ -17,6 +17,7 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
contract MixinNonFungibleToken { contract MixinNonFungibleToken {

View File

@ -17,6 +17,7 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
/// @title ERC-1155 Multi Token Standard /// @title ERC-1155 Multi Token Standard

View File

@ -1,4 +1,23 @@
/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "./IERC1155.sol"; import "./IERC1155.sol";

View File

@ -17,6 +17,7 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
interface IERC1155Receiver { interface IERC1155Receiver {

View File

@ -139,8 +139,8 @@ blockchainTests('Exchange wrapper functions unit tests.', env => {
expect(actual[13]).to.be.eq(expected.takerFeeAssetData); expect(actual[13]).to.be.eq(expected.takerFeeAssetData);
} }
describe('fillOrKillOrder', () => { describe.only('fillOrKillOrder', () => {
it('works if the order is filled by exactly `takerAssetFillAmount`', async () => { it.only('works if the order is filled by exactly `takerAssetFillAmount`', async () => {
const fillAmount = randomAmount(); const fillAmount = randomAmount();
const order = randomOrder({ const order = randomOrder({
// `_fillOrder()` is overridden to always return `order.takerAssetAmount` as // `_fillOrder()` is overridden to always return `order.takerAssetAmount` as

View File

@ -1,21 +1,18 @@
import { import {
AbiDefinition, AbiDefinition,
AbiType, AbiType,
DataItem,
DecodedLogArgs, DecodedLogArgs,
EventAbi, EventAbi,
EventParameter,
LogEntry, LogEntry,
LogWithDecodedArgs, LogWithDecodedArgs,
MethodAbi, MethodAbi,
RawLog, RawLog,
SolidityTypes,
} from 'ethereum-types'; } from 'ethereum-types';
import * as ethers from 'ethers'; import * as ethers from 'ethers';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { AbiEncoder } from '.'; import { AbiEncoder } from '.';
import { addressUtils } from './address_utils';
import { BigNumber } from './configured_bignumber';
import { DecodedCalldata, SelectorToFunctionInfo } from './types'; import { DecodedCalldata, SelectorToFunctionInfo } from './types';
/** /**
@ -56,59 +53,54 @@ export class AbiDecoder {
* @return The decoded log if the requisite ABI was available. Otherwise the log unaltered. * @return The decoded log if the requisite ABI was available. Otherwise the log unaltered.
*/ */
public tryToDecodeLogOrNoop<ArgsType extends DecodedLogArgs>(log: LogEntry): LogWithDecodedArgs<ArgsType> | RawLog { public tryToDecodeLogOrNoop<ArgsType extends DecodedLogArgs>(log: LogEntry): LogWithDecodedArgs<ArgsType> | RawLog {
// Lookup event corresponding to log
const eventId = log.topics[0]; const eventId = log.topics[0];
const numIndexedArgs = log.topics.length - 1; const numIndexedArgs = log.topics.length - 1;
if (this._eventIds[eventId] === undefined || this._eventIds[eventId][numIndexedArgs] === undefined) { if (this._eventIds[eventId] === undefined || this._eventIds[eventId][numIndexedArgs] === undefined) {
return log; return log;
} }
const event = this._eventIds[eventId][numIndexedArgs]; const event = this._eventIds[eventId][numIndexedArgs];
const ethersInterface = new ethers.utils.Interface([event]);
const decodedParams: DecodedLogArgs = {};
let topicsIndex = 1;
let decodedData: any[]; // Create decoders for indexed data
try { const indexedDataDecoders = _.mapValues(_.filter(event.inputs, { indexed: true }), input =>
decodedData = ethersInterface.events[event.name].decode(log.data); // tslint:disable:next-line no-unnecessary-type-assertion
} catch (error) { AbiEncoder.create(input as DataItem),
if (error.code === ethers.errors.INVALID_ARGUMENT) { );
// Because we index events by Method ID, and Method IDs are derived from the method
// name and the input parameters, it's possible that the return value of the event // Decode indexed data
// does not match our ABI. If that's the case, then ethers will throw an error const decodedIndexedData = _.map(
// when we try to parse the event. We handle that case here by returning the log rather log.topics.slice(1), // ignore first topic, which is the event id.
// than throwing an error. (input, i) => indexedDataDecoders[i].decode(input),
return log; );
}
throw error; // Decode non-indexed data
} const decodedNonIndexedData = AbiEncoder.create(_.filter(event.inputs, { indexed: false })).decodeAsArray(
let didFailToDecode = false; log.data,
_.forEach(event.inputs, (param: EventParameter, i: number) => { );
// Indexed parameters are stored in topics. Non-indexed ones in decodedData
let value: BigNumber | string | number = param.indexed ? log.topics[topicsIndex++] : decodedData[i]; // Construct DecodedLogArgs struct by mapping event parameters to their respective decoded argument.
const decodedArgs: DecodedLogArgs = {};
let indexedOffset = 0;
let nonIndexedOffset = 0;
for (const param of event.inputs) {
const value = param.indexed
? decodedIndexedData[indexedOffset++]
: decodedNonIndexedData[nonIndexedOffset++];
if (value === undefined) { if (value === undefined) {
didFailToDecode = true;
return;
}
if (param.type === SolidityTypes.Address) {
const baseHex = 16;
value = addressUtils.padZeros(new BigNumber((value as string).toLowerCase()).toString(baseHex));
} else if (param.type === SolidityTypes.Uint256 || param.type === SolidityTypes.Uint) {
value = new BigNumber(value);
} else if (param.type === SolidityTypes.Uint8) {
value = new BigNumber(value).toNumber();
}
decodedParams[param.name] = value;
});
if (didFailToDecode) {
return log; return log;
} else { }
decodedArgs[param.name] = value;
}
// Decoding was successful. Return decoded log.
return { return {
...log, ...log,
event: event.name, event: event.name,
args: decodedParams, args: decodedArgs as ArgsType,
}; };
} }
}
/** /**
* Decodes calldata for a known ABI. * Decodes calldata for a known ABI.
* @param calldata hex-encoded calldata. * @param calldata hex-encoded calldata.