@0x/contracts-exchange: Major rework of fill_order_combinatorial_utils.

`@0x/contracts-exchange`: Add more arbitrary fee token tests.
`@0x/contracts-exchange`: Fix broken tests.
This commit is contained in:
Lawrence Forman
2019-05-18 02:47:31 -04:00
committed by Amir Bandeali
parent 76d577a08d
commit aebb923c2d
7 changed files with 1201 additions and 656 deletions

View File

@@ -362,6 +362,17 @@ describe('Exchange core', () => {
(transferLogs[1] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>).args._value,
).to.be.bignumber.equal(signedOrder.takerFee);
});
it('should throw if order is expired', async () => {
const currentTimestamp = await getLatestBlockTimestampAsync();
signedOrder = await orderFactory.newSignedOrderAsync({
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
});
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHash, OrderStatus.Expired);
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
return expect(tx).to.revertWith(expectedError);
});
});
describe('Testing exchange of ERC20 tokens with no return values', () => {

View File

@@ -8,7 +8,6 @@ import {
FeeRecipientAddressScenario,
FillScenario,
OrderAssetAmountScenario,
provider,
TakerAssetFillAmountScenario,
TakerScenario,
txDefaults,
@@ -39,15 +38,15 @@ const defaultFillScenario = {
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount,
makerStateScenario: {
traderAssetBalance: BalanceAmountScenario.Higher,
traderAssetAllowance: AllowanceAmountScenario.Higher,
traderAssetAllowance: AllowanceAmountScenario.Unlimited,
feeBalance: BalanceAmountScenario.Higher,
feeAllowance: AllowanceAmountScenario.Higher,
feeAllowance: AllowanceAmountScenario.Unlimited,
},
takerStateScenario: {
traderAssetBalance: BalanceAmountScenario.Higher,
traderAssetAllowance: AllowanceAmountScenario.Higher,
traderAssetAllowance: AllowanceAmountScenario.Unlimited,
feeBalance: BalanceAmountScenario.Higher,
feeAllowance: AllowanceAmountScenario.Higher,
feeAllowance: AllowanceAmountScenario.Unlimited,
},
};
@@ -72,7 +71,7 @@ describe('FillOrder Tests', () => {
_.forEach(fillScenarios, fillScenario => {
const description = `Combinatorial OrderFill: ${JSON.stringify(fillScenario)}`;
it(description, async () => {
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(fillScenario);
});
});
};
@@ -84,8 +83,9 @@ describe('FillOrder Tests', () => {
const fillScenario = {
...defaultFillScenario,
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should transfer the correct amounts when makerAssetAmount > takerAssetAmount', async () => {
const fillScenario = {
...defaultFillScenario,
@@ -94,8 +94,9 @@ describe('FillOrder Tests', () => {
takerAssetAmountScenario: OrderAssetAmountScenario.Small,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should transfer the correct amounts when makerAssetAmount < takerAssetAmount', async () => {
const fillScenario = {
...defaultFillScenario,
@@ -104,8 +105,9 @@ describe('FillOrder Tests', () => {
makerAssetAmountScenario: OrderAssetAmountScenario.Small,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should transfer the correct amounts when makerAssetAmount < takerAssetAmount with zero decimals', async () => {
const fillScenario = {
...defaultFillScenario,
@@ -115,8 +117,9 @@ describe('FillOrder Tests', () => {
makerAssetDataScenario: AssetDataScenario.ERC20ZeroDecimals,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should transfer the correct amounts when taker is specified and order is claimed by taker', async () => {
const fillScenario = {
...defaultFillScenario,
@@ -125,15 +128,49 @@ describe('FillOrder Tests', () => {
takerScenario: TakerScenario.CorrectlySpecified,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should fill remaining value if takerAssetFillAmount > remaining takerAssetAmount', async () => {
const fillScenario = {
...defaultFillScenario,
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount,
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should be able to pay maker fee with taker asset', async () => {
const fillScenario = {
...defaultFillScenario,
orderScenario: {
...defaultFillScenario.orderScenario,
makerFeeAssetDataScenario: FeeAssetDataScenario.TakerToken,
},
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
makerStateScenario: {
...defaultFillScenario.makerStateScenario,
feeBalance: BalanceAmountScenario.Zero,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should be able to pay taker fee with maker asset', async () => {
const fillScenario = {
...defaultFillScenario,
orderScenario: {
...defaultFillScenario.orderScenario,
takerFeeAssetDataScenario: FeeAssetDataScenario.MakerToken,
},
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
takerStateScenario: {
...defaultFillScenario.takerStateScenario,
feeBalance: BalanceAmountScenario.Zero,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should throw when taker is specified and order is claimed by other', async () => {
const fillScenario = {
...defaultFillScenario,
@@ -142,7 +179,7 @@ describe('FillOrder Tests', () => {
takerScenario: TakerScenario.IncorrectlySpecified,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if makerAssetAmount is 0', async () => {
@@ -154,7 +191,7 @@ describe('FillOrder Tests', () => {
},
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount,
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if takerAssetAmount is 0', async () => {
@@ -166,7 +203,7 @@ describe('FillOrder Tests', () => {
},
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.GreaterThanRemainingFillableTakerAssetAmount,
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if takerAssetFillAmount is 0', async () => {
@@ -174,7 +211,7 @@ describe('FillOrder Tests', () => {
...defaultFillScenario,
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.Zero,
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if an order is expired', async () => {
@@ -185,7 +222,7 @@ describe('FillOrder Tests', () => {
expirationTimeSecondsScenario: ExpirationTimeSecondsScenario.InPast,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if maker erc20Balances are too low to fill order', async () => {
@@ -196,7 +233,7 @@ describe('FillOrder Tests', () => {
traderAssetBalance: BalanceAmountScenario.TooLow,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if taker erc20Balances are too low to fill order', async () => {
@@ -207,7 +244,7 @@ describe('FillOrder Tests', () => {
traderAssetBalance: BalanceAmountScenario.TooLow,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if maker allowances are too low to fill order', async () => {
@@ -218,7 +255,7 @@ describe('FillOrder Tests', () => {
traderAssetAllowance: AllowanceAmountScenario.TooLow,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if taker allowances are too low to fill order', async () => {
@@ -229,7 +266,51 @@ describe('FillOrder Tests', () => {
traderAssetAllowance: AllowanceAmountScenario.TooLow,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if maker fee erc20Balances are too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
makerStateScenario: {
...defaultFillScenario.makerStateScenario,
feeBalance: BalanceAmountScenario.TooLow,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if taker fee erc20Balances are too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
takerStateScenario: {
...defaultFillScenario.makerStateScenario,
feeBalance: BalanceAmountScenario.TooLow,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if maker fee allowances are too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
makerStateScenario: {
...defaultFillScenario.makerStateScenario,
feeAllowance: AllowanceAmountScenario.TooLow,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
it('should throw if taker fee allowances are too low to fill order', async () => {
const fillScenario = {
...defaultFillScenario,
takerStateScenario: {
...defaultFillScenario.makerStateScenario,
feeAllowance: AllowanceAmountScenario.TooLow,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioFailureAsync(fillScenario);
});
});
@@ -244,7 +325,7 @@ describe('FillOrder Tests', () => {
},
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount,
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should successfully fill order when makerAsset is ERC721 and takerAsset is ERC20', async () => {
@@ -257,7 +338,7 @@ describe('FillOrder Tests', () => {
},
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount,
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario, true);
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should successfully fill order when makerAsset is ERC20 and takerAsset is ERC721', async () => {
@@ -270,7 +351,7 @@ describe('FillOrder Tests', () => {
},
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyRemainingFillableTakerAssetAmount,
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should successfully fill order when makerAsset is ERC721 and approveAll is set for it', async () => {
@@ -287,7 +368,7 @@ describe('FillOrder Tests', () => {
traderAssetAllowance: AllowanceAmountScenario.Unlimited,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should successfully fill order when makerAsset and takerAsset are ERC721 and approveAll is set for them', async () => {
@@ -308,7 +389,43 @@ describe('FillOrder Tests', () => {
traderAssetAllowance: AllowanceAmountScenario.Unlimited,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioAsync(provider, fillScenario);
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should be able to pay maker fee with taker asset', async () => {
const fillScenario = {
...defaultFillScenario,
orderScenario: {
...defaultFillScenario.orderScenario,
takerAssetDataScenario: AssetDataScenario.ERC721,
makerFeeAssetDataScenario: FeeAssetDataScenario.TakerToken,
},
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
makerStateScenario: {
...defaultFillScenario.makerStateScenario,
feeBalance: BalanceAmountScenario.Zero,
feeAllowance: AllowanceAmountScenario.Unlimited,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
it('should be able to pay taker fee with maker asset', async () => {
const fillScenario = {
...defaultFillScenario,
orderScenario: {
...defaultFillScenario.orderScenario,
makerAssetDataScenario: AssetDataScenario.ERC721,
takerFeeAssetDataScenario: FeeAssetDataScenario.MakerToken,
},
takerAssetFillAmountScenario: TakerAssetFillAmountScenario.ExactlyTakerAssetAmount,
takerStateScenario: {
...defaultFillScenario.takerStateScenario,
feeBalance: BalanceAmountScenario.Zero,
feeAllowance: AllowanceAmountScenario.Unlimited,
},
};
await fillOrderCombinatorialUtils.testFillOrderScenarioSuccessAsync(fillScenario);
});
});
});

View File

@@ -3,7 +3,6 @@ import {
chaiSetup,
constants,
FillResults,
getRevertReasonOrErrorMessageForSendTransactionAsync,
provider,
testCombinatoriallyWithReferenceFuncAsync,
txDefaults,
@@ -12,7 +11,7 @@ import {
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { Order, RevertReason, SignedOrder } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import { BigNumber, providerUtils, StringRevertError } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
@@ -77,9 +76,7 @@ describe('Exchange core internal functions', () => {
txDefaults,
new BigNumber(chainId),
);
overflowErrorForSendTransaction = new Error(
await getRevertReasonOrErrorMessageForSendTransactionAsync(RevertReason.Uint256Overflow),
);
overflowErrorForSendTransaction = new Error(RevertReason.Uint256Overflow);
divisionByZeroErrorForCall = new Error(RevertReason.DivisionByZero);
roundingErrorForCall = new Error(RevertReason.RoundingError);
});
@@ -160,6 +157,21 @@ describe('Exchange core internal functions', () => {
return product.dividedToIntegerBy(denominator);
}
function wrapCallTestFunction<T>(
callAsync: (...args: any[]) => Promise<T>,
): (...args: any[]) => Promise<T> {
return async (...args: any[]): Promise<T> => {
try {
return await callAsync(...args);
} catch (err) {
if (err instanceof StringRevertError) {
throw new Error(err.values.message as string);
}
throw err;
}
};
}
describe('addFillResults', async () => {
function makeFillResults(value: BigNumber): FillResults {
return {
@@ -203,7 +215,7 @@ describe('Exchange core internal functions', () => {
await testCombinatoriallyWithReferenceFuncAsync(
'addFillResults',
referenceAddFillResultsAsync,
testAddFillResultsAsync,
wrapCallTestFunction(testAddFillResultsAsync),
[uint256Values, uint256Values],
);
});
@@ -267,7 +279,7 @@ describe('Exchange core internal functions', () => {
await testCombinatoriallyWithReferenceFuncAsync(
'calculateFillResults',
referenceCalculateFillResultsAsync,
testCalculateFillResultsAsync,
wrapCallTestFunction(testCalculateFillResultsAsync),
[uint256Values, uint256Values, uint256Values],
);
});
@@ -297,7 +309,7 @@ describe('Exchange core internal functions', () => {
await testCombinatoriallyWithReferenceFuncAsync(
'getPartialAmountFloor',
referenceGetPartialAmountFloorAsync,
testGetPartialAmountFloorAsync,
wrapCallTestFunction(testGetPartialAmountFloorAsync),
[uint256Values, uint256Values, uint256Values],
);
});
@@ -334,7 +346,7 @@ describe('Exchange core internal functions', () => {
await testCombinatoriallyWithReferenceFuncAsync(
'getPartialAmountCeil',
referenceGetPartialAmountCeilAsync,
testGetPartialAmountCeilAsync,
wrapCallTestFunction(testGetPartialAmountCeilAsync),
[uint256Values, uint256Values, uint256Values],
);
});
@@ -350,7 +362,7 @@ describe('Exchange core internal functions', () => {
await testCombinatoriallyWithReferenceFuncAsync(
'safeGetPartialAmountFloor',
referenceSafeGetPartialAmountFloorAsync,
testSafeGetPartialAmountFloorAsync,
wrapCallTestFunction(testSafeGetPartialAmountFloorAsync),
[uint256Values, uint256Values, uint256Values],
);
});
@@ -391,7 +403,7 @@ describe('Exchange core internal functions', () => {
await testCombinatoriallyWithReferenceFuncAsync(
'safeGetPartialAmountCeil',
referenceSafeGetPartialAmountCeilAsync,
testSafeGetPartialAmountCeilAsync,
wrapCallTestFunction(testSafeGetPartialAmountCeilAsync),
[uint256Values, uint256Values, uint256Values],
);
});
@@ -407,7 +419,7 @@ describe('Exchange core internal functions', () => {
await testCombinatoriallyWithReferenceFuncAsync(
'isRoundingErrorFloor',
referenceIsRoundingErrorFloorAsync,
testIsRoundingErrorFloorAsync,
wrapCallTestFunction(testIsRoundingErrorFloorAsync),
[uint256Values, uint256Values, uint256Values],
);
});
@@ -423,7 +435,7 @@ describe('Exchange core internal functions', () => {
await testCombinatoriallyWithReferenceFuncAsync(
'isRoundingErrorCeil',
referenceIsRoundingErrorCeilAsync,
testIsRoundingErrorCeilAsync,
wrapCallTestFunction(testIsRoundingErrorCeilAsync),
[uint256Values, uint256Values, uint256Values],
);
});
@@ -475,7 +487,7 @@ describe('Exchange core internal functions', () => {
await testCombinatoriallyWithReferenceFuncAsync(
'updateFilledState',
referenceUpdateFilledStateAsync,
testUpdateFilledStateAsync,
wrapCallTestFunction(testUpdateFilledStateAsync),
[uint256Values, uint256Values, bytes32Values],
);
});

File diff suppressed because it is too large Load Diff

View File

@@ -184,18 +184,10 @@ export class AssetWrapper {
);
if (!isProxyApprovedForAll && desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
const isApproved = true;
await erc721Wrapper.approveProxyForAllAsync(
assetProxyData.tokenAddress,
assetProxyData.tokenId,
isApproved,
);
await erc721Wrapper.approveProxyForAllAsync(assetProxyData.tokenAddress, userAddress, isApproved);
} else if (isProxyApprovedForAll && desiredAllowance.eq(0)) {
const isApproved = false;
await erc721Wrapper.approveProxyForAllAsync(
assetProxyData.tokenAddress,
assetProxyData.tokenId,
isApproved,
);
await erc721Wrapper.approveProxyForAllAsync(assetProxyData.tokenAddress, userAddress, isApproved);
} else if (isProxyApprovedForAll && desiredAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
return; // Noop
}

View File

@@ -0,0 +1,163 @@
import { constants, FillResults, orderUtils } from '@0x/contracts-test-utils';
import {
AbstractBalanceAndProxyAllowanceLazyStore as LazyStore,
ExchangeTransferSimulator,
Order,
TradeSide,
TransferType,
} from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
export enum FillOrderError {
OrderUnfillable = 'ORDER_UNFILLABLE',
InvalidSender = 'INVALID_SENDER',
InvalidTakerAmount = 'INVALID_TAKER_AMOUNT',
InvalidMakerAmount = 'INVALID_MAKER_AMOUNT',
InvalidTaker = 'INVALID_TAKER',
InvalidFillPrice = 'INVALID_FILL_PRICE',
TransferFailed = 'TRANSFER_FAILED',
}
/**
* Simplified fill order simulator.
*/
export class FillOrderSimulator {
public readonly lazyStore: LazyStore;
private readonly _transferSimulator: ExchangeTransferSimulator;
constructor(lazyStore: LazyStore) {
this.lazyStore = lazyStore;
this._transferSimulator = new ExchangeTransferSimulator(lazyStore);
}
public async simulateFillOrderAsync(
order: Order,
takerAddress: string,
takerAssetFillAmount: BigNumber,
takerAssetFilledAmount: BigNumber = constants.ZERO_AMOUNT,
): Promise<FillResults> {
const remainingTakerAssetAmount = order.takerAssetAmount.minus(takerAssetFilledAmount);
const makerAssetFilledAmount = orderUtils.getPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount,
);
validateFill(
order,
takerAddress,
takerAssetFillAmount,
makerAssetFilledAmount,
takerAssetFilledAmount,
remainingTakerAssetAmount,
);
const finalTakerAssetFillAmount = BigNumber.min(takerAssetFillAmount, remainingTakerAssetAmount);
const makerAssetFillAmount = orderUtils.getPartialAmountFloor(
finalTakerAssetFillAmount,
order.takerAssetAmount,
order.makerAssetAmount,
);
const makerFeePaid = orderUtils.getPartialAmountFloor(
makerAssetFillAmount,
order.makerAssetAmount,
order.makerFee,
);
const takerFeePaid = orderUtils.getPartialAmountFloor(
finalTakerAssetFillAmount,
order.takerAssetAmount,
order.takerFee,
);
try {
// Maker -> Taker
await this._transferSimulator.transferFromAsync(
order.makerAssetData,
order.makerAddress,
takerAddress,
makerAssetFillAmount,
TradeSide.Maker,
TransferType.Trade,
);
// Taker -> Maker
await this._transferSimulator.transferFromAsync(
order.takerAssetData,
takerAddress,
order.makerAddress,
finalTakerAssetFillAmount,
TradeSide.Taker,
TransferType.Trade,
);
// Maker fee -> fee recipient
if (makerFeePaid.isGreaterThan(0)) {
await this._transferSimulator.transferFromAsync(
order.makerFeeAssetData,
order.makerAddress,
order.feeRecipientAddress,
makerFeePaid,
TradeSide.Maker,
TransferType.Fee,
);
}
// Taker fee -> fee recipient
if (takerFeePaid.isGreaterThan(0)) {
await this._transferSimulator.transferFromAsync(
order.takerFeeAssetData,
takerAddress,
order.feeRecipientAddress,
takerFeePaid,
TradeSide.Taker,
TransferType.Fee,
);
}
} catch (err) {
throw new Error(FillOrderError.TransferFailed);
}
return {
takerAssetFilledAmount: finalTakerAssetFillAmount,
makerAssetFilledAmount: makerAssetFillAmount,
makerFeePaid,
takerFeePaid,
};
}
}
function validateFill(
order: Order,
takerAddress: string,
takerAssetFillAmount: BigNumber,
makerAssetFilledAmount: BigNumber,
takerAssetFilledAmount: BigNumber,
remainingTakerAssetAmount: BigNumber,
): void {
const now = Math.floor(_.now() / 1000);
if (remainingTakerAssetAmount.isEqualTo(0) || order.expirationTimeSeconds.isLessThanOrEqualTo(now)) {
throw new Error(FillOrderError.OrderUnfillable);
}
if (order.senderAddress !== constants.NULL_ADDRESS && order.senderAddress !== takerAddress) {
throw new Error(FillOrderError.InvalidSender);
}
if (order.takerAddress !== constants.NULL_ADDRESS && order.takerAddress !== takerAddress) {
throw new Error(FillOrderError.InvalidTaker);
}
if (order.makerAssetAmount.isEqualTo(0)) {
throw new Error(FillOrderError.InvalidMakerAmount);
}
if (takerAssetFillAmount.isEqualTo(0)) {
throw new Error(FillOrderError.InvalidTakerAmount);
}
if (
makerAssetFilledAmount
.times(order.takerAssetAmount)
.isGreaterThan(takerAssetFilledAmount.times(order.makerAssetAmount))
) {
throw new Error(FillOrderError.InvalidFillPrice);
}
}