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 {
@ -64,7 +65,7 @@ contract MixinNonFungibleToken {
// A base type has the NF bit but does has an index. // A base type has the NF bit but does has an index.
return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK != 0); return (id & TYPE_NF_BIT == TYPE_NF_BIT) && (id & NF_INDEX_MASK != 0);
} }
/// @dev returns owner of a non-fungible token /// @dev returns owner of a non-fungible token
function ownerOf(uint256 id) public view returns (address) { function ownerOf(uint256 id) public view returns (address) {
return nfOwners[id]; return nfOwners[id];

View File

@ -17,13 +17,14 @@
*/ */
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
/// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md
/// Note: The ERC-165 identifier for this interface is 0xd9b67a26. /// Note: The ERC-165 identifier for this interface is 0xd9b67a26.
interface IERC1155 { interface IERC1155 {
/// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred, /// @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred,
/// including zero value transfers as well as minting or burning. /// including zero value transfers as well as minting or burning.
/// Operator will always be msg.sender. /// Operator will always be msg.sender.

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,10 +17,11 @@
*/ */
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
interface IERC1155Receiver { interface IERC1155Receiver {
/// @notice Handle the receipt of a single ERC1155 token type /// @notice Handle the receipt of a single ERC1155 token type
/// @dev The smart contract calls this function on the recipient /// @dev The smart contract calls this function on the recipient
/// after a `safeTransferFrom`. This function MAY throw to revert and reject the /// after a `safeTransferFrom`. This function MAY throw to revert and reject the

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,58 +53,53 @@ 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),
);
// Decode non-indexed data
const decodedNonIndexedData = AbiEncoder.create(_.filter(event.inputs, { indexed: false })).decodeAsArray(
log.data,
);
// 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) {
return log; return log;
} }
throw error;
}
let didFailToDecode = false;
_.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];
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) { decodedArgs[param.name] = value;
return log;
} else {
return {
...log,
event: event.name,
args: decodedParams,
};
} }
// Decoding was successful. Return decoded log.
return {
...log,
event: event.name,
args: decodedArgs as ArgsType,
};
} }
/** /**
* Decodes calldata for a known ABI. * Decodes calldata for a known ABI.