transaction protocol fee integration tests

This commit is contained in:
Michael Zhu
2019-11-21 14:11:51 -08:00
parent 97e65a02c0
commit 5a79ec28d1
12 changed files with 582 additions and 246 deletions

View File

@@ -19,7 +19,6 @@ import {
} from '@0x/contracts-test-utils';
import { SignedOrder, SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import { Actor } from '../framework/actors/base';
import { FeeRecipient } from '../framework/actors/fee_recipient';
@@ -99,69 +98,6 @@ blockchainTests.resets('Coordinator integration tests', env => {
Actor.count = 0;
});
async function simulateFillsAsync(
orders: SignedOrder[],
txReceipt: TransactionReceiptWithDecodedLogs,
msgValue?: BigNumber,
): Promise<LocalBalanceStore> {
let remainingValue = msgValue || constants.ZERO_AMOUNT;
const localBalanceStore = LocalBalanceStore.create(balanceStore);
// Transaction gas cost
localBalanceStore.burnGas(txReceipt.from, DeploymentManager.gasPrice.times(txReceipt.gasUsed));
for (const order of orders) {
// Taker -> Maker
await localBalanceStore.transferAssetAsync(
taker.address,
maker.address,
order.takerAssetAmount,
order.takerAssetData,
);
// Maker -> Taker
await localBalanceStore.transferAssetAsync(
maker.address,
taker.address,
order.makerAssetAmount,
order.makerAssetData,
);
// Taker -> Fee Recipient
await localBalanceStore.transferAssetAsync(
taker.address,
feeRecipient.address,
order.takerFee,
order.takerFeeAssetData,
);
// Maker -> Fee Recipient
await localBalanceStore.transferAssetAsync(
maker.address,
feeRecipient.address,
order.makerFee,
order.makerFeeAssetData,
);
// Protocol fee
if (remainingValue.isGreaterThanOrEqualTo(DeploymentManager.protocolFee)) {
localBalanceStore.sendEth(
txReceipt.from,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
);
remainingValue = remainingValue.minus(DeploymentManager.protocolFee);
} else {
await localBalanceStore.transferAssetAsync(
taker.address,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
deployment.assetDataEncoder
.ERC20Token(deployment.tokens.weth.address)
.getABIEncodedTransactionData(),
);
}
}
return localBalanceStore;
}
function expectedFillEvent(order: SignedOrder): ExchangeFillEventArgs {
return {
makerAddress: order.makerAddress,
@@ -201,7 +137,14 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, taker.address, transaction.signature, [approval.signature])
.awaitTransactionSuccessAsync({ from: taker.address, value: DeploymentManager.protocolFee });
const expectedBalances = await simulateFillsAsync([order], txReceipt, DeploymentManager.protocolFee);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(
[order],
taker.address,
txReceipt,
deployment,
DeploymentManager.protocolFee,
);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
@@ -212,7 +155,14 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, feeRecipient.address, transaction.signature, [])
.awaitTransactionSuccessAsync({ from: feeRecipient.address, value: DeploymentManager.protocolFee });
const expectedBalances = await simulateFillsAsync([order], txReceipt, DeploymentManager.protocolFee);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(
[order],
taker.address,
txReceipt,
deployment,
DeploymentManager.protocolFee,
);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
@@ -226,9 +176,12 @@ blockchainTests.resets('Coordinator integration tests', env => {
value: DeploymentManager.protocolFee.plus(1),
});
const expectedBalances = await simulateFillsAsync(
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(
[order],
taker.address,
txReceipt,
deployment,
DeploymentManager.protocolFee.plus(1),
);
await balanceStore.updateBalancesAsync();
@@ -241,7 +194,8 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, feeRecipient.address, transaction.signature, [])
.awaitTransactionSuccessAsync({ from: feeRecipient.address });
const expectedBalances = await simulateFillsAsync([order], txReceipt);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills([order], taker.address, txReceipt, deployment);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
@@ -252,7 +206,8 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, feeRecipient.address, transaction.signature, [])
.awaitTransactionSuccessAsync({ from: feeRecipient.address, value: new BigNumber(1) });
const expectedBalances = await simulateFillsAsync([order], txReceipt, new BigNumber(1));
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills([order], taker.address, txReceipt, deployment, new BigNumber(1));
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, [expectedFillEvent(order)], ExchangeEvents.Fill);
@@ -317,7 +272,8 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, taker.address, transaction.signature, [approval.signature])
.awaitTransactionSuccessAsync({ from: taker.address, value });
const expectedBalances = await simulateFillsAsync(orders, txReceipt, value);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(orders, taker.address, txReceipt, deployment, value);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);
@@ -329,7 +285,8 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, feeRecipient.address, transaction.signature, [])
.awaitTransactionSuccessAsync({ from: feeRecipient.address, value });
const expectedBalances = await simulateFillsAsync(orders, txReceipt, value);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(orders, taker.address, txReceipt, deployment, value);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);
@@ -341,7 +298,8 @@ blockchainTests.resets('Coordinator integration tests', env => {
.executeTransaction(transaction, feeRecipient.address, transaction.signature, [])
.awaitTransactionSuccessAsync({ from: feeRecipient.address, value });
const expectedBalances = await simulateFillsAsync(orders, txReceipt, value);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills(orders, taker.address, txReceipt, deployment, value);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);

View File

@@ -132,13 +132,9 @@ blockchainTests.resets('Exchange wrappers', env => {
isValid: boolean;
}
async function simulateFillAsync(
signedOrder: SignedOrder,
expectedFillResults: FillResults,
shouldUseWeth: boolean,
): Promise<void> {
function simulateFill(signedOrder: SignedOrder, expectedFillResults: FillResults, shouldUseWeth: boolean): void {
// taker -> maker
await localBalances.transferAssetAsync(
localBalances.transferAsset(
taker.address,
maker.address,
expectedFillResults.takerAssetFilledAmount,
@@ -146,7 +142,7 @@ blockchainTests.resets('Exchange wrappers', env => {
);
// maker -> taker
await localBalances.transferAssetAsync(
localBalances.transferAsset(
maker.address,
taker.address,
expectedFillResults.makerAssetFilledAmount,
@@ -154,7 +150,7 @@ blockchainTests.resets('Exchange wrappers', env => {
);
// maker -> feeRecipient
await localBalances.transferAssetAsync(
localBalances.transferAsset(
maker.address,
feeRecipient,
expectedFillResults.makerFeePaid,
@@ -162,7 +158,7 @@ blockchainTests.resets('Exchange wrappers', env => {
);
// taker -> feeRecipient
await localBalances.transferAssetAsync(
localBalances.transferAsset(
taker.address,
feeRecipient,
expectedFillResults.takerFeePaid,
@@ -171,7 +167,7 @@ blockchainTests.resets('Exchange wrappers', env => {
// taker -> protocol fees
if (shouldUseWeth) {
await localBalances.transferAssetAsync(
localBalances.transferAsset(
taker.address,
deployment.staking.stakingProxy.address,
expectedFillResults.protocolFeePaid,
@@ -343,7 +339,7 @@ blockchainTests.resets('Exchange wrappers', env => {
const shouldPayWethFees = DeploymentManager.protocolFee.gt(value);
// Simulate filling the order
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
// Ensure that the correct logs were emitted and that the balances are accurate.
await assertResultsAsync(receipt, [{ signedOrder, expectedFillResults, shouldPayWethFees }]);
@@ -444,7 +440,7 @@ blockchainTests.resets('Exchange wrappers', env => {
}
fillTestInfo.push({ signedOrder, expectedFillResults, shouldPayWethFees });
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
}
const contractFn = deployment.exchange.batchFillOrders(
@@ -506,7 +502,7 @@ blockchainTests.resets('Exchange wrappers', env => {
}
fillTestInfo.push({ signedOrder, expectedFillResults, shouldPayWethFees });
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
}
const contractFn = deployment.exchange.batchFillOrKillOrders(
@@ -600,7 +596,7 @@ blockchainTests.resets('Exchange wrappers', env => {
}
fillTestInfo.push({ signedOrder, expectedFillResults, shouldPayWethFees });
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
} else {
totalFillResults.push(nullFillResults);
}
@@ -714,7 +710,7 @@ blockchainTests.resets('Exchange wrappers', env => {
takerFillAmount,
);
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
fillTestInfo.push({ signedOrder, expectedFillResults, shouldPayWethFees });
totalFillResults = addFillResults(totalFillResults, expectedFillResults);
@@ -912,7 +908,7 @@ blockchainTests.resets('Exchange wrappers', env => {
makerAssetBought,
);
await simulateFillAsync(signedOrder, expectedFillResults, shouldPayWethFees);
simulateFill(signedOrder, expectedFillResults, shouldPayWethFees);
fillTestInfo.push({ signedOrder, expectedFillResults, shouldPayWethFees });
totalFillResults = addFillResults(totalFillResults, expectedFillResults);

View File

@@ -99,7 +99,7 @@ export class FillOrderWrapper {
await this._assertOrderStateAsync(signedOrder, initTakerAssetFilledAmount);
// Simulate and execute fill then assert outputs
const [fillResults, fillEvent, txReceipt] = await this._fillOrderAsync(signedOrder, from, opts);
const [simulatedFillResults, simulatedFillEvent, simulatedFinalBalanceStore] = await simulateFillOrderAsync(
const [simulatedFillResults, simulatedFillEvent, simulatedFinalBalanceStore] = simulateFillOrder(
txReceipt,
signedOrder,
from,
@@ -169,13 +169,13 @@ export class FillOrderWrapper {
* @param initBalanceStore Account balances prior to the fill.
* @return The expected account balances, fill results, and fill events.
*/
async function simulateFillOrderAsync(
function simulateFillOrder(
txReceipt: TransactionReceiptWithDecodedLogs,
signedOrder: SignedOrder,
takerAddress: string,
initBalanceStore: BalanceStore,
opts: { takerAssetFillAmount?: BigNumber } = {},
): Promise<[FillResults, FillEventArgs, BalanceStore]> {
): [FillResults, FillEventArgs, BalanceStore] {
const balanceStore = LocalBalanceStore.create(initBalanceStore);
const takerAssetFillAmount =
opts.takerAssetFillAmount !== undefined ? opts.takerAssetFillAmount : signedOrder.takerAssetAmount;
@@ -188,28 +188,28 @@ async function simulateFillOrderAsync(
);
const fillEvent = FillOrderWrapper.simulateFillEvent(signedOrder, takerAddress, fillResults);
// Taker -> Maker
await balanceStore.transferAssetAsync(
balanceStore.transferAsset(
takerAddress,
signedOrder.makerAddress,
fillResults.takerAssetFilledAmount,
signedOrder.takerAssetData,
);
// Maker -> Taker
await balanceStore.transferAssetAsync(
balanceStore.transferAsset(
signedOrder.makerAddress,
takerAddress,
fillResults.makerAssetFilledAmount,
signedOrder.makerAssetData,
);
// Taker -> Fee Recipient
await balanceStore.transferAssetAsync(
balanceStore.transferAsset(
takerAddress,
signedOrder.feeRecipientAddress,
fillResults.takerFeePaid,
signedOrder.takerFeeAssetData,
);
// Maker -> Fee Recipient
await balanceStore.transferAssetAsync(
balanceStore.transferAsset(
signedOrder.makerAddress,
signedOrder.feeRecipientAddress,
fillResults.makerFeePaid,

View File

@@ -112,51 +112,6 @@ blockchainTests.resets('fillOrder integration tests', env => {
Actor.count = 0;
});
async function simulateFillAsync(
order: SignedOrder,
txReceipt: TransactionReceiptWithDecodedLogs,
msgValue?: BigNumber,
): Promise<LocalBalanceStore> {
let remainingValue = msgValue !== undefined ? msgValue : DeploymentManager.protocolFee;
const localBalanceStore = LocalBalanceStore.create(balanceStore);
// Transaction gas cost
localBalanceStore.burnGas(txReceipt.from, DeploymentManager.gasPrice.times(txReceipt.gasUsed));
// Taker -> Maker
await localBalanceStore.transferAssetAsync(
taker.address,
maker.address,
order.takerAssetAmount,
order.takerAssetData,
);
// Maker -> Taker
await localBalanceStore.transferAssetAsync(
maker.address,
taker.address,
order.makerAssetAmount,
order.makerAssetData,
);
// Protocol fee
if (remainingValue.isGreaterThanOrEqualTo(DeploymentManager.protocolFee)) {
localBalanceStore.sendEth(
txReceipt.from,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
);
remainingValue = remainingValue.minus(DeploymentManager.protocolFee);
} else {
await localBalanceStore.transferAssetAsync(
taker.address,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
deployment.assetDataEncoder.ERC20Token(deployment.tokens.weth.address).getABIEncodedTransactionData(),
);
}
return localBalanceStore;
}
function verifyFillEvents(order: SignedOrder, receipt: TransactionReceiptWithDecodedLogs): void {
// Ensure that the fill event was correct.
verifyEvents<ExchangeFillEventArgs>(
@@ -207,7 +162,8 @@ blockchainTests.resets('fillOrder integration tests', env => {
const receipt = await taker.fillOrderAsync(order, order.takerAssetAmount);
// Check balances
const expectedBalances = await simulateFillAsync(order, receipt);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills([order], taker.address, receipt, deployment, DeploymentManager.protocolFee);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
@@ -233,7 +189,8 @@ blockchainTests.resets('fillOrder integration tests', env => {
const receipt = await taker.fillOrderAsync(order, order.takerAssetAmount);
// Check balances
const expectedBalances = await simulateFillAsync(order, receipt);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills([order], taker.address, receipt, deployment, DeploymentManager.protocolFee);
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
@@ -310,7 +267,7 @@ blockchainTests.resets('fillOrder integration tests', env => {
const [finalizePoolReceipt] = await delegator.finalizePoolsAsync([poolId]);
// Check balances
await expectedBalances.transferAssetAsync(
expectedBalances.transferAsset(
deployment.staking.stakingProxy.address,
operator.address,
operatorReward,
@@ -371,7 +328,8 @@ blockchainTests.resets('fillOrder integration tests', env => {
const order = await maker.signOrderAsync();
const receipt = await taker.fillOrderAsync(order, order.takerAssetAmount, { value: constants.ZERO_AMOUNT });
const rewardsAvailable = DeploymentManager.protocolFee;
const expectedBalances = await simulateFillAsync(order, receipt, constants.ZERO_AMOUNT);
const expectedBalances = LocalBalanceStore.create(balanceStore);
expectedBalances.simulateFills([order], taker.address, receipt, deployment);
// End the epoch. This should wrap the staking proxy's ETH balance.
const endEpochReceipt = await delegator.endEpochAsync();
@@ -392,7 +350,7 @@ blockchainTests.resets('fillOrder integration tests', env => {
const [finalizePoolReceipt] = await delegator.finalizePoolsAsync([poolId]);
// Check balances
await expectedBalances.transferAssetAsync(
expectedBalances.transferAsset(
deployment.staking.stakingProxy.address,
operator.address,
operatorReward,

View File

@@ -296,7 +296,7 @@ export class MatchOrderTester {
localBalanceStore.burnGas(takerAddress, DeploymentManager.gasPrice.times(transactionReceipt.gasUsed));
// Simulate the fill.
const expectedMatchResults = await this._simulateMatchOrdersAsync(
const expectedMatchResults = this._simulateMatchOrders(
orders,
takerAddress,
toFullMatchTransferAmounts(expectedTransferAmounts),
@@ -319,12 +319,12 @@ export class MatchOrderTester {
* @param localBalanceStore The balance store to use for the simulation.
* @return The new account balances and fill events that occurred during the match.
*/
protected async _simulateMatchOrdersAsync(
protected _simulateMatchOrders(
orders: MatchedOrders,
takerAddress: string,
transferAmounts: MatchTransferAmounts,
localBalanceStore: LocalBalanceStore,
): Promise<MatchResults> {
): MatchResults {
// prettier-ignore
const matchResults = {
orders: {
@@ -343,7 +343,7 @@ export class MatchOrderTester {
};
// Right maker asset -> left maker
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.rightOrder.makerAddress,
orders.leftOrder.makerAddress,
transferAmounts.rightMakerAssetBoughtByLeftMakerAmount,
@@ -352,7 +352,7 @@ export class MatchOrderTester {
if (orders.leftOrder.makerAddress !== orders.leftOrder.feeRecipientAddress) {
// Left maker fees
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.leftOrder.makerAddress,
orders.leftOrder.feeRecipientAddress,
transferAmounts.leftMakerFeeAssetPaidByLeftMakerAmount,
@@ -361,7 +361,7 @@ export class MatchOrderTester {
}
// Left maker asset -> right maker
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.leftOrder.makerAddress,
orders.rightOrder.makerAddress,
transferAmounts.leftMakerAssetBoughtByRightMakerAmount,
@@ -370,7 +370,7 @@ export class MatchOrderTester {
if (orders.rightOrder.makerAddress !== orders.rightOrder.feeRecipientAddress) {
// Right maker fees
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.rightOrder.makerAddress,
orders.rightOrder.feeRecipientAddress,
transferAmounts.rightMakerFeeAssetPaidByRightMakerAmount,
@@ -379,7 +379,7 @@ export class MatchOrderTester {
}
// Left taker profit
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.leftOrder.makerAddress,
takerAddress,
transferAmounts.leftMakerAssetReceivedByTakerAmount,
@@ -387,7 +387,7 @@ export class MatchOrderTester {
);
// Right taker profit
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
orders.rightOrder.makerAddress,
takerAddress,
transferAmounts.rightMakerAssetReceivedByTakerAmount,
@@ -395,7 +395,7 @@ export class MatchOrderTester {
);
// Left taker fees
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
takerAddress,
orders.leftOrder.feeRecipientAddress,
transferAmounts.leftTakerFeeAssetPaidByTakerAmount,
@@ -403,7 +403,7 @@ export class MatchOrderTester {
);
// Right taker fees
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
takerAddress,
orders.rightOrder.feeRecipientAddress,
transferAmounts.rightTakerFeeAssetPaidByTakerAmount,
@@ -424,13 +424,13 @@ export class MatchOrderTester {
this._deployment.staking.stakingProxy.address,
transferAmounts.rightProtocolFeePaidByTakerInEthAmount,
);
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
takerAddress,
this._deployment.staking.stakingProxy.address,
transferAmounts.leftProtocolFeePaidByTakerInWethAmount,
wethAssetData,
);
await localBalanceStore.transferAssetAsync(
localBalanceStore.transferAsset(
takerAddress,
this._deployment.staking.stakingProxy.address,
transferAmounts.rightProtocolFeePaidByTakerInWethAmount,
@@ -513,7 +513,7 @@ export class MatchOrderTester {
// Add the latest match to the batch match results
batchMatchResults.matches.push(
await this._simulateMatchOrdersAsync(
this._simulateMatchOrders(
matchedOrders,
takerAddress,
toFullMatchTransferAmounts(transferAmounts[i]),

View File

@@ -0,0 +1,414 @@
// tslint:disable: max-file-line-count
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
import { exchangeDataEncoder } from '@0x/contracts-exchange';
import { blockchainTests, constants, describe, ExchangeFunctionName } from '@0x/contracts-test-utils';
import { SignedOrder, SignedZeroExTransaction } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Actor } from '../framework/actors/base';
import { FeeRecipient } from '../framework/actors/fee_recipient';
import { Maker } from '../framework/actors/maker';
import { Taker } from '../framework/actors/taker';
import { actorAddressesByName } from '../framework/actors/utils';
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
import { LocalBalanceStore } from '../framework/balances/local_balance_store';
import { DeploymentManager } from '../framework/deployment_manager';
// tslint:disable:no-unnecessary-type-assertion
blockchainTests.resets('Transaction <> protocol fee integration tests', env => {
let deployment: DeploymentManager;
let balanceStore: BlockchainBalanceStore;
let maker: Maker;
let feeRecipient: FeeRecipient;
let alice: Taker;
let bob: Taker;
let charlie: Taker;
let order: SignedOrder; // All orders will have the same fields, modulo salt and expiration time
let transactionA: SignedZeroExTransaction; // fillOrder transaction signed by Alice
let transactionB: SignedZeroExTransaction; // fillOrder transaction signed by Bob
let transactionC: SignedZeroExTransaction; // fillOrder transaction signed by Charlie
before(async () => {
deployment = await DeploymentManager.deployAsync(env, {
numErc20TokensToDeploy: 4,
numErc721TokensToDeploy: 0,
numErc1155TokensToDeploy: 0,
});
const assetDataEncoder = new IAssetDataContract(constants.NULL_ADDRESS, env.provider);
const [makerToken, takerToken, makerFeeToken, takerFeeToken] = deployment.tokens.erc20;
alice = new Taker({ name: 'Alice', deployment });
bob = new Taker({ name: 'Bob', deployment });
charlie = new Taker({ name: 'Charlie', deployment });
feeRecipient = new FeeRecipient({
name: 'Fee recipient',
deployment,
});
maker = new Maker({
name: 'Maker',
deployment,
orderConfig: {
feeRecipientAddress: feeRecipient.address,
makerAssetData: assetDataEncoder.ERC20Token(makerToken.address).getABIEncodedTransactionData(),
takerAssetData: assetDataEncoder.ERC20Token(takerToken.address).getABIEncodedTransactionData(),
makerFeeAssetData: assetDataEncoder.ERC20Token(makerFeeToken.address).getABIEncodedTransactionData(),
takerFeeAssetData: assetDataEncoder.ERC20Token(takerFeeToken.address).getABIEncodedTransactionData(),
},
});
for (const taker of [alice, bob, charlie]) {
await taker.configureERC20TokenAsync(takerToken);
await taker.configureERC20TokenAsync(takerFeeToken);
await taker.configureERC20TokenAsync(deployment.tokens.weth, deployment.staking.stakingProxy.address);
}
await maker.configureERC20TokenAsync(makerToken);
await maker.configureERC20TokenAsync(makerFeeToken);
balanceStore = new BlockchainBalanceStore(
{
...actorAddressesByName([alice, bob, charlie, maker, feeRecipient]),
StakingProxy: deployment.staking.stakingProxy.address,
},
{ erc20: { makerToken, takerToken, makerFeeToken, takerFeeToken, wETH: deployment.tokens.weth } },
{},
);
await balanceStore.updateBalancesAsync();
order = await maker.signOrderAsync();
let data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
transactionA = await alice.signTransactionAsync({ data });
order = await maker.signOrderAsync();
data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
transactionB = await bob.signTransactionAsync({ data });
order = await maker.signOrderAsync();
data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order]);
transactionC = await charlie.signTransactionAsync({ data });
});
after(async () => {
Actor.count = 0;
});
const REFUND_AMOUNT = new BigNumber(1);
describe('executeTransaction', () => {
const ETH_FEE_WITH_REFUND = DeploymentManager.protocolFee.plus(REFUND_AMOUNT);
let expectedBalances: LocalBalanceStore;
beforeEach(async () => {
await balanceStore.updateBalancesAsync();
expectedBalances = LocalBalanceStore.create(balanceStore);
});
afterEach(async () => {
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
});
describe('Simple', () => {
it('Alice executeTransaction => Alice fillOrder; protocol fee in ETH', async () => {
const txReceipt = await deployment.exchange
.executeTransaction(transactionA, transactionA.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills([order], alice.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
it('Alice executeTransaction => Bob fillOrder; protocol fee in ETH', async () => {
const txReceipt = await deployment.exchange
.executeTransaction(transactionB, transactionB.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills([order], bob.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
it('Alice executeTransaction => Alice fillOrder; protocol fee in wETH', async () => {
const txReceipt = await deployment.exchange
.executeTransaction(transactionA, transactionA.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills([order], alice.address, txReceipt, deployment, REFUND_AMOUNT);
});
it('Alice executeTransaction => Bob fillOrder; protocol fee in wETH', async () => {
const txReceipt = await deployment.exchange
.executeTransaction(transactionB, transactionB.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills([order], bob.address, txReceipt, deployment, REFUND_AMOUNT);
});
it('Alice executeTransaction => Alice batchFillOrders; mixed protocol fees', async () => {
const orders = [order, await maker.signOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchFillOrders, orders);
const batchFillTransaction = await alice.signTransactionAsync({ data });
const txReceipt = await deployment.exchange
.executeTransaction(batchFillTransaction, batchFillTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills(orders, alice.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
it('Alice executeTransaction => Bob batchFillOrders; mixed protocol fees', async () => {
const orders = [order, await maker.signOrderAsync()];
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.BatchFillOrders, orders);
const batchFillTransaction = await bob.signTransactionAsync({ data });
const txReceipt = await deployment.exchange
.executeTransaction(batchFillTransaction, batchFillTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills(orders, bob.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
});
describe('Nested', () => {
it('Alice executeTransaction => Alice executeTransaction => Alice fillOrder; protocol fee in ETH', async () => {
const recursiveData = deployment.exchange
.executeTransaction(transactionA, transactionA.signature)
.getABIEncodedTransactionData();
const recursiveTransaction = await alice.signTransactionAsync({ data: recursiveData });
const txReceipt = await deployment.exchange
.executeTransaction(recursiveTransaction, recursiveTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills([order], alice.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
it('Alice executeTransaction => Alice executeTransaction => Bob fillOrder; protocol fee in ETH', async () => {
const recursiveData = deployment.exchange
.executeTransaction(transactionB, transactionB.signature)
.getABIEncodedTransactionData();
const recursiveTransaction = await alice.signTransactionAsync({ data: recursiveData });
const txReceipt = await deployment.exchange
.executeTransaction(recursiveTransaction, recursiveTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEE_WITH_REFUND });
expectedBalances.simulateFills([order], bob.address, txReceipt, deployment, ETH_FEE_WITH_REFUND);
});
it('Alice executeTransaction => Alice executeTransaction => Alice fillOrder; protocol fee in wETH', async () => {
const recursiveData = deployment.exchange
.executeTransaction(transactionA, transactionA.signature)
.getABIEncodedTransactionData();
const recursiveTransaction = await alice.signTransactionAsync({ data: recursiveData });
const txReceipt = await deployment.exchange
.executeTransaction(recursiveTransaction, recursiveTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills([order], alice.address, txReceipt, deployment, REFUND_AMOUNT);
});
it('Alice executeTransaction => Alice executeTransaction => Bob fillOrder; protocol fee in wETH', async () => {
const recursiveData = deployment.exchange
.executeTransaction(transactionB, transactionB.signature)
.getABIEncodedTransactionData();
const recursiveTransaction = await alice.signTransactionAsync({ data: recursiveData });
const txReceipt = await deployment.exchange
.executeTransaction(recursiveTransaction, recursiveTransaction.signature)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills([order], bob.address, txReceipt, deployment, REFUND_AMOUNT);
});
});
});
describe('batchExecuteTransactions', () => {
let expectedBalances: LocalBalanceStore;
beforeEach(async () => {
await balanceStore.updateBalancesAsync();
expectedBalances = LocalBalanceStore.create(balanceStore);
});
afterEach(async () => {
await balanceStore.updateBalancesAsync();
balanceStore.assertEquals(expectedBalances);
});
describe('Simple', () => {
// All orders' protocol fees paid in ETH by sender
const ETH_FEES_WITH_REFUND = DeploymentManager.protocolFee.times(3).plus(REFUND_AMOUNT);
// First order's protocol fee paid in ETH by sender, the other two paid in WETH by their respective takers
const MIXED_FEES_WITH_REFUND = DeploymentManager.protocolFee.times(1).plus(REFUND_AMOUNT);
it('Alice batchExecuteTransactions => Alice fillOrder, Bob fillOrder, Charlie fillOrder; protocol fees in ETH', async () => {
const transactions = [transactionA, transactionB, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[alice.address, bob.address, charlie.address],
txReceipt,
deployment,
ETH_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Alice fillOrder, Charlie fillOrder; protocol fees in ETH', async () => {
const transactions = [transactionB, transactionA, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, alice.address, charlie.address],
txReceipt,
deployment,
ETH_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Charlie fillOrder, Alice fillOrder; protocol fees in ETH', async () => {
const transactions = [transactionB, transactionC, transactionA];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: ETH_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, charlie.address, alice.address],
txReceipt,
deployment,
ETH_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Alice fillOrder, Bob fillOrder, Charlie fillOrder; protocol fees in wETH', async () => {
const transactions = [transactionA, transactionB, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills(
[order, order, order],
[alice.address, bob.address, charlie.address],
txReceipt,
deployment,
REFUND_AMOUNT,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Alice fillOrder, Charlie fillOrder; protocol fees in wETH', async () => {
const transactions = [transactionB, transactionA, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, alice.address, charlie.address],
txReceipt,
deployment,
REFUND_AMOUNT,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Charlie fillOrder, Alice fillOrder; protocol fees in wETH', async () => {
const transactions = [transactionB, transactionC, transactionA];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: REFUND_AMOUNT });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, charlie.address, alice.address],
txReceipt,
deployment,
REFUND_AMOUNT,
);
});
it('Alice batchExecuteTransactions => Alice fillOrder, Bob fillOrder, Charlie fillOrder; mixed protocol fees', async () => {
const transactions = [transactionA, transactionB, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[alice.address, bob.address, charlie.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Alice fillOrder, Charlie fillOrder; mixed protocol fees', async () => {
const transactions = [transactionB, transactionA, transactionC];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, alice.address, charlie.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Charlie fillOrder, Alice fillOrder; mixed protocol fees', async () => {
const transactions = [transactionB, transactionC, transactionA];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order],
[bob.address, charlie.address, alice.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
});
describe('Nested', () => {
// First two orders' protocol fees paid in ETH by sender, the other three paid in WETH by their respective takers
const MIXED_FEES_WITH_REFUND = DeploymentManager.protocolFee.times(2.5);
// Alice batchExecuteTransactions => Alice fillOrder, Bob fillOrder, Charlie fillOrder
let nestedTransaction: SignedZeroExTransaction;
// Second fillOrder transaction signed by Bob
let transactionB2: SignedZeroExTransaction;
// Second fillOrder transaction signed by Charlie
let transactionC2: SignedZeroExTransaction;
before(async () => {
let newOrder = await maker.signOrderAsync();
let data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [newOrder]);
transactionB2 = await bob.signTransactionAsync({ data });
newOrder = await maker.signOrderAsync();
data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [newOrder]);
transactionC2 = await charlie.signTransactionAsync({ data });
const transactions = [transactionA, transactionB, transactionC];
const signatures = transactions.map(tx => tx.signature);
const recursiveData = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.getABIEncodedTransactionData();
nestedTransaction = await alice.signTransactionAsync({ data: recursiveData });
});
it('Alice batchExecuteTransactions => nested batchExecuteTransactions, Bob fillOrder, Charlie fillOrder; mixed protocol fees', async () => {
const transactions = [nestedTransaction, transactionB2, transactionC2];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order, order, order],
[alice.address, bob.address, charlie.address, bob.address, charlie.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, nested batchExecuteTransactions, Charlie fillOrder; mixed protocol fees', async () => {
const transactions = [transactionB2, nestedTransaction, transactionC2];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order, order, order],
[bob.address, alice.address, bob.address, charlie.address, charlie.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
it('Alice batchExecuteTransactions => Bob fillOrder, Charlie fillOrder, nested batchExecuteTransactions; mixed protocol fees', async () => {
const transactions = [transactionB2, transactionC2, nestedTransaction];
const signatures = transactions.map(tx => tx.signature);
const txReceipt = await deployment.exchange
.batchExecuteTransactions(transactions, signatures)
.awaitTransactionSuccessAsync({ from: alice.address, value: MIXED_FEES_WITH_REFUND });
expectedBalances.simulateFills(
[order, order, order, order, order],
[bob.address, charlie.address, alice.address, bob.address, charlie.address],
txReceipt,
deployment,
MIXED_FEES_WITH_REFUND,
);
});
});
});
});

View File

@@ -637,10 +637,7 @@ blockchainTests.resets('Transaction integration tests', env => {
const transaction1 = await takers[0].signTransactionAsync({ data: data1 });
const transaction2 = await takers[1].signTransactionAsync({ data: data2 });
const transactionReceipt = await deployment.exchange
.batchExecuteTransactions(
[transaction1, transaction2],
[transaction1.signature, constants.NULL_BYTES],
)
.batchExecuteTransactions([transaction1, transaction2], [transaction1.signature, constants.NULL_BYTES])
.awaitTransactionSuccessAsync({ from: takers[1].address });
verifyEventsFromLogs<ExchangeTransactionExecutionEventArgs>(
@@ -677,14 +674,8 @@ blockchainTests.resets('Transaction integration tests', env => {
[transaction1.signature, transaction2.signature],
)
.callAsync({ from: sender.address });
const fillResults1: FillResults = deployment.exchange.getABIDecodedReturnData(
'fillOrder',
returnData[0],
);
const fillResults2: FillResults = deployment.exchange.getABIDecodedReturnData(
'fillOrder',
returnData[1],
);
const fillResults1: FillResults = deployment.exchange.getABIDecodedReturnData('fillOrder', returnData[0]);
const fillResults2: FillResults = deployment.exchange.getABIDecodedReturnData('fillOrder', returnData[1]);
expect(fillResults1).to.deep.equal(
ReferenceFunctions.calculateFillResults(
order1,
@@ -706,9 +697,7 @@ blockchainTests.resets('Transaction integration tests', env => {
const order1 = await maker.signOrderAsync();
const order2 = await maker.signOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [
order2,
]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order2]);
const transaction1 = await takers[0].signTransactionAsync({ data: data1 });
const transaction2 = await maker.signTransactionAsync({ data: data2 });
const transactionReceipt = await deployment.exchange
@@ -750,9 +739,7 @@ blockchainTests.resets('Transaction integration tests', env => {
const order1 = await maker.signOrderAsync();
const order2 = await maker.signOrderAsync();
const data1 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.FillOrder, [order1]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [
order2,
]);
const data2 = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrder, [order2]);
const transaction1 = await takers[0].signTransactionAsync({ data: data1 });
const transaction2 = await maker.signTransactionAsync({ data: data2 });
const returnData = await deployment.exchange
@@ -761,10 +748,7 @@ blockchainTests.resets('Transaction integration tests', env => {
[transaction1.signature, transaction2.signature],
)
.callAsync({ from: sender.address });
const fillResults: FillResults = deployment.exchange.getABIDecodedReturnData(
'fillOrder',
returnData[0],
);
const fillResults: FillResults = deployment.exchange.getABIDecodedReturnData('fillOrder', returnData[0]);
expect(fillResults).to.deep.equal(
ReferenceFunctions.calculateFillResults(
order1,

View File

@@ -511,20 +511,10 @@ blockchainTests('Forwarder integration tests', env => {
// Compute expected balances
const expectedBalances = LocalBalanceStore.create(balanceStore);
await expectedBalances.transferAssetAsync(
maker.address,
taker.address,
makerAssetFillAmount,
makerAssetData,
);
expectedBalances.transferAsset(maker.address, taker.address, makerAssetFillAmount, makerAssetData);
expectedBalances.wrapEth(taker.address, deployment.tokens.weth.address, ethValue);
await expectedBalances.transferAssetAsync(
taker.address,
maker.address,
primaryTakerAssetFillAmount,
wethAssetData,
);
await expectedBalances.transferAssetAsync(
expectedBalances.transferAsset(taker.address, maker.address, primaryTakerAssetFillAmount, wethAssetData);
expectedBalances.transferAsset(
taker.address,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
@@ -568,24 +558,14 @@ blockchainTests('Forwarder integration tests', env => {
// Compute expected balances
const expectedBalances = LocalBalanceStore.create(balanceStore);
await expectedBalances.transferAssetAsync(
maker.address,
taker.address,
makerAssetFillAmount,
makerAssetData,
);
expectedBalances.transferAsset(maker.address, taker.address, makerAssetFillAmount, makerAssetData);
expectedBalances.wrapEth(
taker.address,
deployment.tokens.weth.address,
takerAssetFillAmount.plus(DeploymentManager.protocolFee),
);
await expectedBalances.transferAssetAsync(
taker.address,
maker.address,
takerAssetFillAmount,
wethAssetData,
);
await expectedBalances.transferAssetAsync(
expectedBalances.transferAsset(taker.address, maker.address, takerAssetFillAmount, wethAssetData);
expectedBalances.transferAsset(
taker.address,
deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,

View File

@@ -207,7 +207,7 @@ export class ForwarderTestFactory {
continue;
}
const { wethSpentAmount, makerAssetAcquiredAmount } = await this._simulateSingleFillAsync(
const { wethSpentAmount, makerAssetAcquiredAmount } = this._simulateSingleFill(
balances,
order,
ordersInfoBefore[i].orderTakerAssetFilledAmount,
@@ -232,13 +232,13 @@ export class ForwarderTestFactory {
return { ...currentTotal, balances };
}
private async _simulateSingleFillAsync(
private _simulateSingleFill(
balances: LocalBalanceStore,
order: SignedOrder,
takerAssetFilled: BigNumber,
fillFraction: number,
bridgeExcessBuyAmount: BigNumber,
): Promise<ForwarderFillState> {
): ForwarderFillState {
let { makerAssetAmount, takerAssetAmount, makerFee, takerFee } = order;
makerAssetAmount = makerAssetAmount.times(fillFraction).integerValue(BigNumber.ROUND_CEIL);
takerAssetAmount = takerAssetAmount.times(fillFraction).integerValue(BigNumber.ROUND_CEIL);
@@ -273,42 +273,22 @@ export class ForwarderTestFactory {
// (In reality this is done all at once, but we simulate it order by order)
// Maker -> Forwarder
await balances.transferAssetAsync(
order.makerAddress,
this._forwarder.address,
makerAssetAmount,
order.makerAssetData,
);
balances.transferAsset(order.makerAddress, this._forwarder.address, makerAssetAmount, order.makerAssetData);
// Maker -> Order fee recipient
await balances.transferAssetAsync(
order.makerAddress,
order.feeRecipientAddress,
makerFee,
order.makerFeeAssetData,
);
balances.transferAsset(order.makerAddress, order.feeRecipientAddress, makerFee, order.makerFeeAssetData);
// Forwarder -> Maker
await balances.transferAssetAsync(
this._forwarder.address,
order.makerAddress,
takerAssetAmount,
order.takerAssetData,
);
balances.transferAsset(this._forwarder.address, order.makerAddress, takerAssetAmount, order.takerAssetData);
// Forwarder -> Order fee recipient
await balances.transferAssetAsync(
this._forwarder.address,
order.feeRecipientAddress,
takerFee,
order.takerFeeAssetData,
);
balances.transferAsset(this._forwarder.address, order.feeRecipientAddress, takerFee, order.takerFeeAssetData);
// Forwarder pays the protocol fee in WETH
await balances.transferAssetAsync(
balances.transferAsset(
this._forwarder.address,
this._deployment.staking.stakingProxy.address,
DeploymentManager.protocolFee,
order.takerAssetData,
);
// Forwarder gives acquired maker asset to taker
await balances.transferAssetAsync(
balances.transferAsset(
this._forwarder.address,
this._taker.address,
makerAssetAcquiredAmount,

View File

@@ -37,7 +37,7 @@ export function validStakeAssertion(
before: async (amount: BigNumber, txData: Partial<TxData>) => {
// Simulates the transfer of ZRX from staker to vault
const expectedBalances = LocalBalanceStore.create(balanceStore);
await expectedBalances.transferAssetAsync(
expectedBalances.transferAsset(
txData.from as string,
zrxVault.address,
amount,

View File

@@ -37,7 +37,7 @@ export function validUnstakeAssertion(
before: async (amount: BigNumber, txData: Partial<TxData>) => {
// Simulates the transfer of ZRX from vault to staker
const expectedBalances = LocalBalanceStore.create(balanceStore);
await expectedBalances.transferAssetAsync(
expectedBalances.transferAsset(
zrxVault.address,
txData.from as string,
amount,

View File

@@ -1,9 +1,13 @@
import { IAssetDataContract } from '@0x/contracts-asset-proxy';
import { ReferenceFunctions } from '@0x/contracts-exchange-libs';
import { constants, hexSlice, Numberish, provider } from '@0x/contracts-test-utils';
import { AssetProxyId } from '@0x/types';
import { AssetProxyId, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
import * as _ from 'lodash';
import { DeploymentManager } from '../deployment_manager';
import { BalanceStore } from './balance_store';
import { TokenContractsByName, TokenOwnersByName } from './types';
@@ -74,12 +78,7 @@ export class LocalBalanceStore extends BalanceStore {
* @param amount Amount of asset(s) to transfer
* @param assetData Asset data of assets being transferred.
*/
public async transferAssetAsync(
fromAddress: string,
toAddress: string,
amount: BigNumber,
assetData: string,
): Promise<void> {
public transferAsset(fromAddress: string, toAddress: string, amount: BigNumber, assetData: string): void {
if (fromAddress === toAddress || amount.isZero()) {
return;
}
@@ -174,7 +173,7 @@ export class LocalBalanceStore extends BalanceStore {
>('MultiAsset', assetData);
for (const [i, amt] of amounts.entries()) {
const nestedAmount = amount.times(amt);
await this.transferAssetAsync(fromAddress, toAddress, nestedAmount, nestedAssetData[i]);
this.transferAsset(fromAddress, toAddress, nestedAmount, nestedAssetData[i]);
}
break;
}
@@ -185,4 +184,71 @@ export class LocalBalanceStore extends BalanceStore {
throw new Error(`Unhandled asset proxy ID: ${assetProxyId}`);
}
}
public simulateFills(
orders: SignedOrder[],
takerAddresses: string[] | string,
txReceipt: TransactionReceiptWithDecodedLogs,
deployment: DeploymentManager,
msgValue: BigNumber = constants.ZERO_AMOUNT,
takerAssetFillAmounts?: BigNumber[],
): void {
let remainingValue = msgValue;
// Transaction gas cost
this.burnGas(txReceipt.from, DeploymentManager.gasPrice.times(txReceipt.gasUsed));
for (const [index, order] of orders.entries()) {
const takerAddress = Array.isArray(takerAddresses) ? takerAddresses[index] : takerAddresses;
const fillResults = ReferenceFunctions.calculateFillResults(
order,
takerAssetFillAmounts ? takerAssetFillAmounts[index] : order.takerAssetAmount,
DeploymentManager.protocolFeeMultiplier,
DeploymentManager.gasPrice,
);
// Taker -> Maker
this.transferAsset(
takerAddress,
order.makerAddress,
fillResults.takerAssetFilledAmount,
order.takerAssetData,
);
// Maker -> Taker
this.transferAsset(
order.makerAddress,
takerAddress,
fillResults.makerAssetFilledAmount,
order.makerAssetData,
);
// Taker -> Fee Recipient
this.transferAsset(
takerAddress,
order.feeRecipientAddress,
fillResults.takerFeePaid,
order.takerFeeAssetData,
);
// Maker -> Fee Recipient
this.transferAsset(
order.makerAddress,
order.feeRecipientAddress,
fillResults.makerFeePaid,
order.makerFeeAssetData,
);
// Protocol fee
if (remainingValue.isGreaterThanOrEqualTo(fillResults.protocolFeePaid)) {
this.sendEth(txReceipt.from, deployment.staking.stakingProxy.address, fillResults.protocolFeePaid);
remainingValue = remainingValue.minus(fillResults.protocolFeePaid);
} else {
this.transferAsset(
takerAddress,
deployment.staking.stakingProxy.address,
fillResults.protocolFeePaid,
deployment.assetDataEncoder
.ERC20Token(deployment.tokens.weth.address)
.getABIEncodedTransactionData(),
);
}
}
}
}