POC: Generates an order from spec, get's the amount fillable
This commit is contained in:
@@ -25,8 +25,11 @@ export class ERC20Wrapper {
|
|||||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||||
this._contractOwnerAddress = contractOwnerAddress;
|
this._contractOwnerAddress = contractOwnerAddress;
|
||||||
}
|
}
|
||||||
public async deployDummyTokensAsync(): Promise<DummyERC20TokenContract[]> {
|
public async deployDummyTokensAsync(num?: number, decimals?: BigNumber): Promise<DummyERC20TokenContract[]> {
|
||||||
for (let i = 0; i < constants.NUM_DUMMY_ERC20_TO_DEPLOY; i++) {
|
// TODO(fabio): Remove and refactor all tests
|
||||||
|
const finalNum = _.isUndefined(num) ? constants.NUM_DUMMY_ERC20_TO_DEPLOY : num;
|
||||||
|
const finalDecimals = _.isUndefined(decimals) ? constants.DUMMY_TOKEN_DECIMALS : decimals;
|
||||||
|
for (let i = 0; i < finalNum; i++) {
|
||||||
this._dummyTokenContracts.push(
|
this._dummyTokenContracts.push(
|
||||||
await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.DummyERC20Token,
|
artifacts.DummyERC20Token,
|
||||||
@@ -34,7 +37,7 @@ export class ERC20Wrapper {
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
constants.DUMMY_TOKEN_NAME,
|
constants.DUMMY_TOKEN_NAME,
|
||||||
constants.DUMMY_TOKEN_SYMBOL,
|
constants.DUMMY_TOKEN_SYMBOL,
|
||||||
constants.DUMMY_TOKEN_DECIMALS,
|
finalDecimals,
|
||||||
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -73,6 +76,25 @@ export class ERC20Wrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public async getBalanceAsync(owner: string, token: string): Promise<BigNumber> {
|
||||||
|
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === token);
|
||||||
|
if (_.isUndefined(tokenContractIfExists)) {
|
||||||
|
throw new Error(`Token: ${token} was not deployed through ERC20Wrapper`);
|
||||||
|
}
|
||||||
|
const balance = new BigNumber(await tokenContractIfExists.balanceOf.callAsync(owner));
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
|
public async getProxyAllowanceAsync(owner: string, token: string): Promise<BigNumber> {
|
||||||
|
this._validateProxyContractExistsOrThrow();
|
||||||
|
const tokenContractIfExists = _.find(this._dummyTokenContracts, c => c.address === token);
|
||||||
|
if (_.isUndefined(tokenContractIfExists)) {
|
||||||
|
throw new Error(`Token: ${token} was not deployed through ERC20Wrapper`);
|
||||||
|
}
|
||||||
|
const balance = new BigNumber(
|
||||||
|
await tokenContractIfExists.allowance.callAsync(owner, (this._proxyContract as ERC20ProxyContract).address),
|
||||||
|
);
|
||||||
|
return balance;
|
||||||
|
}
|
||||||
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
|
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
|
||||||
this._validateDummyTokenContractsExistOrThrow();
|
this._validateDummyTokenContractsExistOrThrow();
|
||||||
const balancesByOwner: ERC20BalancesByOwner = {};
|
const balancesByOwner: ERC20BalancesByOwner = {};
|
||||||
|
@@ -2,10 +2,12 @@ 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 { constants } from './constants';
|
import { constants } from './constants';
|
||||||
import {
|
import {
|
||||||
AssetDataScenario,
|
AssetDataScenario,
|
||||||
ERC721Token,
|
ERC721TokenIdsByOwner,
|
||||||
ExpirationTimeSecondsScenario,
|
ExpirationTimeSecondsScenario,
|
||||||
FeeRecipientAddressScenario,
|
FeeRecipientAddressScenario,
|
||||||
OrderAmountScenario,
|
OrderAmountScenario,
|
||||||
@@ -17,14 +19,39 @@ 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;
|
const TEN_MINUTES_MS = 1000 * 60 * 10;
|
||||||
|
|
||||||
export const combinatorialUtils = {
|
/*
|
||||||
generateOrder(
|
* 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 {
|
||||||
|
private _userAddresses: string[];
|
||||||
|
private _zrxAddress: string;
|
||||||
|
private _nonZrxERC20EighteenDecimalTokenAddresses: string[];
|
||||||
|
private _erc20FiveDecimalTokenAddresses: string[];
|
||||||
|
private _erc721Token: DummyERC721TokenContract;
|
||||||
|
private _erc721Balances: ERC721TokenIdsByOwner;
|
||||||
|
private _exchangeAddress: string;
|
||||||
|
constructor(
|
||||||
userAddresses: string[],
|
userAddresses: string[],
|
||||||
zrxAddress: string,
|
zrxAddress: string,
|
||||||
nonZrxERC20EighteenDecimalTokenAddress: string,
|
nonZrxERC20EighteenDecimalTokenAddresses: string[],
|
||||||
erc20FiveDecimalTokenAddress: string,
|
erc20FiveDecimalTokenAddresses: string[],
|
||||||
erc721Token: ERC721Token,
|
erc721Token: DummyERC721TokenContract,
|
||||||
|
erc721Balances: ERC721TokenIdsByOwner,
|
||||||
exchangeAddress: string,
|
exchangeAddress: string,
|
||||||
|
) {
|
||||||
|
this._userAddresses = userAddresses;
|
||||||
|
this._zrxAddress = zrxAddress;
|
||||||
|
this._nonZrxERC20EighteenDecimalTokenAddresses = nonZrxERC20EighteenDecimalTokenAddresses;
|
||||||
|
this._erc20FiveDecimalTokenAddresses = erc20FiveDecimalTokenAddresses;
|
||||||
|
this._erc721Token = erc721Token;
|
||||||
|
this._erc721Balances = erc721Balances;
|
||||||
|
this._exchangeAddress = exchangeAddress;
|
||||||
|
}
|
||||||
|
public generateOrder(
|
||||||
feeRecipientScenario: FeeRecipientAddressScenario,
|
feeRecipientScenario: FeeRecipientAddressScenario,
|
||||||
makerAssetAmountScenario: OrderAmountScenario,
|
makerAssetAmountScenario: OrderAmountScenario,
|
||||||
takerAssetAmountScenario: OrderAmountScenario,
|
takerAssetAmountScenario: OrderAmountScenario,
|
||||||
@@ -34,6 +61,10 @@ export const combinatorialUtils = {
|
|||||||
makerAssetDataScenario: AssetDataScenario,
|
makerAssetDataScenario: AssetDataScenario,
|
||||||
takerAssetDataScenario: AssetDataScenario,
|
takerAssetDataScenario: AssetDataScenario,
|
||||||
): Order {
|
): Order {
|
||||||
|
const makerAddress = this._userAddresses[1];
|
||||||
|
const takerAddress = this._userAddresses[2];
|
||||||
|
const erc721MakerAssetIds = this._erc721Balances[makerAddress][this._erc721Token.address];
|
||||||
|
const erc721TakerAssetIds = this._erc721Balances[takerAddress][this._erc721Token.address];
|
||||||
let feeRecipientAddress;
|
let feeRecipientAddress;
|
||||||
let makerAssetAmount;
|
let makerAssetAmount;
|
||||||
let takerAssetAmount;
|
let takerAssetAmount;
|
||||||
@@ -48,7 +79,7 @@ export const combinatorialUtils = {
|
|||||||
feeRecipientAddress = constants.NULL_ADDRESS;
|
feeRecipientAddress = constants.NULL_ADDRESS;
|
||||||
break;
|
break;
|
||||||
case FeeRecipientAddressScenario.EthUserAddress:
|
case FeeRecipientAddressScenario.EthUserAddress:
|
||||||
feeRecipientAddress = userAddresses[4];
|
feeRecipientAddress = this._userAddresses[4];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', feeRecipientScenario);
|
throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', feeRecipientScenario);
|
||||||
@@ -57,24 +88,34 @@ export const combinatorialUtils = {
|
|||||||
const invalidAssetProxyIdHex = '0A';
|
const invalidAssetProxyIdHex = '0A';
|
||||||
switch (makerAssetDataScenario) {
|
switch (makerAssetDataScenario) {
|
||||||
case AssetDataScenario.ZRXFeeToken:
|
case AssetDataScenario.ZRXFeeToken:
|
||||||
makerAssetData = assetProxyUtils.encodeERC20ProxyData(zrxAddress);
|
makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
||||||
makerAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress);
|
makerAssetData = assetProxyUtils.encodeERC20ProxyData(
|
||||||
|
this._nonZrxERC20EighteenDecimalTokenAddresses[0],
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20FiveDecimals:
|
case AssetDataScenario.ERC20FiveDecimals:
|
||||||
makerAssetData = assetProxyUtils.encodeERC20ProxyData(erc20FiveDecimalTokenAddress);
|
makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[0]);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20InvalidAssetProxyId: {
|
case AssetDataScenario.ERC20InvalidAssetProxyId: {
|
||||||
const validAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress);
|
const validAssetData = assetProxyUtils.encodeERC20ProxyData(
|
||||||
|
this._nonZrxERC20EighteenDecimalTokenAddresses[0],
|
||||||
|
);
|
||||||
makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AssetDataScenario.ERC721ValidAssetProxyId:
|
case AssetDataScenario.ERC721ValidAssetProxyId:
|
||||||
makerAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id);
|
makerAssetData = assetProxyUtils.encodeERC721ProxyData(
|
||||||
|
this._erc721Token.address,
|
||||||
|
erc721MakerAssetIds[0],
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC721InvalidAssetProxyId: {
|
case AssetDataScenario.ERC721InvalidAssetProxyId: {
|
||||||
const validAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id);
|
const validAssetData = assetProxyUtils.encodeERC721ProxyData(
|
||||||
|
this._erc721Token.address,
|
||||||
|
erc721MakerAssetIds[0],
|
||||||
|
);
|
||||||
makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -84,24 +125,34 @@ export const combinatorialUtils = {
|
|||||||
|
|
||||||
switch (takerAssetDataScenario) {
|
switch (takerAssetDataScenario) {
|
||||||
case AssetDataScenario.ZRXFeeToken:
|
case AssetDataScenario.ZRXFeeToken:
|
||||||
takerAssetData = assetProxyUtils.encodeERC20ProxyData(zrxAddress);
|
takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
case AssetDataScenario.ERC20NonZRXEighteenDecimals:
|
||||||
takerAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress);
|
takerAssetData = assetProxyUtils.encodeERC20ProxyData(
|
||||||
|
this._nonZrxERC20EighteenDecimalTokenAddresses[1],
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20FiveDecimals:
|
case AssetDataScenario.ERC20FiveDecimals:
|
||||||
takerAssetData = assetProxyUtils.encodeERC20ProxyData(erc20FiveDecimalTokenAddress);
|
takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[1]);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC20InvalidAssetProxyId: {
|
case AssetDataScenario.ERC20InvalidAssetProxyId: {
|
||||||
const validAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress);
|
const validAssetData = assetProxyUtils.encodeERC20ProxyData(
|
||||||
|
this._nonZrxERC20EighteenDecimalTokenAddresses[1],
|
||||||
|
);
|
||||||
takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AssetDataScenario.ERC721ValidAssetProxyId:
|
case AssetDataScenario.ERC721ValidAssetProxyId:
|
||||||
takerAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id);
|
takerAssetData = assetProxyUtils.encodeERC721ProxyData(
|
||||||
|
this._erc721Token.address,
|
||||||
|
erc721TakerAssetIds[0],
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case AssetDataScenario.ERC721InvalidAssetProxyId: {
|
case AssetDataScenario.ERC721InvalidAssetProxyId: {
|
||||||
const validAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id);
|
const validAssetData = assetProxyUtils.encodeERC721ProxyData(
|
||||||
|
this._erc721Token.address,
|
||||||
|
erc721TakerAssetIds[0],
|
||||||
|
);
|
||||||
takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -194,8 +245,8 @@ export const combinatorialUtils = {
|
|||||||
|
|
||||||
const order: Order = {
|
const order: Order = {
|
||||||
senderAddress: constants.NULL_ADDRESS,
|
senderAddress: constants.NULL_ADDRESS,
|
||||||
makerAddress: userAddresses[1],
|
makerAddress,
|
||||||
takerAddress: userAddresses[2],
|
takerAddress,
|
||||||
makerFee,
|
makerFee,
|
||||||
takerFee,
|
takerFee,
|
||||||
makerAssetAmount,
|
makerAssetAmount,
|
||||||
@@ -203,11 +254,11 @@ export const combinatorialUtils = {
|
|||||||
makerAssetData,
|
makerAssetData,
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
salt: generatePseudoRandomSalt(),
|
salt: generatePseudoRandomSalt(),
|
||||||
exchangeAddress,
|
exchangeAddress: this._exchangeAddress,
|
||||||
feeRecipientAddress,
|
feeRecipientAddress,
|
||||||
expirationTimeSeconds,
|
expirationTimeSeconds,
|
||||||
};
|
};
|
||||||
|
|
||||||
return order;
|
return order;
|
||||||
},
|
}
|
||||||
};
|
}
|
44
packages/contracts/src/utils/order_info_utils.ts
Normal file
44
packages/contracts/src/utils/order_info_utils.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@@ -148,11 +148,6 @@ export interface MatchOrder {
|
|||||||
rightSignature: string;
|
rightSignature: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ERC721Token {
|
|
||||||
address: string;
|
|
||||||
id: BigNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combinatorial testing types
|
// Combinatorial testing types
|
||||||
|
|
||||||
export enum FeeRecipientAddressScenario {
|
export enum FeeRecipientAddressScenario {
|
||||||
|
266
packages/contracts/test/combinatorial_tests.ts
Normal file
266
packages/contracts/test/combinatorial_tests.ts
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
import { BlockchainLifecycle } from '@0xproject/dev-utils';
|
||||||
|
import { assetProxyUtils, crypto, orderHashUtils, OrderStateUtils } from '@0xproject/order-utils';
|
||||||
|
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 { constants } from '../src/utils/constants';
|
||||||
|
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';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||||
|
|
||||||
|
describe('Combinatorial tests', () => {
|
||||||
|
let newOrderFactory: NewOrderFactory;
|
||||||
|
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 () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
});
|
||||||
|
after(async () => {
|
||||||
|
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 () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
describe.only('Fill order', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||||
|
});
|
||||||
|
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
|
||||||
|
|
||||||
|
// TODO: Sign order (for now simply ECSign)
|
||||||
|
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)),
|
||||||
|
// );
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Reference in New Issue
Block a user