Merge pull request #2275 from 0xProject/refactor/integrations/coordinator-tests-part2
Coordinator test refactor [2/2]
This commit is contained in:
commit
096950729e
@ -1,4 +1,4 @@
|
|||||||
import { constants } from '@0x/contracts-test-utils';
|
import { constants, Numberish } from '@0x/contracts-test-utils';
|
||||||
import { assetDataUtils } from '@0x/order-utils';
|
import { assetDataUtils } from '@0x/order-utils';
|
||||||
import { AssetProxyId } from '@0x/types';
|
import { AssetProxyId } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
@ -34,11 +34,24 @@ export class LocalBalanceStore extends BalanceStore {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decreases the ETH balance of an address to simulate gas usage.
|
* Decreases the ETH balance of an address to simulate gas usage.
|
||||||
|
* @param senderAddress Address whose ETH balance to decrease.
|
||||||
|
* @param amount Amount to decrease the balance by.
|
||||||
*/
|
*/
|
||||||
public burnGas(senderAddress: string, amount: BigNumber | number): void {
|
public burnGas(senderAddress: string, amount: Numberish): void {
|
||||||
this._balances.eth[senderAddress] = this._balances.eth[senderAddress].minus(amount);
|
this._balances.eth[senderAddress] = this._balances.eth[senderAddress].minus(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends ETH from `fromAddress` to `toAddress`.
|
||||||
|
* @param fromAddress Sender of ETH.
|
||||||
|
* @param toAddress Receiver of ETH.
|
||||||
|
* @param amount Amount of ETH to transfer.
|
||||||
|
*/
|
||||||
|
public sendEth(fromAddress: string, toAddress: string, amount: Numberish): void {
|
||||||
|
this._balances.eth[fromAddress] = this._balances.eth[fromAddress].minus(amount);
|
||||||
|
this._balances.eth[toAddress] = this._balances.eth[toAddress].plus(amount);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transfers assets from `fromAddress` to `toAddress`.
|
* Transfers assets from `fromAddress` to `toAddress`.
|
||||||
* @param fromAddress Sender of asset(s)
|
* @param fromAddress Sender of asset(s)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ERC1155MintableContract } from '@0x/contracts-erc1155';
|
import { ERC1155MintableContract } from '@0x/contracts-erc1155';
|
||||||
import { DummyERC20TokenContract, DummyNoReturnERC20TokenContract } from '@0x/contracts-erc20';
|
import { DummyERC20TokenContract, DummyNoReturnERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
|
||||||
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ interface TokenData<TERC20, TERC721, TERC1155> {
|
|||||||
export type TokenAddresses = TokenData<address[], address[], address[]>;
|
export type TokenAddresses = TokenData<address[], address[], address[]>;
|
||||||
|
|
||||||
export type TokenContracts = TokenData<
|
export type TokenContracts = TokenData<
|
||||||
Array<DummyERC20TokenContract | DummyNoReturnERC20TokenContract>,
|
Array<DummyERC20TokenContract | DummyNoReturnERC20TokenContract | WETH9Contract>,
|
||||||
DummyERC721TokenContract[],
|
DummyERC721TokenContract[],
|
||||||
ERC1155MintableContract[]
|
ERC1155MintableContract[]
|
||||||
>;
|
>;
|
||||||
@ -29,7 +29,7 @@ export type TokenOwnersByName = Named<address>;
|
|||||||
export type TokenAddressesByName = TokenData<Named<address>, Named<address>, Named<address>>;
|
export type TokenAddressesByName = TokenData<Named<address>, Named<address>, Named<address>>;
|
||||||
|
|
||||||
export type TokenContractsByName = TokenData<
|
export type TokenContractsByName = TokenData<
|
||||||
Named<DummyERC20TokenContract | DummyNoReturnERC20TokenContract>,
|
Named<DummyERC20TokenContract | DummyNoReturnERC20TokenContract | WETH9Contract>,
|
||||||
Named<DummyERC721TokenContract>,
|
Named<DummyERC721TokenContract>,
|
||||||
Named<ERC1155MintableContract>
|
Named<ERC1155MintableContract>
|
||||||
>;
|
>;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
|
import { DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
|
||||||
import { constants } from '@0x/contracts-test-utils';
|
import { constants, TransactionFactory } from '@0x/contracts-test-utils';
|
||||||
|
import { SignatureType, SignedZeroExTransaction, ZeroExTransaction } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { DeploymentManager } from '../utils/deployment_manager';
|
import { DeploymentManager } from '../utils/deployment_manager';
|
||||||
@ -7,21 +8,30 @@ import { DeploymentManager } from '../utils/deployment_manager';
|
|||||||
export type Constructor<T = {}> = new (...args: any[]) => T;
|
export type Constructor<T = {}> = new (...args: any[]) => T;
|
||||||
|
|
||||||
export interface ActorConfig {
|
export interface ActorConfig {
|
||||||
address: string;
|
|
||||||
name?: string;
|
name?: string;
|
||||||
deployment: DeploymentManager;
|
deployment: DeploymentManager;
|
||||||
[mixinProperty: string]: any;
|
[mixinProperty: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Actor {
|
export class Actor {
|
||||||
|
public static count: number = 0;
|
||||||
public readonly address: string;
|
public readonly address: string;
|
||||||
public readonly name: string;
|
public readonly name: string;
|
||||||
|
public readonly privateKey: Buffer;
|
||||||
public readonly deployment: DeploymentManager;
|
public readonly deployment: DeploymentManager;
|
||||||
|
protected readonly transactionFactory: TransactionFactory;
|
||||||
|
|
||||||
constructor(config: ActorConfig) {
|
constructor(config: ActorConfig) {
|
||||||
this.address = config.address;
|
Actor.count++;
|
||||||
this.name = config.name || config.address;
|
this.address = config.deployment.accounts[Actor.count];
|
||||||
|
this.name = config.name || this.address;
|
||||||
this.deployment = config.deployment;
|
this.deployment = config.deployment;
|
||||||
|
this.privateKey = constants.TESTRPC_PRIVATE_KEYS[config.deployment.accounts.indexOf(this.address)];
|
||||||
|
this.transactionFactory = new TransactionFactory(
|
||||||
|
this.privateKey,
|
||||||
|
config.deployment.exchange.address,
|
||||||
|
config.deployment.chainId,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,4 +61,14 @@ export class Actor {
|
|||||||
{ from: this.address },
|
{ from: this.address },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs a transaction.
|
||||||
|
*/
|
||||||
|
public async signTransactionAsync(
|
||||||
|
customTransactionParams: Partial<ZeroExTransaction>,
|
||||||
|
signatureType: SignatureType = SignatureType.EthSign,
|
||||||
|
): Promise<SignedZeroExTransaction> {
|
||||||
|
return this.transactionFactory.newSignedTransactionAsync(customTransactionParams, signatureType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
47
contracts/integrations/test/actors/fee_recipient.ts
Normal file
47
contracts/integrations/test/actors/fee_recipient.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { BaseContract } from '@0x/base-contract';
|
||||||
|
import { ApprovalFactory, SignedCoordinatorApproval } from '@0x/contracts-coordinator';
|
||||||
|
import { SignatureType, SignedZeroExTransaction } from '@0x/types';
|
||||||
|
|
||||||
|
import { Actor, ActorConfig, Constructor } from './base';
|
||||||
|
|
||||||
|
export interface FeeRecipientConfig extends ActorConfig {
|
||||||
|
verifyingContract?: BaseContract;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FeeRecipientMixin<TBase extends Constructor>(Base: TBase) {
|
||||||
|
return class extends Base {
|
||||||
|
public readonly actor: Actor;
|
||||||
|
public readonly approvalFactory?: ApprovalFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mixin pattern requires that this constructor uses `...args: any[]`, but this class
|
||||||
|
* really expects a single `FeeRecipientConfig` parameter (assuming `Actor` is used as the
|
||||||
|
* base class).
|
||||||
|
*/
|
||||||
|
constructor(...args: any[]) {
|
||||||
|
super(...args);
|
||||||
|
this.actor = (this as any) as Actor;
|
||||||
|
|
||||||
|
const { verifyingContract } = args[0] as FeeRecipientConfig;
|
||||||
|
if (verifyingContract !== undefined) {
|
||||||
|
this.approvalFactory = new ApprovalFactory(this.actor.privateKey, verifyingContract.address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs an coordinator transaction.
|
||||||
|
*/
|
||||||
|
public signCoordinatorApproval(
|
||||||
|
transaction: SignedZeroExTransaction,
|
||||||
|
txOrigin: string,
|
||||||
|
signatureType: SignatureType = SignatureType.EthSign,
|
||||||
|
): SignedCoordinatorApproval {
|
||||||
|
if (this.approvalFactory === undefined) {
|
||||||
|
throw new Error('No verifying contract provided in FeeRecipient constructor');
|
||||||
|
}
|
||||||
|
return this.approvalFactory.newSignedApproval(transaction, txOrigin, signatureType);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FeeRecipient extends FeeRecipientMixin(Actor) {}
|
@ -1,3 +1,6 @@
|
|||||||
|
export { Actor } from './base';
|
||||||
export { Maker } from './maker';
|
export { Maker } from './maker';
|
||||||
export { PoolOperator } from './pool_operator';
|
export { PoolOperator } from './pool_operator';
|
||||||
|
export { FeeRecipient } from './fee_recipient';
|
||||||
export * from './hybrids';
|
export * from './hybrids';
|
||||||
|
export * from './utils';
|
||||||
|
@ -31,9 +31,7 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase) {
|
|||||||
chainId: this.actor.deployment.chainId,
|
chainId: this.actor.deployment.chainId,
|
||||||
...orderConfig,
|
...orderConfig,
|
||||||
};
|
};
|
||||||
const privateKey =
|
this.orderFactory = new OrderFactory(this.actor.privateKey, defaultOrderParams);
|
||||||
constants.TESTRPC_PRIVATE_KEYS[this.actor.deployment.accounts.indexOf(this.actor.address)];
|
|
||||||
this.orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
8
contracts/integrations/test/actors/utils.ts
Normal file
8
contracts/integrations/test/actors/utils.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { TokenOwnersByName } from '@0x/contracts-exchange';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { Actor } from './base';
|
||||||
|
|
||||||
|
export function actorAddressesByName(actors: Actor[]): TokenOwnersByName {
|
||||||
|
return _.zipObject(actors.map(actor => actor.name), actors.map(actor => actor.address));
|
||||||
|
}
|
@ -1,419 +1,457 @@
|
|||||||
import { ERC20ProxyContract, ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
import { CoordinatorContract, SignedCoordinatorApproval } from '@0x/contracts-coordinator';
|
||||||
import { ApprovalFactory, artifacts, CoordinatorContract } from '@0x/contracts-coordinator';
|
|
||||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract, WETH9Contract } from '@0x/contracts-erc20';
|
|
||||||
import {
|
import {
|
||||||
artifacts as exchangeArtifacts,
|
BlockchainBalanceStore,
|
||||||
|
LocalBalanceStore,
|
||||||
constants as exchangeConstants,
|
constants as exchangeConstants,
|
||||||
ExchangeContract,
|
ExchangeCancelEventArgs,
|
||||||
|
ExchangeCancelUpToEventArgs,
|
||||||
exchangeDataEncoder,
|
exchangeDataEncoder,
|
||||||
|
ExchangeEvents,
|
||||||
|
ExchangeFillEventArgs,
|
||||||
ExchangeFunctionName,
|
ExchangeFunctionName,
|
||||||
TestProtocolFeeCollectorContract,
|
|
||||||
} from '@0x/contracts-exchange';
|
} from '@0x/contracts-exchange';
|
||||||
import {
|
import { blockchainTests, expect, hexConcat, hexSlice, verifyEvents } from '@0x/contracts-test-utils';
|
||||||
blockchainTests,
|
import { assetDataUtils, CoordinatorRevertErrors, orderHashUtils, transactionHashUtils } from '@0x/order-utils';
|
||||||
constants,
|
import { SignedOrder, SignedZeroExTransaction } from '@0x/types';
|
||||||
hexConcat,
|
|
||||||
hexSlice,
|
|
||||||
OrderFactory,
|
|
||||||
TransactionFactory,
|
|
||||||
} from '@0x/contracts-test-utils';
|
|
||||||
import { assetDataUtils, CoordinatorRevertErrors, transactionHashUtils } from '@0x/order-utils';
|
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
|
|
||||||
import { CoordinatorTestFactory } from './coordinator_test_factory';
|
import { Actor, actorAddressesByName, FeeRecipient, Maker } from '../actors';
|
||||||
|
import { deployCoordinatorAsync } from './deploy_coordinator';
|
||||||
|
import { DeploymentManager } from '../utils/deployment_manager';
|
||||||
|
|
||||||
// tslint:disable:no-unnecessary-type-assertion
|
// tslint:disable:no-unnecessary-type-assertion
|
||||||
blockchainTests.resets('Coordinator tests', env => {
|
blockchainTests.resets('Coordinator tests', env => {
|
||||||
let chainId: number;
|
let deployment: DeploymentManager;
|
||||||
let makerAddress: string;
|
let coordinator: CoordinatorContract;
|
||||||
let owner: string;
|
let balanceStore: BlockchainBalanceStore;
|
||||||
let takerAddress: string;
|
|
||||||
let feeRecipientAddress: string;
|
|
||||||
|
|
||||||
let erc20Proxy: ERC20ProxyContract;
|
let maker: Maker;
|
||||||
let erc20TokenA: DummyERC20TokenContract;
|
let taker: Actor;
|
||||||
let erc20TokenB: DummyERC20TokenContract;
|
let feeRecipient: FeeRecipient;
|
||||||
let makerFeeToken: DummyERC20TokenContract;
|
|
||||||
let takerFeeToken: DummyERC20TokenContract;
|
|
||||||
let coordinatorContract: CoordinatorContract;
|
|
||||||
let exchange: ExchangeContract;
|
|
||||||
let protocolFeeCollector: TestProtocolFeeCollectorContract;
|
|
||||||
let wethContract: WETH9Contract;
|
|
||||||
|
|
||||||
let erc20Wrapper: ERC20Wrapper;
|
|
||||||
let orderFactory: OrderFactory;
|
|
||||||
let takerTransactionFactory: TransactionFactory;
|
|
||||||
let makerTransactionFactory: TransactionFactory;
|
|
||||||
let approvalFactory: ApprovalFactory;
|
|
||||||
let testFactory: CoordinatorTestFactory;
|
|
||||||
|
|
||||||
const GAS_PRICE = new BigNumber(env.txDefaults.gasPrice || constants.DEFAULT_GAS_PRICE);
|
|
||||||
const PROTOCOL_FEE_MULTIPLIER = new BigNumber(150000);
|
|
||||||
const PROTOCOL_FEE = GAS_PRICE.times(PROTOCOL_FEE_MULTIPLIER);
|
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
chainId = await env.getChainIdAsync();
|
deployment = await DeploymentManager.deployAsync(env, {
|
||||||
const accounts = await env.getAccountAddressesAsync();
|
numErc20TokensToDeploy: 4,
|
||||||
const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts);
|
numErc721TokensToDeploy: 0,
|
||||||
|
numErc1155TokensToDeploy: 0,
|
||||||
|
});
|
||||||
|
coordinator = await deployCoordinatorAsync(deployment, env);
|
||||||
|
|
||||||
// Deploy Exchange
|
const [makerToken, takerToken, makerFeeToken, takerFeeToken] = deployment.tokens.erc20;
|
||||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
|
||||||
exchangeArtifacts.Exchange,
|
taker = new Actor({ name: 'Taker', deployment });
|
||||||
env.provider,
|
feeRecipient = new FeeRecipient({
|
||||||
env.txDefaults,
|
name: 'Fee recipient',
|
||||||
|
deployment,
|
||||||
|
verifyingContract: coordinator,
|
||||||
|
});
|
||||||
|
maker = new Maker({
|
||||||
|
name: 'Maker',
|
||||||
|
deployment,
|
||||||
|
orderConfig: {
|
||||||
|
senderAddress: coordinator.address,
|
||||||
|
feeRecipientAddress: feeRecipient.address,
|
||||||
|
makerAssetData: assetDataUtils.encodeERC20AssetData(makerToken.address),
|
||||||
|
takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken.address),
|
||||||
|
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address),
|
||||||
|
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(takerFeeToken.address),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
taker.configureERC20TokenAsync(takerToken);
|
||||||
|
taker.configureERC20TokenAsync(takerFeeToken);
|
||||||
|
taker.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address);
|
||||||
|
maker.configureERC20TokenAsync(makerToken);
|
||||||
|
maker.configureERC20TokenAsync(makerFeeToken);
|
||||||
|
|
||||||
|
balanceStore = new BlockchainBalanceStore(
|
||||||
|
{
|
||||||
|
...actorAddressesByName([maker, taker, feeRecipient]),
|
||||||
|
Coordinator: coordinator.address,
|
||||||
|
StakingProxy: deployment.staking.stakingProxy.address,
|
||||||
|
},
|
||||||
|
{ erc20: { makerToken, takerToken, makerFeeToken, takerFeeToken, wETH: deployment.tokens.weth } },
|
||||||
{},
|
{},
|
||||||
new BigNumber(chainId),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set up ERC20
|
|
||||||
erc20Wrapper = new ERC20Wrapper(env.provider, usedAddresses, owner);
|
|
||||||
erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
|
||||||
const numDummyErc20ToDeploy = 4;
|
|
||||||
[erc20TokenA, erc20TokenB, makerFeeToken, takerFeeToken] = await erc20Wrapper.deployDummyTokensAsync(
|
|
||||||
numDummyErc20ToDeploy,
|
|
||||||
constants.DUMMY_TOKEN_DECIMALS,
|
|
||||||
);
|
|
||||||
await erc20Proxy.addAuthorizedAddress.awaitTransactionSuccessAsync(exchange.address, { from: owner });
|
|
||||||
await exchange.registerAssetProxy.awaitTransactionSuccessAsync(erc20Proxy.address, { from: owner });
|
|
||||||
|
|
||||||
// Set up WETH
|
|
||||||
wethContract = await WETH9Contract.deployFrom0xArtifactAsync(
|
|
||||||
erc20Artifacts.WETH9,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
const weth = new DummyERC20TokenContract(wethContract.address, env.provider);
|
|
||||||
erc20Wrapper.addDummyTokenContract(weth);
|
|
||||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
|
||||||
|
|
||||||
// Set up Protocol Fee Collector
|
|
||||||
protocolFeeCollector = await TestProtocolFeeCollectorContract.deployFrom0xArtifactAsync(
|
|
||||||
exchangeArtifacts.TestProtocolFeeCollector,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
{},
|
|
||||||
weth.address,
|
|
||||||
);
|
|
||||||
await exchange.setProtocolFeeMultiplier.awaitTransactionSuccessAsync(PROTOCOL_FEE_MULTIPLIER);
|
|
||||||
await exchange.setProtocolFeeCollectorAddress.awaitTransactionSuccessAsync(protocolFeeCollector.address);
|
|
||||||
for (const account of usedAddresses) {
|
|
||||||
await wethContract.deposit.awaitTransactionSuccessAsync({
|
|
||||||
from: account,
|
|
||||||
value: constants.ONE_ETHER,
|
|
||||||
});
|
|
||||||
await wethContract.approve.awaitTransactionSuccessAsync(
|
|
||||||
protocolFeeCollector.address,
|
|
||||||
constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
|
|
||||||
{
|
|
||||||
from: account,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
erc20Wrapper.addTokenOwnerAddress(protocolFeeCollector.address);
|
|
||||||
|
|
||||||
// Deploy Coordinator
|
|
||||||
coordinatorContract = await CoordinatorContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.Coordinator,
|
|
||||||
env.provider,
|
|
||||||
env.txDefaults,
|
|
||||||
{ ...exchangeArtifacts, ...artifacts },
|
|
||||||
exchange.address,
|
|
||||||
new BigNumber(chainId),
|
|
||||||
);
|
|
||||||
erc20Wrapper.addTokenOwnerAddress(coordinatorContract.address);
|
|
||||||
|
|
||||||
// Configure order defaults
|
|
||||||
const defaultOrderParams = {
|
|
||||||
...constants.STATIC_ORDER_PARAMS,
|
|
||||||
senderAddress: coordinatorContract.address,
|
|
||||||
makerAddress,
|
|
||||||
feeRecipientAddress,
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
|
|
||||||
makerFeeAssetData: assetDataUtils.encodeERC20AssetData(makerFeeToken.address),
|
|
||||||
takerFeeAssetData: assetDataUtils.encodeERC20AssetData(takerFeeToken.address),
|
|
||||||
exchangeAddress: exchange.address,
|
|
||||||
chainId,
|
|
||||||
};
|
|
||||||
const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
|
||||||
const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(takerAddress)];
|
|
||||||
const feeRecipientPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(feeRecipientAddress)];
|
|
||||||
orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams);
|
|
||||||
makerTransactionFactory = new TransactionFactory(makerPrivateKey, exchange.address, chainId);
|
|
||||||
takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchange.address, chainId);
|
|
||||||
approvalFactory = new ApprovalFactory(feeRecipientPrivateKey, coordinatorContract.address);
|
|
||||||
testFactory = new CoordinatorTestFactory(
|
|
||||||
coordinatorContract,
|
|
||||||
erc20Wrapper,
|
|
||||||
makerAddress,
|
|
||||||
takerAddress,
|
|
||||||
feeRecipientAddress,
|
|
||||||
protocolFeeCollector.address,
|
|
||||||
erc20TokenA.address,
|
|
||||||
erc20TokenB.address,
|
|
||||||
makerFeeToken.address,
|
|
||||||
takerFeeToken.address,
|
|
||||||
weth.address,
|
|
||||||
GAS_PRICE,
|
|
||||||
PROTOCOL_FEE_MULTIPLIER,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('single order fills', () => {
|
function simulateFills(
|
||||||
for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
|
orders: SignedOrder[],
|
||||||
it(`${fnName} should fill the order with a signed approval`, async () => {
|
txReceipt: TransactionReceiptWithDecodedLogs,
|
||||||
const order = await orderFactory.newSignedOrderAsync();
|
msgValue: BigNumber = new BigNumber(0),
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
|
): LocalBalanceStore {
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
const localBalanceStore = LocalBalanceStore.create(balanceStore);
|
||||||
const approval = approvalFactory.newSignedApproval(transaction, takerAddress);
|
// Transaction gas cost
|
||||||
const txData = { from: takerAddress, value: PROTOCOL_FEE };
|
localBalanceStore.burnGas(txReceipt.from, DeploymentManager.gasPrice.times(txReceipt.gasUsed));
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
|
||||||
[order],
|
for (const order of orders) {
|
||||||
transaction,
|
// Taker -> Maker
|
||||||
takerAddress,
|
localBalanceStore.transferAsset(taker.address, maker.address, order.takerAssetAmount, order.takerAssetData);
|
||||||
[approval.signature],
|
// Maker -> Taker
|
||||||
txData,
|
localBalanceStore.transferAsset(maker.address, taker.address, order.makerAssetAmount, order.makerAssetData);
|
||||||
|
// Taker -> Fee Recipient
|
||||||
|
localBalanceStore.transferAsset(
|
||||||
|
taker.address,
|
||||||
|
feeRecipient.address,
|
||||||
|
order.takerFee,
|
||||||
|
order.takerFeeAssetData,
|
||||||
|
);
|
||||||
|
// Maker -> Fee Recipient
|
||||||
|
localBalanceStore.transferAsset(
|
||||||
|
maker.address,
|
||||||
|
feeRecipient.address,
|
||||||
|
order.makerFee,
|
||||||
|
order.makerFeeAssetData,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Protocol fee
|
||||||
|
if (msgValue.isGreaterThanOrEqualTo(DeploymentManager.protocolFee)) {
|
||||||
|
localBalanceStore.sendEth(
|
||||||
|
txReceipt.from,
|
||||||
|
deployment.staking.stakingProxy.address,
|
||||||
|
DeploymentManager.protocolFee,
|
||||||
);
|
);
|
||||||
|
msgValue = msgValue.minus(DeploymentManager.protocolFee);
|
||||||
|
} else {
|
||||||
|
localBalanceStore.transferAsset(
|
||||||
|
taker.address,
|
||||||
|
deployment.staking.stakingProxy.address,
|
||||||
|
DeploymentManager.protocolFee,
|
||||||
|
assetDataUtils.encodeERC20AssetData(deployment.tokens.weth.address),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return localBalanceStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectedFillEvent(order: SignedOrder): ExchangeFillEventArgs {
|
||||||
|
return {
|
||||||
|
makerAddress: order.makerAddress,
|
||||||
|
takerAddress: taker.address,
|
||||||
|
senderAddress: order.senderAddress,
|
||||||
|
feeRecipientAddress: order.feeRecipientAddress,
|
||||||
|
makerAssetData: order.makerAssetData,
|
||||||
|
takerAssetData: order.takerAssetData,
|
||||||
|
makerFeeAssetData: order.makerFeeAssetData,
|
||||||
|
takerFeeAssetData: order.takerFeeAssetData,
|
||||||
|
makerAssetFilledAmount: order.makerAssetAmount,
|
||||||
|
takerAssetFilledAmount: order.takerAssetAmount,
|
||||||
|
makerFeePaid: order.makerFee,
|
||||||
|
takerFeePaid: order.takerFee,
|
||||||
|
protocolFeePaid: DeploymentManager.protocolFee,
|
||||||
|
orderHash: orderHashUtils.getOrderHashHex(order),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('single order fills', () => {
|
||||||
|
let order: SignedOrder;
|
||||||
|
let data: string;
|
||||||
|
let transaction: SignedZeroExTransaction;
|
||||||
|
let approval: SignedCoordinatorApproval;
|
||||||
|
|
||||||
|
for (const fnName of exchangeConstants.SINGLE_FILL_FN_NAMES) {
|
||||||
|
before(async () => {
|
||||||
|
order = await maker.signOrderAsync();
|
||||||
|
data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
|
||||||
|
transaction = await taker.signTransactionAsync({
|
||||||
|
data,
|
||||||
|
gasPrice: DeploymentManager.gasPrice,
|
||||||
|
});
|
||||||
|
approval = feeRecipient.signCoordinatorApproval(transaction, taker.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${fnName} should fill the order with a signed approval`, async () => {
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
|
transaction,
|
||||||
|
taker.address,
|
||||||
|
transaction.signature,
|
||||||
|
[approval.signature],
|
||||||
|
{ from: taker.address, value: DeploymentManager.protocolFee },
|
||||||
|
);
|
||||||
|
|
||||||
|
const expectedBalances = simulateFills([order], txReceipt, DeploymentManager.protocolFee);
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
|
||||||
});
|
});
|
||||||
it(`${fnName} should fill the order if called by approver (eth protocol fee, no refund)`, async () => {
|
it(`${fnName} should fill the order if called by approver (eth protocol fee, no refund)`, async () => {
|
||||||
const order = await orderFactory.newSignedOrderAsync();
|
await balanceStore.updateBalancesAsync();
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
|
const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
|
||||||
const txData = { from: feeRecipientAddress, value: PROTOCOL_FEE };
|
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
|
||||||
[order],
|
|
||||||
transaction,
|
transaction,
|
||||||
feeRecipientAddress,
|
feeRecipient.address,
|
||||||
|
transaction.signature,
|
||||||
[],
|
[],
|
||||||
txData,
|
{ from: feeRecipient.address, value: DeploymentManager.protocolFee },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const expectedBalances = simulateFills([order], txReceipt, DeploymentManager.protocolFee);
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
|
||||||
});
|
});
|
||||||
it(`${fnName} should fill the order if called by approver (eth protocol fee, refund)`, async () => {
|
it(`${fnName} should fill the order if called by approver (eth protocol fee, refund)`, async () => {
|
||||||
const order = await orderFactory.newSignedOrderAsync();
|
await balanceStore.updateBalancesAsync();
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
|
const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
|
||||||
const txData = { from: feeRecipientAddress, value: PROTOCOL_FEE.plus(1) };
|
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
|
||||||
[order],
|
|
||||||
transaction,
|
transaction,
|
||||||
feeRecipientAddress,
|
feeRecipient.address,
|
||||||
|
transaction.signature,
|
||||||
[],
|
[],
|
||||||
txData,
|
{ from: feeRecipient.address, value: DeploymentManager.protocolFee.plus(1) },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const expectedBalances = simulateFills([order], txReceipt, DeploymentManager.protocolFee.plus(1));
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
|
||||||
});
|
});
|
||||||
it(`${fnName} should fill the order if called by approver (weth protocol fee, no refund)`, async () => {
|
it(`${fnName} should fill the order if called by approver (weth protocol fee, no refund)`, async () => {
|
||||||
const order = await orderFactory.newSignedOrderAsync();
|
await balanceStore.updateBalancesAsync();
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
|
const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
|
||||||
const txData = { from: feeRecipientAddress };
|
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
|
||||||
[order],
|
|
||||||
transaction,
|
transaction,
|
||||||
feeRecipientAddress,
|
feeRecipient.address,
|
||||||
|
transaction.signature,
|
||||||
[],
|
[],
|
||||||
txData,
|
{ from: feeRecipient.address },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const expectedBalances = simulateFills([order], txReceipt);
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
|
||||||
});
|
});
|
||||||
it(`${fnName} should fill the order if called by approver (weth protocol fee, refund)`, async () => {
|
it(`${fnName} should fill the order if called by approver (weth protocol fee, refund)`, async () => {
|
||||||
const order = await orderFactory.newSignedOrderAsync();
|
await balanceStore.updateBalancesAsync();
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
|
const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
|
||||||
const txData = { from: feeRecipientAddress, value: new BigNumber(1) };
|
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
|
||||||
[order],
|
|
||||||
transaction,
|
transaction,
|
||||||
feeRecipientAddress,
|
feeRecipient.address,
|
||||||
|
transaction.signature,
|
||||||
[],
|
[],
|
||||||
txData,
|
{ from: feeRecipient.address, value: new BigNumber(1) },
|
||||||
);
|
|
||||||
});
|
|
||||||
it(`${fnName} should fill the order if called by approver`, async () => {
|
|
||||||
const order = await orderFactory.newSignedOrderAsync();
|
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, [order]);
|
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
|
||||||
const txData = { from: feeRecipientAddress, value: PROTOCOL_FEE };
|
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
|
||||||
[order],
|
|
||||||
transaction,
|
|
||||||
feeRecipientAddress,
|
|
||||||
[],
|
|
||||||
txData,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const expectedBalances = simulateFills([order], txReceipt, new BigNumber(1));
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
|
||||||
});
|
});
|
||||||
it(`${fnName} should revert with no approval signature`, async () => {
|
it(`${fnName} should revert with no approval signature`, async () => {
|
||||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
|
||||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
const tx = coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
orders,
|
|
||||||
transaction,
|
transaction,
|
||||||
takerAddress,
|
taker.address,
|
||||||
|
transaction.signature,
|
||||||
[],
|
[],
|
||||||
{
|
{ from: taker.address, value: DeploymentManager.protocolFee },
|
||||||
from: takerAddress,
|
|
||||||
gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
|
|
||||||
value: PROTOCOL_FEE,
|
|
||||||
},
|
|
||||||
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, feeRecipientAddress),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const expectedError = new CoordinatorRevertErrors.InvalidApprovalSignatureError(
|
||||||
|
transactionHash,
|
||||||
|
feeRecipient.address,
|
||||||
|
);
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
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()];
|
const approvalSignature = hexConcat(
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
|
||||||
const approval = approvalFactory.newSignedApproval(transaction, takerAddress);
|
|
||||||
const signature = hexConcat(
|
|
||||||
hexSlice(approval.signature, 0, 2),
|
hexSlice(approval.signature, 0, 2),
|
||||||
'0xFFFFFFFF',
|
'0xFFFFFFFF',
|
||||||
hexSlice(approval.signature, 6),
|
hexSlice(approval.signature, 6),
|
||||||
);
|
);
|
||||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
const tx = coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
orders,
|
|
||||||
transaction,
|
transaction,
|
||||||
takerAddress,
|
taker.address,
|
||||||
[signature],
|
transaction.signature,
|
||||||
{ from: takerAddress, value: PROTOCOL_FEE },
|
[approvalSignature],
|
||||||
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, feeRecipientAddress),
|
{ from: taker.address, value: DeploymentManager.protocolFee },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const expectedError = new CoordinatorRevertErrors.InvalidApprovalSignatureError(
|
||||||
|
transactionHash,
|
||||||
|
feeRecipient.address,
|
||||||
|
);
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
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 () => {
|
||||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
const tx = coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
|
||||||
const approval = approvalFactory.newSignedApproval(transaction, takerAddress);
|
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
|
||||||
orders,
|
|
||||||
transaction,
|
transaction,
|
||||||
takerAddress,
|
taker.address,
|
||||||
|
transaction.signature,
|
||||||
[approval.signature],
|
[approval.signature],
|
||||||
{ from: owner, value: PROTOCOL_FEE },
|
{ from: maker.address, value: DeploymentManager.protocolFee },
|
||||||
new CoordinatorRevertErrors.InvalidOriginError(takerAddress),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const expectedError = new CoordinatorRevertErrors.InvalidOriginError(taker.address);
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
describe('batch order fills', () => {
|
describe('batch order fills', () => {
|
||||||
|
let orders: SignedOrder[];
|
||||||
|
let data: string;
|
||||||
|
let transaction: SignedZeroExTransaction;
|
||||||
|
let approval: SignedCoordinatorApproval;
|
||||||
|
|
||||||
for (const fnName of [...exchangeConstants.MARKET_FILL_FN_NAMES, ...exchangeConstants.BATCH_FILL_FN_NAMES]) {
|
for (const fnName of [...exchangeConstants.MARKET_FILL_FN_NAMES, ...exchangeConstants.BATCH_FILL_FN_NAMES]) {
|
||||||
|
before(async () => {
|
||||||
|
orders = [await maker.signOrderAsync(), await maker.signOrderAsync()];
|
||||||
|
data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
||||||
|
transaction = await taker.signTransactionAsync({
|
||||||
|
data,
|
||||||
|
gasPrice: DeploymentManager.gasPrice,
|
||||||
|
});
|
||||||
|
approval = feeRecipient.signCoordinatorApproval(transaction, taker.address);
|
||||||
|
});
|
||||||
|
|
||||||
it(`${fnName} should fill the orders with a signed approval`, async () => {
|
it(`${fnName} should fill the orders with a signed approval`, async () => {
|
||||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
await balanceStore.updateBalancesAsync();
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
const value = DeploymentManager.protocolFee.times(orders.length);
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
const approval = approvalFactory.newSignedApproval(transaction, takerAddress);
|
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
|
||||||
orders,
|
|
||||||
transaction,
|
transaction,
|
||||||
takerAddress,
|
taker.address,
|
||||||
|
transaction.signature,
|
||||||
[approval.signature],
|
[approval.signature],
|
||||||
{
|
{ from: taker.address, value },
|
||||||
from: takerAddress,
|
|
||||||
gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
|
|
||||||
value: PROTOCOL_FEE.times(orders.length),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const expectedBalances = simulateFills(orders, txReceipt, value);
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);
|
||||||
});
|
});
|
||||||
it(`${fnName} should fill the orders if called by approver (eth fee, no refund)`, async () => {
|
it(`${fnName} should fill the orders if called by approver (eth fee, no refund)`, async () => {
|
||||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
await balanceStore.updateBalancesAsync();
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
const value = DeploymentManager.protocolFee.times(orders.length);
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
await testFactory.executeFillTransactionTestAsync(orders, transaction, feeRecipientAddress, [], {
|
transaction,
|
||||||
from: feeRecipientAddress,
|
feeRecipient.address,
|
||||||
gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
|
transaction.signature,
|
||||||
value: PROTOCOL_FEE.times(orders.length),
|
[],
|
||||||
});
|
{ from: feeRecipient.address, value },
|
||||||
|
);
|
||||||
|
|
||||||
|
const expectedBalances = simulateFills(orders, txReceipt, value);
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);
|
||||||
});
|
});
|
||||||
it(`${fnName} should fill the orders if called by approver (mixed fees, refund)`, async () => {
|
it(`${fnName} should fill the orders if called by approver (mixed fees, refund)`, async () => {
|
||||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
await balanceStore.updateBalancesAsync();
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
const value = DeploymentManager.protocolFee.plus(1);
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
await testFactory.executeFillTransactionTestAsync(orders, transaction, feeRecipientAddress, [], {
|
transaction,
|
||||||
from: feeRecipientAddress,
|
feeRecipient.address,
|
||||||
gas: constants.MAX_EXECUTE_TRANSACTION_GAS,
|
transaction.signature,
|
||||||
value: PROTOCOL_FEE.times(orders.length).plus(1),
|
[],
|
||||||
});
|
{ from: feeRecipient.address, value },
|
||||||
|
);
|
||||||
|
|
||||||
|
const expectedBalances = simulateFills(orders, txReceipt, value);
|
||||||
|
await balanceStore.updateBalancesAsync();
|
||||||
|
balanceStore.assertEquals(expectedBalances);
|
||||||
|
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);
|
||||||
});
|
});
|
||||||
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 approvalSignature = hexConcat(
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
|
||||||
const approval = approvalFactory.newSignedApproval(transaction, takerAddress);
|
|
||||||
const signature = hexConcat(
|
|
||||||
hexSlice(approval.signature, 0, 2),
|
hexSlice(approval.signature, 0, 2),
|
||||||
'0xFFFFFFFF',
|
'0xFFFFFFFF',
|
||||||
hexSlice(approval.signature, 6),
|
hexSlice(approval.signature, 6),
|
||||||
);
|
);
|
||||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
const tx = coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
orders,
|
|
||||||
transaction,
|
transaction,
|
||||||
takerAddress,
|
taker.address,
|
||||||
[signature],
|
transaction.signature,
|
||||||
{ from: takerAddress, value: PROTOCOL_FEE.times(orders.length) },
|
[approvalSignature],
|
||||||
new CoordinatorRevertErrors.InvalidApprovalSignatureError(transactionHash, feeRecipientAddress),
|
{ from: taker.address, value: DeploymentManager.protocolFee.times(orders.length) },
|
||||||
);
|
);
|
||||||
|
const expectedError = new CoordinatorRevertErrors.InvalidApprovalSignatureError(
|
||||||
|
transactionHash,
|
||||||
|
feeRecipient.address,
|
||||||
|
);
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
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 () => {
|
||||||
const orders = [await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync()];
|
const tx = coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(fnName, orders);
|
|
||||||
const transaction = await takerTransactionFactory.newSignedTransactionAsync({ data });
|
|
||||||
const approval = approvalFactory.newSignedApproval(transaction, takerAddress);
|
|
||||||
await testFactory.executeFillTransactionTestAsync(
|
|
||||||
orders,
|
|
||||||
transaction,
|
transaction,
|
||||||
takerAddress,
|
taker.address,
|
||||||
|
transaction.signature,
|
||||||
[approval.signature],
|
[approval.signature],
|
||||||
{ from: owner, value: PROTOCOL_FEE.times(orders.length) },
|
{ from: maker.address, value: DeploymentManager.protocolFee.times(orders.length) },
|
||||||
new CoordinatorRevertErrors.InvalidOriginError(takerAddress),
|
|
||||||
);
|
);
|
||||||
|
const expectedError = new CoordinatorRevertErrors.InvalidOriginError(taker.address);
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
describe('cancels', () => {
|
describe('cancels', () => {
|
||||||
|
function expectedCancelEvent(order: SignedOrder): ExchangeCancelEventArgs {
|
||||||
|
return {
|
||||||
|
makerAddress: order.makerAddress,
|
||||||
|
senderAddress: order.senderAddress,
|
||||||
|
feeRecipientAddress: order.feeRecipientAddress,
|
||||||
|
makerAssetData: order.makerAssetData,
|
||||||
|
takerAssetData: order.takerAssetData,
|
||||||
|
orderHash: orderHashUtils.getOrderHashHex(order),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
it('cancelOrder call should be successful without an approval', async () => {
|
it('cancelOrder call should be successful without an approval', async () => {
|
||||||
const orders = [await orderFactory.newSignedOrderAsync()];
|
const order = await maker.signOrderAsync();
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, orders);
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order]);
|
||||||
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
|
const transaction = await maker.signTransactionAsync({
|
||||||
await testFactory.executeCancelTransactionTestAsync(
|
data,
|
||||||
ExchangeFunctionName.CancelOrder,
|
gasPrice: DeploymentManager.gasPrice,
|
||||||
orders,
|
});
|
||||||
|
const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
transaction,
|
transaction,
|
||||||
makerAddress,
|
maker.address,
|
||||||
|
transaction.signature,
|
||||||
[],
|
[],
|
||||||
{
|
{ from: maker.address },
|
||||||
from: makerAddress,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
verifyEvents(txReceipt, [expectedCancelEvent(order)], ExchangeEvents.Cancel);
|
||||||
});
|
});
|
||||||
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 maker.signOrderAsync(), await maker.signOrderAsync()];
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchCancelOrders, orders);
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchCancelOrders, orders);
|
||||||
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
|
const transaction = await maker.signTransactionAsync({
|
||||||
await testFactory.executeCancelTransactionTestAsync(
|
data,
|
||||||
ExchangeFunctionName.BatchCancelOrders,
|
gasPrice: DeploymentManager.gasPrice,
|
||||||
orders,
|
});
|
||||||
|
const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
transaction,
|
transaction,
|
||||||
makerAddress,
|
maker.address,
|
||||||
|
transaction.signature,
|
||||||
[],
|
[],
|
||||||
{
|
{ from: maker.address },
|
||||||
from: makerAddress,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
verifyEvents(txReceipt, orders.map(order => expectedCancelEvent(order)), ExchangeEvents.Cancel);
|
||||||
});
|
});
|
||||||
it('cancelOrdersUpTo call should be successful without an approval', async () => {
|
it('cancelOrdersUpTo call should be successful without an approval', async () => {
|
||||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrdersUpTo, []);
|
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrdersUpTo, []);
|
||||||
const transaction = await makerTransactionFactory.newSignedTransactionAsync({ data });
|
const transaction = await maker.signTransactionAsync({
|
||||||
await testFactory.executeCancelTransactionTestAsync(
|
data,
|
||||||
ExchangeFunctionName.CancelOrdersUpTo,
|
gasPrice: DeploymentManager.gasPrice,
|
||||||
[],
|
});
|
||||||
|
const txReceipt = await coordinator.executeTransaction.awaitTransactionSuccessAsync(
|
||||||
transaction,
|
transaction,
|
||||||
makerAddress,
|
maker.address,
|
||||||
|
transaction.signature,
|
||||||
[],
|
[],
|
||||||
{
|
{ from: maker.address },
|
||||||
from: makerAddress,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const expectedEvent: ExchangeCancelUpToEventArgs = {
|
||||||
|
makerAddress: maker.address,
|
||||||
|
orderSenderAddress: coordinator.address,
|
||||||
|
orderEpoch: new BigNumber(1),
|
||||||
|
};
|
||||||
|
verifyEvents(txReceipt, [expectedEvent], ExchangeEvents.CancelUpTo);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,247 +0,0 @@
|
|||||||
import { ERC20Wrapper } from '@0x/contracts-asset-proxy';
|
|
||||||
import { CoordinatorContract } from '@0x/contracts-coordinator';
|
|
||||||
import {
|
|
||||||
ExchangeCancelEventArgs,
|
|
||||||
ExchangeCancelUpToEventArgs,
|
|
||||||
ExchangeEvents,
|
|
||||||
ExchangeFillEventArgs,
|
|
||||||
ExchangeFunctionName,
|
|
||||||
} from '@0x/contracts-exchange';
|
|
||||||
import { expect, Numberish, TokenBalances, verifyEvents, web3Wrapper } from '@0x/contracts-test-utils';
|
|
||||||
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
|
|
||||||
import { SignedOrder, SignedZeroExTransaction } from '@0x/types';
|
|
||||||
import { BigNumber, RevertError } from '@0x/utils';
|
|
||||||
import { TransactionReceiptWithDecodedLogs, TxData } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
export class CoordinatorTestFactory {
|
|
||||||
private readonly _addresses: string[];
|
|
||||||
private readonly _protocolFee: BigNumber;
|
|
||||||
|
|
||||||
private static _expectedCancelEvent(order: SignedOrder): ExchangeCancelEventArgs {
|
|
||||||
return {
|
|
||||||
makerAddress: order.makerAddress,
|
|
||||||
senderAddress: order.senderAddress,
|
|
||||||
feeRecipientAddress: order.feeRecipientAddress,
|
|
||||||
makerAssetData: order.makerAssetData,
|
|
||||||
takerAssetData: order.takerAssetData,
|
|
||||||
orderHash: orderHashUtils.getOrderHashHex(order),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly _coordinatorContract: CoordinatorContract,
|
|
||||||
private readonly _erc20Wrapper: ERC20Wrapper,
|
|
||||||
private readonly _makerAddress: string,
|
|
||||||
private readonly _takerAddress: string,
|
|
||||||
private readonly _feeRecipientAddress: string,
|
|
||||||
private readonly _protocolFeeCollectorAddress: string,
|
|
||||||
private readonly _makerAssetAddress: string,
|
|
||||||
private readonly _takerAssetAddress: string,
|
|
||||||
private readonly _makerFeeAssetAddress: string,
|
|
||||||
private readonly _takerFeeAssetAddress: string,
|
|
||||||
private readonly _wethAddress: string,
|
|
||||||
private readonly _gasPrice: BigNumber,
|
|
||||||
_protocolFeeMultiplier: BigNumber,
|
|
||||||
) {
|
|
||||||
this._addresses = [
|
|
||||||
_makerAddress,
|
|
||||||
_takerAddress,
|
|
||||||
_coordinatorContract.address,
|
|
||||||
_feeRecipientAddress,
|
|
||||||
_protocolFeeCollectorAddress,
|
|
||||||
];
|
|
||||||
this._protocolFee = _gasPrice.times(_protocolFeeMultiplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async executeFillTransactionTestAsync(
|
|
||||||
orders: SignedOrder[],
|
|
||||||
transaction: SignedZeroExTransaction,
|
|
||||||
txOrigin: string,
|
|
||||||
approvalSignatures: string[],
|
|
||||||
txData: Partial<TxData>,
|
|
||||||
revertError?: RevertError,
|
|
||||||
): Promise<void> {
|
|
||||||
const initBalances = await this._getTokenBalancesAsync();
|
|
||||||
const tx = this._coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
|
|
||||||
transaction,
|
|
||||||
txOrigin,
|
|
||||||
transaction.signature,
|
|
||||||
approvalSignatures,
|
|
||||||
txData,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (revertError !== undefined) {
|
|
||||||
return expect(tx).to.revertWith(revertError);
|
|
||||||
}
|
|
||||||
|
|
||||||
const transactionReceipt = await tx;
|
|
||||||
verifyEvents(transactionReceipt, orders.map(order => this._expectedFillEvent(order)), ExchangeEvents.Fill);
|
|
||||||
|
|
||||||
const expectedBalances = this._getExpectedBalances(initBalances, orders, transactionReceipt, txData.value);
|
|
||||||
await this._verifyBalancesAsync(expectedBalances);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async executeCancelTransactionTestAsync(
|
|
||||||
fnName: ExchangeFunctionName,
|
|
||||||
orders: SignedOrder[],
|
|
||||||
transaction: SignedZeroExTransaction,
|
|
||||||
txOrigin: string,
|
|
||||||
approvalSignatures: string[],
|
|
||||||
txData: Partial<TxData>,
|
|
||||||
): Promise<void> {
|
|
||||||
const transactionReceipt = await this._coordinatorContract.executeTransaction.awaitTransactionSuccessAsync(
|
|
||||||
transaction,
|
|
||||||
txOrigin,
|
|
||||||
transaction.signature,
|
|
||||||
approvalSignatures,
|
|
||||||
txData,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (fnName === ExchangeFunctionName.CancelOrdersUpTo) {
|
|
||||||
const expectedEvent: ExchangeCancelUpToEventArgs = {
|
|
||||||
makerAddress: this._makerAddress,
|
|
||||||
orderSenderAddress: this._coordinatorContract.address,
|
|
||||||
orderEpoch: new BigNumber(1),
|
|
||||||
};
|
|
||||||
verifyEvents(transactionReceipt, [expectedEvent], ExchangeEvents.CancelUpTo);
|
|
||||||
} else {
|
|
||||||
verifyEvents(
|
|
||||||
transactionReceipt,
|
|
||||||
orders.map(order => CoordinatorTestFactory._expectedCancelEvent(order)),
|
|
||||||
ExchangeEvents.Cancel,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _getTokenBalancesAsync(): Promise<TokenBalances> {
|
|
||||||
const erc20Balances = await this._erc20Wrapper.getBalancesAsync();
|
|
||||||
const ethBalances = _.zipObject(
|
|
||||||
this._addresses,
|
|
||||||
await Promise.all(this._addresses.map(address => web3Wrapper.getBalanceInWeiAsync(address))),
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
erc20: erc20Balances,
|
|
||||||
erc721: {},
|
|
||||||
erc1155: {},
|
|
||||||
eth: ethBalances,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getExpectedBalances(
|
|
||||||
initBalances: TokenBalances,
|
|
||||||
orders: SignedOrder[],
|
|
||||||
txReceipt: TransactionReceiptWithDecodedLogs,
|
|
||||||
txValue?: Numberish,
|
|
||||||
): TokenBalances {
|
|
||||||
const { erc20: erc20Balances, eth: ethBalances } = initBalances;
|
|
||||||
let remainingValue = new BigNumber(txValue || 0);
|
|
||||||
ethBalances[txReceipt.from] = ethBalances[txReceipt.from].minus(this._gasPrice.times(txReceipt.gasUsed));
|
|
||||||
|
|
||||||
for (const order of orders) {
|
|
||||||
const [makerAssetAddress, takerAssetAddress, makerFeeAssetAddress, takerFeeAssetAddress] = [
|
|
||||||
order.makerAssetData,
|
|
||||||
order.takerAssetData,
|
|
||||||
order.makerFeeAssetData,
|
|
||||||
order.takerFeeAssetData,
|
|
||||||
].map(assetData => assetDataUtils.decodeERC20AssetData(assetData).tokenAddress);
|
|
||||||
|
|
||||||
erc20Balances[order.makerAddress][makerAssetAddress] = erc20Balances[order.makerAddress][
|
|
||||||
makerAssetAddress
|
|
||||||
].minus(order.makerAssetAmount);
|
|
||||||
erc20Balances[this._takerAddress][makerAssetAddress] = erc20Balances[this._takerAddress][
|
|
||||||
makerAssetAddress
|
|
||||||
].plus(order.makerAssetAmount);
|
|
||||||
erc20Balances[order.makerAddress][takerAssetAddress] = erc20Balances[order.makerAddress][
|
|
||||||
takerAssetAddress
|
|
||||||
].plus(order.takerAssetAmount);
|
|
||||||
erc20Balances[this._takerAddress][takerAssetAddress] = erc20Balances[this._takerAddress][
|
|
||||||
takerAssetAddress
|
|
||||||
].minus(order.takerAssetAmount);
|
|
||||||
erc20Balances[order.makerAddress][makerFeeAssetAddress] = erc20Balances[order.makerAddress][
|
|
||||||
makerFeeAssetAddress
|
|
||||||
].minus(order.makerFee);
|
|
||||||
erc20Balances[this._takerAddress][takerFeeAssetAddress] = erc20Balances[this._takerAddress][
|
|
||||||
takerFeeAssetAddress
|
|
||||||
].minus(order.takerFee);
|
|
||||||
erc20Balances[order.feeRecipientAddress][makerFeeAssetAddress] = erc20Balances[order.feeRecipientAddress][
|
|
||||||
makerFeeAssetAddress
|
|
||||||
].plus(order.makerFee);
|
|
||||||
erc20Balances[order.feeRecipientAddress][takerFeeAssetAddress] = erc20Balances[order.feeRecipientAddress][
|
|
||||||
takerFeeAssetAddress
|
|
||||||
].plus(order.takerFee);
|
|
||||||
|
|
||||||
if (remainingValue.isGreaterThanOrEqualTo(this._protocolFee)) {
|
|
||||||
ethBalances[txReceipt.from] = ethBalances[txReceipt.from].minus(this._protocolFee);
|
|
||||||
ethBalances[this._protocolFeeCollectorAddress] = ethBalances[this._protocolFeeCollectorAddress].plus(
|
|
||||||
this._protocolFee,
|
|
||||||
);
|
|
||||||
remainingValue = remainingValue.minus(this._protocolFee);
|
|
||||||
} else {
|
|
||||||
erc20Balances[this._takerAddress][this._wethAddress] = erc20Balances[this._takerAddress][
|
|
||||||
this._wethAddress
|
|
||||||
].minus(this._protocolFee);
|
|
||||||
erc20Balances[this._protocolFeeCollectorAddress][this._wethAddress] = erc20Balances[
|
|
||||||
this._protocolFeeCollectorAddress
|
|
||||||
][this._wethAddress].plus(this._protocolFee);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
erc20: erc20Balances,
|
|
||||||
erc721: {},
|
|
||||||
erc1155: {},
|
|
||||||
eth: ethBalances,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _verifyBalancesAsync(expectedBalances: TokenBalances): Promise<void> {
|
|
||||||
const { erc20: expectedErc20Balances, eth: expectedEthBalances } = expectedBalances;
|
|
||||||
const { erc20: actualErc20Balances, eth: actualEthBalances } = await this._getTokenBalancesAsync();
|
|
||||||
const ownersByName = {
|
|
||||||
maker: this._makerAddress,
|
|
||||||
taker: this._takerAddress,
|
|
||||||
feeRecipient: this._feeRecipientAddress,
|
|
||||||
coordinator: this._coordinatorContract.address,
|
|
||||||
protocolFeeCollector: this._protocolFeeCollectorAddress,
|
|
||||||
};
|
|
||||||
const tokensByName = {
|
|
||||||
makerAsset: this._makerAssetAddress,
|
|
||||||
takerAsset: this._takerAssetAddress,
|
|
||||||
makerFeeAsset: this._makerFeeAssetAddress,
|
|
||||||
takerFeeAsset: this._takerFeeAssetAddress,
|
|
||||||
weth: this._wethAddress,
|
|
||||||
};
|
|
||||||
_.forIn(ownersByName, (ownerAddress, ownerName) => {
|
|
||||||
expect(actualEthBalances[ownerAddress], `${ownerName} eth balance`).to.bignumber.equal(
|
|
||||||
expectedEthBalances[ownerAddress],
|
|
||||||
);
|
|
||||||
_.forIn(tokensByName, (tokenAddress, tokenName) => {
|
|
||||||
expect(
|
|
||||||
actualErc20Balances[ownerAddress][tokenAddress],
|
|
||||||
`${ownerName} ${tokenName} balance`,
|
|
||||||
).to.bignumber.equal(expectedErc20Balances[ownerAddress][tokenAddress]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _expectedFillEvent(order: SignedOrder): ExchangeFillEventArgs {
|
|
||||||
return {
|
|
||||||
makerAddress: order.makerAddress,
|
|
||||||
takerAddress: this._takerAddress,
|
|
||||||
senderAddress: order.senderAddress,
|
|
||||||
feeRecipientAddress: order.feeRecipientAddress,
|
|
||||||
makerAssetData: order.makerAssetData,
|
|
||||||
takerAssetData: order.takerAssetData,
|
|
||||||
makerFeeAssetData: order.makerFeeAssetData,
|
|
||||||
takerFeeAssetData: order.takerFeeAssetData,
|
|
||||||
makerAssetFilledAmount: order.makerAssetAmount,
|
|
||||||
takerAssetFilledAmount: order.takerAssetAmount,
|
|
||||||
makerFeePaid: order.makerFee,
|
|
||||||
takerFeePaid: order.takerFee,
|
|
||||||
protocolFeePaid: this._protocolFee,
|
|
||||||
orderHash: orderHashUtils.getOrderHashHex(order),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,20 @@
|
|||||||
|
import { artifacts, CoordinatorContract } from '@0x/contracts-coordinator';
|
||||||
|
import { artifacts as exchangeArtifacts } from '@0x/contracts-exchange';
|
||||||
|
import { BlockchainTestsEnvironment } from '@0x/contracts-test-utils';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
import { DeploymentManager } from '../utils/deployment_manager';
|
||||||
|
|
||||||
|
export async function deployCoordinatorAsync(
|
||||||
|
deployment: DeploymentManager,
|
||||||
|
environment: BlockchainTestsEnvironment,
|
||||||
|
): Promise<CoordinatorContract> {
|
||||||
|
return await CoordinatorContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.Coordinator,
|
||||||
|
environment.provider,
|
||||||
|
deployment.txDefaults,
|
||||||
|
{ ...exchangeArtifacts, ...artifacts },
|
||||||
|
deployment.exchange.address,
|
||||||
|
new BigNumber(deployment.chainId),
|
||||||
|
);
|
||||||
|
}
|
@ -122,7 +122,9 @@ export interface DeploymentOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DeploymentManager {
|
export class DeploymentManager {
|
||||||
public static protocolFeeMultiplier = new BigNumber(150000);
|
public static readonly protocolFeeMultiplier = new BigNumber(150000);
|
||||||
|
public static readonly gasPrice = new BigNumber(1e9); // 1 Gwei
|
||||||
|
public static readonly protocolFee = DeploymentManager.gasPrice.times(DeploymentManager.protocolFeeMultiplier);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fully deploy the 0x exchange and staking contracts and configure the system with the
|
* Fully deploy the 0x exchange and staking contracts and configure the system with the
|
||||||
@ -141,6 +143,7 @@ export class DeploymentManager {
|
|||||||
const txDefaults = {
|
const txDefaults = {
|
||||||
...environment.txDefaults,
|
...environment.txDefaults,
|
||||||
from: owner,
|
from: owner,
|
||||||
|
gasPrice: DeploymentManager.gasPrice,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Deploy the contracts using the same owner and environment.
|
// Deploy the contracts using the same owner and environment.
|
||||||
@ -148,7 +151,7 @@ export class DeploymentManager {
|
|||||||
const exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
const exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||||
exchangeArtifacts.Exchange,
|
exchangeArtifacts.Exchange,
|
||||||
environment.provider,
|
environment.provider,
|
||||||
environment.txDefaults,
|
txDefaults,
|
||||||
{ ...ERC20Artifacts, ...exchangeArtifacts },
|
{ ...ERC20Artifacts, ...exchangeArtifacts },
|
||||||
new BigNumber(chainId),
|
new BigNumber(chainId),
|
||||||
);
|
);
|
||||||
@ -200,7 +203,7 @@ export class DeploymentManager {
|
|||||||
staking.stakingProxy,
|
staking.stakingProxy,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return new DeploymentManager(assetProxies, governor, exchange, staking, tokens, chainId, accounts);
|
return new DeploymentManager(assetProxies, governor, exchange, staking, tokens, chainId, accounts, txDefaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -493,5 +496,6 @@ export class DeploymentManager {
|
|||||||
public tokens: TokenContracts,
|
public tokens: TokenContracts,
|
||||||
public chainId: number,
|
public chainId: number,
|
||||||
public accounts: string[],
|
public accounts: string[],
|
||||||
|
public txDefaults: Partial<TxData>,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"sourceMap": true
|
"sourceMap": true
|
||||||
},
|
},
|
||||||
// These are not working right now
|
// These are not working right now
|
||||||
"exclude": ["./contracts/extensions/**/*", "./contracts/coordinator/**/*"],
|
"exclude": ["./contracts/extensions/**/*"],
|
||||||
// The root of the project is just a list of references and does not contain
|
// The root of the project is just a list of references and does not contain
|
||||||
// any top-level TypeScript code.
|
// any top-level TypeScript code.
|
||||||
"include": [],
|
"include": [],
|
||||||
@ -26,6 +26,7 @@
|
|||||||
{ "path": "./contracts/erc20" },
|
{ "path": "./contracts/erc20" },
|
||||||
{ "path": "./contracts/erc721" },
|
{ "path": "./contracts/erc721" },
|
||||||
{ "path": "./contracts/exchange" },
|
{ "path": "./contracts/exchange" },
|
||||||
|
{ "path": "./contracts/coordinator" },
|
||||||
{ "path": "./contracts/exchange-forwarder" },
|
{ "path": "./contracts/exchange-forwarder" },
|
||||||
{ "path": "./contracts/exchange-libs" },
|
{ "path": "./contracts/exchange-libs" },
|
||||||
// { "path": "./contracts/extensions" },
|
// { "path": "./contracts/extensions" },
|
||||||
@ -33,6 +34,7 @@
|
|||||||
{ "path": "./contracts/test-utils" },
|
{ "path": "./contracts/test-utils" },
|
||||||
{ "path": "./contracts/utils" },
|
{ "path": "./contracts/utils" },
|
||||||
{ "path": "./contracts/dev-utils" },
|
{ "path": "./contracts/dev-utils" },
|
||||||
|
{ "path": "./contracts/integrations" },
|
||||||
{ "path": "./packages/0x.js" },
|
{ "path": "./packages/0x.js" },
|
||||||
{ "path": "./packages/abi-gen-wrappers" },
|
{ "path": "./packages/abi-gen-wrappers" },
|
||||||
{ "path": "./packages/abi-gen" },
|
{ "path": "./packages/abi-gen" },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user