Implement initial set of orderFill combinatorial tests
This commit is contained in:
445
packages/contracts/src/utils/core_combinatorial_utils.ts
Normal file
445
packages/contracts/src/utils/core_combinatorial_utils.ts
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
import {
|
||||||
|
assetProxyUtils,
|
||||||
|
BalanceAndProxyAllowanceLazyStore,
|
||||||
|
ExchangeTransferSimulator,
|
||||||
|
orderHashUtils,
|
||||||
|
OrderStateUtils,
|
||||||
|
OrderValidationUtils,
|
||||||
|
} from '@0xproject/order-utils';
|
||||||
|
import { AssetProxyId, Order, SignatureType, SignedOrder } from '@0xproject/types';
|
||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
import { BlockParamLiteral, LogWithDecodedArgs, Provider, TxData } from 'ethereum-types';
|
||||||
|
// import ethUtil = require('ethereumjs-util');
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import 'make-promises-safe';
|
||||||
|
|
||||||
|
import { ExchangeContract, FillContractEventArgs } from '../generated_contract_wrappers/exchange';
|
||||||
|
import { artifacts } from '../utils/artifacts';
|
||||||
|
import { expectRevertOrAlwaysFailingTransactionAsync } from '../utils/assertions';
|
||||||
|
import { AssetWrapper } from '../utils/asset_wrapper';
|
||||||
|
import { chaiSetup } from '../utils/chai_setup';
|
||||||
|
import { constants } from '../utils/constants';
|
||||||
|
import { ERC20Wrapper } from '../utils/erc20_wrapper';
|
||||||
|
import { ERC721Wrapper } from '../utils/erc721_wrapper';
|
||||||
|
import { ExchangeWrapper } from '../utils/exchange_wrapper';
|
||||||
|
import { NewOrderFactory } from '../utils/new_order_factory';
|
||||||
|
import { orderUtils } from '../utils/order_utils';
|
||||||
|
import { signingUtils } from '../utils/signing_utils';
|
||||||
|
import { SimpleAssetBalanceAndProxyAllowanceFetcher } from '../utils/simple_asset_balance_and_proxy_allowance_fetcher';
|
||||||
|
import { SimpleOrderFilledCancelledFetcher } from '../utils/simple_order_filled_cancelled_fetcher';
|
||||||
|
import {
|
||||||
|
AssetDataScenario,
|
||||||
|
ExpirationTimeSecondsScenario,
|
||||||
|
FeeRecipientAddressScenario,
|
||||||
|
OrderAmountScenario,
|
||||||
|
OrderScenario,
|
||||||
|
} 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.
|
||||||
|
* @param web3Wrapper Web3Wrapper instance
|
||||||
|
* @param txDefaults Default Ethereum tx options
|
||||||
|
* @return CoreCombinatorialUtils instance
|
||||||
|
*/
|
||||||
|
export async function coreCombinatorialUtilsFactoryAsync(
|
||||||
|
web3Wrapper: Web3Wrapper,
|
||||||
|
txDefaults: Partial<TxData>,
|
||||||
|
): Promise<CoreCombinatorialUtils> {
|
||||||
|
const userAddresses = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
|
const [ownerAddress, makerAddress, takerAddress] = userAddresses;
|
||||||
|
const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
|
||||||
|
|
||||||
|
const provider = web3Wrapper.getProvider();
|
||||||
|
const erc20Wrapper = new ERC20Wrapper(provider, userAddresses, ownerAddress);
|
||||||
|
const erc721Wrapper = new ERC721Wrapper(provider, userAddresses, ownerAddress);
|
||||||
|
|
||||||
|
const erc20EighteenDecimalTokenCount = 3;
|
||||||
|
const eighteenDecimals = new BigNumber(18);
|
||||||
|
const [
|
||||||
|
erc20EighteenDecimalTokenA,
|
||||||
|
erc20EighteenDecimalTokenB,
|
||||||
|
zrxToken,
|
||||||
|
] = await erc20Wrapper.deployDummyTokensAsync(erc20EighteenDecimalTokenCount, eighteenDecimals);
|
||||||
|
const zrxAssetData = assetProxyUtils.encodeERC20AssetData(zrxToken.address);
|
||||||
|
|
||||||
|
const erc20FiveDecimalTokenCount = 2;
|
||||||
|
const fiveDecimals = new BigNumber(18);
|
||||||
|
const [erc20FiveDecimalTokenA, erc20FiveDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync(
|
||||||
|
erc20FiveDecimalTokenCount,
|
||||||
|
fiveDecimals,
|
||||||
|
);
|
||||||
|
const erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
||||||
|
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
||||||
|
|
||||||
|
const [erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
|
||||||
|
const erc721Proxy = await erc721Wrapper.deployProxyAsync();
|
||||||
|
await erc721Wrapper.setBalancesAndAllowancesAsync();
|
||||||
|
const erc721Balances = await erc721Wrapper.getBalancesAsync();
|
||||||
|
|
||||||
|
const assetWrapper = new AssetWrapper([erc20Wrapper, erc721Wrapper]);
|
||||||
|
|
||||||
|
const exchangeContract = await ExchangeContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.Exchange,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
zrxAssetData,
|
||||||
|
);
|
||||||
|
const exchangeWrapper = new ExchangeWrapper(exchangeContract, provider);
|
||||||
|
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, ownerAddress);
|
||||||
|
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, erc721Proxy.address, ownerAddress);
|
||||||
|
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, {
|
||||||
|
from: ownerAddress,
|
||||||
|
}),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeContract.address, {
|
||||||
|
from: ownerAddress,
|
||||||
|
}),
|
||||||
|
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||||
|
);
|
||||||
|
|
||||||
|
const orderFactory = new NewOrderFactory(
|
||||||
|
userAddresses,
|
||||||
|
zrxToken.address,
|
||||||
|
[erc20EighteenDecimalTokenA.address, erc20EighteenDecimalTokenB.address],
|
||||||
|
[erc20FiveDecimalTokenA.address, erc20FiveDecimalTokenB.address],
|
||||||
|
erc721Token,
|
||||||
|
erc721Balances,
|
||||||
|
exchangeContract.address,
|
||||||
|
);
|
||||||
|
|
||||||
|
const coreCombinatorialUtils = new CoreCombinatorialUtils(
|
||||||
|
orderFactory,
|
||||||
|
ownerAddress,
|
||||||
|
makerAddress,
|
||||||
|
makerPrivateKey,
|
||||||
|
takerAddress,
|
||||||
|
zrxAssetData,
|
||||||
|
exchangeWrapper,
|
||||||
|
assetWrapper,
|
||||||
|
);
|
||||||
|
return coreCombinatorialUtils;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CoreCombinatorialUtils {
|
||||||
|
public orderFactory: NewOrderFactory;
|
||||||
|
public ownerAddress: string;
|
||||||
|
public makerAddress: string;
|
||||||
|
public makerPrivateKey: Buffer;
|
||||||
|
public takerAddress: string;
|
||||||
|
public zrxAssetData: string;
|
||||||
|
public exchangeWrapper: ExchangeWrapper;
|
||||||
|
public assetWrapper: AssetWrapper;
|
||||||
|
public static generateOrderCombinations(): OrderScenario[] {
|
||||||
|
const feeRecipientScenarios = [FeeRecipientAddressScenario.EthUserAddress];
|
||||||
|
const makerAssetAmountScenario = [OrderAmountScenario.NonZero];
|
||||||
|
const takerAssetAmountScenario = [OrderAmountScenario.NonZero];
|
||||||
|
const makerFeeScenario = [OrderAmountScenario.NonZero];
|
||||||
|
const takerFeeScenario = [OrderAmountScenario.NonZero];
|
||||||
|
const expirationTimeSecondsScenario = [ExpirationTimeSecondsScenario.InFuture];
|
||||||
|
const makerAssetDataScenario = [
|
||||||
|
AssetDataScenario.ERC20FiveDecimals,
|
||||||
|
AssetDataScenario.ERC20NonZRXEighteenDecimals,
|
||||||
|
AssetDataScenario.ERC721,
|
||||||
|
AssetDataScenario.ZRXFeeToken,
|
||||||
|
];
|
||||||
|
const takerAssetDataScenario = [
|
||||||
|
AssetDataScenario.ERC20FiveDecimals,
|
||||||
|
AssetDataScenario.ERC20NonZRXEighteenDecimals,
|
||||||
|
AssetDataScenario.ERC721,
|
||||||
|
AssetDataScenario.ZRXFeeToken,
|
||||||
|
];
|
||||||
|
const orderScenarioArrays = CoreCombinatorialUtils._allPossibleCases([
|
||||||
|
feeRecipientScenarios,
|
||||||
|
makerAssetAmountScenario,
|
||||||
|
takerAssetAmountScenario,
|
||||||
|
makerFeeScenario,
|
||||||
|
takerFeeScenario,
|
||||||
|
expirationTimeSecondsScenario,
|
||||||
|
makerAssetDataScenario,
|
||||||
|
takerAssetDataScenario,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const orderScenarios = _.map(orderScenarioArrays, orderScenarioArray => {
|
||||||
|
const orderScenario: OrderScenario = {
|
||||||
|
feeRecipientScenario: orderScenarioArray[0] as FeeRecipientAddressScenario,
|
||||||
|
makerAssetAmountScenario: orderScenarioArray[1] as OrderAmountScenario,
|
||||||
|
takerAssetAmountScenario: orderScenarioArray[2] as OrderAmountScenario,
|
||||||
|
makerFeeScenario: orderScenarioArray[3] as OrderAmountScenario,
|
||||||
|
takerFeeScenario: orderScenarioArray[4] as OrderAmountScenario,
|
||||||
|
expirationTimeSecondsScenario: orderScenarioArray[5] as ExpirationTimeSecondsScenario,
|
||||||
|
makerAssetDataScenario: orderScenarioArray[6] as AssetDataScenario,
|
||||||
|
takerAssetDataScenario: orderScenarioArray[7] as AssetDataScenario,
|
||||||
|
};
|
||||||
|
return orderScenario;
|
||||||
|
});
|
||||||
|
|
||||||
|
return orderScenarios;
|
||||||
|
}
|
||||||
|
private static _allPossibleCases(arrays: string[][]): string[][] {
|
||||||
|
if (arrays.length === 1) {
|
||||||
|
const remainingVals = _.map(arrays[0], val => {
|
||||||
|
return [val];
|
||||||
|
});
|
||||||
|
return remainingVals;
|
||||||
|
} else {
|
||||||
|
const result = [];
|
||||||
|
const allCasesOfRest = CoreCombinatorialUtils._allPossibleCases(arrays.slice(1)); // recur with the rest of array
|
||||||
|
// tslint:disable:prefer-for-of
|
||||||
|
for (let i = 0; i < allCasesOfRest.length; i++) {
|
||||||
|
for (let j = 0; j < arrays[0].length; j++) {
|
||||||
|
result.push([arrays[0][j], ...allCasesOfRest[i]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tslint:enable:prefer-for-of
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constructor(
|
||||||
|
orderFactory: NewOrderFactory,
|
||||||
|
ownerAddress: string,
|
||||||
|
makerAddress: string,
|
||||||
|
makerPrivateKey: Buffer,
|
||||||
|
takerAddress: string,
|
||||||
|
zrxAssetData: string,
|
||||||
|
exchangeWrapper: ExchangeWrapper,
|
||||||
|
assetWrapper: AssetWrapper,
|
||||||
|
) {
|
||||||
|
this.orderFactory = orderFactory;
|
||||||
|
this.ownerAddress = ownerAddress;
|
||||||
|
this.makerAddress = makerAddress;
|
||||||
|
this.makerPrivateKey = makerPrivateKey;
|
||||||
|
this.takerAddress = takerAddress;
|
||||||
|
this.zrxAssetData = zrxAssetData;
|
||||||
|
this.exchangeWrapper = exchangeWrapper;
|
||||||
|
this.assetWrapper = assetWrapper;
|
||||||
|
}
|
||||||
|
public async testFillOrderScenarioAsync(order: Order, provider: Provider): Promise<void> {
|
||||||
|
// 2. Sign order
|
||||||
|
const orderHashBuff = orderHashUtils.getOrderHashBuff(order);
|
||||||
|
const signature = signingUtils.signMessage(orderHashBuff, this.makerPrivateKey, SignatureType.EthSign);
|
||||||
|
const signedOrder = {
|
||||||
|
...order,
|
||||||
|
signature: `0x${signature.toString('hex')}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. Permutate the maker and taker balance/allowance scenarios
|
||||||
|
|
||||||
|
// 4. Figure out fill amount OR error
|
||||||
|
const balanceAndProxyAllowanceFetcher = new SimpleAssetBalanceAndProxyAllowanceFetcher(this.assetWrapper);
|
||||||
|
const orderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher(
|
||||||
|
this.exchangeWrapper,
|
||||||
|
this.zrxAssetData,
|
||||||
|
);
|
||||||
|
const orderStateUtils = new OrderStateUtils(balanceAndProxyAllowanceFetcher, orderFilledCancelledFetcher);
|
||||||
|
|
||||||
|
const fillableTakerAssetAmount = await orderStateUtils.getMaxFillableTakerAssetAmountAsync(
|
||||||
|
signedOrder,
|
||||||
|
this.takerAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
// If order is fillable, decide how much to fill
|
||||||
|
// TODO: Make this configurable
|
||||||
|
const takerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.takerAssetData);
|
||||||
|
const makerAssetProxyId = assetProxyUtils.decodeAssetDataId(signedOrder.makerAssetData);
|
||||||
|
const isEitherAssetERC721 = takerAssetProxyId === ERC721_PROXY_ID || makerAssetProxyId === ERC721_PROXY_ID;
|
||||||
|
const takerAssetFillAmount = isEitherAssetERC721
|
||||||
|
? fillableTakerAssetAmount
|
||||||
|
: fillableTakerAssetAmount.div(2).floor();
|
||||||
|
|
||||||
|
// 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 _fillOrderAndAssertOutcomeAsync(
|
||||||
|
signedOrder: SignedOrder,
|
||||||
|
takerAssetFillAmount: BigNumber,
|
||||||
|
lazyStore: BalanceAndProxyAllowanceLazyStore,
|
||||||
|
isFillFailureExpected: boolean,
|
||||||
|
provider: Provider,
|
||||||
|
): Promise<void> {
|
||||||
|
if (isFillFailureExpected) {
|
||||||
|
return expectRevertOrAlwaysFailingTransactionAsync(
|
||||||
|
this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, { takerAssetFillAmount }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const makerAddress = signedOrder.makerAddress;
|
||||||
|
const makerAssetData = signedOrder.makerAssetData;
|
||||||
|
const takerAssetData = signedOrder.takerAssetData;
|
||||||
|
const feeRecipient = signedOrder.feeRecipientAddress;
|
||||||
|
|
||||||
|
const expMakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(makerAssetData, makerAddress);
|
||||||
|
const expMakerAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(makerAssetData, makerAddress);
|
||||||
|
const expTakerAssetBalanceOfMaker = await lazyStore.getBalanceAsync(takerAssetData, makerAddress);
|
||||||
|
const expZRXAssetBalanceOfMaker = await lazyStore.getBalanceAsync(this.zrxAssetData, makerAddress);
|
||||||
|
const expZRXAssetAllowanceOfMaker = await lazyStore.getProxyAllowanceAsync(this.zrxAssetData, makerAddress);
|
||||||
|
const expTakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(takerAssetData, this.takerAddress);
|
||||||
|
const expTakerAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync(takerAssetData, this.takerAddress);
|
||||||
|
const expMakerAssetBalanceOfTaker = await lazyStore.getBalanceAsync(makerAssetData, this.takerAddress);
|
||||||
|
const expZRXAssetBalanceOfTaker = await lazyStore.getBalanceAsync(this.zrxAssetData, this.takerAddress);
|
||||||
|
const expZRXAssetAllowanceOfTaker = await lazyStore.getProxyAllowanceAsync(
|
||||||
|
this.zrxAssetData,
|
||||||
|
this.takerAddress,
|
||||||
|
);
|
||||||
|
const expZRXAssetBalanceOfFeeRecipient = await lazyStore.getBalanceAsync(this.zrxAssetData, feeRecipient);
|
||||||
|
|
||||||
|
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
|
||||||
|
const initialFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash);
|
||||||
|
const expFilledTakerAmount = initialFilledTakerAmount.add(takerAssetFillAmount);
|
||||||
|
|
||||||
|
const expFilledMakerAmount = orderUtils.getPartialAmount(
|
||||||
|
takerAssetFillAmount,
|
||||||
|
signedOrder.takerAssetAmount,
|
||||||
|
signedOrder.makerAssetAmount,
|
||||||
|
);
|
||||||
|
|
||||||
|
// - Let's fill the order!
|
||||||
|
const txReceipt = await this.exchangeWrapper.fillOrderAsync(signedOrder, this.takerAddress, {
|
||||||
|
takerAssetFillAmount,
|
||||||
|
});
|
||||||
|
|
||||||
|
const actFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash);
|
||||||
|
expect(actFilledTakerAmount).to.be.bignumber.equal(expFilledTakerAmount, 'filledTakerAmount');
|
||||||
|
|
||||||
|
expect(txReceipt.logs.length).to.be.equal(1, 'logs length');
|
||||||
|
// tslint:disable-next-line:no-unnecessary-type-assertion
|
||||||
|
const log = txReceipt.logs[0] as LogWithDecodedArgs<FillContractEventArgs>;
|
||||||
|
expect(log.args.makerAddress).to.be.equal(makerAddress, 'log.args.makerAddress');
|
||||||
|
expect(log.args.takerAddress).to.be.equal(this.takerAddress, 'log.args.this.takerAddress');
|
||||||
|
expect(log.args.feeRecipientAddress).to.be.equal(feeRecipient, 'log.args.feeRecipientAddress');
|
||||||
|
expect(log.args.makerAssetFilledAmount).to.be.bignumber.equal(
|
||||||
|
expFilledMakerAmount,
|
||||||
|
'log.args.makerAssetFilledAmount',
|
||||||
|
);
|
||||||
|
expect(log.args.takerAssetFilledAmount).to.be.bignumber.equal(
|
||||||
|
takerAssetFillAmount,
|
||||||
|
'log.args.takerAssetFilledAmount',
|
||||||
|
);
|
||||||
|
const expMakerFeePaid = orderUtils.getPartialAmount(
|
||||||
|
expFilledTakerAmount,
|
||||||
|
signedOrder.takerAssetAmount,
|
||||||
|
signedOrder.makerFee,
|
||||||
|
);
|
||||||
|
expect(log.args.makerFeePaid).to.be.bignumber.equal(expMakerFeePaid, 'log.args.makerFeePaid');
|
||||||
|
const expTakerFeePaid = orderUtils.getPartialAmount(
|
||||||
|
expFilledTakerAmount,
|
||||||
|
signedOrder.takerAssetAmount,
|
||||||
|
signedOrder.takerFee,
|
||||||
|
);
|
||||||
|
expect(log.args.takerFeePaid).to.be.bignumber.equal(expTakerFeePaid, 'logs.args.takerFeePaid');
|
||||||
|
expect(log.args.orderHash).to.be.equal(orderHash, 'log.args.orderHash');
|
||||||
|
expect(log.args.makerAssetData).to.be.equal(makerAssetData, 'log.args.makerAssetData');
|
||||||
|
expect(log.args.takerAssetData).to.be.equal(takerAssetData, 'log.args.takerAssetData');
|
||||||
|
|
||||||
|
const actMakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, makerAssetData);
|
||||||
|
expect(actMakerAssetBalanceOfMaker).to.be.bignumber.equal(
|
||||||
|
expMakerAssetBalanceOfMaker,
|
||||||
|
'makerAssetBalanceOfMaker',
|
||||||
|
);
|
||||||
|
|
||||||
|
const actMakerAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync(
|
||||||
|
makerAddress,
|
||||||
|
makerAssetData,
|
||||||
|
);
|
||||||
|
expect(actMakerAssetAllowanceOfMaker).to.be.bignumber.equal(
|
||||||
|
expMakerAssetAllowanceOfMaker,
|
||||||
|
'makerAssetAllowanceOfMaker',
|
||||||
|
);
|
||||||
|
|
||||||
|
const actTakerAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, takerAssetData);
|
||||||
|
expect(actTakerAssetBalanceOfMaker).to.be.bignumber.equal(
|
||||||
|
expTakerAssetBalanceOfMaker,
|
||||||
|
'takerAssetBalanceOfMaker',
|
||||||
|
);
|
||||||
|
|
||||||
|
const actZRXAssetBalanceOfMaker = await this.assetWrapper.getBalanceAsync(makerAddress, this.zrxAssetData);
|
||||||
|
expect(actZRXAssetBalanceOfMaker).to.be.bignumber.equal(expZRXAssetBalanceOfMaker, 'ZRXAssetBalanceOfMaker');
|
||||||
|
|
||||||
|
const actZRXAssetAllowanceOfMaker = await this.assetWrapper.getProxyAllowanceAsync(
|
||||||
|
makerAddress,
|
||||||
|
this.zrxAssetData,
|
||||||
|
);
|
||||||
|
expect(actZRXAssetAllowanceOfMaker).to.be.bignumber.equal(
|
||||||
|
expZRXAssetAllowanceOfMaker,
|
||||||
|
'ZRXAssetAllowanceOfMaker',
|
||||||
|
);
|
||||||
|
|
||||||
|
const actTakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, takerAssetData);
|
||||||
|
expect(actTakerAssetBalanceOfTaker).to.be.bignumber.equal(
|
||||||
|
expTakerAssetBalanceOfTaker,
|
||||||
|
'TakerAssetBalanceOfTaker',
|
||||||
|
);
|
||||||
|
|
||||||
|
const actTakerAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync(
|
||||||
|
this.takerAddress,
|
||||||
|
takerAssetData,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(actTakerAssetAllowanceOfTaker).to.be.bignumber.equal(
|
||||||
|
expTakerAssetAllowanceOfTaker,
|
||||||
|
'TakerAssetAllowanceOfTaker',
|
||||||
|
);
|
||||||
|
|
||||||
|
const actMakerAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, makerAssetData);
|
||||||
|
expect(actMakerAssetBalanceOfTaker).to.be.bignumber.equal(
|
||||||
|
expMakerAssetBalanceOfTaker,
|
||||||
|
'MakerAssetBalanceOfTaker',
|
||||||
|
);
|
||||||
|
|
||||||
|
const actZRXAssetBalanceOfTaker = await this.assetWrapper.getBalanceAsync(this.takerAddress, this.zrxAssetData);
|
||||||
|
expect(actZRXAssetBalanceOfTaker).to.be.bignumber.equal(expZRXAssetBalanceOfTaker, 'ZRXAssetBalanceOfTaker');
|
||||||
|
|
||||||
|
const actZRXAssetAllowanceOfTaker = await this.assetWrapper.getProxyAllowanceAsync(
|
||||||
|
this.takerAddress,
|
||||||
|
this.zrxAssetData,
|
||||||
|
);
|
||||||
|
expect(actZRXAssetAllowanceOfTaker).to.be.bignumber.equal(
|
||||||
|
expZRXAssetAllowanceOfTaker,
|
||||||
|
'ZRXAssetAllowanceOfTaker',
|
||||||
|
);
|
||||||
|
|
||||||
|
const actZRXAssetBalanceOfFeeRecipient = await this.assetWrapper.getBalanceAsync(
|
||||||
|
feeRecipient,
|
||||||
|
this.zrxAssetData,
|
||||||
|
);
|
||||||
|
expect(actZRXAssetBalanceOfFeeRecipient).to.be.bignumber.equal(
|
||||||
|
expZRXAssetBalanceOfFeeRecipient,
|
||||||
|
'ZRXAssetBalanceOfFeeRecipient',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -227,6 +227,10 @@ export class ExchangeWrapper {
|
|||||||
const filledAmount = new BigNumber(await this._exchange.filled.callAsync(orderHashHex));
|
const filledAmount = new BigNumber(await this._exchange.filled.callAsync(orderHashHex));
|
||||||
return filledAmount;
|
return filledAmount;
|
||||||
}
|
}
|
||||||
|
public async isCancelledAsync(orderHashHex: string): Promise<boolean> {
|
||||||
|
const isCancelled = await this._exchange.cancelled.callAsync(orderHashHex);
|
||||||
|
return isCancelled;
|
||||||
|
}
|
||||||
public async getOrderInfoAsync(signedOrder: SignedOrder): Promise<OrderInfo> {
|
public async getOrderInfoAsync(signedOrder: SignedOrder): Promise<OrderInfo> {
|
||||||
const orderInfo = (await this._exchange.getOrderInfo.callAsync(signedOrder)) as OrderInfo;
|
const orderInfo = (await this._exchange.getOrderInfo.callAsync(signedOrder)) as OrderInfo;
|
||||||
return orderInfo;
|
return orderInfo;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-util
|
|||||||
import { Order } from '@0xproject/types';
|
import { Order } from '@0xproject/types';
|
||||||
import { BigNumber, errorUtils } from '@0xproject/utils';
|
import { BigNumber, errorUtils } from '@0xproject/utils';
|
||||||
|
|
||||||
import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token';
|
import { DummyERC721TokenContract } from '../generated_contract_wrappers/dummy_e_r_c721_token';
|
||||||
|
|
||||||
import { constants } from './constants';
|
import { constants } from './constants';
|
||||||
import {
|
import {
|
||||||
@@ -11,20 +11,13 @@ import {
|
|||||||
ExpirationTimeSecondsScenario,
|
ExpirationTimeSecondsScenario,
|
||||||
FeeRecipientAddressScenario,
|
FeeRecipientAddressScenario,
|
||||||
OrderAmountScenario,
|
OrderAmountScenario,
|
||||||
|
OrderScenario,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000);
|
const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000);
|
||||||
const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000);
|
const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000);
|
||||||
const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000);
|
const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000);
|
||||||
const ONE_NFT_UNIT = new BigNumber(1);
|
const ONE_NFT_UNIT = new BigNumber(1);
|
||||||
const TEN_MINUTES_MS = 1000 * 60 * 10;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO:
|
|
||||||
* - Write function that given an order, fillAmount, retrieves orderRelevantState and maps it to expected test outcome.
|
|
||||||
* - Write function that generates order permutations.
|
|
||||||
* - Write functions for other steps that must be permutated
|
|
||||||
*/
|
|
||||||
|
|
||||||
export class NewOrderFactory {
|
export class NewOrderFactory {
|
||||||
private _userAddresses: string[];
|
private _userAddresses: string[];
|
||||||
@@ -51,16 +44,7 @@ export class NewOrderFactory {
|
|||||||
this._erc721Balances = erc721Balances;
|
this._erc721Balances = erc721Balances;
|
||||||
this._exchangeAddress = exchangeAddress;
|
this._exchangeAddress = exchangeAddress;
|
||||||
}
|
}
|
||||||
public generateOrder(
|
public generateOrder(orderScenario: OrderScenario): Order {
|
||||||
feeRecipientScenario: FeeRecipientAddressScenario,
|
|
||||||
makerAssetAmountScenario: OrderAmountScenario,
|
|
||||||
takerAssetAmountScenario: OrderAmountScenario,
|
|
||||||
makerFeeScenario: OrderAmountScenario,
|
|
||||||
takerFeeScenario: OrderAmountScenario,
|
|
||||||
expirationTimeSecondsScenario: ExpirationTimeSecondsScenario,
|
|
||||||
makerAssetDataScenario: AssetDataScenario,
|
|
||||||
takerAssetDataScenario: AssetDataScenario,
|
|
||||||
): Order {
|
|
||||||
const makerAddress = this._userAddresses[1];
|
const makerAddress = this._userAddresses[1];
|
||||||
const takerAddress = this._userAddresses[2];
|
const takerAddress = this._userAddresses[2];
|
||||||
const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721Token.address];
|
const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721Token.address];
|
||||||
@@ -74,7 +58,7 @@ export class NewOrderFactory {
|
|||||||
let makerAssetData;
|
let makerAssetData;
|
||||||
let takerAssetData;
|
let takerAssetData;
|
||||||
|
|
||||||
switch (feeRecipientScenario) {
|
switch (orderScenario.feeRecipientScenario) {
|
||||||
case FeeRecipientAddressScenario.BurnAddress:
|
case FeeRecipientAddressScenario.BurnAddress:
|
||||||
feeRecipientAddress = constants.NULL_ADDRESS;
|
feeRecipientAddress = constants.NULL_ADDRESS;
|
||||||
break;
|
break;
|
||||||
@@ -82,135 +66,102 @@ export class NewOrderFactory {
|
|||||||
feeRecipientAddress = this._userAddresses[4];
|
feeRecipientAddress = this._userAddresses[4];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', feeRecipientScenario);
|
throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', orderScenario.feeRecipientScenario);
|
||||||
}
|
}
|
||||||
|
|
||||||
const invalidAssetProxyIdHex = '0A';
|
switch (orderScenario.makerAssetDataScenario) {
|
||||||
switch (makerAssetDataScenario) {
|
|
||||||
case AssetDataScenario.ZRXFeeToken:
|
case AssetDataScenario.ZRXFeeToken:
|
||||||
makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress);
|
makerAssetData = assetProxyUtils.encodeERC20AssetData(this._zrxAddress);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
||||||
makerAssetData = assetProxyUtils.encodeERC20ProxyData(
|
makerAssetData = assetProxyUtils.encodeERC20AssetData(
|
||||||
this._nonZrxERC20EighteenDecimalTokenAddresses[0],
|
this._nonZrxERC20EighteenDecimalTokenAddresses[0],
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20FiveDecimals:
|
case AssetDataScenario.ERC20FiveDecimals:
|
||||||
makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[0]);
|
makerAssetData = assetProxyUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[0]);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20InvalidAssetProxyId: {
|
case AssetDataScenario.ERC721:
|
||||||
const validAssetData = assetProxyUtils.encodeERC20ProxyData(
|
makerAssetData = assetProxyUtils.encodeERC721AssetData(
|
||||||
this._nonZrxERC20EighteenDecimalTokenAddresses[0],
|
|
||||||
);
|
|
||||||
makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case AssetDataScenario.ERC721ValidAssetProxyId:
|
|
||||||
makerAssetData = assetProxyUtils.encodeERC721ProxyData(
|
|
||||||
this._erc721Token.address,
|
this._erc721Token.address,
|
||||||
erc721MakerAssetIds[0],
|
erc721MakerAssetIds[0],
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC721InvalidAssetProxyId: {
|
|
||||||
const validAssetData = assetProxyUtils.encodeERC721ProxyData(
|
|
||||||
this._erc721Token.address,
|
|
||||||
erc721MakerAssetIds[0],
|
|
||||||
);
|
|
||||||
makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario);
|
throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (takerAssetDataScenario) {
|
switch (orderScenario.takerAssetDataScenario) {
|
||||||
case AssetDataScenario.ZRXFeeToken:
|
case AssetDataScenario.ZRXFeeToken:
|
||||||
takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress);
|
takerAssetData = assetProxyUtils.encodeERC20AssetData(this._zrxAddress);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
||||||
takerAssetData = assetProxyUtils.encodeERC20ProxyData(
|
takerAssetData = assetProxyUtils.encodeERC20AssetData(
|
||||||
this._nonZrxERC20EighteenDecimalTokenAddresses[1],
|
this._nonZrxERC20EighteenDecimalTokenAddresses[1],
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20FiveDecimals:
|
case AssetDataScenario.ERC20FiveDecimals:
|
||||||
takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[1]);
|
takerAssetData = assetProxyUtils.encodeERC20AssetData(this._erc20FiveDecimalTokenAddresses[1]);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20InvalidAssetProxyId: {
|
case AssetDataScenario.ERC721:
|
||||||
const validAssetData = assetProxyUtils.encodeERC20ProxyData(
|
takerAssetData = assetProxyUtils.encodeERC721AssetData(
|
||||||
this._nonZrxERC20EighteenDecimalTokenAddresses[1],
|
|
||||||
);
|
|
||||||
takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case AssetDataScenario.ERC721ValidAssetProxyId:
|
|
||||||
takerAssetData = assetProxyUtils.encodeERC721ProxyData(
|
|
||||||
this._erc721Token.address,
|
this._erc721Token.address,
|
||||||
erc721TakerAssetIds[0],
|
erc721TakerAssetIds[0],
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC721InvalidAssetProxyId: {
|
|
||||||
const validAssetData = assetProxyUtils.encodeERC721ProxyData(
|
|
||||||
this._erc721Token.address,
|
|
||||||
erc721TakerAssetIds[0],
|
|
||||||
);
|
|
||||||
takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario);
|
throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (makerAssetAmountScenario) {
|
switch (orderScenario.makerAssetAmountScenario) {
|
||||||
case OrderAmountScenario.NonZero:
|
case OrderAmountScenario.NonZero:
|
||||||
switch (makerAssetDataScenario) {
|
switch (orderScenario.makerAssetDataScenario) {
|
||||||
|
case AssetDataScenario.ZRXFeeToken:
|
||||||
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
||||||
case AssetDataScenario.ERC20InvalidAssetProxyId:
|
|
||||||
makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS;
|
makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS;
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20FiveDecimals:
|
case AssetDataScenario.ERC20FiveDecimals:
|
||||||
makerAssetAmount = TEN_UNITS_FIVE_DECIMALS;
|
makerAssetAmount = TEN_UNITS_FIVE_DECIMALS;
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC721ValidAssetProxyId:
|
case AssetDataScenario.ERC721:
|
||||||
case AssetDataScenario.ERC721InvalidAssetProxyId:
|
|
||||||
makerAssetAmount = ONE_NFT_UNIT;
|
makerAssetAmount = ONE_NFT_UNIT;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario);
|
throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.makerAssetDataScenario);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OrderAmountScenario.Zero:
|
case OrderAmountScenario.Zero:
|
||||||
makerAssetAmount = new BigNumber(0);
|
makerAssetAmount = new BigNumber(0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerAssetAmountScenario);
|
throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.makerAssetAmountScenario);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (takerAssetAmountScenario) {
|
switch (orderScenario.takerAssetAmountScenario) {
|
||||||
case OrderAmountScenario.NonZero:
|
case OrderAmountScenario.NonZero:
|
||||||
switch (takerAssetDataScenario) {
|
switch (orderScenario.takerAssetDataScenario) {
|
||||||
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
||||||
case AssetDataScenario.ERC20InvalidAssetProxyId:
|
case AssetDataScenario.ZRXFeeToken:
|
||||||
takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS;
|
takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS;
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20FiveDecimals:
|
case AssetDataScenario.ERC20FiveDecimals:
|
||||||
takerAssetAmount = TEN_UNITS_FIVE_DECIMALS;
|
takerAssetAmount = TEN_UNITS_FIVE_DECIMALS;
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC721ValidAssetProxyId:
|
case AssetDataScenario.ERC721:
|
||||||
case AssetDataScenario.ERC721InvalidAssetProxyId:
|
|
||||||
takerAssetAmount = ONE_NFT_UNIT;
|
takerAssetAmount = ONE_NFT_UNIT;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario);
|
throw errorUtils.spawnSwitchErr('AssetDataScenario', orderScenario.takerAssetDataScenario);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OrderAmountScenario.Zero:
|
case OrderAmountScenario.Zero:
|
||||||
takerAssetAmount = new BigNumber(0);
|
takerAssetAmount = new BigNumber(0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerAssetAmountScenario);
|
throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.takerAssetAmountScenario);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (makerFeeScenario) {
|
switch (orderScenario.makerFeeScenario) {
|
||||||
case OrderAmountScenario.NonZero:
|
case OrderAmountScenario.NonZero:
|
||||||
makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS;
|
makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS;
|
||||||
break;
|
break;
|
||||||
@@ -218,10 +169,10 @@ export class NewOrderFactory {
|
|||||||
makerFee = new BigNumber(0);
|
makerFee = new BigNumber(0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerFeeScenario);
|
throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.makerFeeScenario);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (takerFeeScenario) {
|
switch (orderScenario.takerFeeScenario) {
|
||||||
case OrderAmountScenario.NonZero:
|
case OrderAmountScenario.NonZero:
|
||||||
takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS;
|
takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS;
|
||||||
break;
|
break;
|
||||||
@@ -229,18 +180,21 @@ export class NewOrderFactory {
|
|||||||
takerFee = new BigNumber(0);
|
takerFee = new BigNumber(0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerFeeScenario);
|
throw errorUtils.spawnSwitchErr('OrderAmountScenario', orderScenario.takerFeeScenario);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (expirationTimeSecondsScenario) {
|
switch (orderScenario.expirationTimeSecondsScenario) {
|
||||||
case ExpirationTimeSecondsScenario.InFuture:
|
case ExpirationTimeSecondsScenario.InFuture:
|
||||||
expirationTimeSeconds = new BigNumber(Date.now() + TEN_MINUTES_MS);
|
expirationTimeSeconds = new BigNumber(2524604400); // Close to infinite
|
||||||
break;
|
break;
|
||||||
case ExpirationTimeSecondsScenario.InPast:
|
case ExpirationTimeSecondsScenario.InPast:
|
||||||
expirationTimeSeconds = new BigNumber(Date.now() - TEN_MINUTES_MS);
|
expirationTimeSeconds = new BigNumber(0); // Jan 1, 1970
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw errorUtils.spawnSwitchErr('ExpirationTimeSecondsScenario', expirationTimeSecondsScenario);
|
throw errorUtils.spawnSwitchErr(
|
||||||
|
'ExpirationTimeSecondsScenario',
|
||||||
|
orderScenario.expirationTimeSecondsScenario,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const order: Order = {
|
const order: Order = {
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
import { assetProxyUtils, OrderStateUtils } from '@0xproject/order-utils';
|
|
||||||
import { SignedOrder } from '@0xproject/types';
|
|
||||||
import { BigNumber } from '@0xproject/utils';
|
|
||||||
|
|
||||||
import { ExchangeContract } from '../contract_wrappers/generated/exchange';
|
|
||||||
|
|
||||||
import { constants } from './constants';
|
|
||||||
import { ERC20Wrapper } from './erc20_wrapper';
|
|
||||||
import { SimpleERC20BalanceAndProxyAllowanceFetcher } from './simple_erc20_balance_and_allowance_fetcher';
|
|
||||||
import { SimpleOrderFilledCancelledFetcher } from './simple_filled_cancelled_fetcher';
|
|
||||||
|
|
||||||
export class OrderInfoUtils {
|
|
||||||
private _orderStateUtils: OrderStateUtils;
|
|
||||||
private _erc20Wrapper: ERC20Wrapper;
|
|
||||||
constructor(exchangeContract: ExchangeContract, erc20Wrapper: ERC20Wrapper, zrxAddress: string) {
|
|
||||||
this._erc20Wrapper = erc20Wrapper;
|
|
||||||
const simpleOrderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher(exchangeContract, zrxAddress);
|
|
||||||
const simpleERC20BalanceAndProxyAllowanceFetcher = new SimpleERC20BalanceAndProxyAllowanceFetcher(erc20Wrapper);
|
|
||||||
this._orderStateUtils = new OrderStateUtils(
|
|
||||||
simpleERC20BalanceAndProxyAllowanceFetcher,
|
|
||||||
simpleOrderFilledCancelledFetcher,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
public async getFillableTakerAssetAmountAsync(signedOrder: SignedOrder, takerAddress: string): Promise<BigNumber> {
|
|
||||||
const orderRelevantState = await this._orderStateUtils.getOrderRelevantStateAsync(signedOrder);
|
|
||||||
console.log('orderRelevantState', orderRelevantState);
|
|
||||||
if (takerAddress === constants.NULL_ADDRESS) {
|
|
||||||
return orderRelevantState.remainingFillableTakerAssetAmount;
|
|
||||||
}
|
|
||||||
const takerAssetData = assetProxyUtils.decodeERC20ProxyData(signedOrder.takerAssetData);
|
|
||||||
const takerBalance = await this._erc20Wrapper.getBalanceAsync(takerAddress, takerAssetData.tokenAddress);
|
|
||||||
const takerAllowance = await this._erc20Wrapper.getProxyAllowanceAsync(
|
|
||||||
takerAddress,
|
|
||||||
takerAssetData.tokenAddress,
|
|
||||||
);
|
|
||||||
// TODO: We also need to make sure taker has sufficient ZRX for fees...
|
|
||||||
const fillableTakerAssetAmount = BigNumber.min([
|
|
||||||
takerBalance,
|
|
||||||
takerAllowance,
|
|
||||||
orderRelevantState.remainingFillableTakerAssetAmount,
|
|
||||||
]);
|
|
||||||
return fillableTakerAssetAmount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,6 +4,13 @@ import { BigNumber } from '@0xproject/utils';
|
|||||||
import { CancelOrder, MatchOrder } from './types';
|
import { CancelOrder, MatchOrder } from './types';
|
||||||
|
|
||||||
export const orderUtils = {
|
export const orderUtils = {
|
||||||
|
getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
|
||||||
|
const partialAmount = numerator
|
||||||
|
.mul(target)
|
||||||
|
.div(denominator)
|
||||||
|
.floor();
|
||||||
|
return partialAmount;
|
||||||
|
},
|
||||||
createFill: (signedOrder: SignedOrder, takerAssetFillAmount?: BigNumber) => {
|
createFill: (signedOrder: SignedOrder, takerAssetFillAmount?: BigNumber) => {
|
||||||
const fill = {
|
const fill = {
|
||||||
order: orderUtils.getOrderWithoutExchangeAddress(signedOrder),
|
order: orderUtils.getOrderWithoutExchangeAddress(signedOrder),
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { AbstractBalanceAndProxyAllowanceFetcher } from '@0xproject/order-utils';
|
||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
|
||||||
|
import { AssetWrapper } from './asset_wrapper';
|
||||||
|
|
||||||
|
export class SimpleAssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher {
|
||||||
|
private _assetWrapper: AssetWrapper;
|
||||||
|
constructor(assetWrapper: AssetWrapper) {
|
||||||
|
this._assetWrapper = assetWrapper;
|
||||||
|
}
|
||||||
|
public async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
|
||||||
|
const balance = await this._assetWrapper.getBalanceAsync(userAddress, assetData);
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
public async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
|
||||||
|
const proxyAllowance = await this._assetWrapper.getProxyAllowanceAsync(userAddress, assetData);
|
||||||
|
return proxyAllowance;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { AbstractBalanceAndProxyAllowanceFetcher } from '@0xproject/order-utils';
|
|
||||||
import { BigNumber } from '@0xproject/utils';
|
|
||||||
|
|
||||||
import { ERC20Wrapper } from './erc20_wrapper';
|
|
||||||
|
|
||||||
// TODO(fabio): Refactor this to also work for ERC721!
|
|
||||||
export class SimpleERC20BalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher {
|
|
||||||
private _erc20TokenWrapper: ERC20Wrapper;
|
|
||||||
constructor(erc20TokenWrapper: ERC20Wrapper) {
|
|
||||||
this._erc20TokenWrapper = erc20TokenWrapper;
|
|
||||||
}
|
|
||||||
public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
|
|
||||||
const balance = await this._erc20TokenWrapper.getBalanceAsync(userAddress, tokenAddress);
|
|
||||||
return balance;
|
|
||||||
}
|
|
||||||
public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> {
|
|
||||||
const proxyAllowance = await this._erc20TokenWrapper.getProxyAllowanceAsync(userAddress, tokenAddress);
|
|
||||||
return proxyAllowance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils';
|
|
||||||
import { BigNumber } from '@0xproject/utils';
|
|
||||||
import { BlockParamLiteral } from 'ethereum-types';
|
|
||||||
|
|
||||||
import {
|
|
||||||
CancelContractEventArgs,
|
|
||||||
ExchangeContract,
|
|
||||||
FillContractEventArgs,
|
|
||||||
} from '../contract_wrappers/generated/exchange';
|
|
||||||
|
|
||||||
export class SimpleOrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher {
|
|
||||||
private _exchangeContract: ExchangeContract;
|
|
||||||
private _zrxAddress: string;
|
|
||||||
constructor(exchange: ExchangeContract, zrxAddress: string) {
|
|
||||||
this._exchangeContract = exchange;
|
|
||||||
this._zrxAddress = zrxAddress;
|
|
||||||
}
|
|
||||||
public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
|
|
||||||
const filledTakerAmount = new BigNumber(await this._exchangeContract.filled.callAsync(orderHash));
|
|
||||||
return filledTakerAmount;
|
|
||||||
}
|
|
||||||
public async isOrderCancelledAsync(orderHash: string): Promise<boolean> {
|
|
||||||
const methodOpts = {
|
|
||||||
defaultBlock: BlockParamLiteral.Latest,
|
|
||||||
};
|
|
||||||
const isCancelled = await this._exchangeContract.cancelled.callAsync(orderHash);
|
|
||||||
return isCancelled;
|
|
||||||
}
|
|
||||||
public getZRXTokenAddress(): string {
|
|
||||||
return this._zrxAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils';
|
||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
|
||||||
|
import { ExchangeWrapper } from './exchange_wrapper';
|
||||||
|
|
||||||
|
export class SimpleOrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher {
|
||||||
|
private _exchangeWrapper: ExchangeWrapper;
|
||||||
|
private _zrxAssetData: string;
|
||||||
|
constructor(exchange: ExchangeWrapper, zrxAssetData: string) {
|
||||||
|
this._exchangeWrapper = exchange;
|
||||||
|
this._zrxAssetData = zrxAssetData;
|
||||||
|
}
|
||||||
|
public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
|
||||||
|
const filledTakerAmount = new BigNumber(await this._exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash));
|
||||||
|
return filledTakerAmount;
|
||||||
|
}
|
||||||
|
public async isOrderCancelledAsync(orderHash: string): Promise<boolean> {
|
||||||
|
const isCancelled = await this._exchangeWrapper.isCancelledAsync(orderHash);
|
||||||
|
return isCancelled;
|
||||||
|
}
|
||||||
|
public getZRXAssetData(): string {
|
||||||
|
return this._zrxAssetData;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -169,10 +169,19 @@ export enum ExpirationTimeSecondsScenario {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum AssetDataScenario {
|
export enum AssetDataScenario {
|
||||||
ERC721ValidAssetProxyId = 'ERC721_VALID_ASSET_PROXY_ID',
|
ERC721 = 'ERC721',
|
||||||
ERC721InvalidAssetProxyId = 'ERC721_INVALID_ASSET_PROXY_ID',
|
|
||||||
ZRXFeeToken = 'ZRX_FEE_TOKEN',
|
ZRXFeeToken = 'ZRX_FEE_TOKEN',
|
||||||
ERC20InvalidAssetProxyId = 'ERC20_INVALID_ASSET_PROXY_ID',
|
|
||||||
ERC20FiveDecimals = 'ERC20_FIVE_DECIMALS',
|
ERC20FiveDecimals = 'ERC20_FIVE_DECIMALS',
|
||||||
ERC20NonZRXEighteenDecimals = 'ERC20_NON_ZRX_EIGHTEEN_DECIMALS',
|
ERC20NonZRXEighteenDecimals = 'ERC20_NON_ZRX_EIGHTEEN_DECIMALS',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OrderScenario {
|
||||||
|
feeRecipientScenario: FeeRecipientAddressScenario;
|
||||||
|
makerAssetAmountScenario: OrderAmountScenario;
|
||||||
|
takerAssetAmountScenario: OrderAmountScenario;
|
||||||
|
makerFeeScenario: OrderAmountScenario;
|
||||||
|
takerFeeScenario: OrderAmountScenario;
|
||||||
|
expirationTimeSecondsScenario: ExpirationTimeSecondsScenario;
|
||||||
|
makerAssetDataScenario: AssetDataScenario;
|
||||||
|
takerAssetDataScenario: AssetDataScenario;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,266 +1,48 @@
|
|||||||
import { BlockchainLifecycle } from '@0xproject/dev-utils';
|
import { BlockchainLifecycle } from '@0xproject/dev-utils';
|
||||||
import { assetProxyUtils, crypto, orderHashUtils, OrderStateUtils } from '@0xproject/order-utils';
|
import * as _ from 'lodash';
|
||||||
import { AssetProxyId, SignatureType, SignedOrder } from '@0xproject/types';
|
|
||||||
import { BigNumber } from '@0xproject/utils';
|
|
||||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
|
||||||
import * as chai from 'chai';
|
|
||||||
import { LogWithDecodedArgs } from 'ethereum-types';
|
|
||||||
import ethUtil = require('ethereumjs-util');
|
|
||||||
import 'make-promises-safe';
|
|
||||||
|
|
||||||
import { DummyERC20TokenContract } from '../src/contract_wrappers/generated/dummy_e_r_c20_token';
|
|
||||||
import { DummyERC721TokenContract } from '../src/contract_wrappers/generated/dummy_e_r_c721_token';
|
|
||||||
import { ERC20ProxyContract } from '../src/contract_wrappers/generated/e_r_c20_proxy';
|
|
||||||
import { ERC721ProxyContract } from '../src/contract_wrappers/generated/e_r_c721_proxy';
|
|
||||||
import {
|
|
||||||
CancelContractEventArgs,
|
|
||||||
ExchangeContract,
|
|
||||||
FillContractEventArgs,
|
|
||||||
} from '../src/contract_wrappers/generated/exchange';
|
|
||||||
import { artifacts } from '../src/utils/artifacts';
|
|
||||||
import { chaiSetup } from '../src/utils/chai_setup';
|
import { chaiSetup } from '../src/utils/chai_setup';
|
||||||
import { constants } from '../src/utils/constants';
|
import { CoreCombinatorialUtils, coreCombinatorialUtilsFactoryAsync } from '../src/utils/core_combinatorial_utils';
|
||||||
import { ERC20Wrapper } from '../src/utils/erc20_wrapper';
|
|
||||||
import { ERC721Wrapper } from '../src/utils/erc721_wrapper';
|
|
||||||
import { ExchangeWrapper } from '../src/utils/exchange_wrapper';
|
|
||||||
import { NewOrderFactory } from '../src/utils/new_order_factory';
|
|
||||||
import { OrderInfoUtils } from '../src/utils/order_info_utils';
|
|
||||||
import { orderUtils } from '../src/utils/order_utils';
|
|
||||||
import { signingUtils } from '../src/utils/signing_utils';
|
|
||||||
import {
|
|
||||||
AssetDataScenario,
|
|
||||||
ContractName,
|
|
||||||
ERC20BalancesByOwner,
|
|
||||||
ExpirationTimeSecondsScenario,
|
|
||||||
FeeRecipientAddressScenario,
|
|
||||||
OrderAmountScenario,
|
|
||||||
OrderStatus,
|
|
||||||
} from '../src/utils/types';
|
|
||||||
|
|
||||||
import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper';
|
import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper';
|
||||||
|
|
||||||
|
import { OrderScenario } from '../src/utils/types';
|
||||||
|
|
||||||
chaiSetup.configure();
|
chaiSetup.configure();
|
||||||
const expect = chai.expect;
|
|
||||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||||
|
|
||||||
describe('Combinatorial tests', () => {
|
describe('Combinatorial tests', () => {
|
||||||
let newOrderFactory: NewOrderFactory;
|
let coreCombinatorialUtils: CoreCombinatorialUtils;
|
||||||
let usedAddresses: string[];
|
|
||||||
|
|
||||||
let makerAddress: string;
|
|
||||||
let owner: string;
|
|
||||||
let takerAddress: string;
|
|
||||||
let feeRecipientAddress: string;
|
|
||||||
|
|
||||||
let erc20EighteenDecimalTokenA: DummyERC20TokenContract;
|
|
||||||
let erc20EighteenDecimalTokenB: DummyERC20TokenContract;
|
|
||||||
let erc20FiveDecimalTokenA: DummyERC20TokenContract;
|
|
||||||
let erc20FiveDecimalTokenB: DummyERC20TokenContract;
|
|
||||||
let zrxToken: DummyERC20TokenContract;
|
|
||||||
let erc721Token: DummyERC721TokenContract;
|
|
||||||
let exchange: ExchangeContract;
|
|
||||||
let erc20Proxy: ERC20ProxyContract;
|
|
||||||
let erc721Proxy: ERC721ProxyContract;
|
|
||||||
|
|
||||||
let erc20Balances: ERC20BalancesByOwner;
|
|
||||||
let exchangeWrapper: ExchangeWrapper;
|
|
||||||
let erc20Wrapper: ERC20Wrapper;
|
|
||||||
let erc721Wrapper: ERC721Wrapper;
|
|
||||||
|
|
||||||
let erc721MakerAssetIds: BigNumber[];
|
|
||||||
let erc721TakerAssetIds: BigNumber[];
|
|
||||||
|
|
||||||
let defaultMakerAssetAddress: string;
|
|
||||||
let defaultTakerAssetAddress: string;
|
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await blockchainLifecycle.startAsync();
|
await blockchainLifecycle.startAsync();
|
||||||
|
coreCombinatorialUtils = await coreCombinatorialUtilsFactoryAsync(web3Wrapper, txDefaults);
|
||||||
});
|
});
|
||||||
after(async () => {
|
after(async () => {
|
||||||
await blockchainLifecycle.revertAsync();
|
await blockchainLifecycle.revertAsync();
|
||||||
});
|
});
|
||||||
before(async () => {
|
|
||||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
|
||||||
usedAddresses = [owner, makerAddress, takerAddress, feeRecipientAddress] = accounts;
|
|
||||||
|
|
||||||
erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner);
|
|
||||||
erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner);
|
|
||||||
|
|
||||||
const erc20EighteenDecimalTokenCount = 3;
|
|
||||||
const eighteenDecimals = new BigNumber(18);
|
|
||||||
[erc20EighteenDecimalTokenA, erc20EighteenDecimalTokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync(
|
|
||||||
erc20EighteenDecimalTokenCount,
|
|
||||||
eighteenDecimals,
|
|
||||||
);
|
|
||||||
|
|
||||||
const erc20FiveDecimalTokenCount = 2;
|
|
||||||
const fiveDecimals = new BigNumber(18);
|
|
||||||
[erc20FiveDecimalTokenA, erc20FiveDecimalTokenB] = await erc20Wrapper.deployDummyTokensAsync(
|
|
||||||
erc20FiveDecimalTokenCount,
|
|
||||||
fiveDecimals,
|
|
||||||
);
|
|
||||||
erc20Proxy = await erc20Wrapper.deployProxyAsync();
|
|
||||||
await erc20Wrapper.setBalancesAndAllowancesAsync();
|
|
||||||
|
|
||||||
[erc721Token] = await erc721Wrapper.deployDummyTokensAsync();
|
|
||||||
erc721Proxy = await erc721Wrapper.deployProxyAsync();
|
|
||||||
await erc721Wrapper.setBalancesAndAllowancesAsync();
|
|
||||||
const erc721Balances = await erc721Wrapper.getBalancesAsync();
|
|
||||||
erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address];
|
|
||||||
erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address];
|
|
||||||
|
|
||||||
exchange = await ExchangeContract.deployFrom0xArtifactAsync(
|
|
||||||
artifacts.Exchange,
|
|
||||||
provider,
|
|
||||||
txDefaults,
|
|
||||||
assetProxyUtils.encodeERC20ProxyData(zrxToken.address),
|
|
||||||
);
|
|
||||||
exchangeWrapper = new ExchangeWrapper(exchange, provider);
|
|
||||||
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner);
|
|
||||||
await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, erc721Proxy.address, owner);
|
|
||||||
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
|
|
||||||
from: owner,
|
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
|
||||||
await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, {
|
|
||||||
from: owner,
|
|
||||||
}),
|
|
||||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
|
||||||
);
|
|
||||||
|
|
||||||
defaultMakerAssetAddress = erc20EighteenDecimalTokenA.address;
|
|
||||||
defaultTakerAssetAddress = erc20EighteenDecimalTokenB.address;
|
|
||||||
|
|
||||||
newOrderFactory = new NewOrderFactory(
|
|
||||||
usedAddresses,
|
|
||||||
zrxToken.address,
|
|
||||||
[erc20EighteenDecimalTokenA.address, erc20EighteenDecimalTokenB.address],
|
|
||||||
[erc20FiveDecimalTokenA.address, erc20FiveDecimalTokenB.address],
|
|
||||||
erc721Token,
|
|
||||||
erc721Balances,
|
|
||||||
exchange.address,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await blockchainLifecycle.startAsync();
|
await blockchainLifecycle.startAsync();
|
||||||
});
|
});
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
await blockchainLifecycle.revertAsync();
|
await blockchainLifecycle.revertAsync();
|
||||||
});
|
});
|
||||||
describe.only('Fill order', () => {
|
const test = (orderScenarios: OrderScenario[]) => {
|
||||||
beforeEach(async () => {
|
_.forEach(orderScenarios, orderScenario => {
|
||||||
erc20Balances = await erc20Wrapper.getBalancesAsync();
|
const description = `Combinatorial OrderFill: ${orderScenario.feeRecipientScenario} ${
|
||||||
|
orderScenario.makerAssetAmountScenario
|
||||||
|
} ${orderScenario.takerAssetAmountScenario} ${orderScenario.makerFeeScenario} ${
|
||||||
|
orderScenario.takerFeeScenario
|
||||||
|
} ${orderScenario.expirationTimeSecondsScenario} ${orderScenario.makerAssetDataScenario} ${
|
||||||
|
orderScenario.takerAssetDataScenario
|
||||||
|
}`;
|
||||||
|
it(description, async () => {
|
||||||
|
const order = coreCombinatorialUtils.orderFactory.generateOrder(orderScenario);
|
||||||
|
await coreCombinatorialUtils.testFillOrderScenarioAsync(order, provider);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => {
|
};
|
||||||
const order = newOrderFactory.generateOrder(
|
|
||||||
FeeRecipientAddressScenario.EthUserAddress,
|
|
||||||
OrderAmountScenario.NonZero,
|
|
||||||
OrderAmountScenario.NonZero,
|
|
||||||
OrderAmountScenario.Zero,
|
|
||||||
OrderAmountScenario.Zero,
|
|
||||||
ExpirationTimeSecondsScenario.InFuture,
|
|
||||||
AssetDataScenario.ERC20NonZRXEighteenDecimals,
|
|
||||||
AssetDataScenario.ERC20NonZRXEighteenDecimals,
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO: Permute signature types
|
const allOrderScenarios = CoreCombinatorialUtils.generateOrderCombinations();
|
||||||
|
|
||||||
// TODO: Sign order (for now simply ECSign)
|
describe.only('Fills orders', () => test(allOrderScenarios));
|
||||||
const orderHashBuff = orderHashUtils.getOrderHashBuff(order);
|
|
||||||
const privateKey = constants.TESTRPC_PRIVATE_KEYS[usedAddresses.indexOf(makerAddress)];
|
|
||||||
const signature = signingUtils.signMessage(orderHashBuff, privateKey, SignatureType.EthSign);
|
|
||||||
const signedOrder = {
|
|
||||||
...order,
|
|
||||||
signature: `0x${signature.toString('hex')}`,
|
|
||||||
};
|
|
||||||
console.log('signedOrder', signedOrder);
|
|
||||||
|
|
||||||
// TODO: Get orderRelevantState
|
|
||||||
const orderInfoUtils = new OrderInfoUtils(exchange, erc20Wrapper, zrxToken.address);
|
|
||||||
// 1. How much of this order can I fill?
|
|
||||||
const fillableTakerAssetAmount = await orderInfoUtils.getFillableTakerAssetAmountAsync(
|
|
||||||
signedOrder,
|
|
||||||
takerAddress,
|
|
||||||
);
|
|
||||||
console.log('fillableTakerAssetAmount', fillableTakerAssetAmount);
|
|
||||||
|
|
||||||
// TODO: Decide how much to fill (all, some)
|
|
||||||
const takerFillAmount = fillableTakerAssetAmount.div(2); // some for now
|
|
||||||
|
|
||||||
// 2. If I fill it by X, what are the resulting balances/allowances/filled amounts expected?
|
|
||||||
// NOTE: we can't use orderStateUtils for this :( We need to do this ourselves.
|
|
||||||
|
|
||||||
// This doesn't include taker balance/allowance checks...
|
|
||||||
/*
|
|
||||||
Inputs:
|
|
||||||
- signedOrder
|
|
||||||
- takerAddress
|
|
||||||
Outputs:
|
|
||||||
- Check fillable amount
|
|
||||||
- maker token balance & allowance
|
|
||||||
- maker ZRX balance & allowance
|
|
||||||
- taker token balance & allowance
|
|
||||||
- taker ZRX balance & allowance
|
|
||||||
Test:
|
|
||||||
- If fillable >= fillAmount:
|
|
||||||
- check that filled by fillAmount
|
|
||||||
- check makerBalance
|
|
||||||
*/
|
|
||||||
|
|
||||||
// signedOrder = orderFactory.newSignedOrder({
|
|
||||||
// makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
|
||||||
// takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
|
||||||
// }); );
|
|
||||||
//
|
|
||||||
// const takerAssetFilledAmountBefore = await exchangeWrapper.getTakerAssetFilledAmountAsync(
|
|
||||||
// orderHashUtils.getOrderHashHex(signedOrder),
|
|
||||||
// );
|
|
||||||
// expect(takerAssetFilledAmountBefore).to.be.bignumber.equal(0);
|
|
||||||
//
|
|
||||||
// const takerAssetFillAmount = signedOrder.takerAssetAmount.div(2);
|
|
||||||
// await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { takerAssetFillAmount });
|
|
||||||
//
|
|
||||||
// const makerAmountBoughtAfter = await exchangeWrapper.getTakerAssetFilledAmountAsync(
|
|
||||||
// orderHashUtils.getOrderHashHex(signedOrder),
|
|
||||||
// );
|
|
||||||
// expect(makerAmountBoughtAfter).to.be.bignumber.equal(takerAssetFillAmount);
|
|
||||||
//
|
|
||||||
// const newBalances = await erc20Wrapper.getBalancesAsync();
|
|
||||||
//
|
|
||||||
// const makerAssetFilledAmount = takerAssetFillAmount
|
|
||||||
// .times(signedOrder.makerAssetAmount)
|
|
||||||
// .dividedToIntegerBy(signedOrder.takerAssetAmount);
|
|
||||||
// const makerFeePaid = signedOrder.makerFee
|
|
||||||
// .times(makerAssetFilledAmount)
|
|
||||||
// .dividedToIntegerBy(signedOrder.makerAssetAmount);
|
|
||||||
// const takerFeePaid = signedOrder.takerFee
|
|
||||||
// .times(makerAssetFilledAmount)
|
|
||||||
// .dividedToIntegerBy(signedOrder.makerAssetAmount);
|
|
||||||
// expect(newBalances[makerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
// erc20Balances[makerAddress][defaultMakerAssetAddress].minus(makerAssetFilledAmount),
|
|
||||||
// );
|
|
||||||
// expect(newBalances[makerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
// erc20Balances[makerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount),
|
|
||||||
// );
|
|
||||||
// expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal(
|
|
||||||
// erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid),
|
|
||||||
// );
|
|
||||||
// expect(newBalances[takerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
// erc20Balances[takerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount),
|
|
||||||
// );
|
|
||||||
// expect(newBalances[takerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal(
|
|
||||||
// erc20Balances[takerAddress][defaultMakerAssetAddress].add(makerAssetFilledAmount),
|
|
||||||
// );
|
|
||||||
// expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal(
|
|
||||||
// erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid),
|
|
||||||
// );
|
|
||||||
// expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
|
|
||||||
// erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)),
|
|
||||||
// );
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { ExchangeContractErrs } from '@0xproject/types';
|
import { AssetProxyId, ExchangeContractErrs } from '@0xproject/types';
|
||||||
import { BigNumber } from '@0xproject/utils';
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
|
||||||
import { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store';
|
import { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store';
|
||||||
|
import { assetProxyUtils } from './asset_proxy_utils';
|
||||||
import { constants } from './constants';
|
import { constants } from './constants';
|
||||||
import { TradeSide, TransferType } from './types';
|
import { TradeSide, TransferType } from './types';
|
||||||
|
|
||||||
@@ -89,8 +90,13 @@ export class ExchangeTransferSimulator {
|
|||||||
userAddress: string,
|
userAddress: string,
|
||||||
amountInBaseUnits: BigNumber,
|
amountInBaseUnits: BigNumber,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const assetProxyId = assetProxyUtils.decodeAssetDataId(assetData);
|
||||||
|
const isERC721Asset = assetProxyId === AssetProxyId.ERC721;
|
||||||
const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, userAddress);
|
const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, userAddress);
|
||||||
if (!proxyAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS)) {
|
// HACK: This code assumes that all tokens with an UNLIMITED_ALLOWANCE_IN_BASE_UNITS set,
|
||||||
|
// are UnlimitedAllowanceTokens. This is however not true, it just so happens that all
|
||||||
|
// DummyERC20Tokens we use in tests are.
|
||||||
|
if (!proxyAllowance.eq(constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS) && !isERC721Asset) {
|
||||||
this._store.setProxyAllowance(assetData, userAddress, proxyAllowance.minus(amountInBaseUnits));
|
this._store.setProxyAllowance(assetData, userAddress, proxyAllowance.minus(amountInBaseUnits));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,43 +7,85 @@ import {
|
|||||||
SignedOrder,
|
SignedOrder,
|
||||||
} from '@0xproject/types';
|
} from '@0xproject/types';
|
||||||
import { BigNumber } from '@0xproject/utils';
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
|
import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
|
||||||
import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
|
import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
|
||||||
import { assetProxyUtils } from './asset_proxy_utils';
|
import { constants } from './constants';
|
||||||
import { orderHashUtils } from './order_hash';
|
import { orderHashUtils } from './order_hash';
|
||||||
import { RemainingFillableCalculator } from './remaining_fillable_calculator';
|
import { RemainingFillableCalculator } from './remaining_fillable_calculator';
|
||||||
|
|
||||||
|
interface SidedOrderRelevantState {
|
||||||
|
isMakerSide: boolean;
|
||||||
|
traderBalance: BigNumber;
|
||||||
|
traderProxyAllowance: BigNumber;
|
||||||
|
traderFeeBalance: BigNumber;
|
||||||
|
traderFeeProxyAllowance: BigNumber;
|
||||||
|
filledTakerAssetAmount: BigNumber;
|
||||||
|
remainingFillableAssetAmount: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001;
|
const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001;
|
||||||
|
|
||||||
export class OrderStateUtils {
|
export class OrderStateUtils {
|
||||||
private _balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher;
|
private _balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher;
|
||||||
private _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher;
|
private _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher;
|
||||||
private static _validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void {
|
private static _validateIfOrderIsValid(
|
||||||
const availableTakerAssetAmount = signedOrder.takerAssetAmount.minus(orderRelevantState.filledTakerAssetAmount);
|
signedOrder: SignedOrder,
|
||||||
|
sidedOrderRelevantState: SidedOrderRelevantState,
|
||||||
|
): void {
|
||||||
|
const isMakerSide = sidedOrderRelevantState.isMakerSide;
|
||||||
|
const availableTakerAssetAmount = signedOrder.takerAssetAmount.minus(
|
||||||
|
sidedOrderRelevantState.filledTakerAssetAmount,
|
||||||
|
);
|
||||||
if (availableTakerAssetAmount.eq(0)) {
|
if (availableTakerAssetAmount.eq(0)) {
|
||||||
throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
|
throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (orderRelevantState.makerBalance.eq(0)) {
|
if (sidedOrderRelevantState.traderBalance.eq(0)) {
|
||||||
throw new Error(ExchangeContractErrs.InsufficientMakerBalance);
|
throw new Error(
|
||||||
|
isMakerSide
|
||||||
|
? ExchangeContractErrs.InsufficientMakerBalance
|
||||||
|
: ExchangeContractErrs.InsufficientTakerBalance,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (orderRelevantState.makerProxyAllowance.eq(0)) {
|
if (sidedOrderRelevantState.traderProxyAllowance.eq(0)) {
|
||||||
throw new Error(ExchangeContractErrs.InsufficientMakerAllowance);
|
throw new Error(
|
||||||
|
isMakerSide
|
||||||
|
? ExchangeContractErrs.InsufficientMakerAllowance
|
||||||
|
: ExchangeContractErrs.InsufficientTakerAllowance,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!signedOrder.makerFee.eq(0)) {
|
if (!signedOrder.makerFee.eq(0)) {
|
||||||
if (orderRelevantState.makerFeeBalance.eq(0)) {
|
if (sidedOrderRelevantState.traderFeeBalance.eq(0)) {
|
||||||
throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance);
|
throw new Error(
|
||||||
|
isMakerSide
|
||||||
|
? ExchangeContractErrs.InsufficientMakerFeeBalance
|
||||||
|
: ExchangeContractErrs.InsufficientTakerFeeBalance,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (orderRelevantState.makerFeeProxyAllowance.eq(0)) {
|
if (sidedOrderRelevantState.traderFeeProxyAllowance.eq(0)) {
|
||||||
throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance);
|
throw new Error(
|
||||||
|
isMakerSide
|
||||||
|
? ExchangeContractErrs.InsufficientMakerFeeAllowance
|
||||||
|
: ExchangeContractErrs.InsufficientTakerFeeAllowance,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const minFillableTakerAssetAmountWithinNoRoundingErrorRange = signedOrder.takerAssetAmount
|
|
||||||
.dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
|
let minFillableTakerAssetAmountWithinNoRoundingErrorRange;
|
||||||
.dividedBy(signedOrder.makerAssetAmount);
|
if (isMakerSide) {
|
||||||
|
minFillableTakerAssetAmountWithinNoRoundingErrorRange = signedOrder.takerAssetAmount
|
||||||
|
.dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
|
||||||
|
.dividedBy(signedOrder.makerAssetAmount);
|
||||||
|
} else {
|
||||||
|
minFillableTakerAssetAmountWithinNoRoundingErrorRange = signedOrder.makerAssetAmount
|
||||||
|
.dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
|
||||||
|
.dividedBy(signedOrder.takerAssetAmount);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
orderRelevantState.remainingFillableTakerAssetAmount.lessThan(
|
sidedOrderRelevantState.remainingFillableAssetAmount.lessThan(
|
||||||
minFillableTakerAssetAmountWithinNoRoundingErrorRange,
|
minFillableTakerAssetAmountWithinNoRoundingErrorRange,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
@@ -57,11 +99,20 @@ export class OrderStateUtils {
|
|||||||
this._balanceAndProxyAllowanceFetcher = balanceAndProxyAllowanceFetcher;
|
this._balanceAndProxyAllowanceFetcher = balanceAndProxyAllowanceFetcher;
|
||||||
this._orderFilledCancelledFetcher = orderFilledCancelledFetcher;
|
this._orderFilledCancelledFetcher = orderFilledCancelledFetcher;
|
||||||
}
|
}
|
||||||
public async getOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> {
|
public async getOpenOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> {
|
||||||
const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder);
|
const orderRelevantState = await this.getOpenOrderRelevantStateAsync(signedOrder);
|
||||||
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
|
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
|
||||||
|
const sidedOrderRelevantState = {
|
||||||
|
isMakerSide: true,
|
||||||
|
traderBalance: orderRelevantState.makerBalance,
|
||||||
|
traderProxyAllowance: orderRelevantState.makerProxyAllowance,
|
||||||
|
traderFeeBalance: orderRelevantState.makerFeeBalance,
|
||||||
|
traderFeeProxyAllowance: orderRelevantState.makerFeeProxyAllowance,
|
||||||
|
filledTakerAssetAmount: orderRelevantState.filledTakerAssetAmount,
|
||||||
|
remainingFillableAssetAmount: orderRelevantState.remainingFillableMakerAssetAmount,
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
OrderStateUtils._validateIfOrderIsValid(signedOrder, orderRelevantState);
|
OrderStateUtils._validateIfOrderIsValid(signedOrder, sidedOrderRelevantState);
|
||||||
const orderState: OrderStateValid = {
|
const orderState: OrderStateValid = {
|
||||||
isValid: true,
|
isValid: true,
|
||||||
orderHash,
|
orderHash,
|
||||||
@@ -77,63 +128,152 @@ export class OrderStateUtils {
|
|||||||
return orderState;
|
return orderState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public async getOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
|
public async getOpenOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
|
||||||
const zrxTokenAddress = this._orderFilledCancelledFetcher.getZRXTokenAddress();
|
const isMaker = true;
|
||||||
const makerProxyData = assetProxyUtils.decodeERC20AssetData(signedOrder.makerAssetData);
|
const sidedOrderRelevantState = await this._getSidedOrderRelevantStateAsync(
|
||||||
const makerAssetAddress = makerProxyData.tokenAddress;
|
isMaker,
|
||||||
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
|
signedOrder,
|
||||||
const makerBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
|
signedOrder.takerAddress,
|
||||||
makerAssetAddress,
|
|
||||||
signedOrder.makerAddress,
|
|
||||||
);
|
);
|
||||||
const makerProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
|
const remainingFillableTakerAssetAmount = sidedOrderRelevantState.remainingFillableAssetAmount
|
||||||
makerAssetAddress,
|
|
||||||
signedOrder.makerAddress,
|
|
||||||
);
|
|
||||||
const makerFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
|
|
||||||
zrxTokenAddress,
|
|
||||||
signedOrder.makerAddress,
|
|
||||||
);
|
|
||||||
const makerFeeProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
|
|
||||||
zrxTokenAddress,
|
|
||||||
signedOrder.makerAddress,
|
|
||||||
);
|
|
||||||
const filledTakerAssetAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
|
|
||||||
const isOrderCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(orderHash);
|
|
||||||
const totalMakerAssetAmount = signedOrder.makerAssetAmount;
|
|
||||||
const totalTakerAssetAmount = signedOrder.takerAssetAmount;
|
|
||||||
const remainingTakerAssetAmount = isOrderCancelled
|
|
||||||
? new BigNumber(0)
|
|
||||||
: totalTakerAssetAmount.minus(filledTakerAssetAmount);
|
|
||||||
const remainingMakerAssetAmount = remainingTakerAssetAmount
|
|
||||||
.times(totalMakerAssetAmount)
|
|
||||||
.dividedToIntegerBy(totalTakerAssetAmount);
|
|
||||||
const transferrableMakerAssetAmount = BigNumber.min([makerProxyAllowance, makerBalance]);
|
|
||||||
const transferrableFeeAssetAmount = BigNumber.min([makerFeeProxyAllowance, makerFeeBalance]);
|
|
||||||
|
|
||||||
const zrxAssetData = assetProxyUtils.encodeERC20AssetData(zrxTokenAddress);
|
|
||||||
const isMakerAssetZRX = signedOrder.makerAssetData === zrxAssetData;
|
|
||||||
const remainingFillableCalculator = new RemainingFillableCalculator(
|
|
||||||
signedOrder.makerFee,
|
|
||||||
signedOrder.makerAssetAmount,
|
|
||||||
isMakerAssetZRX,
|
|
||||||
transferrableMakerAssetAmount,
|
|
||||||
transferrableFeeAssetAmount,
|
|
||||||
remainingMakerAssetAmount,
|
|
||||||
);
|
|
||||||
const remainingFillableMakerAssetAmount = remainingFillableCalculator.computeRemainingFillable();
|
|
||||||
const remainingFillableTakerAssetAmount = remainingFillableMakerAssetAmount
|
|
||||||
.times(signedOrder.takerAssetAmount)
|
.times(signedOrder.takerAssetAmount)
|
||||||
.dividedToIntegerBy(signedOrder.makerAssetAmount);
|
.dividedToIntegerBy(signedOrder.makerAssetAmount);
|
||||||
|
|
||||||
const orderRelevantState = {
|
const orderRelevantState = {
|
||||||
makerBalance,
|
makerBalance: sidedOrderRelevantState.traderBalance,
|
||||||
makerProxyAllowance,
|
makerProxyAllowance: sidedOrderRelevantState.traderProxyAllowance,
|
||||||
makerFeeBalance,
|
makerFeeBalance: sidedOrderRelevantState.traderFeeBalance,
|
||||||
makerFeeProxyAllowance,
|
makerFeeProxyAllowance: sidedOrderRelevantState.traderFeeProxyAllowance,
|
||||||
filledTakerAssetAmount,
|
filledTakerAssetAmount: sidedOrderRelevantState.filledTakerAssetAmount,
|
||||||
remainingFillableMakerAssetAmount,
|
remainingFillableMakerAssetAmount: sidedOrderRelevantState.remainingFillableAssetAmount,
|
||||||
remainingFillableTakerAssetAmount,
|
remainingFillableTakerAssetAmount,
|
||||||
};
|
};
|
||||||
return orderRelevantState;
|
return orderRelevantState;
|
||||||
}
|
}
|
||||||
|
public async getMaxFillableTakerAssetAmountAsync(
|
||||||
|
signedOrder: SignedOrder,
|
||||||
|
takerAddress: string,
|
||||||
|
): Promise<BigNumber> {
|
||||||
|
// Get max fillable amount for an order, considering the makers ability to fill
|
||||||
|
let isMaker = true;
|
||||||
|
const orderRelevantMakerState = await this._getSidedOrderRelevantStateAsync(
|
||||||
|
isMaker,
|
||||||
|
signedOrder,
|
||||||
|
signedOrder.takerAddress,
|
||||||
|
);
|
||||||
|
const remainingFillableTakerAssetAmountGivenMakersStatus = orderRelevantMakerState.remainingFillableAssetAmount;
|
||||||
|
|
||||||
|
// Get max fillable amount for an order, considering the takers ability to fill
|
||||||
|
isMaker = false;
|
||||||
|
const orderRelevantTakerState = await this._getSidedOrderRelevantStateAsync(isMaker, signedOrder, takerAddress);
|
||||||
|
const remainingFillableTakerAssetAmountGivenTakersStatus = orderRelevantTakerState.remainingFillableAssetAmount;
|
||||||
|
|
||||||
|
// The min of these two in the actualy max fillable by either party
|
||||||
|
const fillableTakerAssetAmount = BigNumber.min([
|
||||||
|
remainingFillableTakerAssetAmountGivenMakersStatus,
|
||||||
|
remainingFillableTakerAssetAmountGivenTakersStatus,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return fillableTakerAssetAmount;
|
||||||
|
}
|
||||||
|
public async getMaxFillableTakerAssetAmountForFailingOrderAsync(
|
||||||
|
signedOrder: SignedOrder,
|
||||||
|
takerAddress: string,
|
||||||
|
): Promise<BigNumber> {
|
||||||
|
// Get min of taker balance & allowance
|
||||||
|
const takerAssetBalanceOfTaker = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
|
||||||
|
signedOrder.takerAssetData,
|
||||||
|
takerAddress,
|
||||||
|
);
|
||||||
|
const takerAssetAllowanceOfTaker = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
|
||||||
|
signedOrder.takerAssetData,
|
||||||
|
takerAddress,
|
||||||
|
);
|
||||||
|
const minTakerAssetAmount = BigNumber.min([takerAssetBalanceOfTaker, takerAssetAllowanceOfTaker]);
|
||||||
|
|
||||||
|
// get remainingFillAmount
|
||||||
|
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
|
||||||
|
const filledTakerAssetAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
|
||||||
|
const remainingFillTakerAssetAmount = signedOrder.takerAssetAmount.minus(filledTakerAssetAmount);
|
||||||
|
|
||||||
|
if (minTakerAssetAmount.gte(remainingFillTakerAssetAmount)) {
|
||||||
|
return remainingFillTakerAssetAmount;
|
||||||
|
} else {
|
||||||
|
return minTakerAssetAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private async _getSidedOrderRelevantStateAsync(
|
||||||
|
isMakerSide: boolean,
|
||||||
|
signedOrder: SignedOrder,
|
||||||
|
takerAddress: string,
|
||||||
|
): Promise<SidedOrderRelevantState> {
|
||||||
|
let traderAddress;
|
||||||
|
let assetData;
|
||||||
|
let assetAmount;
|
||||||
|
let feeAmount;
|
||||||
|
if (isMakerSide) {
|
||||||
|
traderAddress = signedOrder.makerAddress;
|
||||||
|
assetData = signedOrder.makerAssetData;
|
||||||
|
assetAmount = signedOrder.makerAssetAmount;
|
||||||
|
feeAmount = signedOrder.makerFee;
|
||||||
|
} else {
|
||||||
|
traderAddress = takerAddress;
|
||||||
|
assetData = signedOrder.takerAssetData;
|
||||||
|
assetAmount = signedOrder.takerAssetAmount;
|
||||||
|
feeAmount = signedOrder.takerFee;
|
||||||
|
}
|
||||||
|
const zrxAssetData = this._orderFilledCancelledFetcher.getZRXAssetData();
|
||||||
|
const isAssetZRX = assetData === zrxAssetData;
|
||||||
|
|
||||||
|
const traderBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress);
|
||||||
|
const traderProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
|
||||||
|
assetData,
|
||||||
|
traderAddress,
|
||||||
|
);
|
||||||
|
const traderFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
|
||||||
|
zrxAssetData,
|
||||||
|
traderAddress,
|
||||||
|
);
|
||||||
|
const traderFeeProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
|
||||||
|
zrxAssetData,
|
||||||
|
traderAddress,
|
||||||
|
);
|
||||||
|
|
||||||
|
const transferrableTraderAssetAmount = BigNumber.min([traderProxyAllowance, traderBalance]);
|
||||||
|
const transferrableFeeAssetAmount = BigNumber.min([traderFeeProxyAllowance, traderFeeBalance]);
|
||||||
|
|
||||||
|
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
|
||||||
|
const filledTakerAssetAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
|
||||||
|
const totalMakerAssetAmount = signedOrder.makerAssetAmount;
|
||||||
|
const totalTakerAssetAmount = signedOrder.takerAssetAmount;
|
||||||
|
const isOrderCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(orderHash);
|
||||||
|
const remainingTakerAssetAmount = isOrderCancelled
|
||||||
|
? new BigNumber(0)
|
||||||
|
: totalTakerAssetAmount.minus(filledTakerAssetAmount);
|
||||||
|
const remainingMakerAssetAmount = remainingTakerAssetAmount.eq(0)
|
||||||
|
? new BigNumber(0)
|
||||||
|
: remainingTakerAssetAmount.times(totalMakerAssetAmount).dividedToIntegerBy(totalTakerAssetAmount);
|
||||||
|
const remainingAssetAmount = isMakerSide ? remainingMakerAssetAmount : remainingTakerAssetAmount;
|
||||||
|
|
||||||
|
const remainingFillableCalculator = new RemainingFillableCalculator(
|
||||||
|
feeAmount,
|
||||||
|
assetAmount,
|
||||||
|
isAssetZRX,
|
||||||
|
transferrableTraderAssetAmount,
|
||||||
|
transferrableFeeAssetAmount,
|
||||||
|
remainingAssetAmount,
|
||||||
|
);
|
||||||
|
const remainingFillableAssetAmount = remainingFillableCalculator.computeRemainingFillable();
|
||||||
|
|
||||||
|
const sidedOrderRelevantState = {
|
||||||
|
isMakerSide,
|
||||||
|
traderBalance,
|
||||||
|
traderProxyAllowance,
|
||||||
|
traderFeeBalance,
|
||||||
|
traderFeeProxyAllowance,
|
||||||
|
filledTakerAssetAmount,
|
||||||
|
remainingFillableAssetAmount,
|
||||||
|
};
|
||||||
|
return sidedOrderRelevantState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user