Update coordinator tests

This commit is contained in:
Michael Zhu 2019-09-30 19:22:18 -07:00
parent 0e7387550c
commit 9b922f746b
14 changed files with 455 additions and 508 deletions

View File

@ -48,7 +48,7 @@ contract MixinSignatureValidator is
} }
// Pop last byte off of signature byte array. // Pop last byte off of signature byte array.
uint8 signatureTypeRaw = uint8(signature.popLastByte()); uint8 signatureTypeRaw = uint8(signature[signature.length - 1]);
// Ensure signature is supported // Ensure signature is supported
if (signatureTypeRaw >= uint8(SignatureType.NSignatureTypes)) { if (signatureTypeRaw >= uint8(SignatureType.NSignatureTypes)) {

View File

@ -35,7 +35,7 @@
"compile:truffle": "truffle compile" "compile:truffle": "truffle compile"
}, },
"config": { "config": {
"abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry).json", "abis": "./generated-artifacts/@(Coordinator|CoordinatorRegistry|ICoordinatorApprovalVerifier|ICoordinatorCore|ICoordinatorRegistryCore|ICoordinatorSignatureValidator|LibConstants|LibCoordinatorApproval|LibCoordinatorRichErrors|LibEIP712CoordinatorDomain|MixinCoordinatorApprovalVerifier|MixinCoordinatorCore|MixinCoordinatorRegistryCore|MixinSignatureValidator).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually." "abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
}, },
"repository": { "repository": {

View File

@ -7,7 +7,31 @@ import { ContractArtifact } from 'ethereum-types';
import * as Coordinator from '../generated-artifacts/Coordinator.json'; import * as Coordinator from '../generated-artifacts/Coordinator.json';
import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json'; import * as CoordinatorRegistry from '../generated-artifacts/CoordinatorRegistry.json';
import * as ICoordinatorApprovalVerifier from '../generated-artifacts/ICoordinatorApprovalVerifier.json';
import * as ICoordinatorCore from '../generated-artifacts/ICoordinatorCore.json';
import * as ICoordinatorRegistryCore from '../generated-artifacts/ICoordinatorRegistryCore.json';
import * as ICoordinatorSignatureValidator from '../generated-artifacts/ICoordinatorSignatureValidator.json';
import * as LibConstants from '../generated-artifacts/LibConstants.json';
import * as LibCoordinatorApproval from '../generated-artifacts/LibCoordinatorApproval.json';
import * as LibCoordinatorRichErrors from '../generated-artifacts/LibCoordinatorRichErrors.json';
import * as LibEIP712CoordinatorDomain from '../generated-artifacts/LibEIP712CoordinatorDomain.json';
import * as MixinCoordinatorApprovalVerifier from '../generated-artifacts/MixinCoordinatorApprovalVerifier.json';
import * as MixinCoordinatorCore from '../generated-artifacts/MixinCoordinatorCore.json';
import * as MixinCoordinatorRegistryCore from '../generated-artifacts/MixinCoordinatorRegistryCore.json';
import * as MixinSignatureValidator from '../generated-artifacts/MixinSignatureValidator.json';
export const artifacts = { export const artifacts = {
Coordinator: Coordinator as ContractArtifact, Coordinator: Coordinator as ContractArtifact,
MixinCoordinatorApprovalVerifier: MixinCoordinatorApprovalVerifier as ContractArtifact,
MixinCoordinatorCore: MixinCoordinatorCore as ContractArtifact,
MixinSignatureValidator: MixinSignatureValidator as ContractArtifact,
ICoordinatorApprovalVerifier: ICoordinatorApprovalVerifier as ContractArtifact,
ICoordinatorCore: ICoordinatorCore as ContractArtifact,
ICoordinatorSignatureValidator: ICoordinatorSignatureValidator as ContractArtifact,
LibConstants: LibConstants as ContractArtifact,
LibCoordinatorApproval: LibCoordinatorApproval as ContractArtifact,
LibCoordinatorRichErrors: LibCoordinatorRichErrors as ContractArtifact,
LibEIP712CoordinatorDomain: LibEIP712CoordinatorDomain as ContractArtifact,
CoordinatorRegistry: CoordinatorRegistry as ContractArtifact, CoordinatorRegistry: CoordinatorRegistry as ContractArtifact,
MixinCoordinatorRegistryCore: MixinCoordinatorRegistryCore as ContractArtifact,
ICoordinatorRegistryCore: ICoordinatorRegistryCore as ContractArtifact,
}; };

View File

@ -5,3 +5,15 @@
*/ */
export * from '../generated-wrappers/coordinator'; export * from '../generated-wrappers/coordinator';
export * from '../generated-wrappers/coordinator_registry'; export * from '../generated-wrappers/coordinator_registry';
export * from '../generated-wrappers/i_coordinator_approval_verifier';
export * from '../generated-wrappers/i_coordinator_core';
export * from '../generated-wrappers/i_coordinator_registry_core';
export * from '../generated-wrappers/i_coordinator_signature_validator';
export * from '../generated-wrappers/lib_constants';
export * from '../generated-wrappers/lib_coordinator_approval';
export * from '../generated-wrappers/lib_coordinator_rich_errors';
export * from '../generated-wrappers/lib_e_i_p712_coordinator_domain';
export * from '../generated-wrappers/mixin_coordinator_approval_verifier';
export * from '../generated-wrappers/mixin_coordinator_core';
export * from '../generated-wrappers/mixin_coordinator_registry_core';
export * from '../generated-wrappers/mixin_signature_validator';

View File

@ -7,35 +7,28 @@ import {
ExchangeCancelUpToEventArgs, ExchangeCancelUpToEventArgs,
ExchangeContract, ExchangeContract,
exchangeDataEncoder, exchangeDataEncoder,
ExchangeEvents,
ExchangeFillEventArgs, ExchangeFillEventArgs,
ExchangeFunctionName, ExchangeFunctionName,
} from '@0x/contracts-exchange'; } from '@0x/contracts-exchange';
import { import {
chaiSetup, blockchainTests,
constants, constants,
expectTransactionFailedAsync, expect,
filterLogsToArguments,
getLatestBlockTimestampAsync, getLatestBlockTimestampAsync,
OrderFactory, OrderFactory,
provider,
TransactionFactory, TransactionFactory,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils'; } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils, CoordinatorRevertErrors, orderHashUtils, transactionHashUtils } from '@0x/order-utils';
import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; import { SignedOrder } from '@0x/types';
import { RevertReason } from '@0x/types'; import { BigNumber } from '@0x/utils';
import { BigNumber, providerUtils } from '@0x/utils'; import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
import { ApprovalFactory, artifacts, CoordinatorContract } from '../src'; import { ApprovalFactory, artifacts, CoordinatorContract } from '../src';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
// tslint:disable:no-unnecessary-type-assertion // tslint:disable:no-unnecessary-type-assertion
describe('Coordinator tests', () => { blockchainTests.resets('Coordinator tests', env => {
let chainId: number; let chainId: number;
let makerAddress: string; let makerAddress: string;
let owner: string; let owner: string;
@ -55,18 +48,17 @@ describe('Coordinator tests', () => {
let makerTransactionFactory: TransactionFactory; let makerTransactionFactory: TransactionFactory;
let approvalFactory: ApprovalFactory; let approvalFactory: ApprovalFactory;
before(async () => { // const GAS_PRICE = new BigNumber(env.txDefaults.gasPrice || constants.DEFAULT_GAS_PRICE);
await blockchainLifecycle.startAsync(); // const PROTOCOL_FEE_MULTIPLIER = new BigNumber(150);
}); // const PROTOCOL_FEE = GAS_PRICE.times(PROTOCOL_FEE_MULTIPLIER);
after(async () => { const PROTOCOL_FEE = new BigNumber(0);
await blockchainLifecycle.revertAsync();
});
before(async () => {
chainId = await providerUtils.getChainIdAsync(provider);
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts.slice(0, 4));
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); before(async () => {
chainId = await env.getChainIdAsync();
const accounts = await env.getAccountAddressesAsync();
const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts);
erc20Wrapper = new ERC20Wrapper(env.provider, usedAddresses, owner);
erc20Proxy = await erc20Wrapper.deployProxyAsync(); erc20Proxy = await erc20Wrapper.deployProxyAsync();
const numDummyErc20ToDeploy = 3; const numDummyErc20ToDeploy = 3;
[erc20TokenA, erc20TokenB, makerFeeToken] = await erc20Wrapper.deployDummyTokensAsync( [erc20TokenA, erc20TokenB, makerFeeToken] = await erc20Wrapper.deployDummyTokensAsync(
@ -77,26 +69,28 @@ describe('Coordinator tests', () => {
exchange = await ExchangeContract.deployFrom0xArtifactAsync( exchange = await ExchangeContract.deployFrom0xArtifactAsync(
exchangeArtifacts.Exchange, exchangeArtifacts.Exchange,
provider, env.provider,
txDefaults, env.txDefaults,
{},
new BigNumber(chainId), new BigNumber(chainId),
); );
await web3Wrapper.awaitTransactionSuccessAsync( await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }), exchange.address,
{ from: owner },
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(
await web3Wrapper.awaitTransactionSuccessAsync( erc20Proxy.address,
await exchange.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }), { from: owner },
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync( coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
artifacts.Coordinator, artifacts.Coordinator,
provider, env.provider,
txDefaults, env.txDefaults,
artifacts, { ...exchangeArtifacts, ...artifacts },
exchange.address, exchange.address,
new BigNumber(chainId), new BigNumber(chainId),
); );
@ -106,6 +100,7 @@ describe('Coordinator tests', () => {
...constants.STATIC_ORDER_PARAMS, ...constants.STATIC_ORDER_PARAMS,
senderAddress: coordinatorContract.address, senderAddress: coordinatorContract.address,
makerAddress, makerAddress,
takerAddress,
feeRecipientAddress, feeRecipientAddress,
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address), makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address), takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
@ -122,12 +117,48 @@ describe('Coordinator tests', () => {
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address, chainId); takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address, chainId);
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address); approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address);
}); });
beforeEach(async () => {
await blockchainLifecycle.startAsync(); function verifyEvents<TEventArgs>(
}); txReceipt: TransactionReceiptWithDecodedLogs,
afterEach(async () => { expectedEvents: TEventArgs[],
await blockchainLifecycle.revertAsync(); eventName: string,
): void {
const logs = filterLogsToArguments<TEventArgs>(txReceipt.logs, eventName);
expect(logs.length).to.eq(expectedEvents.length);
logs.forEach((log, index) => {
expect(log).to.deep.equal(expectedEvents[index]);
}); });
}
function expectedFillEvent(order: SignedOrder): ExchangeFillEventArgs {
return {
makerAddress: order.makerAddress,
feeRecipientAddress: order.feeRecipientAddress,
makerAssetData: order.makerAssetData,
takerAssetData: order.takerAssetData,
makerFeeAssetData: order.makerFeeAssetData,
takerFeeAssetData: order.takerFeeAssetData,
takerAddress: order.takerAddress,
senderAddress: order.senderAddress,
makerAssetFilledAmount: order.makerAssetAmount,
takerAssetFilledAmount: order.takerAssetAmount,
makerFeePaid: order.makerFee,
takerFeePaid: order.takerFee,
protocolFeePaid: PROTOCOL_FEE,
orderHash: orderHashUtils.getOrderHashHex(order),
};
}
function expectedCancelEvent(order: SignedOrder): ExchangeCancelEventArgs {
return {
makerAddress: order.makerAddress,
feeRecipientAddress: order.feeRecipientAddress,
makerAssetData: order.makerAssetData,
takerAssetData: order.takerAssetData,
senderAddress: order.senderAddress,
orderHash: orderHashUtils.getOrderHashHex(order),
};
}
describe('single order fills', () => { describe('single order fills', () => {
for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) { for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
@ -142,72 +173,38 @@ describe('Coordinator tests', () => {
takerAddress, takerAddress,
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( const transactionReceipt = await coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction, transaction,
takerAddress, takerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approval.signature], [approval.signature],
{ from: takerAddress }, { from: takerAddress },
),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', verifyEvents(transactionReceipt, [expectedFillEvent(orders[0])], ExchangeEvents.Fill);
);
expect(fillLogs.length).to.eq(1);
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
}); });
it(`${fnName} should fill the order if called by approver`, async () => { it(`${fnName} should fill the order if called by approver`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()]; const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( const transactionReceipt = await coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction, transaction,
feeRecipientAddress, feeRecipientAddress,
transaction.signature, transaction.signature,
[], [],
[], [],
{ from: feeRecipientAddress }, { from: feeRecipientAddress },
),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const fillLogs = transactionReceipt.logs.filter( verifyEvents(transactionReceipt, [expectedFillEvent(orders[0])], ExchangeEvents.Fill);
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill',
);
expect(fillLogs.length).to.eq(1);
const fillLogArgs = (fillLogs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(orders[0].makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(orders[0].takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(orders[0].makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(orders[0].takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
}); });
it(`${fnName} should revert with no approval signature`, async () => { it(`${fnName} should revert with no approval signature`, async () => {
const orders = [await orderFactory.newSignedOrderAsync()]; const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
await expectTransactionFailedAsync( const tx = coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction, transaction,
takerAddress, takerAddress,
transaction.signature, transaction.signature,
@ -217,8 +214,11 @@ describe('Coordinator tests', () => {
from: takerAddress, from: takerAddress,
gas: constants.MAX_EXECUTE_TRANSACTION_GAS, gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
}, },
), );
RevertReason.InvalidApprovalSignature,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, feeRecipientAddress),
); );
}); });
it(`${fnName} should revert with an invalid approval signature`, async () => { it(`${fnName} should revert with an invalid approval signature`, async () => {
@ -233,16 +233,18 @@ describe('Coordinator tests', () => {
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`; const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
await expectTransactionFailedAsync( const tx = coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction, transaction,
takerAddress, takerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[signature], [signature],
{ from: takerAddress }, { from: takerAddress },
), );
RevertReason.InvalidApprovalSignature,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, feeRecipientAddress),
); );
}); });
it(`${fnName} should revert with an expired approval`, async () => { it(`${fnName} should revert with an expired approval`, async () => {
@ -256,16 +258,19 @@ describe('Coordinator tests', () => {
takerAddress, takerAddress,
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync( const tx = coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
transaction, transaction,
takerAddress, takerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approval.signature], [approval.signature],
{ from: takerAddress }, { from: takerAddress },
), );
RevertReason.ApprovalExpired,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.ApprovalExpiredError(transactionHash, approvalExpirationTimeSeconds),
); );
}); });
it(`${fnName} should revert if not called by tx signer or approver`, async () => { it(`${fnName} should revert if not called by tx signer or approver`, async () => {
@ -279,17 +284,16 @@ describe('Coordinator tests', () => {
takerAddress, takerAddress,
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync( const tx = coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
transaction, transaction,
takerAddress, takerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approval.signature], [approval.signature],
{ from: owner }, { from: owner },
),
RevertReason.InvalidOrigin,
); );
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(takerAddress));
}); });
} }
}); });
@ -306,69 +310,35 @@ describe('Coordinator tests', () => {
takerAddress, takerAddress,
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( const transactionReceipt = await coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction, transaction,
takerAddress, takerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approval.signature], [approval.signature],
{ from: takerAddress, gas: constants.MAX_EXECUTE_TRANSACTION_GAS }, { from: takerAddress, gas: constants.MAX_EXECUTE_TRANSACTION_GAS },
),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', const expectedEvents = orders.map(order => expectedFillEvent(order));
); verifyEvents(transactionReceipt, expectedEvents, ExchangeEvents.Fill);
expect(fillLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
}); });
it(`${fnName} should fill the orders if called by approver`, async () => { it(`${fnName} should fill the orders if called by approver`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data }); const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( const transactionReceipt = await coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction, transaction,
feeRecipientAddress, feeRecipientAddress,
transaction.signature, transaction.signature,
[], [],
[], [],
{ from: feeRecipientAddress, gas: constants.MAX_EXECUTE_TRANSACTION_GAS }, { from: feeRecipientAddress, gas: constants.MAX_EXECUTE_TRANSACTION_GAS },
),
constants.AWAIT_TRANSACTION_MINED_MS, constants.AWAIT_TRANSACTION_MINED_MS,
); );
const fillLogs = transactionReceipt.logs.filter(
log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', const expectedEvents = orders.map(order => expectedFillEvent(order));
); verifyEvents(transactionReceipt, expectedEvents, ExchangeEvents.Fill);
expect(fillLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const fillLogArgs = (fillLogs[index] as LogWithDecodedArgs<ExchangeFillEventArgs>).args;
expect(fillLogArgs.makerAddress).to.eq(makerAddress);
expect(fillLogArgs.takerAddress).to.eq(takerAddress);
expect(fillLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(fillLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(fillLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(fillLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(fillLogArgs.makerAssetFilledAmount).to.bignumber.eq(order.makerAssetAmount);
expect(fillLogArgs.takerAssetFilledAmount).to.bignumber.eq(order.takerAssetAmount);
expect(fillLogArgs.makerFeePaid).to.bignumber.eq(order.makerFee);
expect(fillLogArgs.takerFeePaid).to.bignumber.eq(order.takerFee);
expect(fillLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
}); });
it(`${fnName} should revert with an invalid approval signature`, async () => { it(`${fnName} should revert with an invalid approval signature`, async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
@ -382,16 +352,18 @@ describe('Coordinator tests', () => {
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`; const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
await expectTransactionFailedAsync( const tx = coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction, transaction,
takerAddress, takerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[signature], [signature],
{ from: takerAddress }, { from: takerAddress },
), );
RevertReason.InvalidApprovalSignature,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, feeRecipientAddress),
); );
}); });
it(`${fnName} should revert with an expired approval`, async () => { it(`${fnName} should revert with an expired approval`, async () => {
@ -405,16 +377,18 @@ describe('Coordinator tests', () => {
takerAddress, takerAddress,
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
await expectTransactionFailedAsync( const tx = coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
coordinatorContract.executeTransaction.sendTransactionAsync(
transaction, transaction,
takerAddress, takerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approval.signature], [approval.signature],
{ from: takerAddress }, { from: takerAddress },
), );
RevertReason.ApprovalExpired,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.ApprovalExpiredError(transactionHash, approvalExpirationTimeSeconds),
); );
}); });
it(`${fnName} should revert if not called by tx signer or approver`, async () => { it(`${fnName} should revert if not called by tx signer or approver`, async () => {
@ -428,17 +402,16 @@ describe('Coordinator tests', () => {
takerAddress, takerAddress,
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
await expectTransactionFailedAsync(
coordinatorContract.executeTransaction.sendTransactionAsync( const tx = coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
transaction, transaction,
takerAddress, takerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approval.signature], [approval.signature],
{ from: owner }, { from: owner },
),
RevertReason.InvalidOrigin,
); );
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(takerAddress));
}); });
} }
}); });
@ -447,8 +420,7 @@ describe('Coordinator tests', () => {
const orders = [await orderFactory.newSignedOrderAsync()]; const orders = [await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders);
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data }); const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( const transactionReceipt = await coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction, transaction,
makerAddress, makerAddress,
transaction.signature, transaction.signature,
@ -457,26 +429,14 @@ describe('Coordinator tests', () => {
{ {
from: makerAddress, from: makerAddress,
}, },
),
); );
const cancelLogs = transactionReceipt.logs.filter( verifyEvents(transactionReceipt, [expectedCancelEvent(orders[0])], ExchangeEvents.Cancel);
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel',
);
expect(cancelLogs.length).to.eq(1);
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(orders[0].makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(orders[0].takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(orders[0]));
}); });
it('batchCancelOrders call should be successful without an approval', async () => { it('batchCancelOrders call should be successful without an approval', async () => {
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()]; const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchCancelOrders, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchCancelOrders, orders);
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data }); const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( const transactionReceipt = await coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction, transaction,
makerAddress, makerAddress,
transaction.signature, transaction.signature,
@ -485,28 +445,15 @@ describe('Coordinator tests', () => {
{ {
from: makerAddress, from: makerAddress,
}, },
),
); );
const cancelLogs = transactionReceipt.logs.filter( const expectedEvents = orders.map(order => expectedCancelEvent(order));
log => (log as LogWithDecodedArgs<ExchangeCancelEventArgs>).event === 'Cancel', verifyEvents(transactionReceipt, expectedEvents, ExchangeEvents.Cancel);
);
expect(cancelLogs.length).to.eq(orders.length);
orders.forEach((order, index) => {
const cancelLogArgs = (cancelLogs[index] as LogWithDecodedArgs<ExchangeCancelEventArgs>).args;
expect(cancelLogArgs.makerAddress).to.eq(makerAddress);
expect(cancelLogArgs.senderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.feeRecipientAddress).to.eq(feeRecipientAddress);
expect(cancelLogArgs.makerAssetData).to.eq(order.makerAssetData);
expect(cancelLogArgs.takerAssetData).to.eq(order.takerAssetData);
expect(cancelLogArgs.orderHash).to.eq(orderHashUtils.getOrderHashHex(order));
});
}); });
it('cancelOrdersUpTo call should be successful without an approval', async () => { it('cancelOrdersUpTo call should be successful without an approval', async () => {
const targetEpoch = constants.ZERO_AMOUNT; const targetEpoch = constants.ZERO_AMOUNT;
const data = exchange.cancelOrdersUpTo.getABIEncodedTransactionData(targetEpoch); const data = exchange.cancelOrdersUpTo.getABIEncodedTransactionData(targetEpoch);
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data }); const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
const transactionReceipt = await web3Wrapper.awaitTransactionSuccessAsync( const transactionReceipt = await coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
await coordinatorContract.executeTransaction.sendTransactionAsync(
transaction, transaction,
makerAddress, makerAddress,
transaction.signature, transaction.signature,
@ -515,16 +462,13 @@ describe('Coordinator tests', () => {
{ {
from: makerAddress, from: makerAddress,
}, },
),
); );
const cancelLogs = transactionReceipt.logs.filter( const expectedEvent: ExchangeCancelUpToEventArgs = {
log => (log as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).event === 'CancelUpTo', makerAddress,
); orderSenderAddress: coordinatorContract.address,
expect(cancelLogs.length).to.eq(1); orderEpoch: targetEpoch.plus(1),
const cancelLogArgs = (cancelLogs[0] as LogWithDecodedArgs<ExchangeCancelUpToEventArgs>).args; };
expect(cancelLogArgs.makerAddress).to.eq(makerAddress); verifyEvents(transactionReceipt, [expectedEvent], ExchangeEvents.CancelUpTo);
expect(cancelLogArgs.orderSenderAddress).to.eq(coordinatorContract.address);
expect(cancelLogArgs.orderEpoch).to.bignumber.eq(targetEpoch.plus(1));
}); });
}); });
}); });

