diff options
author | Fabio Berger <me@fabioberger.com> | 2018-06-08 00:00:13 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-06-08 00:00:13 +0800 |
commit | ab5e021bda5cc8e39d8595580c09c3540a09aff5 (patch) | |
tree | e40c3be709d10e1a2463cb0807419c7b276f1548 /packages/contracts/src | |
parent | 0fc981400442e4c567ca363bdf0f4c03ba87473d (diff) | |
download | dexon-0x-contracts-ab5e021bda5cc8e39d8595580c09c3540a09aff5.tar.gz dexon-0x-contracts-ab5e021bda5cc8e39d8595580c09c3540a09aff5.tar.zst dexon-0x-contracts-ab5e021bda5cc8e39d8595580c09c3540a09aff5.zip |
POC: Generates an order from spec, get's the amount fillable
Diffstat (limited to 'packages/contracts/src')
-rw-r--r-- | packages/contracts/src/utils/erc20_wrapper.ts | 28 | ||||
-rw-r--r-- | packages/contracts/src/utils/new_order_factory.ts (renamed from packages/contracts/src/utils/combinatorial_utils.ts) | 99 | ||||
-rw-r--r-- | packages/contracts/src/utils/order_info_utils.ts | 44 | ||||
-rw-r--r-- | packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts | 20 | ||||
-rw-r--r-- | packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts | 32 | ||||
-rw-r--r-- | packages/contracts/src/utils/types.ts | 5 |
6 files changed, 196 insertions, 32 deletions
diff --git a/packages/contracts/src/utils/erc20_wrapper.ts b/packages/contracts/src/utils/erc20_wrapper.ts index dceeceeea..efb245c89 100644 --- a/packages/contracts/src/utils/erc20_wrapper.ts +++ b/packages/contracts/src/utils/erc20_wrapper.ts @@ -25,8 +25,11 @@ export class ERC20Wrapper { this._tokenOwnerAddresses = tokenOwnerAddresses; this._contractOwnerAddress = contractOwnerAddress; } - public async deployDummyTokensAsync(): Promise<DummyERC20TokenContract[]> { - for (let i = 0; i < constants.NUM_DUMMY_ERC20_TO_DEPLOY; i++) { + public async deployDummyTokensAsync(num?: number, decimals?: BigNumber): Promise<DummyERC20TokenContract[]> { + // 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( await DummyERC20TokenContract.deployFrom0xArtifactAsync( artifacts.DummyERC20Token, @@ -34,7 +37,7 @@ export class ERC20Wrapper { txDefaults, constants.DUMMY_TOKEN_NAME, constants.DUMMY_TOKEN_SYMBOL, - constants.DUMMY_TOKEN_DECIMALS, + finalDecimals, 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> { this._validateDummyTokenContractsExistOrThrow(); const balancesByOwner: ERC20BalancesByOwner = {}; diff --git a/packages/contracts/src/utils/combinatorial_utils.ts b/packages/contracts/src/utils/new_order_factory.ts index c7d48c86d..a4ded4230 100644 --- a/packages/contracts/src/utils/combinatorial_utils.ts +++ b/packages/contracts/src/utils/new_order_factory.ts @@ -2,10 +2,12 @@ import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-util import { Order } from '@0xproject/types'; import { BigNumber, errorUtils } from '@0xproject/utils'; +import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token'; + import { constants } from './constants'; import { AssetDataScenario, - ERC721Token, + ERC721TokenIdsByOwner, ExpirationTimeSecondsScenario, FeeRecipientAddressScenario, OrderAmountScenario, @@ -17,14 +19,39 @@ const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000); const ONE_NFT_UNIT = new BigNumber(1); 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[], zrxAddress: string, - nonZrxERC20EighteenDecimalTokenAddress: string, - erc20FiveDecimalTokenAddress: string, - erc721Token: ERC721Token, + nonZrxERC20EighteenDecimalTokenAddresses: string[], + erc20FiveDecimalTokenAddresses: string[], + erc721Token: DummyERC721TokenContract, + erc721Balances: ERC721TokenIdsByOwner, 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, makerAssetAmountScenario: OrderAmountScenario, takerAssetAmountScenario: OrderAmountScenario, @@ -34,6 +61,10 @@ export const combinatorialUtils = { makerAssetDataScenario: AssetDataScenario, takerAssetDataScenario: AssetDataScenario, ): 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 makerAssetAmount; let takerAssetAmount; @@ -48,7 +79,7 @@ export const combinatorialUtils = { feeRecipientAddress = constants.NULL_ADDRESS; break; case FeeRecipientAddressScenario.EthUserAddress: - feeRecipientAddress = userAddresses[4]; + feeRecipientAddress = this._userAddresses[4]; break; default: throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', feeRecipientScenario); @@ -57,24 +88,34 @@ export const combinatorialUtils = { const invalidAssetProxyIdHex = '0A'; switch (makerAssetDataScenario) { case AssetDataScenario.ZRXFeeToken: - makerAssetData = assetProxyUtils.encodeERC20ProxyData(zrxAddress); + makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress); break; case AssetDataScenario.ERC20NonZRXEighteenDecimals: - makerAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); + makerAssetData = assetProxyUtils.encodeERC20ProxyData( + this._nonZrxERC20EighteenDecimalTokenAddresses[0], + ); break; case AssetDataScenario.ERC20FiveDecimals: - makerAssetData = assetProxyUtils.encodeERC20ProxyData(erc20FiveDecimalTokenAddress); + makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[0]); break; case AssetDataScenario.ERC20InvalidAssetProxyId: { - const validAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); + const validAssetData = assetProxyUtils.encodeERC20ProxyData( + this._nonZrxERC20EighteenDecimalTokenAddresses[0], + ); makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; break; } case AssetDataScenario.ERC721ValidAssetProxyId: - makerAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id); + makerAssetData = assetProxyUtils.encodeERC721ProxyData( + this._erc721Token.address, + erc721MakerAssetIds[0], + ); break; 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}`; break; } @@ -84,24 +125,34 @@ export const combinatorialUtils = { switch (takerAssetDataScenario) { case AssetDataScenario.ZRXFeeToken: - takerAssetData = assetProxyUtils.encodeERC20ProxyData(zrxAddress); + takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress); break; case AssetDataScenario.ERC20NonZRXEighteenDecimals: - takerAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); + takerAssetData = assetProxyUtils.encodeERC20ProxyData( + this._nonZrxERC20EighteenDecimalTokenAddresses[1], + ); break; case AssetDataScenario.ERC20FiveDecimals: - takerAssetData = assetProxyUtils.encodeERC20ProxyData(erc20FiveDecimalTokenAddress); + takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[1]); break; case AssetDataScenario.ERC20InvalidAssetProxyId: { - const validAssetData = assetProxyUtils.encodeERC20ProxyData(nonZrxERC20EighteenDecimalTokenAddress); + const validAssetData = assetProxyUtils.encodeERC20ProxyData( + this._nonZrxERC20EighteenDecimalTokenAddresses[1], + ); takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; break; } case AssetDataScenario.ERC721ValidAssetProxyId: - takerAssetData = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721Token.id); + takerAssetData = assetProxyUtils.encodeERC721ProxyData( + this._erc721Token.address, + erc721TakerAssetIds[0], + ); break; 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}`; break; } @@ -194,8 +245,8 @@ export const combinatorialUtils = { const order: Order = { senderAddress: constants.NULL_ADDRESS, - makerAddress: userAddresses[1], - takerAddress: userAddresses[2], + makerAddress, + takerAddress, makerFee, takerFee, makerAssetAmount, @@ -203,11 +254,11 @@ export const combinatorialUtils = { makerAssetData, takerAssetData, salt: generatePseudoRandomSalt(), - exchangeAddress, + exchangeAddress: this._exchangeAddress, feeRecipientAddress, expirationTimeSeconds, }; return order; - }, -}; + } +} diff --git a/packages/contracts/src/utils/order_info_utils.ts b/packages/contracts/src/utils/order_info_utils.ts new file mode 100644 index 000000000..9df627da3 --- /dev/null +++ b/packages/contracts/src/utils/order_info_utils.ts @@ -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; + } +} diff --git a/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts b/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts new file mode 100644 index 000000000..6eed9227a --- /dev/null +++ b/packages/contracts/src/utils/simple_erc20_balance_and_allowance_fetcher.ts @@ -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; + } +} diff --git a/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts b/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts new file mode 100644 index 000000000..0a80637cc --- /dev/null +++ b/packages/contracts/src/utils/simple_filled_cancelled_fetcher.ts @@ -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; + } +} diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 94c7f627f..64f6400c5 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -148,11 +148,6 @@ export interface MatchOrder { rightSignature: string; } -export interface ERC721Token { - address: string; - id: BigNumber; -} - // Combinatorial testing types export enum FeeRecipientAddressScenario { |