aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts/src')
-rw-r--r--packages/contracts/src/utils/core_combinatorial_utils.ts343
-rw-r--r--packages/contracts/src/utils/types.ts15
2 files changed, 323 insertions, 35 deletions
diff --git a/packages/contracts/src/utils/core_combinatorial_utils.ts b/packages/contracts/src/utils/core_combinatorial_utils.ts
index 776d7b2fe..1735b3191 100644
--- a/packages/contracts/src/utils/core_combinatorial_utils.ts
+++ b/packages/contracts/src/utils/core_combinatorial_utils.ts
@@ -38,13 +38,13 @@ import {
OrderScenario,
TakerAssetFillAmountScenario,
TakerScenario,
+ TokenAmountScenario,
+ TraderStateScenario,
} from '../utils/types';
chaiSetup.configure();
const expect = chai.expect;
-const ERC721_PROXY_ID = 2;
-
/**
* Instantiates a new instance of CoreCombinatorialUtils. Since this method has some
* required async setup, a factory method is required.
@@ -192,6 +192,18 @@ export class CoreCombinatorialUtils {
takerAssetDataScenario: fillScenarioArray[8] as AssetDataScenario,
},
takerAssetFillAmountScenario: fillScenarioArray[9] as TakerAssetFillAmountScenario,
+ makerStateScenario: {
+ traderAssetBalance: TokenAmountScenario.Higher,
+ traderAssetAllowance: TokenAmountScenario.Higher,
+ zrxFeeBalance: TokenAmountScenario.Higher,
+ zrxFeeAllowance: TokenAmountScenario.Higher,
+ },
+ takerStateScenario: {
+ traderAssetBalance: TokenAmountScenario.Higher,
+ traderAssetAllowance: TokenAmountScenario.Higher,
+ zrxFeeBalance: TokenAmountScenario.Higher,
+ zrxFeeAllowance: TokenAmountScenario.Higher,
+ },
};
return fillScenario;
});
@@ -248,24 +260,305 @@ export class CoreCombinatorialUtils {
signature: `0x${signature.toString('hex')}`,
};
- // 3. Permutate the maker and taker balance/allowance scenarios
- // TODO(fabio)
-
- // 4. Figure out fill amount
const balanceAndProxyAllowanceFetcher = new SimpleAssetBalanceAndProxyAllowanceFetcher(this.assetWrapper);
const orderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher(
this.exchangeWrapper,
this.zrxAssetData,
);
- const orderStateUtils = new OrderStateUtils(balanceAndProxyAllowanceFetcher, orderFilledCancelledFetcher);
+ // 3. Figure out fill amount
+ const takerAssetFillAmount = await this._getTakerAssetFillAmountAsync(
+ signedOrder,
+ fillScenario.takerAssetFillAmountScenario,
+ balanceAndProxyAllowanceFetcher,
+ orderFilledCancelledFetcher,
+ );
+
+ // 4. Permutate the maker and taker balance/allowance scenarios
+ await this._modifyTraderStateAsync(
+ fillScenario.makerStateScenario,
+ fillScenario.takerStateScenario,
+ signedOrder,
+ takerAssetFillAmount,
+ balanceAndProxyAllowanceFetcher,
+ );
+
+ // 5. If I fill it by X, what are the resulting balances/allowances/filled amounts exp?
+ const orderValidationUtils = new OrderValidationUtils(orderFilledCancelledFetcher);
+ const lazyStore = new BalanceAndProxyAllowanceLazyStore(balanceAndProxyAllowanceFetcher);
+ const exchangeTransferSimulator = new ExchangeTransferSimulator(lazyStore);
+
+ let isFillFailureExpected = false;
+ try {
+ await orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
+ exchangeTransferSimulator,
+ provider,
+ signedOrder,
+ takerAssetFillAmount,
+ this.takerAddress,
+ this.zrxAssetData,
+ );
+ } catch (err) {
+ isFillFailureExpected = true;
+ }
+
+ await this._fillOrderAndAssertOutcomeAsync(
+ signedOrder,
+ takerAssetFillAmount,
+ lazyStore,
+ isFillFailureExpected,
+ provider,
+ );
+ }
+ private async _modifyTraderStateAsync(
+ makerStateScenario: TraderStateScenario,
+ takerStateScenario: TraderStateScenario,
+ signedOrder: SignedOrder,
+ takerAssetFillAmount: BigNumber,
+ balanceAndProxyAllowanceFetcher: SimpleAssetBalanceAndProxyAllowanceFetcher,
+ ): Promise<void> {
+ const makerAssetFillAmount = orderUtils.getPartialAmount(
+ takerAssetFillAmount,
+ signedOrder.takerAssetAmount,
+ signedOrder.makerAssetAmount,
+ );
+ switch (makerStateScenario.traderAssetBalance) {
+ case TokenAmountScenario.Higher:
+ break; // Noop since this is already the default
+
+ case TokenAmountScenario.TooLow:
+ if (makerAssetFillAmount.eq(0)) {
+ throw new Error(`Cannot set makerAssetBalanceOfMaker TooLow if makerAssetFillAmount is 0`);
+ }
+ const tooLowBalance = makerAssetFillAmount.minus(1);
+ await this.assetWrapper.setBalanceAsync(
+ signedOrder.makerAddress,
+ signedOrder.makerAssetData,
+ tooLowBalance,
+ );
+ break;
+
+ case TokenAmountScenario.Exact:
+ const exactBalance = makerAssetFillAmount;
+ await this.assetWrapper.setBalanceAsync(
+ signedOrder.makerAddress,
+ signedOrder.makerAssetData,
+ exactBalance,
+ );
+ break;
+
+ default:
+ throw errorUtils.spawnSwitchErr(
+ 'makerStateScenario.traderAssetBalance',
+ makerStateScenario.traderAssetBalance,
+ );
+ }
+
+ const makerFee = orderUtils.getPartialAmount(
+ takerAssetFillAmount,
+ signedOrder.takerAssetAmount,
+ signedOrder.makerFee,
+ );
+ switch (makerStateScenario.zrxFeeBalance) {
+ case TokenAmountScenario.Higher:
+ break; // Noop since this is already the default
+
+ case TokenAmountScenario.TooLow:
+ if (makerFee.eq(0)) {
+ throw new Error(`Cannot set zrxAsserBalanceOfMaker TooLow if makerFee is 0`);
+ }
+ const tooLowBalance = makerFee.minus(1);
+ await this.assetWrapper.setBalanceAsync(signedOrder.makerAddress, this.zrxAssetData, tooLowBalance);
+ break;
+
+ case TokenAmountScenario.Exact:
+ const exactBalance = makerFee;
+ await this.assetWrapper.setBalanceAsync(signedOrder.makerAddress, this.zrxAssetData, exactBalance);
+ break;
+
+ default:
+ throw errorUtils.spawnSwitchErr('makerStateScenario.zrxFeeBalance', makerStateScenario.zrxFeeBalance);
+ }
+
+ switch (makerStateScenario.traderAssetAllowance) {
+ case TokenAmountScenario.Higher:
+ break; // Noop since this is already the default
+
+ case TokenAmountScenario.TooLow:
+ const tooLowAllowance = makerAssetFillAmount.minus(1);
+ await this.assetWrapper.setProxyAllowanceAsync(
+ signedOrder.makerAddress,
+ signedOrder.makerAssetData,
+ tooLowAllowance,
+ );
+ break;
+
+ case TokenAmountScenario.Exact:
+ const exactAllowance = makerAssetFillAmount;
+ await this.assetWrapper.setProxyAllowanceAsync(
+ signedOrder.makerAddress,
+ signedOrder.makerAssetData,
+ exactAllowance,
+ );
+ break;
+
+ default:
+ throw errorUtils.spawnSwitchErr(
+ 'makerStateScenario.traderAssetAllowance',
+ makerStateScenario.traderAssetAllowance,
+ );
+ }
+
+ switch (makerStateScenario.zrxFeeAllowance) {
+ case TokenAmountScenario.Higher:
+ break; // Noop since this is already the default
+
+ case TokenAmountScenario.TooLow:
+ const tooLowAllowance = makerFee.minus(1);
+ await this.assetWrapper.setProxyAllowanceAsync(
+ signedOrder.makerAddress,
+ this.zrxAssetData,
+ tooLowAllowance,
+ );
+ break;
+
+ case TokenAmountScenario.Exact:
+ const exactAllowance = makerFee;
+ await this.assetWrapper.setProxyAllowanceAsync(
+ signedOrder.makerAddress,
+ this.zrxAssetData,
+ exactAllowance,
+ );
+ break;
+
+ default:
+ throw errorUtils.spawnSwitchErr(
+ 'makerStateScenario.zrxFeeAllowance',
+ makerStateScenario.zrxFeeAllowance,
+ );
+ }
+
+ switch (takerStateScenario.traderAssetBalance) {
+ case TokenAmountScenario.Higher:
+ break; // Noop since this is already the default
+
+ case TokenAmountScenario.TooLow:
+ if (takerAssetFillAmount.eq(0)) {
+ throw new Error(`Cannot set takerAssetBalanceOfTaker TooLow if takerAssetFillAmount is 0`);
+ }
+ const tooLowBalance = takerAssetFillAmount.minus(1);
+ await this.assetWrapper.setBalanceAsync(this.takerAddress, signedOrder.takerAssetData, tooLowBalance);
+ break;
+
+ case TokenAmountScenario.Exact:
+ const exactBalance = takerAssetFillAmount;
+ await this.assetWrapper.setBalanceAsync(this.takerAddress, signedOrder.takerAssetData, exactBalance);
+ break;
+
+ default:
+ throw errorUtils.spawnSwitchErr(
+ 'takerStateScenario.traderAssetBalance',
+ takerStateScenario.traderAssetBalance,
+ );
+ }
+
+ const takerFee = orderUtils.getPartialAmount(
+ takerAssetFillAmount,
+ signedOrder.takerAssetAmount,
+ signedOrder.takerFee,
+ );
+ switch (takerStateScenario.zrxFeeBalance) {
+ case TokenAmountScenario.Higher:
+ break; // Noop since this is already the default
+
+ case TokenAmountScenario.TooLow:
+ if (takerFee.eq(0)) {
+ throw new Error(`Cannot set zrxAssetBalanceOfTaker TooLow if takerFee is 0`);
+ }
+ const tooLowBalance = takerFee.minus(1);
+ await this.assetWrapper.setBalanceAsync(this.takerAddress, this.zrxAssetData, tooLowBalance);
+ break;
+
+ case TokenAmountScenario.Exact:
+ const exactBalance = takerFee;
+ await this.assetWrapper.setBalanceAsync(this.takerAddress, this.zrxAssetData, exactBalance);
+ break;
+
+ default:
+ throw errorUtils.spawnSwitchErr('takerStateScenario.zrxFeeBalance', takerStateScenario.zrxFeeBalance);
+ }
+
+ switch (takerStateScenario.traderAssetAllowance) {
+ case TokenAmountScenario.Higher:
+ break; // Noop since this is already the default
+
+ case TokenAmountScenario.TooLow:
+ const tooLowAllowance = takerAssetFillAmount.minus(1);
+ await this.assetWrapper.setProxyAllowanceAsync(
+ this.takerAddress,
+ signedOrder.takerAssetData,
+ tooLowAllowance,
+ );
+ break;
+
+ case TokenAmountScenario.Exact:
+ const exactAllowance = takerAssetFillAmount;
+ await this.assetWrapper.setProxyAllowanceAsync(
+ this.takerAddress,
+ signedOrder.takerAssetData,
+ exactAllowance,
+ );
+ break;
+
+ default:
+ throw errorUtils.spawnSwitchErr(
+ 'takerStateScenario.traderAssetAllowance',
+ takerStateScenario.traderAssetAllowance,
+ );
+ }
+
+ switch (takerStateScenario.zrxFeeAllowance) {
+ case TokenAmountScenario.Higher:
+ break; // Noop since this is already the default
+
+ case TokenAmountScenario.TooLow:
+ const tooLowAllowance = takerFee.minus(1);
+ await this.assetWrapper.setProxyAllowanceAsync(
+ signedOrder.takerAddress,
+ this.zrxAssetData,
+ tooLowAllowance,
+ );
+ break;
+
+ case TokenAmountScenario.Exact:
+ const exactAllowance = takerFee;
+ await this.assetWrapper.setProxyAllowanceAsync(
+ signedOrder.takerAddress,
+ this.zrxAssetData,
+ exactAllowance,
+ );
+ break;
+
+ default:
+ throw errorUtils.spawnSwitchErr(
+ 'takerStateScenario.zrxFeeAllowance',
+ takerStateScenario.zrxFeeAllowance,
+ );
+ }
+ }
+ private async _getTakerAssetFillAmountAsync(
+ signedOrder: SignedOrder,
+ takerAssetFillAmountScenario: TakerAssetFillAmountScenario,
+ balanceAndProxyAllowanceFetcher: SimpleAssetBalanceAndProxyAllowanceFetcher,
+ orderFilledCancelledFetcher: SimpleOrderFilledCancelledFetcher,
+ ): Promise<BigNumber> {
+ const orderStateUtils = new OrderStateUtils(balanceAndProxyAllowanceFetcher, orderFilledCancelledFetcher);
const fillableTakerAssetAmount = await orderStateUtils.getMaxFillableTakerAssetAmountAsync(
signedOrder,
this.takerAddress,
);
let takerAssetFillAmount;
- const takerAssetFillAmountScenario = fillScenario.takerAssetFillAmountScenario;
switch (takerAssetFillAmountScenario) {
case TakerAssetFillAmountScenario.Zero:
takerAssetFillAmount = new BigNumber(0);
@@ -283,7 +576,7 @@ export class CoreCombinatorialUtils {
const takerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.takerAssetData);
const makerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.makerAssetData);
const isEitherAssetERC721 =
- takerAssetProxyId === ERC721_PROXY_ID || makerAssetProxyId === ERC721_PROXY_ID;
+ takerAssetProxyId === constants.ERC721_PROXY_ID || makerAssetProxyId === constants.ERC721_PROXY_ID;
if (isEitherAssetERC721) {
throw new Error(
'Cannot test `TakerAssetFillAmountScenario.LessThanRemainingFillableTakerAssetAmount` together with ERC721 assets since orders involving ERC721 must always be filled exactly.',
@@ -296,32 +589,7 @@ export class CoreCombinatorialUtils {
throw errorUtils.spawnSwitchErr('TakerAssetFillAmountScenario', takerAssetFillAmountScenario);
}
- // 5. If I fill it by X, what are the resulting balances/allowances/filled amounts exp?
- const orderValidationUtils = new OrderValidationUtils(orderFilledCancelledFetcher);
- const lazyStore = new BalanceAndProxyAllowanceLazyStore(balanceAndProxyAllowanceFetcher);
- const exchangeTransferSimulator = new ExchangeTransferSimulator(lazyStore);
-
- let isFillFailureExpected = false;
- try {
- await orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTransferSimulator,
- provider,
- signedOrder,
- takerAssetFillAmount,
- this.takerAddress,
- this.zrxAssetData,
- );
- } catch (err) {
- isFillFailureExpected = true;
- }
-
- await this._fillOrderAndAssertOutcomeAsync(
- signedOrder,
- takerAssetFillAmount,
- lazyStore,
- isFillFailureExpected,
- provider,
- );
+ return takerAssetFillAmount;
}
private async _fillOrderAndAssertOutcomeAsync(
signedOrder: SignedOrder,
@@ -369,6 +637,11 @@ export class CoreCombinatorialUtils {
signedOrder.makerAssetAmount,
);
+ const beforeMakerAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync(
+ makerAddress,
+ makerAssetData,
+ );
+
// - Let's fill the order!
const txReceipt = await this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, {
takerAssetFillAmount,
diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts
index f0e2dde02..d71c14495 100644
--- a/packages/contracts/src/utils/types.ts
+++ b/packages/contracts/src/utils/types.ts
@@ -201,7 +201,22 @@ export interface OrderScenario {
takerAssetDataScenario: AssetDataScenario;
}
+export enum TokenAmountScenario {
+ Exact = 'EXACT',
+ TooLow = 'TOO_LOW',
+ Higher = 'HIGHER',
+}
+
+export interface TraderStateScenario {
+ traderAssetBalance: TokenAmountScenario;
+ traderAssetAllowance: TokenAmountScenario;
+ zrxFeeBalance: TokenAmountScenario;
+ zrxFeeAllowance: TokenAmountScenario;
+}
+
export interface FillScenario {
orderScenario: OrderScenario;
takerAssetFillAmountScenario: TakerAssetFillAmountScenario;
+ makerStateScenario: TraderStateScenario;
+ takerStateScenario: TraderStateScenario;
}