Add back batchFillOrders
This commit is contained in:
parent
ea8669439f
commit
cb8cf1f107
@ -48,6 +48,8 @@ contract LibTransactionDecoder {
|
|||||||
|
|
||||||
if (functionSelector == IExchange(address(0)).batchCancelOrders.selector) {
|
if (functionSelector == IExchange(address(0)).batchCancelOrders.selector) {
|
||||||
functionName = "batchCancelOrders";
|
functionName = "batchCancelOrders";
|
||||||
|
} else if (functionSelector == IExchange(address(0)).batchFillOrders.selector) {
|
||||||
|
functionName = "batchFillOrders";
|
||||||
} else if (functionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector) {
|
} else if (functionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector) {
|
||||||
functionName = "batchFillOrdersNoThrow";
|
functionName = "batchFillOrdersNoThrow";
|
||||||
} else if (functionSelector == IExchange(address(0)).batchFillOrKillOrders.selector) {
|
} else if (functionSelector == IExchange(address(0)).batchFillOrKillOrders.selector) {
|
||||||
@ -84,6 +86,7 @@ contract LibTransactionDecoder {
|
|||||||
signatures = new bytes[](0);
|
signatures = new bytes[](0);
|
||||||
} else if (
|
} else if (
|
||||||
functionSelector == IExchange(address(0)).batchFillOrKillOrders.selector ||
|
functionSelector == IExchange(address(0)).batchFillOrKillOrders.selector ||
|
||||||
|
functionSelector == IExchange(address(0)).batchFillOrders.selector ||
|
||||||
functionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector
|
functionSelector == IExchange(address(0)).batchFillOrdersNoThrow.selector
|
||||||
) {
|
) {
|
||||||
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForBatchFill(transactionData);
|
(orders, takerAssetFillAmounts, signatures) = _makeReturnValuesForBatchFill(transactionData);
|
||||||
|
@ -56,7 +56,7 @@ describe('LibTransactionDecoder', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const func of ['batchFillOrdersNoThrow', 'batchFillOrKillOrders']) {
|
for (const func of ['batchFillOrders', 'batchFillOrdersNoThrow', 'batchFillOrKillOrders']) {
|
||||||
const input = (exchangeInterface as any)[func].getABIEncodedTransactionData(
|
const input = (exchangeInterface as any)[func].getABIEncodedTransactionData(
|
||||||
[order, order],
|
[order, order],
|
||||||
[takerAssetFillAmount, takerAssetFillAmount],
|
[takerAssetFillAmount, takerAssetFillAmount],
|
||||||
|
@ -59,6 +59,34 @@ contract MixinWrapperFunctions is
|
|||||||
return fillResults;
|
return fillResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Executes multiple calls of fillOrder.
|
||||||
|
/// @param orders Array of order specifications.
|
||||||
|
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
|
||||||
|
/// @param signatures Proofs that orders have been created by makers.
|
||||||
|
/// @return Array of amounts filled and fees paid by makers and taker.
|
||||||
|
function batchFillOrders(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
uint256[] memory takerAssetFillAmounts,
|
||||||
|
bytes[] memory signatures
|
||||||
|
)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
nonReentrant
|
||||||
|
refundFinalBalance
|
||||||
|
returns (LibFillResults.FillResults[] memory fillResults)
|
||||||
|
{
|
||||||
|
uint256 ordersLength = orders.length;
|
||||||
|
fillResults = new LibFillResults.FillResults[](ordersLength);
|
||||||
|
for (uint256 i = 0; i != ordersLength; i++) {
|
||||||
|
fillResults[i] = _fillOrder(
|
||||||
|
orders[i],
|
||||||
|
takerAssetFillAmounts[i],
|
||||||
|
signatures[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return fillResults;
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Executes multiple calls of fillOrKill.
|
/// @dev Executes multiple calls of fillOrKill.
|
||||||
/// @param orders Array of order specifications.
|
/// @param orders Array of order specifications.
|
||||||
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
|
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
|
||||||
|
@ -38,6 +38,20 @@ contract IWrapperFunctions {
|
|||||||
payable
|
payable
|
||||||
returns (LibFillResults.FillResults memory fillResults);
|
returns (LibFillResults.FillResults memory fillResults);
|
||||||
|
|
||||||
|
/// @dev Executes multiple calls of fillOrder.
|
||||||
|
/// @param orders Array of order specifications.
|
||||||
|
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
|
||||||
|
/// @param signatures Proofs that orders have been created by makers.
|
||||||
|
/// @return Array of amounts filled and fees paid by makers and taker.
|
||||||
|
function batchFillOrders(
|
||||||
|
LibOrder.Order[] memory orders,
|
||||||
|
uint256[] memory takerAssetFillAmounts,
|
||||||
|
bytes[] memory signatures
|
||||||
|
)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
returns (LibFillResults.FillResults[] memory fillResults);
|
||||||
|
|
||||||
/// @dev Synchronously executes multiple calls of fillOrKill.
|
/// @dev Synchronously executes multiple calls of fillOrKill.
|
||||||
/// @param orders Array of order specifications.
|
/// @param orders Array of order specifications.
|
||||||
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
|
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
|
||||||
|
@ -84,7 +84,7 @@ contract TestProtocolFeesReceiver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Forward all of the value sent to the contract to `batchFillOrders()`.
|
// Forward all of the value sent to the contract to `batchFillOrders()`.
|
||||||
testProtocolFees.batchFillOrKillOrders.value(msg.value)(orders, takerAssetFilledAmounts, signatures);
|
testProtocolFees.batchFillOrders.value(msg.value)(orders, takerAssetFilledAmounts, signatures);
|
||||||
|
|
||||||
// If the `protocolFeeCollector` was set, ensure that the protocol fees were paid correctly.
|
// If the `protocolFeeCollector` was set, ensure that the protocol fees were paid correctly.
|
||||||
// Otherwise, the protocol fees should not have been paid.
|
// Otherwise, the protocol fees should not have been paid.
|
||||||
|
@ -13,7 +13,11 @@ export const constants = {
|
|||||||
ExchangeFunctionName.SetProtocolFeeCollectorAddress,
|
ExchangeFunctionName.SetProtocolFeeCollectorAddress,
|
||||||
],
|
],
|
||||||
SINGLE_FILL_FN_NAMES: [ExchangeFunctionName.FillOrder, ExchangeFunctionName.FillOrKillOrder],
|
SINGLE_FILL_FN_NAMES: [ExchangeFunctionName.FillOrder, ExchangeFunctionName.FillOrKillOrder],
|
||||||
BATCH_FILL_FN_NAMES: [ExchangeFunctionName.BatchFillOrKillOrders, ExchangeFunctionName.BatchFillOrdersNoThrow],
|
BATCH_FILL_FN_NAMES: [
|
||||||
|
ExchangeFunctionName.BatchFillOrders,
|
||||||
|
ExchangeFunctionName.BatchFillOrKillOrders,
|
||||||
|
ExchangeFunctionName.BatchFillOrdersNoThrow,
|
||||||
|
],
|
||||||
MARKET_FILL_FN_NAMES: [
|
MARKET_FILL_FN_NAMES: [
|
||||||
ExchangeFunctionName.MarketBuyOrdersFillOrKill,
|
ExchangeFunctionName.MarketBuyOrdersFillOrKill,
|
||||||
ExchangeFunctionName.MarketSellOrdersFillOrKill,
|
ExchangeFunctionName.MarketSellOrdersFillOrKill,
|
||||||
|
@ -54,6 +54,20 @@ export class ExchangeWrapper {
|
|||||||
);
|
);
|
||||||
return txReceipt;
|
return txReceipt;
|
||||||
}
|
}
|
||||||
|
public async batchFillOrdersAsync(
|
||||||
|
orders: SignedOrder[],
|
||||||
|
from: string,
|
||||||
|
opts: { takerAssetFillAmounts?: BigNumber[]; gasPrice?: BigNumber } = {},
|
||||||
|
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||||
|
return this._exchange.batchFillOrders.awaitTransactionSuccessAsync(
|
||||||
|
orders,
|
||||||
|
opts.takerAssetFillAmounts === undefined
|
||||||
|
? orders.map(signedOrder => signedOrder.takerAssetAmount)
|
||||||
|
: opts.takerAssetFillAmounts,
|
||||||
|
orders.map(signedOrder => signedOrder.signature),
|
||||||
|
{ from, gasPrice: opts.gasPrice },
|
||||||
|
);
|
||||||
|
}
|
||||||
public async batchFillOrKillOrdersAsync(
|
public async batchFillOrKillOrdersAsync(
|
||||||
orders: SignedOrder[],
|
orders: SignedOrder[],
|
||||||
from: string,
|
from: string,
|
||||||
|
@ -216,6 +216,74 @@ blockchainTests.resets('Exchange wrappers', env => {
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('batchFillOrders', () => {
|
||||||
|
it('should transfer the correct amounts', async () => {
|
||||||
|
const makerAssetAddress = erc20TokenA.address;
|
||||||
|
const takerAssetAddress = erc20TokenB.address;
|
||||||
|
|
||||||
|
const takerAssetFillAmounts: BigNumber[] = [];
|
||||||
|
const expectedFillResults: FillResults[] = [];
|
||||||
|
|
||||||
|
_.forEach(signedOrders, signedOrder => {
|
||||||
|
const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2);
|
||||||
|
const makerAssetFilledAmount = takerAssetFillAmount
|
||||||
|
.times(signedOrder.makerAssetAmount)
|
||||||
|
.dividedToIntegerBy(signedOrder.takerAssetAmount);
|
||||||
|
const makerFee = signedOrder.makerFee
|
||||||
|
.times(makerAssetFilledAmount)
|
||||||
|
.dividedToIntegerBy(signedOrder.makerAssetAmount);
|
||||||
|
const takerFee = signedOrder.takerFee
|
||||||
|
.times(makerAssetFilledAmount)
|
||||||
|
.dividedToIntegerBy(signedOrder.makerAssetAmount);
|
||||||
|
|
||||||
|
takerAssetFillAmounts.push(takerAssetFillAmount);
|
||||||
|
expectedFillResults.push({
|
||||||
|
takerAssetFilledAmount: takerAssetFillAmount,
|
||||||
|
makerAssetFilledAmount,
|
||||||
|
makerFeePaid: makerFee,
|
||||||
|
takerFeePaid: takerFee,
|
||||||
|
protocolFeePaid: constants.ZERO_AMOUNT,
|
||||||
|
});
|
||||||
|
|
||||||
|
erc20Balances[makerAddress][makerAssetAddress] = erc20Balances[makerAddress][
|
||||||
|
makerAssetAddress
|
||||||
|
].minus(makerAssetFilledAmount);
|
||||||
|
erc20Balances[makerAddress][takerAssetAddress] = erc20Balances[makerAddress][
|
||||||
|
takerAssetAddress
|
||||||
|
].plus(takerAssetFillAmount);
|
||||||
|
erc20Balances[makerAddress][feeToken.address] = erc20Balances[makerAddress][feeToken.address].minus(
|
||||||
|
makerFee,
|
||||||
|
);
|
||||||
|
erc20Balances[takerAddress][makerAssetAddress] = erc20Balances[takerAddress][
|
||||||
|
makerAssetAddress
|
||||||
|
].plus(makerAssetFilledAmount);
|
||||||
|
erc20Balances[takerAddress][takerAssetAddress] = erc20Balances[takerAddress][
|
||||||
|
takerAssetAddress
|
||||||
|
].minus(takerAssetFillAmount);
|
||||||
|
erc20Balances[takerAddress][feeToken.address] = erc20Balances[takerAddress][feeToken.address].minus(
|
||||||
|
takerFee,
|
||||||
|
);
|
||||||
|
erc20Balances[feeRecipientAddress][feeToken.address] = erc20Balances[feeRecipientAddress][
|
||||||
|
feeToken.address
|
||||||
|
].plus(makerFee.plus(takerFee));
|
||||||
|
});
|
||||||
|
|
||||||
|
const fillResults = await exchange.batchFillOrders.callAsync(
|
||||||
|
signedOrders,
|
||||||
|
takerAssetFillAmounts,
|
||||||
|
signedOrders.map(signedOrder => signedOrder.signature),
|
||||||
|
{ from: takerAddress },
|
||||||
|
);
|
||||||
|
await exchangeWrapper.batchFillOrdersAsync(signedOrders, takerAddress, {
|
||||||
|
takerAssetFillAmounts,
|
||||||
|
});
|
||||||
|
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||||
|
|
||||||
|
expect(fillResults).to.deep.equal(expectedFillResults);
|
||||||
|
expect(newBalances).to.be.deep.equal(erc20Balances);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('batchFillOrKillOrders', () => {
|
describe('batchFillOrKillOrders', () => {
|
||||||
it('should transfer the correct amounts', async () => {
|
it('should transfer the correct amounts', async () => {
|
||||||
const makerAssetAddress = erc20TokenA.address;
|
const makerAssetAddress = erc20TokenA.address;
|
||||||
|
@ -251,6 +251,92 @@ blockchainTests('Exchange wrapper functions unit tests.', env => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('batchFillOrders', () => {
|
||||||
|
it('works with no fills', async () => {
|
||||||
|
const [actualResult, receipt] = await txHelper.getResultAndReceiptAsync(
|
||||||
|
testContract.batchFillOrders,
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
expect(actualResult).to.deep.eq([]);
|
||||||
|
assertFillOrderCallsFromLogs(receipt.logs, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works with one fill', async () => {
|
||||||
|
const COUNT = 1;
|
||||||
|
const orders = _.times(COUNT, () => randomOrder());
|
||||||
|
const fillAmounts = _.times(COUNT, i => orders[i].takerAssetAmount);
|
||||||
|
const signatures = _.times(COUNT, i => createOrderSignature(orders[i]));
|
||||||
|
const expectedResult = _.times(COUNT, i => getExpectedFillResults(orders[i], signatures[i]));
|
||||||
|
const expectedCalls = _.zip(orders, fillAmounts, signatures);
|
||||||
|
const [actualResult, receipt] = await txHelper.getResultAndReceiptAsync(
|
||||||
|
testContract.batchFillOrders,
|
||||||
|
orders,
|
||||||
|
fillAmounts,
|
||||||
|
signatures,
|
||||||
|
);
|
||||||
|
expect(actualResult).to.deep.eq(expectedResult);
|
||||||
|
assertFillOrderCallsFromLogs(receipt.logs, expectedCalls as any);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works with many fills', async () => {
|
||||||
|
const COUNT = 8;
|
||||||
|
const orders = _.times(COUNT, () => randomOrder());
|
||||||
|
const fillAmounts = _.times(COUNT, i => orders[i].takerAssetAmount);
|
||||||
|
const signatures = _.times(COUNT, i => createOrderSignature(orders[i]));
|
||||||
|
const expectedResult = _.times(COUNT, i => getExpectedFillResults(orders[i], signatures[i]));
|
||||||
|
const expectedCalls = _.zip(orders, fillAmounts, signatures);
|
||||||
|
const [actualResult, receipt] = await txHelper.getResultAndReceiptAsync(
|
||||||
|
testContract.batchFillOrders,
|
||||||
|
orders,
|
||||||
|
fillAmounts,
|
||||||
|
signatures,
|
||||||
|
);
|
||||||
|
expect(actualResult).to.deep.eq(expectedResult);
|
||||||
|
assertFillOrderCallsFromLogs(receipt.logs, expectedCalls as any);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works with duplicate orders', async () => {
|
||||||
|
const NUM_UNIQUE_ORDERS = 2;
|
||||||
|
const COUNT = 4;
|
||||||
|
const uniqueOrders = _.times(NUM_UNIQUE_ORDERS, () => randomOrder());
|
||||||
|
const orders = _.shuffle(_.flatten(_.times(COUNT / NUM_UNIQUE_ORDERS, () => uniqueOrders)));
|
||||||
|
const fillAmounts = _.times(COUNT, i => orders[i].takerAssetAmount.dividedToIntegerBy(COUNT));
|
||||||
|
const signatures = _.times(COUNT, i => createOrderSignature(orders[i]));
|
||||||
|
const expectedResult = _.times(COUNT, i => getExpectedFillResults(orders[i], signatures[i]));
|
||||||
|
const expectedCalls = _.zip(orders, fillAmounts, signatures);
|
||||||
|
const [actualResult, receipt] = await txHelper.getResultAndReceiptAsync(
|
||||||
|
testContract.batchFillOrders,
|
||||||
|
orders,
|
||||||
|
fillAmounts,
|
||||||
|
signatures,
|
||||||
|
);
|
||||||
|
expect(actualResult).to.deep.eq(expectedResult);
|
||||||
|
assertFillOrderCallsFromLogs(receipt.logs, expectedCalls as any);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reverts if there are more orders than fill amounts', async () => {
|
||||||
|
const COUNT = 8;
|
||||||
|
const orders = _.times(COUNT, () => randomOrder());
|
||||||
|
const fillAmounts = _.times(COUNT - 1, i => orders[i].takerAssetAmount);
|
||||||
|
const signatures = _.times(COUNT, i => createOrderSignature(orders[i]));
|
||||||
|
const expectedError = new AnyRevertError(); // Just a generic revert.
|
||||||
|
const tx = txHelper.getResultAndReceiptAsync(testContract.batchFillOrders, orders, fillAmounts, signatures);
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reverts if there are more orders than signatures', async () => {
|
||||||
|
const COUNT = 8;
|
||||||
|
const orders = _.times(COUNT, () => randomOrder());
|
||||||
|
const fillAmounts = _.times(COUNT, i => orders[i].takerAssetAmount);
|
||||||
|
const signatures = _.times(COUNT - 1, i => createOrderSignature(orders[i]));
|
||||||
|
const expectedError = new AnyRevertError(); // Just a generic revert.
|
||||||
|
const tx = txHelper.getResultAndReceiptAsync(testContract.batchFillOrders, orders, fillAmounts, signatures);
|
||||||
|
return expect(tx).to.revertWith(expectedError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('batchFillOrKillOrders', () => {
|
describe('batchFillOrKillOrders', () => {
|
||||||
it('works with no fills', async () => {
|
it('works with no fills', async () => {
|
||||||
const [actualResult, receipt] = await txHelper.getResultAndReceiptAsync(
|
const [actualResult, receipt] = await txHelper.getResultAndReceiptAsync(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user