import { assetProxyUtils, generatePseudoRandomSalt } from '@0xproject/order-utils'; 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, ERC721TokenIdsByOwner, ExpirationTimeSecondsScenario, FeeRecipientAddressScenario, OrderAmountScenario, } from './types'; const TEN_UNITS_EIGHTEEN_DECIMALS = new BigNumber(10000000000000000000); const POINT_ONE_UNITS_EIGHTEEN_DECIMALS = new BigNumber(100000000000000000); const TEN_UNITS_FIVE_DECIMALS = new BigNumber(1000000); 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 { 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, 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, makerFeeScenario: OrderAmountScenario, takerFeeScenario: OrderAmountScenario, expirationTimeSecondsScenario: ExpirationTimeSecondsScenario, 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; let makerFee; let takerFee; let expirationTimeSeconds; let makerAssetData; let takerAssetData; switch (feeRecipientScenario) { case FeeRecipientAddressScenario.BurnAddress: feeRecipientAddress = constants.NULL_ADDRESS; break; case FeeRecipientAddressScenario.EthUserAddress: feeRecipientAddress = this._userAddresses[4]; break; default: throw errorUtils.spawnSwitchErr('FeeRecipientAddressScenario', feeRecipientScenario); } const invalidAssetProxyIdHex = '0A'; switch (makerAssetDataScenario) { case AssetDataScenario.ZRXFeeToken: makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress); break; case AssetDataScenario.ERC20NonZRXEighteenDecimals: makerAssetData = assetProxyUtils.encodeERC20ProxyData( this._nonZrxERC20EighteenDecimalTokenAddresses[0], ); break; case AssetDataScenario.ERC20FiveDecimals: makerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[0]); break; case AssetDataScenario.ERC20InvalidAssetProxyId: { const validAssetData = assetProxyUtils.encodeERC20ProxyData( this._nonZrxERC20EighteenDecimalTokenAddresses[0], ); makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; break; } case AssetDataScenario.ERC721ValidAssetProxyId: makerAssetData = assetProxyUtils.encodeERC721ProxyData( this._erc721Token.address, erc721MakerAssetIds[0], ); break; case AssetDataScenario.ERC721InvalidAssetProxyId: { const validAssetData = assetProxyUtils.encodeERC721ProxyData( this._erc721Token.address, erc721MakerAssetIds[0], ); makerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; break; } default: throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario); } switch (takerAssetDataScenario) { case AssetDataScenario.ZRXFeeToken: takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._zrxAddress); break; case AssetDataScenario.ERC20NonZRXEighteenDecimals: takerAssetData = assetProxyUtils.encodeERC20ProxyData( this._nonZrxERC20EighteenDecimalTokenAddresses[1], ); break; case AssetDataScenario.ERC20FiveDecimals: takerAssetData = assetProxyUtils.encodeERC20ProxyData(this._erc20FiveDecimalTokenAddresses[1]); break; case AssetDataScenario.ERC20InvalidAssetProxyId: { const validAssetData = assetProxyUtils.encodeERC20ProxyData( this._nonZrxERC20EighteenDecimalTokenAddresses[1], ); takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; break; } case AssetDataScenario.ERC721ValidAssetProxyId: takerAssetData = assetProxyUtils.encodeERC721ProxyData( this._erc721Token.address, erc721TakerAssetIds[0], ); break; case AssetDataScenario.ERC721InvalidAssetProxyId: { const validAssetData = assetProxyUtils.encodeERC721ProxyData( this._erc721Token.address, erc721TakerAssetIds[0], ); takerAssetData = `${validAssetData.slice(0, -2)}${invalidAssetProxyIdHex}`; break; } default: throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario); } switch (makerAssetAmountScenario) { case OrderAmountScenario.NonZero: switch (makerAssetDataScenario) { case AssetDataScenario.ERC20NonZRXEighteenDecimals: case AssetDataScenario.ERC20InvalidAssetProxyId: makerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; break; case AssetDataScenario.ERC20FiveDecimals: makerAssetAmount = TEN_UNITS_FIVE_DECIMALS; break; case AssetDataScenario.ERC721ValidAssetProxyId: case AssetDataScenario.ERC721InvalidAssetProxyId: makerAssetAmount = ONE_NFT_UNIT; break; default: throw errorUtils.spawnSwitchErr('AssetDataScenario', makerAssetDataScenario); } break; case OrderAmountScenario.Zero: makerAssetAmount = new BigNumber(0); break; default: throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerAssetAmountScenario); } switch (takerAssetAmountScenario) { case OrderAmountScenario.NonZero: switch (takerAssetDataScenario) { case AssetDataScenario.ERC20NonZRXEighteenDecimals: case AssetDataScenario.ERC20InvalidAssetProxyId: takerAssetAmount = TEN_UNITS_EIGHTEEN_DECIMALS; break; case AssetDataScenario.ERC20FiveDecimals: takerAssetAmount = TEN_UNITS_FIVE_DECIMALS; break; case AssetDataScenario.ERC721ValidAssetProxyId: case AssetDataScenario.ERC721InvalidAssetProxyId: takerAssetAmount = ONE_NFT_UNIT; break; default: throw errorUtils.spawnSwitchErr('AssetDataScenario', takerAssetDataScenario); } break; case OrderAmountScenario.Zero: takerAssetAmount = new BigNumber(0); break; default: throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerAssetAmountScenario); } switch (makerFeeScenario) { case OrderAmountScenario.NonZero: makerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; break; case OrderAmountScenario.Zero: makerFee = new BigNumber(0); break; default: throw errorUtils.spawnSwitchErr('OrderAmountScenario', makerFeeScenario); } switch (takerFeeScenario) { case OrderAmountScenario.NonZero: takerFee = POINT_ONE_UNITS_EIGHTEEN_DECIMALS; break; case OrderAmountScenario.Zero: takerFee = new BigNumber(0); break; default: throw errorUtils.spawnSwitchErr('OrderAmountScenario', takerFeeScenario); } switch (expirationTimeSecondsScenario) { case ExpirationTimeSecondsScenario.InFuture: expirationTimeSeconds = new BigNumber(Date.now() + TEN_MINUTES_MS); break; case ExpirationTimeSecondsScenario.InPast: expirationTimeSeconds = new BigNumber(Date.now() - TEN_MINUTES_MS); break; default: throw errorUtils.spawnSwitchErr('ExpirationTimeSecondsScenario', expirationTimeSecondsScenario); } const order: Order = { senderAddress: constants.NULL_ADDRESS, makerAddress, takerAddress, makerFee, takerFee, makerAssetAmount, takerAssetAmount, makerAssetData, takerAssetData, salt: generatePseudoRandomSalt(), exchangeAddress: this._exchangeAddress, feeRecipientAddress, expirationTimeSeconds, }; return order; } }