move log tests to @0x/abi-gen; delete exchange_wrapper_test (#2160)

This commit is contained in:
Xianny
2019-09-16 10:17:49 -07:00
committed by GitHub
parent 50f86dd61b
commit b3f71af850
7 changed files with 314 additions and 590 deletions

File diff suppressed because one or more lines are too long

View File

@@ -97,12 +97,6 @@ contract AbiGenDummy
return ecrecover(prefixedHash, v, r, s); return ecrecover(prefixedHash, v, r, s);
} }
event Withdrawal(address indexed _owner, uint _value);
function withdraw(uint wad) public {
emit Withdrawal(msg.sender, wad);
}
// test: generated code should normalize address inputs to lowercase // test: generated code should normalize address inputs to lowercase
// add extra inputs to make sure it works with address in any position // add extra inputs to make sure it works with address in any position
function withAddressInput(address x, uint256 a, uint256 b, address y, uint256 c) function withAddressInput(address x, uint256 a, uint256 b, address y, uint256 c)
@@ -113,8 +107,6 @@ contract AbiGenDummy
return x; return x;
} }
event AnEvent(uint8 param);
function acceptsBytes(bytes memory a) public pure {} function acceptsBytes(bytes memory a) public pure {}
/// @dev a method that accepts an array of bytes /// @dev a method that accepts an array of bytes
@@ -180,6 +172,22 @@ contract AbiGenDummy
function overloadedMethod(int a) public pure {} function overloadedMethod(int a) public pure {}
function overloadedMethod(string memory a) public pure {} function overloadedMethod(string memory a) public pure {}
event Withdrawal(address indexed _owner, uint _value);
function withdraw(uint wad) public {
emit Withdrawal(msg.sender, wad);
}
event SimpleEvent(bytes someBytes, string someString);
function emitSimpleEvent() public {
emit SimpleEvent(
hex'12345678',
"lorem"
);
}
// begin tests for `decodeTransactionData`, `decodeReturnData` // begin tests for `decodeTransactionData`, `decodeReturnData`
/// @dev complex input is dynamic and more difficult to decode than simple input. /// @dev complex input is dynamic and more difficult to decode than simple input.
struct ComplexInput { struct ComplexInput {

File diff suppressed because one or more lines are too long

View File

@@ -31,11 +31,11 @@ import { assert } from '@0x/assert';
import * as ethers from 'ethers'; import * as ethers from 'ethers';
// tslint:enable:no-unused-variable // tslint:enable:no-unused-variable
export type AbiGenDummyEventArgs = AbiGenDummyWithdrawalEventArgs | AbiGenDummyAnEventEventArgs; export type AbiGenDummyEventArgs = AbiGenDummyWithdrawalEventArgs | AbiGenDummySimpleEventEventArgs;
export enum AbiGenDummyEvents { export enum AbiGenDummyEvents {
Withdrawal = 'Withdrawal', Withdrawal = 'Withdrawal',
AnEvent = 'AnEvent', SimpleEvent = 'SimpleEvent',
} }
export interface AbiGenDummyWithdrawalEventArgs extends DecodedLogArgs { export interface AbiGenDummyWithdrawalEventArgs extends DecodedLogArgs {
@@ -43,8 +43,9 @@ export interface AbiGenDummyWithdrawalEventArgs extends DecodedLogArgs {
_value: BigNumber; _value: BigNumber;
} }
export interface AbiGenDummyAnEventEventArgs extends DecodedLogArgs { export interface AbiGenDummySimpleEventEventArgs extends DecodedLogArgs {
param: number; someBytes: string;
someString: string;
} }
/* istanbul ignore next */ /* istanbul ignore next */
@@ -1809,6 +1810,147 @@ export class AbiGenDummyContract extends BaseContract {
return abiDecodedReturnData; return abiDecodedReturnData;
}, },
}; };
public emitSimpleEvent = {
/**
* Sends an Ethereum transaction executing this method with the supplied parameters. This is a read/write
* Ethereum operation and will cost gas.
* @param txData Additional data for transaction
* @returns The hash of the transaction
*/
async sendTransactionAsync(txData?: Partial<TxData> | undefined): Promise<string> {
const self = (this as any) as AbiGenDummyContract;
const encodedData = self._strictEncodeArguments('emitSimpleEvent()', []);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...txData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
self.emitSimpleEvent.estimateGasAsync.bind(self),
);
if (txDataWithDefaults.from !== undefined) {
txDataWithDefaults.from = txDataWithDefaults.from.toLowerCase();
}
const txHash = await self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
return txHash;
},
/**
* Sends an Ethereum transaction and waits until the transaction has been successfully mined without reverting.
* If the transaction was mined, but reverted, an error is thrown.
* @param txData Additional data for transaction
* @param pollingIntervalMs Interval at which to poll for success
* @returns A promise that resolves when the transaction is successful
*/
awaitTransactionSuccessAsync(
txData?: Partial<TxData>,
pollingIntervalMs?: number,
timeoutMs?: number,
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
const self = (this as any) as AbiGenDummyContract;
const txHashPromise = self.emitSimpleEvent.sendTransactionAsync(txData);
return new PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs>(
txHashPromise,
(async (): Promise<TransactionReceiptWithDecodedLogs> => {
// When the transaction hash resolves, wait for it to be mined.
return self._web3Wrapper.awaitTransactionSuccessAsync(
await txHashPromise,
pollingIntervalMs,
timeoutMs,
);
})(),
);
},
/**
* Estimates the gas cost of sending an Ethereum transaction calling this method with these arguments.
* @param txData Additional data for transaction
* @returns The hash of the transaction
*/
async estimateGasAsync(txData?: Partial<TxData> | undefined): Promise<number> {
const self = (this as any) as AbiGenDummyContract;
const encodedData = self._strictEncodeArguments('emitSimpleEvent()', []);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...txData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
if (txDataWithDefaults.from !== undefined) {
txDataWithDefaults.from = txDataWithDefaults.from.toLowerCase();
}
const gas = await self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
return gas;
},
async validateAndSendTransactionAsync(txData?: Partial<TxData> | undefined): Promise<string> {
await (this as any).emitSimpleEvent.callAsync(txData);
const txHash = await (this as any).emitSimpleEvent.sendTransactionAsync(txData);
return txHash;
},
/**
* Sends a read-only call to the contract method. Returns the result that would happen if one were to send an
* Ethereum transaction to this method, given the current state of the blockchain. Calls do not cost gas
* since they don't modify state.
*/
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<void> {
assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (defaultBlock !== undefined) {
assert.isBlockParam('defaultBlock', defaultBlock);
}
const self = (this as any) as AbiGenDummyContract;
const encodedData = self._strictEncodeArguments('emitSimpleEvent()', []);
const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...callData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
callDataWithDefaults.from = callDataWithDefaults.from
? callDataWithDefaults.from.toLowerCase()
: callDataWithDefaults.from;
const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock);
BaseContract._throwIfRevertWithReasonCallResult(rawCallResult);
const abiEncoder = self._lookupAbiEncoder('emitSimpleEvent()');
// tslint:disable boolean-naming
const result = abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
// tslint:enable boolean-naming
return result;
},
/**
* Returns the ABI encoded transaction data needed to send an Ethereum transaction calling this method. Before
* sending the Ethereum tx, this encoded tx data can first be sent to a separate signing service or can be used
* to create a 0x transaction (see protocol spec for more details).
*/
getABIEncodedTransactionData(): string {
const self = (this as any) as AbiGenDummyContract;
const abiEncodedTransactionData = self._strictEncodeArguments('emitSimpleEvent()', []);
return abiEncodedTransactionData;
},
getABIDecodedTransactionData(callData: string): void {
const self = (this as any) as AbiGenDummyContract;
const abiEncoder = self._lookupAbiEncoder('emitSimpleEvent()');
// tslint:disable boolean-naming
const abiDecodedCallData = abiEncoder.strictDecode<void>(callData);
return abiDecodedCallData;
},
getABIDecodedReturnData(returnData: string): void {
const self = (this as any) as AbiGenDummyContract;
const abiEncoder = self._lookupAbiEncoder('emitSimpleEvent()');
// tslint:disable boolean-naming
const abiDecodedReturnData = abiEncoder.strictDecodeReturnValue<void>(returnData);
return abiDecodedReturnData;
},
};
/** /**
* a method that returns a struct * a method that returns a struct
*/ */
@@ -2712,6 +2854,15 @@ export class AbiGenDummyContract extends BaseContract {
stateMutability: 'pure', stateMutability: 'pure',
type: 'function', type: 'function',
}, },
{
constant: false,
inputs: [],
name: 'emitSimpleEvent',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{ {
constant: true, constant: true,
inputs: [], inputs: [],
@@ -2822,12 +2973,17 @@ export class AbiGenDummyContract extends BaseContract {
anonymous: false, anonymous: false,
inputs: [ inputs: [
{ {
name: 'param', name: 'someBytes',
type: 'uint8', type: 'bytes',
indexed: false,
},
{
name: 'someString',
type: 'string',
indexed: false, indexed: false,
}, },
], ],
name: 'AnEvent', name: 'SimpleEvent',
outputs: [], outputs: [],
type: 'event', type: 'event',
}, },

View File

@@ -1,15 +1,21 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0x/dev-utils'; import { BlockchainLifecycle, devConstants, web3Factory } from '@0x/dev-utils';
import { Web3ProviderEngine } from '@0x/subproviders'; import { Web3ProviderEngine } from '@0x/subproviders';
import { BigNumber, providerUtils } from '@0x/utils'; import { BigNumber, providerUtils } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper'; import { BlockParamLiteral, Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai'; import * as chai from 'chai';
import * as chaiAsPromised from 'chai-as-promised'; import * as chaiAsPromised from 'chai-as-promised';
import * as ChaiBigNumber from 'chai-bignumber'; import * as ChaiBigNumber from 'chai-bignumber';
import * as dirtyChai from 'dirty-chai'; import * as dirtyChai from 'dirty-chai';
import * as Sinon from 'sinon'; import * as Sinon from 'sinon';
import { constants } from 'websocket';
import { AbiGenDummyContract, AbiGenDummyEvents, artifacts, TestLibDummyContract } from '../src'; import {
AbiGenDummyContract,
AbiGenDummyEvents,
AbiGenDummyWithdrawalEventArgs,
artifacts,
TestLibDummyContract,
} from '../src';
const txDefaults = { const txDefaults = {
from: devConstants.TESTRPC_FIRST_ADDRESS, from: devConstants.TESTRPC_FIRST_ADDRESS,
@@ -163,6 +169,45 @@ describe('AbiGenDummy Contract', () => {
}); });
}); });
describe('getLogsAsync', () => {
const blockRange = {
fromBlock: 0,
toBlock: BlockParamLiteral.Latest,
};
it('should get logs with decoded args emitted by EventWithStruct', async () => {
await abiGenDummy.emitSimpleEvent.awaitTransactionSuccessAsync();
const eventName = AbiGenDummyEvents.SimpleEvent;
const indexFilterValues = {};
const logs = await abiGenDummy.getLogsAsync(eventName, blockRange, indexFilterValues);
expect(logs).to.have.length(1);
expect(logs[0].event).to.be.equal(eventName);
});
it('should only get the logs with the correct event name', async () => {
await abiGenDummy.emitSimpleEvent.awaitTransactionSuccessAsync();
const differentEventName = AbiGenDummyEvents.Withdrawal;
const indexFilterValues = {};
const logs = await abiGenDummy.getLogsAsync(differentEventName, blockRange, indexFilterValues);
expect(logs).to.have.length(0);
});
it('should only get the logs with the correct indexed fields', async () => {
const [addressOne, addressTwo] = await web3Wrapper.getAvailableAddressesAsync();
await abiGenDummy.withdraw.awaitTransactionSuccessAsync(new BigNumber(1), { from: addressOne });
await abiGenDummy.withdraw.awaitTransactionSuccessAsync(new BigNumber(1), { from: addressTwo });
const eventName = AbiGenDummyEvents.Withdrawal;
const indexFilterValues = {
_owner: addressOne,
};
const logs = await abiGenDummy.getLogsAsync<AbiGenDummyWithdrawalEventArgs>(
eventName,
blockRange,
indexFilterValues,
);
expect(logs).to.have.length(1);
const args = logs[0].args;
expect(args._owner).to.be.equal(addressOne);
});
});
describe('withAddressInput', () => { describe('withAddressInput', () => {
it('should normalize address inputs to lowercase', async () => { it('should normalize address inputs to lowercase', async () => {
const xAddress = devConstants.TESTRPC_FIRST_ADDRESS.toUpperCase(); const xAddress = devConstants.TESTRPC_FIRST_ADDRESS.toUpperCase();

View File

@@ -50,8 +50,12 @@
"@0x/assert": "^2.1.5", "@0x/assert": "^2.1.5",
"@0x/contracts-test-utils": "^3.1.15", "@0x/contracts-test-utils": "^3.1.15",
"@0x/coordinator-server": "^0.1.3", "@0x/coordinator-server": "^0.1.3",
"@0x/dev-utils": "^2.3.2",
"@0x/fill-scenarios": "^3.0.18",
"@0x/json-schemas": "^4.0.1", "@0x/json-schemas": "^4.0.1",
"@0x/ts-doc-gen": "^0.0.22", "@0x/ts-doc-gen": "^0.0.22",
"@0x/migrations": "^4.3.1",
"@0x/subproviders": "^5.0.3",
"@0x/tslint-config": "^3.0.1", "@0x/tslint-config": "^3.0.1",
"@0x/types": "^2.4.2", "@0x/types": "^2.4.2",
"@0x/utils": "^4.5.1", "@0x/utils": "^4.5.1",
@@ -62,7 +66,6 @@
"chai-bignumber": "^3.0.0", "chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"ethereum-types": "^2.1.5", "ethereum-types": "^2.1.5",
"ethers": "~4.0.4",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"mocha": "^6.2.0", "mocha": "^6.2.0",
"nock": "^10.0.6", "nock": "^10.0.6",
@@ -76,11 +79,8 @@
"@0x/base-contract": "^5.3.3", "@0x/base-contract": "^5.3.3",
"@0x/contract-addresses": "^3.1.0", "@0x/contract-addresses": "^3.1.0",
"@0x/contract-artifacts": "^2.2.1", "@0x/contract-artifacts": "^2.2.1",
"@0x/dev-utils": "^2.3.2",
"@0x/fill-scenarios": "^3.0.18",
"@0x/migrations": "^4.3.1",
"@0x/order-utils": "^8.3.1", "@0x/order-utils": "^8.3.1",
"@0x/subproviders": "^5.0.3", "ethers": "~4.0.4",
"http-status-codes": "^1.3.2" "http-status-codes": "^1.3.2"
}, },
"publishConfig": { "publishConfig": {

View File

@@ -1,550 +0,0 @@
import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils, orderHashUtils, signatureUtils } from '@0x/order-utils';
import { DoneCallback, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { BlockParamLiteral } from 'ethereum-types';
import 'mocha';
import { ContractWrappers, ExchangeCancelEventArgs, ExchangeEvents, ExchangeFillEventArgs, OrderStatus } from '../src';
import { DecodedLogEvent } from '../src/types';
import { _getDefaultContractAddresses } from '../src/utils/contract_addresses';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { tokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('ExchangeWrapper', () => {
let contractWrappers: ContractWrappers;
let userAddresses: string[];
let zrxTokenAddress: string;
let fillScenarios: FillScenarios;
let exchangeContractAddress: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let makerAddress: string;
let anotherMakerAddress: string;
let takerAddress: string;
let makerAssetData: string;
let takerAssetData: string;
const fillableAmount = new BigNumber(5);
const takerTokenFillAmount = new BigNumber(5);
let signedOrder: SignedOrder;
let anotherSignedOrder: SignedOrder;
before(async () => {
const contractAddresses = await migrateOnceAsync();
await blockchainLifecycle.startAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
zrxTokenAddress = contractAddresses.zrxToken;
fillScenarios = new FillScenarios(
provider,
userAddresses,
zrxTokenAddress,
exchangeContractAddress,
contractWrappers.erc20Proxy.address,
contractWrappers.erc721Proxy.address,
);
[, makerAddress, takerAddress, , anotherMakerAddress] = userAddresses;
[makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
[makerAssetData, takerAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
);
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('fill order(s)', () => {
describe('#fillOrderAsync', () => {
it('should fill a valid order', async () => {
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
});
});
describe('#fillOrderNoThrowAsync', () => {
it('should fill a valid order', async () => {
await contractWrappers.exchange.fillOrderNoThrow.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
describe('#fillOrKillOrderAsync', () => {
it('should fill or kill a valid order', async () => {
await contractWrappers.exchange.fillOrKillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
});
});
describe('#batchFillOrdersAsync', () => {
it('should fill a batch of valid orders', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount];
await contractWrappers.exchange.batchFillOrders.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmounts,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
});
});
describe('#marketBuyOrdersAsync', () => {
it('should maker buy', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const makerAssetFillAmount = takerTokenFillAmount;
await contractWrappers.exchange.marketBuyOrders.awaitTransactionSuccessAsync(
signedOrders,
makerAssetFillAmount,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
});
});
describe('#marketBuyOrdersNoThrowAsync', () => {
it('should no throw maker buy', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const makerAssetFillAmount = takerTokenFillAmount;
await contractWrappers.exchange.marketBuyOrdersNoThrow.awaitTransactionSuccessAsync(
signedOrders,
makerAssetFillAmount,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
describe('#marketSellOrdersAsync', () => {
it('should maker sell', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmount = takerTokenFillAmount;
await contractWrappers.exchange.marketSellOrders.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmount,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
});
});
describe('#marketSellOrdersNoThrowAsync', () => {
it('should no throw maker sell', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmount = takerTokenFillAmount;
await contractWrappers.exchange.marketSellOrdersNoThrow.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmount,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
describe('#batchFillOrdersNoThrowAsync', () => {
it('should fill a batch of valid orders', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount];
await contractWrappers.exchange.batchFillOrdersNoThrow.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmounts,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
let orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(anotherSignedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
describe('#batchFillOrKillOrdersAsync', () => {
it('should fill or kill a batch of valid orders', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount];
await contractWrappers.exchange.batchFillOrKillOrders.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmounts,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
});
});
describe('#matchOrdersAsync', () => {
it('should match two valid ordersr', async () => {
const matchingSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
takerAssetData,
makerAssetData,
makerAddress,
takerAddress,
fillableAmount,
);
await contractWrappers.exchange.matchOrders.awaitTransactionSuccessAsync(
signedOrder,
matchingSignedOrder,
signedOrder.signature,
matchingSignedOrder.signature,
{ from: takerAddress },
);
});
});
});
describe('cancel order(s)', () => {
describe('#cancelOrderAsync', () => {
it('should cancel a valid order', async () => {
await contractWrappers.exchange.cancelOrder.awaitTransactionSuccessAsync(signedOrder, {
from: makerAddress,
});
});
});
describe('#batchCancelOrdersAsync', () => {
it('should cancel a batch of valid orders', async () => {
const orders = [signedOrder, anotherSignedOrder];
await contractWrappers.exchange.batchCancelOrders.awaitTransactionSuccessAsync(orders, {
from: makerAddress,
});
});
});
describe('#cancelOrdersUpTo/getOrderEpochAsync', () => {
it('should cancel orders up to target order epoch', async () => {
const targetOrderEpoch = new BigNumber(42);
await contractWrappers.exchange.cancelOrdersUpTo.awaitTransactionSuccessAsync(targetOrderEpoch, {
from: makerAddress,
});
const orderEpoch = await contractWrappers.exchange.orderEpoch.callAsync(
makerAddress,
constants.NULL_ADDRESS,
);
expect(orderEpoch).to.be.bignumber.equal(targetOrderEpoch.plus(1));
});
});
});
describe('#getOrderInfoAsync', () => {
it('should get the order info', async () => {
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
expect(orderInfo.orderHash).to.be.equal(orderHash);
});
});
describe('#getOrdersInfoAsync', () => {
it('should get the orders info', async () => {
const ordersInfo = await contractWrappers.exchange.getOrdersInfo.callAsync([
signedOrder,
anotherSignedOrder,
]);
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
expect(ordersInfo[0].orderHash).to.be.equal(orderHash);
const anotherOrderHash = orderHashUtils.getOrderHashHex(anotherSignedOrder);
expect(ordersInfo[1].orderHash).to.be.equal(anotherOrderHash);
});
});
describe('#isValidSignature', () => {
it('should check if the signature is valid', async () => {
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
let isValid = await contractWrappers.exchange.isValidSignature.callAsync(
orderHash,
signedOrder.makerAddress,
signedOrder.signature,
);
expect(isValid).to.be.true();
isValid = await contractWrappers.exchange.isValidSignature.callAsync(
orderHash,
signedOrder.takerAddress,
signedOrder.signature,
);
expect(isValid).to.be.false();
});
});
describe('#isAllowedValidatorAsync', () => {
it('should check if the validator is allowed', async () => {
const signerAddress = makerAddress;
const validatorAddress = constants.NULL_ADDRESS;
const isAllowed = await contractWrappers.exchange.allowedValidators.callAsync(
signerAddress,
validatorAddress,
);
expect(isAllowed).to.be.false();
});
});
describe('#setSignatureValidatorApproval', () => {
it('should set signature validator approval', async () => {
const validatorAddress = constants.NULL_ADDRESS;
const isApproved = true;
const senderAddress = makerAddress;
await contractWrappers.exchange.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorAddress,
isApproved,
{ from: senderAddress },
);
});
});
describe('#isTransactionExecutedAsync', () => {
it('should check if the transaction is executed', async () => {
const transactionHash = '0x0000000000000000000000000000000000000000000000000000000000000000';
const isExecuted = await contractWrappers.exchange.transactions.callAsync(transactionHash);
expect(isExecuted).to.be.false();
});
});
describe('#getAssetProxyBySignatureAsync', () => {
it('should fill or kill a valid order', async () => {
const erc20ProxyId = await contractWrappers.erc20Proxy.getProxyId.callAsync();
const erc20ProxyAddressById = await contractWrappers.exchange.getAssetProxy.callAsync(erc20ProxyId);
const erc20ProxyAddress = contractWrappers.erc20Proxy.address;
expect(erc20ProxyAddressById).to.be.equal(erc20ProxyAddress);
const erc721ProxyId = await contractWrappers.erc721Proxy.getProxyId.callAsync();
const erc721ProxyAddressById = await contractWrappers.exchange.getAssetProxy.callAsync(erc721ProxyId);
const erc721ProxyAddress = contractWrappers.erc721Proxy.address;
expect(erc721ProxyAddressById).to.be.equal(erc721ProxyAddress);
});
});
describe('#preSign/isPresigned', () => {
it('should preSign the hash', async () => {
const senderAddress = takerAddress;
const hash = orderHashUtils.getOrderHashHex(signedOrder);
const signerAddress = signedOrder.makerAddress;
let isPreSigned = await contractWrappers.exchange.preSigned.callAsync(hash, signerAddress);
expect(isPreSigned).to.be.false();
await contractWrappers.exchange.preSign.awaitTransactionSuccessAsync(
hash,
signerAddress,
signedOrder.signature,
{ from: senderAddress },
);
isPreSigned = await contractWrappers.exchange.preSigned.callAsync(hash, signerAddress);
expect(isPreSigned).to.be.true();
const preSignedSignature = '0x06';
const isValidSignature = await contractWrappers.exchange.isValidSignature.callAsync(
hash,
signerAddress,
preSignedSignature,
);
expect(isValidSignature).to.be.true();
// Test our TS implementation of signature validation
const isValidSignatureInTs = await signatureUtils.isValidSignatureAsync(
provider,
hash,
preSignedSignature,
signerAddress,
);
expect(isValidSignatureInTs).to.be.true();
});
});
describe('#getVersionAsync', () => {
it('should return version the hash', async () => {
const version = await contractWrappers.exchange.VERSION.callAsync();
const VERSION = '2.0.0';
expect(version).to.be.equal(VERSION);
});
});
describe('#subscribe', () => {
const indexFilterValues = {};
const takerTokenFillAmountInBaseUnits = new BigNumber(1);
beforeEach(async () => {
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
);
});
afterEach(async () => {
contractWrappers.exchange.unsubscribeAll();
});
// Hack: Mocha does not allow a test to be both async and have a `done` callback
// Since we need to await the receipt of the event in the `subscribe` callback,
// we do need both. A hack is to make the top-level a sync fn w/ a done callback and then
// wrap the rest of the test in an async block
// Source: https://github.com/mochajs/mocha/issues/2407
it('Should receive the Fill event when an order is filled', (done: DoneCallback) => {
(async () => {
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<ExchangeFillEventArgs>) => {
expect(logEvent.log.event).to.be.equal(ExchangeEvents.Fill);
},
);
contractWrappers.exchange.subscribe(ExchangeEvents.Fill, indexFilterValues, callback);
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmountInBaseUnits,
signedOrder.signature,
{ from: takerAddress },
);
})().catch(done);
});
it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => {
(async () => {
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<ExchangeCancelEventArgs>) => {
expect(logEvent.log.event).to.be.equal(ExchangeEvents.Cancel);
},
);
contractWrappers.exchange.subscribe(ExchangeEvents.Cancel, indexFilterValues, callback);
await contractWrappers.exchange.cancelOrder.awaitTransactionSuccessAsync(signedOrder, {
from: makerAddress,
});
})().catch(done);
});
it('Outstanding subscriptions are cancelled when contractWrappers.unsubscribeAll called', (done: DoneCallback) => {
(async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<ExchangeFillEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled'));
},
);
contractWrappers.exchange.subscribe(ExchangeEvents.Fill, indexFilterValues, callbackNeverToBeCalled);
contractWrappers.unsubscribeAll();
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<ExchangeFillEventArgs>) => {
expect(logEvent.log.event).to.be.equal(ExchangeEvents.Fill);
},
);
contractWrappers.exchange.subscribe(ExchangeEvents.Fill, indexFilterValues, callback);
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmountInBaseUnits,
signedOrder.signature,
{ from: takerAddress },
);
})().catch(done);
});
it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
(async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(_logEvent: DecodedLogEvent<ExchangeFillEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled'));
},
);
const subscriptionToken = contractWrappers.exchange.subscribe(
ExchangeEvents.Fill,
indexFilterValues,
callbackNeverToBeCalled,
);
contractWrappers.exchange.unsubscribe(subscriptionToken);
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmountInBaseUnits,
signedOrder.signature,
{ from: takerAddress },
);
done();
})().catch(done);
});
});
describe('#getLogsAsync', () => {
const blockRange = {
fromBlock: 0,
toBlock: BlockParamLiteral.Latest,
};
it('should get logs with decoded args emitted by Fill', async () => {
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
const eventName = ExchangeEvents.Fill;
const indexFilterValues = {};
const logs = await contractWrappers.exchange.getLogsAsync(eventName, blockRange, indexFilterValues);
expect(logs).to.have.length(1);
expect(logs[0].event).to.be.equal(eventName);
});
it('should only get the logs with the correct event name', async () => {
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
const differentEventName = ExchangeEvents.Cancel;
const indexFilterValues = {};
const logs = await contractWrappers.exchange.getLogsAsync(
differentEventName,
blockRange,
indexFilterValues,
);
expect(logs).to.have.length(0);
});
it('should only get the logs with the correct indexed fields', async () => {
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
const signedOrderWithAnotherMakerAddress = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
anotherMakerAddress,
takerAddress,
fillableAmount,
);
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrderWithAnotherMakerAddress,
takerTokenFillAmount,
signedOrderWithAnotherMakerAddress.signature,
{ from: takerAddress },
);
const eventName = ExchangeEvents.Fill;
const indexFilterValues = {
makerAddress: anotherMakerAddress,
};
const logs = await contractWrappers.exchange.getLogsAsync<ExchangeFillEventArgs>(
eventName,
blockRange,
indexFilterValues,
);
expect(logs).to.have.length(1);
const args = logs[0].args;
expect(args.makerAddress).to.be.equal(anotherMakerAddress);
});
});
}); // tslint:disable:max-file-line-count