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);
}
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
// 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)
@@ -113,8 +107,6 @@ contract AbiGenDummy
return x;
}
event AnEvent(uint8 param);
function acceptsBytes(bytes memory a) public pure {}
/// @dev a method that accepts an array of bytes
@@ -180,6 +172,22 @@ contract AbiGenDummy
function overloadedMethod(int 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`
/// @dev complex input is dynamic and more difficult to decode than simple input.
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';
// tslint:enable:no-unused-variable
export type AbiGenDummyEventArgs = AbiGenDummyWithdrawalEventArgs | AbiGenDummyAnEventEventArgs;
export type AbiGenDummyEventArgs = AbiGenDummyWithdrawalEventArgs | AbiGenDummySimpleEventEventArgs;
export enum AbiGenDummyEvents {
Withdrawal = 'Withdrawal',
AnEvent = 'AnEvent',
SimpleEvent = 'SimpleEvent',
}
export interface AbiGenDummyWithdrawalEventArgs extends DecodedLogArgs {
@@ -43,8 +43,9 @@ export interface AbiGenDummyWithdrawalEventArgs extends DecodedLogArgs {
_value: BigNumber;
}
export interface AbiGenDummyAnEventEventArgs extends DecodedLogArgs {
param: number;
export interface AbiGenDummySimpleEventEventArgs extends DecodedLogArgs {
someBytes: string;
someString: string;
}
/* istanbul ignore next */
@@ -1809,6 +1810,147 @@ export class AbiGenDummyContract extends BaseContract {
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
*/
@@ -2712,6 +2854,15 @@ export class AbiGenDummyContract extends BaseContract {
stateMutability: 'pure',
type: 'function',
},
{
constant: false,
inputs: [],
name: 'emitSimpleEvent',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: true,
inputs: [],
@@ -2822,12 +2973,17 @@ export class AbiGenDummyContract extends BaseContract {
anonymous: false,
inputs: [
{
name: 'param',
type: 'uint8',
name: 'someBytes',
type: 'bytes',
indexed: false,
},
{
name: 'someString',
type: 'string',
indexed: false,
},
],
name: 'AnEvent',
name: 'SimpleEvent',
outputs: [],
type: 'event',
},

View File

@@ -1,15 +1,21 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0x/dev-utils';
import { Web3ProviderEngine } from '@0x/subproviders';
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 chaiAsPromised from 'chai-as-promised';
import * as ChaiBigNumber from 'chai-bignumber';
import * as dirtyChai from 'dirty-chai';
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 = {
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', () => {
it('should normalize address inputs to lowercase', async () => {
const xAddress = devConstants.TESTRPC_FIRST_ADDRESS.toUpperCase();

View File

@@ -50,8 +50,12 @@
"@0x/assert": "^2.1.5",
"@0x/contracts-test-utils": "^3.1.15",
"@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/ts-doc-gen": "^0.0.22",
"@0x/migrations": "^4.3.1",
"@0x/subproviders": "^5.0.3",
"@0x/tslint-config": "^3.0.1",
"@0x/types": "^2.4.2",
"@0x/utils": "^4.5.1",
@@ -62,7 +66,6 @@
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"ethereum-types": "^2.1.5",
"ethers": "~4.0.4",
"lodash": "^4.17.11",
"mocha": "^6.2.0",
"nock": "^10.0.6",
@@ -76,11 +79,8 @@
"@0x/base-contract": "^5.3.3",
"@0x/contract-addresses": "^3.1.0",
"@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/subproviders": "^5.0.3",
"ethers": "~4.0.4",
"http-status-codes": "^1.3.2"
},
"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