Make fillOrderNoThrow internal only, remove batchFillOrders
This commit is contained in:
parent
b888e48a30
commit
c0f1e5f17f
@ -59,71 +59,6 @@ contract MixinWrapperFunctions is
|
|||||||
return fillResults;
|
return fillResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Note: This function only needs `refundFinalBalance` modifier because ether will not
|
|
||||||
// be returned in the event that the delegatecall fails. This said, there is no
|
|
||||||
// reason to invoke `disableRefundUntilEnd` because it is cheaper to use this modifier
|
|
||||||
// and the inner refund will not affect the logic of this call.
|
|
||||||
/// @dev Fills the input order.
|
|
||||||
/// Returns a null FillResults instance if the transaction would otherwise revert.
|
|
||||||
/// @param order Order struct containing order specifications.
|
|
||||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
|
||||||
/// @param signature Proof that order has been created by maker.
|
|
||||||
/// @return Amounts filled and fees paid by maker and taker.
|
|
||||||
function fillOrderNoThrow(
|
|
||||||
LibOrder.Order memory order,
|
|
||||||
uint256 takerAssetFillAmount,
|
|
||||||
bytes memory signature
|
|
||||||
)
|
|
||||||
public
|
|
||||||
payable
|
|
||||||
refundFinalBalance
|
|
||||||
returns (LibFillResults.FillResults memory fillResults)
|
|
||||||
{
|
|
||||||
// ABI encode calldata for `fillOrder`
|
|
||||||
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
|
||||||
IExchangeCore(address(0)).fillOrder.selector,
|
|
||||||
order,
|
|
||||||
takerAssetFillAmount,
|
|
||||||
signature
|
|
||||||
);
|
|
||||||
|
|
||||||
(bool didSucceed, bytes memory returnData) = address(this).delegatecall(fillOrderCalldata);
|
|
||||||
if (didSucceed) {
|
|
||||||
assert(returnData.length == 160);
|
|
||||||
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
|
|
||||||
}
|
|
||||||
// fillResults values will be 0 by default if call was unsuccessful
|
|
||||||
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.
|
||||||
@ -170,7 +105,7 @@ contract MixinWrapperFunctions is
|
|||||||
uint256 ordersLength = orders.length;
|
uint256 ordersLength = orders.length;
|
||||||
fillResults = new LibFillResults.FillResults[](ordersLength);
|
fillResults = new LibFillResults.FillResults[](ordersLength);
|
||||||
for (uint256 i = 0; i != ordersLength; i++) {
|
for (uint256 i = 0; i != ordersLength; i++) {
|
||||||
fillResults[i] = fillOrderNoThrow(
|
fillResults[i] = _fillOrderNoThrow(
|
||||||
orders[i],
|
orders[i],
|
||||||
takerAssetFillAmounts[i],
|
takerAssetFillAmounts[i],
|
||||||
signatures[i]
|
signatures[i]
|
||||||
@ -208,7 +143,7 @@ contract MixinWrapperFunctions is
|
|||||||
orders[i].takerAssetData = takerAssetData;
|
orders[i].takerAssetData = takerAssetData;
|
||||||
|
|
||||||
// Attempt to sell the remaining amount of takerAsset
|
// Attempt to sell the remaining amount of takerAsset
|
||||||
LibFillResults.FillResults memory singleFillResults = fillOrderNoThrow(
|
LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
|
||||||
orders[i],
|
orders[i],
|
||||||
remainingTakerAssetFillAmount,
|
remainingTakerAssetFillAmount,
|
||||||
signatures[i]
|
signatures[i]
|
||||||
@ -262,7 +197,7 @@ contract MixinWrapperFunctions is
|
|||||||
orders[i].makerAssetData = makerAssetData;
|
orders[i].makerAssetData = makerAssetData;
|
||||||
|
|
||||||
// Attempt to sell the remaining amount of takerAsset
|
// Attempt to sell the remaining amount of takerAsset
|
||||||
LibFillResults.FillResults memory singleFillResults = fillOrderNoThrow(
|
LibFillResults.FillResults memory singleFillResults = _fillOrderNoThrow(
|
||||||
orders[i],
|
orders[i],
|
||||||
remainingTakerAssetFillAmount,
|
remainingTakerAssetFillAmount,
|
||||||
signatures[i]
|
signatures[i]
|
||||||
@ -367,4 +302,39 @@ contract MixinWrapperFunctions is
|
|||||||
}
|
}
|
||||||
return fillResults;
|
return fillResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Note: This function only needs `refundFinalBalance` modifier because ether will not
|
||||||
|
// be returned in the event that the delegatecall fails. This said, there is no
|
||||||
|
// reason to invoke `disableRefundUntilEnd` because it is cheaper to use this modifier
|
||||||
|
// and the inner refund will not affect the logic of this call.
|
||||||
|
/// @dev Fills the input order.
|
||||||
|
/// Returns a null FillResults instance if the transaction would otherwise revert.
|
||||||
|
/// @param order Order struct containing order specifications.
|
||||||
|
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
||||||
|
/// @param signature Proof that order has been created by maker.
|
||||||
|
/// @return Amounts filled and fees paid by maker and taker.
|
||||||
|
function _fillOrderNoThrow(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 takerAssetFillAmount,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
|
{
|
||||||
|
// ABI encode calldata for `fillOrder`
|
||||||
|
bytes memory fillOrderCalldata = abi.encodeWithSelector(
|
||||||
|
IExchangeCore(address(0)).fillOrder.selector,
|
||||||
|
order,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
|
||||||
|
(bool didSucceed, bytes memory returnData) = address(this).delegatecall(fillOrderCalldata);
|
||||||
|
if (didSucceed) {
|
||||||
|
assert(returnData.length == 160);
|
||||||
|
fillResults = abi.decode(returnData, (LibFillResults.FillResults));
|
||||||
|
}
|
||||||
|
// fillResults values will be 0 by default if call was unsuccessful
|
||||||
|
return fillResults;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,35 +38,6 @@ contract IWrapperFunctions {
|
|||||||
payable
|
payable
|
||||||
returns (LibFillResults.FillResults memory fillResults);
|
returns (LibFillResults.FillResults memory fillResults);
|
||||||
|
|
||||||
/// @dev Fills an order with specified parameters and ECDSA signature.
|
|
||||||
/// Returns false if the transaction would otherwise revert.
|
|
||||||
/// @param order LibOrder.Order struct containing order specifications.
|
|
||||||
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
|
|
||||||
/// @param signature Proof that order has been created by maker.
|
|
||||||
/// @return Amounts filled and fees paid by maker and taker.
|
|
||||||
function fillOrderNoThrow(
|
|
||||||
LibOrder.Order memory order,
|
|
||||||
uint256 takerAssetFillAmount,
|
|
||||||
bytes memory signature
|
|
||||||
)
|
|
||||||
public
|
|
||||||
payable
|
|
||||||
returns (LibFillResults.FillResults memory fillResults);
|
|
||||||
|
|
||||||
/// @dev Synchronously 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.batchFillOrders.value(msg.value)(orders, takerAssetFilledAmounts, signatures);
|
testProtocolFees.batchFillOrKillOrders.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.
|
||||||
|
@ -65,6 +65,21 @@ contract TestWrapperFunctions is
|
|||||||
orderInfo.orderHash = order.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
|
orderInfo.orderHash = order.getTypedDataHash(EIP712_EXCHANGE_DOMAIN_HASH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fillOrderNoThrow(
|
||||||
|
LibOrder.Order memory order,
|
||||||
|
uint256 takerAssetFillAmount,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
public
|
||||||
|
returns (LibFillResults.FillResults memory fillResults)
|
||||||
|
{
|
||||||
|
return _fillOrderNoThrow(
|
||||||
|
order,
|
||||||
|
takerAssetFillAmount,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Overridden to log arguments, be deterministic, and revert with certain inputs.
|
/// @dev Overridden to log arguments, be deterministic, and revert with certain inputs.
|
||||||
function _fillOrder(
|
function _fillOrder(
|
||||||
LibOrder.Order memory order,
|
LibOrder.Order memory order,
|
||||||
|
@ -54,34 +54,6 @@ export class ExchangeWrapper {
|
|||||||
);
|
);
|
||||||
return txReceipt;
|
return txReceipt;
|
||||||
}
|
}
|
||||||
public async fillOrderNoThrowAsync(
|
|
||||||
signedOrder: SignedOrder,
|
|
||||||
from: string,
|
|
||||||
opts: { takerAssetFillAmount?: BigNumber; gas?: number; gasPrice?: BigNumber } = {},
|
|
||||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
|
||||||
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
|
|
||||||
const txReceipt = await this._exchange.fillOrderNoThrow.awaitTransactionSuccessAsync(
|
|
||||||
params.order,
|
|
||||||
params.takerAssetFillAmount,
|
|
||||||
params.signature,
|
|
||||||
{ from, gas: opts.gas, gasPrice: opts.gasPrice },
|
|
||||||
);
|
|
||||||
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,
|
||||||
|
@ -217,260 +217,6 @@ blockchainTests.resets('Exchange wrappers', env => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fillOrderNoThrow', () => {
|
|
||||||
it('should transfer the correct amounts', async () => {
|
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
|
|
||||||
});
|
|
||||||
const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2);
|
|
||||||
|
|
||||||
const fillResults = await exchange.fillOrderNoThrow.callAsync(
|
|
||||||
signedOrder,
|
|
||||||
takerAssetFillAmount,
|
|
||||||
signedOrder.signature,
|
|
||||||
{ from: takerAddress },
|
|
||||||
);
|
|
||||||
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress, {
|
|
||||||
takerAssetFillAmount,
|
|
||||||
});
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
expect(fillResults.makerAssetFilledAmount).to.bignumber.equal(makerAssetFilledAmount);
|
|
||||||
expect(fillResults.takerAssetFilledAmount).to.bignumber.equal(takerAssetFillAmount);
|
|
||||||
expect(fillResults.makerFeePaid).to.bignumber.equal(makerFee);
|
|
||||||
expect(fillResults.takerFeePaid).to.bignumber.equal(takerFee);
|
|
||||||
|
|
||||||
expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount),
|
|
||||||
);
|
|
||||||
expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20Balances[makerAddress][defaultTakerAssetAddress].plus(takerAssetFillAmount),
|
|
||||||
);
|
|
||||||
expect(newBalances[makerAddress][feeToken.address]).to.be.bignumber.equal(
|
|
||||||
erc20Balances[makerAddress][feeToken.address].minus(makerFee),
|
|
||||||
);
|
|
||||||
expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount),
|
|
||||||
);
|
|
||||||
expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
erc20Balances[takerAddress][defaultMakerAssetAddress].plus(makerAssetFilledAmount),
|
|
||||||
);
|
|
||||||
expect(newBalances[takerAddress][feeToken.address]).to.be.bignumber.equal(
|
|
||||||
erc20Balances[takerAddress][feeToken.address].minus(takerFee),
|
|
||||||
);
|
|
||||||
expect(newBalances[feeRecipientAddress][feeToken.address]).to.be.bignumber.equal(
|
|
||||||
erc20Balances[feeRecipientAddress][feeToken.address].plus(makerFee.plus(takerFee)),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not change erc20Balances if maker erc20Balances are too low to fill order', async () => {
|
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18),
|
|
||||||
});
|
|
||||||
|
|
||||||
const fillResults = await exchange.fillOrderNoThrow.callAsync(
|
|
||||||
signedOrder,
|
|
||||||
signedOrder.takerAssetAmount,
|
|
||||||
signedOrder.signature,
|
|
||||||
{ from: takerAddress },
|
|
||||||
);
|
|
||||||
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
|
|
||||||
expect(fillResults).to.deep.equal(nullFillResults);
|
|
||||||
expect(newBalances).to.be.deep.equal(erc20Balances);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not change erc20Balances if taker erc20Balances are too low to fill order', async () => {
|
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
|
||||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100000), 18),
|
|
||||||
});
|
|
||||||
|
|
||||||
const fillResults = await exchange.fillOrderNoThrow.callAsync(
|
|
||||||
signedOrder,
|
|
||||||
signedOrder.takerAssetAmount,
|
|
||||||
signedOrder.signature,
|
|
||||||
{ from: takerAddress },
|
|
||||||
);
|
|
||||||
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
|
|
||||||
expect(fillResults).to.deep.equal(nullFillResults);
|
|
||||||
expect(newBalances).to.be.deep.equal(erc20Balances);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not change erc20Balances if maker allowances are too low to fill order', async () => {
|
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync();
|
|
||||||
|
|
||||||
await erc20TokenA.approve.awaitTransactionSuccessAsync(erc20Proxy.address, new BigNumber(0), {
|
|
||||||
from: makerAddress,
|
|
||||||
});
|
|
||||||
const fillResults = await exchange.fillOrderNoThrow.callAsync(
|
|
||||||
signedOrder,
|
|
||||||
signedOrder.takerAssetAmount,
|
|
||||||
signedOrder.signature,
|
|
||||||
{ from: takerAddress },
|
|
||||||
);
|
|
||||||
await erc20TokenA.approve.awaitTransactionSuccessAsync(
|
|
||||||
erc20Proxy.address,
|
|
||||||
constants.INITIAL_ERC20_ALLOWANCE,
|
|
||||||
{ from: makerAddress },
|
|
||||||
);
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
|
|
||||||
expect(fillResults).to.deep.equal(nullFillResults);
|
|
||||||
expect(newBalances).to.be.deep.equal(erc20Balances);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not change erc20Balances if taker allowances are too low to fill order', async () => {
|
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync();
|
|
||||||
|
|
||||||
await erc20TokenB.approve.awaitTransactionSuccessAsync(erc20Proxy.address, new BigNumber(0), {
|
|
||||||
from: takerAddress,
|
|
||||||
});
|
|
||||||
const fillResults = await exchange.fillOrderNoThrow.callAsync(
|
|
||||||
signedOrder,
|
|
||||||
signedOrder.takerAssetAmount,
|
|
||||||
signedOrder.signature,
|
|
||||||
{ from: takerAddress },
|
|
||||||
);
|
|
||||||
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
|
||||||
await erc20TokenB.approve.sendTransactionAsync(erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, {
|
|
||||||
from: takerAddress,
|
|
||||||
});
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
|
|
||||||
expect(fillResults).to.deep.equal(nullFillResults);
|
|
||||||
expect(newBalances).to.be.deep.equal(erc20Balances);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not change erc20Balances if makerAssetAddress is ZRX, makerAssetAmount + makerFee > maker balance', async () => {
|
|
||||||
const makerZRXBalance = new BigNumber(erc20Balances[makerAddress][feeToken.address]);
|
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: makerZRXBalance,
|
|
||||||
makerFee: new BigNumber(1),
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(feeToken.address),
|
|
||||||
});
|
|
||||||
|
|
||||||
const fillResults = await exchange.fillOrderNoThrow.callAsync(
|
|
||||||
signedOrder,
|
|
||||||
signedOrder.takerAssetAmount,
|
|
||||||
signedOrder.signature,
|
|
||||||
{ from: takerAddress },
|
|
||||||
);
|
|
||||||
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
|
|
||||||
expect(fillResults).to.deep.equal(nullFillResults);
|
|
||||||
expect(newBalances).to.be.deep.equal(erc20Balances);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not change erc20Balances if makerAssetAddress is ZRX, makerAssetAmount + makerFee > maker allowance', async () => {
|
|
||||||
const makerZRXAllowance = await feeToken.allowance.callAsync(makerAddress, erc20Proxy.address);
|
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: new BigNumber(makerZRXAllowance),
|
|
||||||
makerFee: new BigNumber(1),
|
|
||||||
makerAssetData: assetDataUtils.encodeERC20AssetData(feeToken.address),
|
|
||||||
});
|
|
||||||
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
expect(newBalances).to.be.deep.equal(erc20Balances);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not change erc20Balances if takerAssetAddress is ZRX, takerAssetAmount + takerFee > taker balance', async () => {
|
|
||||||
const takerZRXBalance = new BigNumber(erc20Balances[takerAddress][feeToken.address]);
|
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
|
||||||
takerAssetAmount: takerZRXBalance,
|
|
||||||
takerFee: new BigNumber(1),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(feeToken.address),
|
|
||||||
});
|
|
||||||
|
|
||||||
const fillResults = await exchange.fillOrderNoThrow.callAsync(
|
|
||||||
signedOrder,
|
|
||||||
signedOrder.takerAssetAmount,
|
|
||||||
signedOrder.signature,
|
|
||||||
{ from: takerAddress },
|
|
||||||
);
|
|
||||||
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
|
|
||||||
expect(fillResults).to.deep.equal(nullFillResults);
|
|
||||||
expect(newBalances).to.be.deep.equal(erc20Balances);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not change erc20Balances if takerAssetAddress is ZRX, takerAssetAmount + takerFee > taker allowance', async () => {
|
|
||||||
const takerZRXAllowance = await feeToken.allowance.callAsync(takerAddress, erc20Proxy.address);
|
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
|
||||||
takerAssetAmount: new BigNumber(takerZRXAllowance),
|
|
||||||
takerFee: new BigNumber(1),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(feeToken.address),
|
|
||||||
});
|
|
||||||
|
|
||||||
const fillResults = await exchange.fillOrderNoThrow.callAsync(
|
|
||||||
signedOrder,
|
|
||||||
signedOrder.takerAssetAmount,
|
|
||||||
signedOrder.signature,
|
|
||||||
{ from: takerAddress },
|
|
||||||
);
|
|
||||||
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
|
||||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
|
|
||||||
expect(fillResults).to.deep.equal(nullFillResults);
|
|
||||||
expect(newBalances).to.be.deep.equal(erc20Balances);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should successfully exchange ERC721 tokens', async () => {
|
|
||||||
// Construct Exchange parameters
|
|
||||||
const makerAssetId = erc721MakerAssetId;
|
|
||||||
const takerAssetId = erc721TakerAssetId;
|
|
||||||
const signedOrder = await orderFactory.newSignedOrderAsync({
|
|
||||||
makerAssetAmount: new BigNumber(1),
|
|
||||||
takerAssetAmount: new BigNumber(1),
|
|
||||||
makerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, makerAssetId),
|
|
||||||
takerAssetData: assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId),
|
|
||||||
});
|
|
||||||
// Verify pre-conditions
|
|
||||||
const initialOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
|
|
||||||
expect(initialOwnerMakerAsset).to.be.bignumber.equal(makerAddress);
|
|
||||||
const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId);
|
|
||||||
expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress);
|
|
||||||
// Call Exchange
|
|
||||||
const takerAssetFillAmount = signedOrder.takerAssetAmount;
|
|
||||||
|
|
||||||
const fillResults = await exchange.fillOrderNoThrow.callAsync(
|
|
||||||
signedOrder,
|
|
||||||
takerAssetFillAmount,
|
|
||||||
signedOrder.signature,
|
|
||||||
{ from: takerAddress },
|
|
||||||
);
|
|
||||||
await exchangeWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress, {
|
|
||||||
takerAssetFillAmount,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Verify post-conditions
|
|
||||||
expect(fillResults.makerAssetFilledAmount).to.bignumber.equal(signedOrder.makerAssetAmount);
|
|
||||||
expect(fillResults.takerAssetFilledAmount).to.bignumber.equal(signedOrder.takerAssetAmount);
|
|
||||||
expect(fillResults.makerFeePaid).to.bignumber.equal(signedOrder.makerFee);
|
|
||||||
expect(fillResults.takerFeePaid).to.bignumber.equal(signedOrder.takerFee);
|
|
||||||
|
|
||||||
const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(makerAssetId);
|
|
||||||
expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress);
|
|
||||||
const newOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId);
|
|
||||||
expect(newOwnerTakerAsset).to.be.bignumber.equal(makerAddress);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('batch functions', () => {
|
describe('batch functions', () => {
|
||||||
let signedOrders: SignedOrder[];
|
let signedOrders: SignedOrder[];
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -481,74 +227,6 @@ 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, gasPrice: DEFAULT_GAS_PRICE },
|
|
||||||
);
|
|
||||||
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;
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
import { ReferenceFunctions as UtilReferenceFunctions } from '@0x/contracts-utils';
|
import { ReferenceFunctions as UtilReferenceFunctions } from '@0x/contracts-utils';
|
||||||
import { ExchangeRevertErrors, orderHashUtils } from '@0x/order-utils';
|
import { ExchangeRevertErrors, orderHashUtils } from '@0x/order-utils';
|
||||||
import { FillResults, OrderInfo, OrderStatus, OrderWithoutDomain as Order } from '@0x/types';
|
import { FillResults, OrderWithoutDomain as Order } from '@0x/types';
|
||||||
import { AnyRevertError, BigNumber, SafeMathRevertErrors, StringRevertError } from '@0x/utils';
|
import { AnyRevertError, BigNumber, SafeMathRevertErrors, StringRevertError } from '@0x/utils';
|
||||||
import { LogEntry, LogWithDecodedArgs } from 'ethereum-types';
|
import { LogEntry, LogWithDecodedArgs } from 'ethereum-types';
|
||||||
import * as ethjs from 'ethereumjs-util';
|
import * as ethjs from 'ethereumjs-util';
|
||||||
@ -251,92 +251,6 @@ 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