View File

@ -1,44 +1,25 @@
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange'; import { blockchainTests, expect } from '@0x/contracts-test-utils';
import { chaiSetup, provider, web3Wrapper } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types'; import { LogWithDecodedArgs } from 'ethereum-types';
import { CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src'; import { CoordinatorRegistryCoordinatorEndpointSetEventArgs } from '../src';
import { CoordinatorRegistryWrapper } from './utils/coordinator_registry_wrapper'; import { CoordinatorRegistryWrapper } from './utils/coordinator_registry_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
web3Wrapper.abiDecoder.addABI(exchangeArtifacts.Exchange.compilerOutput.abi);
// tslint:disable:no-unnecessary-type-assertion // tslint:disable:no-unnecessary-type-assertion
describe('Coordinator Registry tests', () => { blockchainTests.resets('Coordinator Registry tests', env => {
let coordinatorOperator: string; let coordinatorOperator: string;
const coordinatorEndpoint = 'http://sometec.0x.org'; const coordinatorEndpoint = 'http://sometec.0x.org';
const nilCoordinatorEndpoint = ''; const nilCoordinatorEndpoint = '';
let coordinatorRegistryWrapper: CoordinatorRegistryWrapper; let coordinatorRegistryWrapper: CoordinatorRegistryWrapper;
// tests // tests
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => { before(async () => {
// setup accounts (skip owner) // setup accounts (skip owner)
const accounts = await web3Wrapper.getAvailableAddressesAsync(); const accounts = await env.getAccountAddressesAsync();
[, coordinatorOperator] = accounts; [, coordinatorOperator] = accounts;
// deploy coordinator registry // deploy coordinator registry
coordinatorRegistryWrapper = new CoordinatorRegistryWrapper(provider); coordinatorRegistryWrapper = new CoordinatorRegistryWrapper(env.provider);
await coordinatorRegistryWrapper.deployCoordinatorRegistryAsync(); await coordinatorRegistryWrapper.deployCoordinatorRegistryAsync();
}); });
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('core', () => { describe('core', () => {
it('Should successfully set a Coordinator endpoint', async () => { it('Should successfully set a Coordinator endpoint', async () => {
await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint); await coordinatorRegistryWrapper.setCoordinatorEndpointAsync(coordinatorOperator, coordinatorEndpoint);

View File

@ -1,66 +1,31 @@
import { chaiSetup, constants, provider, randomAddress, txDefaults, web3Wrapper } from '@0x/contracts-test-utils'; import { blockchainTests, constants, expect, randomAddress } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { transactionHashUtils } from '@0x/order-utils'; import { transactionHashUtils } from '@0x/order-utils';
import { BigNumber, providerUtils } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { artifacts, CoordinatorContract, hashUtils } from '../src'; import { artifacts, CoordinatorContract, hashUtils } from '../src';
chaiSetup.configure(); blockchainTests.resets('Libs tests', env => {
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('Libs tests', () => {
let coordinatorContract: CoordinatorContract; let coordinatorContract: CoordinatorContract;
let chainId: number; let chainId: number;
const exchangeAddress = randomAddress(); const exchangeAddress = randomAddress();
before(async () => { before(async () => {
await blockchainLifecycle.startAsync(); chainId = await env.getChainIdAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
chainId = await providerUtils.getChainIdAsync(provider);
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync( coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
artifacts.Coordinator, artifacts.Coordinator,
provider, env.provider,
txDefaults, env.txDefaults,
artifacts, artifacts,
exchangeAddress, exchangeAddress,
new BigNumber(chainId), new BigNumber(chainId),
); );
}); });
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('getTransactionHash', () => {
it('should return the correct transaction hash', async () => {
const tx = {
salt: new BigNumber(0),
expirationTimeSeconds: new BigNumber(0),
signerAddress: constants.NULL_ADDRESS,
data: '0x1234',
domain: {
verifyingContract: exchangeAddress,
chainId,
},
};
const expectedTxHash = transactionHashUtils.getTransactionHashHex(tx);
const txHash = await coordinatorContract.getTransactionHash.callAsync(tx);
expect(expectedTxHash).to.eq(txHash);
});
});
describe('getApprovalHash', () => { describe('getApprovalHash', () => {
it('should return the correct approval hash', async () => { it('should return the correct approval hash', async () => {
const signedTx = { const signedTx = {
salt: new BigNumber(0), salt: new BigNumber(0),
gasPrice: new BigNumber(0),
expirationTimeSeconds: new BigNumber(0), expirationTimeSeconds: new BigNumber(0),
signerAddress: constants.NULL_ADDRESS, signerAddress: constants.NULL_ADDRESS,
data: '0x1234', data: '0x1234',

View File

@ -1,29 +1,20 @@
import { constants as exchangeConstants, exchangeDataEncoder, ExchangeFunctionName } from '@0x/contracts-exchange'; import { constants as exchangeConstants, exchangeDataEncoder, ExchangeFunctionName } from '@0x/contracts-exchange';
import { import {
chaiSetup, blockchainTests,
constants, constants,
expectContractCallFailedAsync, expect,
getLatestBlockTimestampAsync, getLatestBlockTimestampAsync,
provider,
randomAddress, randomAddress,
TransactionFactory, TransactionFactory,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils'; } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils'; import { CoordinatorRevertErrors, transactionHashUtils } from '@0x/order-utils';
import { transactionHashUtils } from '@0x/order-utils'; import { SignatureType, SignedOrder } from '@0x/types';
import { RevertReason, SignatureType, SignedOrder } from '@0x/types'; import { BigNumber, LibBytesRevertErrors } from '@0x/utils';
import { BigNumber, LibBytesRevertErrors, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util'; import * as ethUtil from 'ethereumjs-util';
import { ApprovalFactory, artifacts, CoordinatorContract } from '../src'; import { ApprovalFactory, artifacts, CoordinatorContract } from '../src';
chaiSetup.configure(); blockchainTests.resets('Mixins tests', env => {
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('Mixins tests', () => {
let chainId: number; let chainId: number;
let transactionSignerAddress: string; let transactionSignerAddress: string;
let approvalSignerAddress1: string; let approvalSignerAddress1: string;
@ -36,23 +27,17 @@ describe('Mixins tests', () => {
const exchangeAddress = randomAddress(); const exchangeAddress = randomAddress();
before(async () => { before(async () => {
await blockchainLifecycle.startAsync(); chainId = await env.getChainIdAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
chainId = await providerUtils.getChainIdAsync(provider);
mixins = await CoordinatorContract.deployFrom0xArtifactAsync( mixins = await CoordinatorContract.deployFrom0xArtifactAsync(
artifacts.Coordinator, artifacts.Coordinator,
provider, env.provider,
txDefaults, env.txDefaults,
artifacts, artifacts,
exchangeAddress, exchangeAddress,
new BigNumber(chainId), new BigNumber(chainId),
); );
const accounts = await web3Wrapper.getAvailableAddressesAsync(); const accounts = await env.getAccountAddressesAsync();
[transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts.slice(0, 3); [transactionSignerAddress, approvalSignerAddress1, approvalSignerAddress2] = accounts;
defaultOrder = { defaultOrder = {
makerAddress: constants.NULL_ADDRESS, makerAddress: constants.NULL_ADDRESS,
takerAddress: constants.NULL_ADDRESS, takerAddress: constants.NULL_ADDRESS,
@ -79,12 +64,6 @@ describe('Mixins tests', () => {
approvalFactory1 = new ApprovalFactory(approvalSignerPrivateKey1, mixins.address); approvalFactory1 = new ApprovalFactory(approvalSignerPrivateKey1, mixins.address);
approvalFactory2 = new ApprovalFactory(approvalSignerPrivateKey2, mixins.address); approvalFactory2 = new ApprovalFactory(approvalSignerPrivateKey2, mixins.address);
}); });
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('getSignerAddress', () => { describe('getSignerAddress', () => {
it('should return the correct address using the EthSign signature type', async () => { it('should return the correct address using the EthSign signature type', async () => {
@ -110,8 +89,12 @@ describe('Mixins tests', () => {
transaction.signature.length - 2, transaction.signature.length - 2,
)}${illegalSignatureByte}`; )}${illegalSignatureByte}`;
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction); const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith( expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
RevertReason.SignatureIllegal, new CoordinatorRevertErrors.SignatureError(
CoordinatorRevertErrors.SignatureErrorCodes.Illegal,
transactionHash,
transaction.signature,
),
); );
}); });
it('should revert with with the Invalid signature type', async () => { it('should revert with with the Invalid signature type', async () => {
@ -120,8 +103,12 @@ describe('Mixins tests', () => {
const invalidSignatureByte = ethUtil.toBuffer(SignatureType.Invalid).toString('hex'); const invalidSignatureByte = ethUtil.toBuffer(SignatureType.Invalid).toString('hex');
transaction.signature = `0x${invalidSignatureByte}`; transaction.signature = `0x${invalidSignatureByte}`;
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction); const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith( expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
RevertReason.SignatureInvalid, new CoordinatorRevertErrors.SignatureError(
CoordinatorRevertErrors.SignatureErrorCodes.Invalid,
transactionHash,
transaction.signature,
),
); );
}); });
it("should revert with with a signature type that doesn't exist", async () => { it("should revert with with a signature type that doesn't exist", async () => {
@ -133,8 +120,12 @@ describe('Mixins tests', () => {
transaction.signature.length - 2, transaction.signature.length - 2,
)}${invalidSignatureByte}`; )}${invalidSignatureByte}`;
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction); const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.be.rejectedWith( expect(mixins.getSignerAddress.callAsync(transactionHash, transaction.signature)).to.revertWith(
RevertReason.SignatureUnsupported, new CoordinatorRevertErrors.SignatureError(
CoordinatorRevertErrors.SignatureErrorCodes.Unsupported,
transactionHash,
transaction.signature,
),
); );
}); });
}); });
@ -320,16 +311,18 @@ describe('Mixins tests', () => {
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`; const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
expectContractCallFailedAsync( const tx = mixins.assertValidCoordinatorApprovals.callAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction, transaction,
transactionSignerAddress, transactionSignerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[signature], [signature],
{ from: transactionSignerAddress }, { from: transactionSignerAddress },
), );
RevertReason.InvalidApprovalSignature,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress1),
); );
}); });
it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => { it(`Should revert: function=${fnName}, caller=tx_signer, senderAddress=[verifier], approval_sig=[approver1], expiration=[invalid]`, async () => {
@ -343,16 +336,18 @@ describe('Mixins tests', () => {
transactionSignerAddress, transactionSignerAddress,
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
expectContractCallFailedAsync( const tx = mixins.assertValidCoordinatorApprovals.callAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction, transaction,
transactionSignerAddress, transactionSignerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approval.signature], [approval.signature],
{ from: transactionSignerAddress }, { from: transactionSignerAddress },
), );
RevertReason.ApprovalExpired,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.ApprovalExpiredError(transactionHash, approvalExpirationTimeSeconds),
); );
}); });
it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => { it(`Should revert: function=${fnName}, caller=approver2, senderAddress=[verifier], approval_sig=[approver1], expiration=[valid]`, async () => {
@ -366,17 +361,16 @@ describe('Mixins tests', () => {
transactionSignerAddress, transactionSignerAddress,
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync( const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction, transaction,
transactionSignerAddress, transactionSignerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approval.signature], [approval.signature],
{ from: approvalSignerAddress2 }, { from: approvalSignerAddress2 },
),
RevertReason.InvalidOrigin,
); );
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
}); });
} }
}); });
@ -514,32 +508,33 @@ describe('Mixins tests', () => {
transactionSignerAddress, transactionSignerAddress,
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync( const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction, transaction,
transactionSignerAddress, transactionSignerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approval2.signature], [approval2.signature],
{ from: approvalSignerAddress1 }, { from: approvalSignerAddress1 },
),
RevertReason.InvalidOrigin,
); );
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
}); });
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[], expiration=[]`, async () => { it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[], expiration=[]`, async () => {
const orders = [defaultOrder, defaultOrder]; const orders = [defaultOrder, defaultOrder];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders); const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
const transaction = await transactionFactory.newSignedTransactionAsync({ data }); const transaction = await transactionFactory.newSignedTransactionAsync({ data });
expectContractCallFailedAsync( const tx = mixins.assertValidCoordinatorApprovals.callAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction, transaction,
transactionSignerAddress, transactionSignerAddress,
transaction.signature, transaction.signature,
[], [],
[], [],
{ from: transactionSignerAddress }, { from: transactionSignerAddress },
), );
RevertReason.InvalidApprovalSignature,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress1),
); );
}); });
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid], expiration=[valid]`, async () => { it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[invalid], expiration=[valid]`, async () => {
@ -554,16 +549,18 @@ describe('Mixins tests', () => {
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`; const signature = `${approval.signature.slice(0, 4)}FFFFFFFF${approval.signature.slice(12)}`;
expectContractCallFailedAsync( const tx = mixins.assertValidCoordinatorApprovals.callAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction, transaction,
transactionSignerAddress, transactionSignerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[signature], [signature],
{ from: transactionSignerAddress }, { from: transactionSignerAddress },
), );
RevertReason.InvalidApprovalSignature,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress1),
); );
}); });
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid], expiration=[valid,valid]`, async () => { it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,invalid], expiration=[valid,valid]`, async () => {
@ -583,16 +580,18 @@ describe('Mixins tests', () => {
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`; const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
expectContractCallFailedAsync( const tx = mixins.assertValidCoordinatorApprovals.callAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction, transaction,
transactionSignerAddress, transactionSignerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds, approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds, approvalExpirationTimeSeconds],
[approval1.signature, approvalSignature2], [approval1.signature, approvalSignature2],
{ from: transactionSignerAddress }, { from: transactionSignerAddress },
), );
RevertReason.InvalidApprovalSignature,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress2),
); );
}); });
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid], expiration=[valid]`, async () => { it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[invalid], expiration=[valid]`, async () => {
@ -607,16 +606,18 @@ describe('Mixins tests', () => {
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`; const approvalSignature2 = `${approval2.signature.slice(0, 4)}FFFFFFFF${approval2.signature.slice(12)}`;
expectContractCallFailedAsync( const tx = mixins.assertValidCoordinatorApprovals.callAsync(
mixins.assertValidCoordinatorApprovals.callAsync(
transaction, transaction,
approvalSignerAddress1, approvalSignerAddress1,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approvalSignature2], [approvalSignature2],
{ from: approvalSignerAddress1 }, { from: approvalSignerAddress1 },
), );
RevertReason.InvalidApprovalSignature,
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, approvalSignerAddress2),
); );
}); });
it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,valid], expiration=[valid,invalid]`, async () => { it(`Should revert: function=${fnName} caller=tx_signer, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid,valid], expiration=[valid,invalid]`, async () => {
@ -636,16 +637,18 @@ describe('Mixins tests', () => {
transactionSignerAddress, transactionSignerAddress,
approvalExpirationTimeSeconds2, approvalExpirationTimeSeconds2,
); );
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync( const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction, transaction,
transactionSignerAddress, transactionSignerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2], [approvalExpirationTimeSeconds1, approvalExpirationTimeSeconds2],
[approval1.signature, approval2.signature], [approval1.signature, approval2.signature],
{ from: transactionSignerAddress }, { from: transactionSignerAddress },
), );
RevertReason.ApprovalExpired, const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.ApprovalExpiredError(transactionHash, approvalExpirationTimeSeconds2),
); );
}); });
it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid], expiration=[invalid]`, async () => { it(`Should revert: function=${fnName} caller=approver1, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver2], approval_sig=[valid], expiration=[invalid]`, async () => {
@ -659,16 +662,18 @@ describe('Mixins tests', () => {
transactionSignerAddress, transactionSignerAddress,
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync( const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction, transaction,
approvalSignerAddress1, approvalSignerAddress1,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approval2.signature], [approval2.signature],
{ from: approvalSignerAddress1 }, { from: approvalSignerAddress1 },
), );
RevertReason.ApprovalExpired, const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
expect(tx).to.revertWith(
new CoordinatorRevertErrors.ApprovalExpiredError(transactionHash, approvalExpirationTimeSeconds),
); );
}); });
it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid], expiration=[valid]`, async () => { it(`Should revert: function=${fnName} caller=approver2, senderAddress=[verifier,verifier], feeRecipient=[approver1, approver1], approval_sig=[valid], expiration=[valid]`, async () => {
@ -682,17 +687,16 @@ describe('Mixins tests', () => {
transactionSignerAddress, transactionSignerAddress,
approvalExpirationTimeSeconds, approvalExpirationTimeSeconds,
); );
expectContractCallFailedAsync(
mixins.assertValidCoordinatorApprovals.callAsync( const tx = mixins.assertValidCoordinatorApprovals.callAsync(
transaction, transaction,
transactionSignerAddress, transactionSignerAddress,
transaction.signature, transaction.signature,
[approvalExpirationTimeSeconds], [approvalExpirationTimeSeconds],
[approval1.signature], [approval1.signature],
{ from: approvalSignerAddress2 }, { from: approvalSignerAddress2 },
),
RevertReason.InvalidOrigin,
); );
expect(tx).to.revertWith(new CoordinatorRevertErrors.InvalidOriginError(transactionSignerAddress));
}); });
} }
}); });

View File

@ -1,3 +1,4 @@
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
import { LogDecoder, txDefaults } from '@0x/contracts-test-utils'; import { LogDecoder, txDefaults } from '@0x/contracts-test-utils';
import { Web3Wrapper } from '@0x/web3-wrapper'; import { Web3Wrapper } from '@0x/web3-wrapper';
import { TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types'; import { TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types';
@ -17,7 +18,7 @@ export class CoordinatorRegistryWrapper {
constructor(provider: ZeroExProvider) { constructor(provider: ZeroExProvider) {
this._web3Wrapper = new Web3Wrapper(provider); this._web3Wrapper = new Web3Wrapper(provider);
this._provider = provider; this._provider = provider;
this._logDecoder = new LogDecoder(this._web3Wrapper, artifacts); this._logDecoder = new LogDecoder(this._web3Wrapper, { ...exchangeArtifacts, ...artifacts });
} }
public async deployCoordinatorRegistryAsync(): Promise<CoordinatorRegistryContract> { public async deployCoordinatorRegistryAsync(): Promise<CoordinatorRegistryContract> {
this._coordinatorRegistryContract = await CoordinatorRegistryContract.deployFrom0xArtifactAsync( this._coordinatorRegistryContract = await CoordinatorRegistryContract.deployFrom0xArtifactAsync(

View File

@ -2,6 +2,21 @@
"extends": "../../tsconfig", "extends": "../../tsconfig",
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true }, "compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": ["generated-artifacts/Coordinator.json", "generated-artifacts/CoordinatorRegistry.json"], "files": [
"generated-artifacts/Coordinator.json",
"generated-artifacts/CoordinatorRegistry.json",
"generated-artifacts/ICoordinatorApprovalVerifier.json",
"generated-artifacts/ICoordinatorCore.json",
"generated-artifacts/ICoordinatorRegistryCore.json",
"generated-artifacts/ICoordinatorSignatureValidator.json",
"generated-artifacts/LibConstants.json",
"generated-artifacts/LibCoordinatorApproval.json",
"generated-artifacts/LibCoordinatorRichErrors.json",
"generated-artifacts/LibEIP712CoordinatorDomain.json",
"generated-artifacts/MixinCoordinatorApprovalVerifier.json",
"generated-artifacts/MixinCoordinatorCore.json",
"generated-artifacts/MixinCoordinatorRegistryCore.json",
"generated-artifacts/MixinSignatureValidator.json"
],
"exclude": ["./deploy/solc/solc_bin"] "exclude": ["./deploy/solc/solc_bin"]
} }

View File

@ -140,7 +140,7 @@ export const constants = {
], ],
}, },
COORDINATOR_DOMAIN_NAME: '0x Protocol Coordinator', COORDINATOR_DOMAIN_NAME: '0x Protocol Coordinator',
COORDINATOR_DOMAIN_VERSION: '2.0.0', COORDINATOR_DOMAIN_VERSION: '3.0.0',
COORDINATOR_APPROVAL_SCHEMA: { COORDINATOR_APPROVAL_SCHEMA: {
name: 'CoordinatorApproval', name: 'CoordinatorApproval',
parameters: [ parameters: [

View File

@ -34,17 +34,17 @@ export class ApprovalExpiredError extends RevertError {
} }
} }
export class InvalidApprovalSignature extends RevertError { export class InvalidApprovalSignatureError extends RevertError {
constructor(transactionHash?: string, approverAddress?: string) { constructor(transactionHash?: string, approverAddress?: string) {
super( super(
'InvalidApprovalSignature', 'InvalidApprovalSignatureError',
'InvalidApprovalSignature(bytes32 transactionHash, address approverAddress)', 'InvalidApprovalSignatureError(bytes32 transactionHash, address approverAddress)',
{ transactionHash, approverAddress }, { transactionHash, approverAddress },
); );
} }
} }
const types = [SignatureError, InvalidOriginError, ApprovalExpiredError, InvalidApprovalSignature]; const types = [SignatureError, InvalidOriginError, ApprovalExpiredError, InvalidApprovalSignatureError];
// Register the types we've defined. // Register the types we've defined.
for (const type of types) { for (const type of types) {

View File

@ -95,7 +95,7 @@ export const eip712Utils = {
return typedData; return typedData;
}, },
/** /**
* Creates an Coordiantor typedData EIP712TypedData object for use with the Coordinator extension contract * Creates an Coordinator typedData EIP712TypedData object for use with the Coordinator extension contract
* @param transaction A 0x transaction * @param transaction A 0x transaction
* @param verifyingContract The coordinator extension contract address that will be verifying the typedData * @param verifyingContract The coordinator extension contract address that will be verifying the typedData
* @param txOrigin The desired `tx.origin` that should be able to submit an Ethereum txn involving this 0x transaction * @param txOrigin The desired `tx.origin` that should be able to submit an Ethereum txn involving this 0x transaction

View File

@ -1,3 +1,4 @@
export import CoordinatorRevertErrors = require('./coordinator_revert_errors');
export import ExchangeRevertErrors = require('./exchange_revert_errors'); export import ExchangeRevertErrors = require('./exchange_revert_errors');
export import ForwarderRevertErrors = require('./forwarder_revert_errors'); export import ForwarderRevertErrors = require('./forwarder_revert_errors');
export import LibMathRevertErrors = require('./lib_math_revert_errors'); export import LibMathRevertErrors = require('./lib_math_revert_errors');