diff options
author | Brandon Millman <brandon.millman@gmail.com> | 2018-07-24 13:43:26 +0800 |
---|---|---|
committer | Brandon Millman <brandon.millman@gmail.com> | 2018-07-24 13:43:26 +0800 |
commit | e49d136b99cea375052c7278c0bca0df6524d2d8 (patch) | |
tree | d9ef67f50cfa860fd5da76b686aca2a7578f1d27 /packages/contracts/test/utils/forwarder_wrapper.ts | |
parent | 6ffa907f0ef3c94d3ea7d79d99a24939f62e0eb8 (diff) | |
parent | a05b14e4d9659be1cc495ee33fd8962ce773f87f (diff) | |
download | dexon-sol-tools-e49d136b99cea375052c7278c0bca0df6524d2d8.tar dexon-sol-tools-e49d136b99cea375052c7278c0bca0df6524d2d8.tar.gz dexon-sol-tools-e49d136b99cea375052c7278c0bca0df6524d2d8.tar.bz2 dexon-sol-tools-e49d136b99cea375052c7278c0bca0df6524d2d8.tar.lz dexon-sol-tools-e49d136b99cea375052c7278c0bca0df6524d2d8.tar.xz dexon-sol-tools-e49d136b99cea375052c7278c0bca0df6524d2d8.tar.zst dexon-sol-tools-e49d136b99cea375052c7278c0bca0df6524d2d8.zip |
Merge branch 'v2-prototype' into feature/website/jobs-page-part2
* v2-prototype: (38 commits)
Revert "Publish"
Publish
Remove ERC721 callback functions
Use != instead of > in loops, add sanity checks to market fill functions
Add more tests and fixes
Remove MConstants and MixinConstants for LibConstants
Remove redundant external call by reimplementing fillOrderNoThrow
Remove orders length check
Add assertValidFillResults
Update web3Wrapper CHANGELOG
Get actual gasPrice from transaction instead of setting default
Store orders length in varible before looping over orders
Use transferFrom instead of safeTransferFrom
Fix minimal tests
Fix rounding error issues, use different logic when makerAsset is ZRX
Rename marketSellEth => marketSellWeth
Update percentage constants
Update transferEthFeeAndRefund, add check to ERC721 transfer
Refactor forwarding contract architecture, remove batch functions
Updated CHANGELOGS
...
Diffstat (limited to 'packages/contracts/test/utils/forwarder_wrapper.ts')
-rw-r--r-- | packages/contracts/test/utils/forwarder_wrapper.ts | 226 |
1 files changed, 62 insertions, 164 deletions
diff --git a/packages/contracts/test/utils/forwarder_wrapper.ts b/packages/contracts/test/utils/forwarder_wrapper.ts index e39df14b1..ef7476e36 100644 --- a/packages/contracts/test/utils/forwarder_wrapper.ts +++ b/packages/contracts/test/utils/forwarder_wrapper.ts @@ -1,5 +1,4 @@ -import { assetDataUtils } from '@0xproject/order-utils'; -import { AssetProxyId, SignedOrder } from '@0xproject/types'; +import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Provider, TransactionReceiptWithDecodedLogs, TxDataPayable } from 'ethereum-types'; @@ -12,209 +11,108 @@ import { formatters } from './formatters'; import { LogDecoder } from './log_decoder'; import { MarketSellOrders } from './types'; -const DEFAULT_FEE_PROPORTION = 0; -const PERCENTAGE_DENOMINATOR = 10000; -const ZERO_AMOUNT = new BigNumber(0); -const INSUFFICENT_ORDERS_FOR_MAKER_AMOUNT = 'Unable to satisfy makerAssetFillAmount with provided orders'; - export class ForwarderWrapper { private readonly _web3Wrapper: Web3Wrapper; private readonly _forwarderContract: ForwarderContract; private readonly _logDecoder: LogDecoder; - private readonly _zrxAddress: string; - private static _createOptimizedSellOrders(signedOrders: SignedOrder[]): MarketSellOrders { - const marketSellOrders = formatters.createMarketSellOrders(signedOrders, ZERO_AMOUNT); - const assetDataId = assetDataUtils.decodeAssetProxyId(signedOrders[0].makerAssetData); - // Contract will fill this in for us as all of the assetData is assumed to be the same - for (let i = 0; i < signedOrders.length; i++) { - if (i !== 0 && assetDataId === AssetProxyId.ERC20) { - // Forwarding contract will fill this in from the first order - marketSellOrders.orders[i].makerAssetData = constants.NULL_BYTES; + public static getPercentageOfValue(value: BigNumber, percentage: number): BigNumber { + const numerator = constants.PERCENTAGE_DENOMINATOR.times(percentage).dividedToIntegerBy(100); + const newValue = value.times(numerator).dividedToIntegerBy(constants.PERCENTAGE_DENOMINATOR); + return newValue; + } + public static getWethForFeeOrders(feeAmount: BigNumber, feeOrders: SignedOrder[]): BigNumber { + let wethAmount = new BigNumber(0); + let remainingFeeAmount = feeAmount; + _.forEach(feeOrders, feeOrder => { + const feeAvailable = feeOrder.makerAssetAmount.minus(feeOrder.takerFee); + if (!remainingFeeAmount.isZero() && feeAvailable.gt(remainingFeeAmount)) { + wethAmount = wethAmount + .plus(feeOrder.takerAssetAmount.times(remainingFeeAmount).dividedToIntegerBy(feeAvailable)) + .plus(1); + remainingFeeAmount = new BigNumber(0); + } else if (!remainingFeeAmount.isZero()) { + wethAmount = wethAmount.plus(feeOrder.takerAssetAmount); + remainingFeeAmount = remainingFeeAmount.minus(feeAvailable); } - marketSellOrders.orders[i].takerAssetData = constants.NULL_BYTES; - } - return marketSellOrders; + }); + return wethAmount; } - private static _createOptimizedZRXSellOrders(signedOrders: SignedOrder[]): MarketSellOrders { - const marketSellOrders = formatters.createMarketSellOrders(signedOrders, ZERO_AMOUNT); - // Contract will fill this in for us as all of the assetData is assumed to be the same - for (let i = 0; i < signedOrders.length; i++) { - marketSellOrders.orders[i].makerAssetData = constants.NULL_BYTES; - marketSellOrders.orders[i].takerAssetData = constants.NULL_BYTES; - } - return marketSellOrders; + private static _createOptimizedOrders(signedOrders: SignedOrder[]): MarketSellOrders { + _.forEach(signedOrders, (signedOrder, index) => { + signedOrder.takerAssetData = constants.NULL_BYTES; + if (index > 0) { + signedOrder.makerAssetData = constants.NULL_BYTES; + } + }); + const params = formatters.createMarketSellOrders(signedOrders, constants.ZERO_AMOUNT); + return params; } - private static _calculateAdditionalFeeProportionAmount(feeProportion: number, fillAmountWei: BigNumber): BigNumber { - if (feeProportion > 0) { - // Add to the total ETH transaction to ensure all NFTs can be filled after fees - // 150 = 1.5% = 0.015 - const denominator = new BigNumber(1).minus(new BigNumber(feeProportion).dividedBy(PERCENTAGE_DENOMINATOR)); - return fillAmountWei.dividedBy(denominator).round(0, BigNumber.ROUND_FLOOR); - } - return fillAmountWei; + private static _createOptimizedZrxOrders(signedOrders: SignedOrder[]): MarketSellOrders { + _.forEach(signedOrders, signedOrder => { + signedOrder.makerAssetData = constants.NULL_BYTES; + signedOrder.takerAssetData = constants.NULL_BYTES; + }); + const params = formatters.createMarketSellOrders(signedOrders, constants.ZERO_AMOUNT); + return params; } - constructor(contractInstance: ForwarderContract, provider: Provider, zrxAddress: string) { + constructor(contractInstance: ForwarderContract, provider: Provider) { this._forwarderContract = contractInstance; this._web3Wrapper = new Web3Wrapper(provider); this._logDecoder = new LogDecoder(this._web3Wrapper, this._forwarderContract.address); - // this._web3Wrapper.abiDecoder.addABI(contractInstance.abi); - this._zrxAddress = zrxAddress; } - public async marketBuyTokensWithEthAsync( + public async marketSellOrdersWithEthAsync( orders: SignedOrder[], feeOrders: SignedOrder[], - makerTokenBuyAmount: BigNumber, txData: TxDataPayable, - opts: { feeProportion?: number; feeRecipient?: string } = {}, + opts: { feePercentage?: BigNumber; feeRecipient?: string } = {}, ): Promise<TransactionReceiptWithDecodedLogs> { - const params = ForwarderWrapper._createOptimizedSellOrders(orders); - const feeParams = ForwarderWrapper._createOptimizedZRXSellOrders(feeOrders); - const feeProportion = _.isUndefined(opts.feeProportion) ? DEFAULT_FEE_PROPORTION : opts.feeProportion; + const params = ForwarderWrapper._createOptimizedOrders(orders); + const feeParams = ForwarderWrapper._createOptimizedZrxOrders(feeOrders); + const feePercentage = _.isUndefined(opts.feePercentage) ? constants.ZERO_AMOUNT : opts.feePercentage; const feeRecipient = _.isUndefined(opts.feeRecipient) ? constants.NULL_ADDRESS : opts.feeRecipient; - const txHash: string = await this._forwarderContract.marketBuyTokensWithEth.sendTransactionAsync( + const txHash = await this._forwarderContract.marketSellOrdersWithEth.sendTransactionAsync( params.orders, params.signatures, feeParams.orders, feeParams.signatures, - makerTokenBuyAmount, - feeProportion, + feePercentage, feeRecipient, txData, ); const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); return tx; } - public async marketSellEthForERC20Async( + public async marketBuyOrdersWithEthAsync( orders: SignedOrder[], feeOrders: SignedOrder[], + makerAssetFillAmount: BigNumber, txData: TxDataPayable, - opts: { feeProportion?: number; feeRecipient?: string } = {}, + opts: { feePercentage?: BigNumber; feeRecipient?: string } = {}, ): Promise<TransactionReceiptWithDecodedLogs> { - const assetDataId = assetDataUtils.decodeAssetProxyId(orders[0].makerAssetData); - if (assetDataId !== AssetProxyId.ERC20) { - throw new Error('Asset type not supported by marketSellEthForERC20'); - } - const params = ForwarderWrapper._createOptimizedSellOrders(orders); - const feeParams = ForwarderWrapper._createOptimizedZRXSellOrders(feeOrders); - const feeProportion = _.isUndefined(opts.feeProportion) ? DEFAULT_FEE_PROPORTION : opts.feeProportion; + const params = ForwarderWrapper._createOptimizedOrders(orders); + const feeParams = ForwarderWrapper._createOptimizedZrxOrders(feeOrders); + const feePercentage = _.isUndefined(opts.feePercentage) ? constants.ZERO_AMOUNT : opts.feePercentage; const feeRecipient = _.isUndefined(opts.feeRecipient) ? constants.NULL_ADDRESS : opts.feeRecipient; - const txHash: string = await this._forwarderContract.marketSellEthForERC20.sendTransactionAsync( + const txHash = await this._forwarderContract.marketBuyOrdersWithEth.sendTransactionAsync( params.orders, + makerAssetFillAmount, params.signatures, feeParams.orders, feeParams.signatures, - feeProportion, + feePercentage, feeRecipient, txData, ); const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); return tx; } - public async calculateMarketBuyFillAmountWeiAsync( - orders: SignedOrder[], - feeOrders: SignedOrder[], - feeProportion: number, - makerAssetFillAmount: BigNumber, - ): Promise<BigNumber> { - const assetProxyId = assetDataUtils.decodeAssetProxyId(orders[0].makerAssetData); - switch (assetProxyId) { - case AssetProxyId.ERC20: { - const fillAmountWei = this._calculateMarketBuyERC20FillAmountAsync( - orders, - feeOrders, - feeProportion, - makerAssetFillAmount, - ); - return fillAmountWei; - } - case AssetProxyId.ERC721: { - const fillAmountWei = await this._calculateMarketBuyERC721FillAmountAsync( - orders, - feeOrders, - feeProportion, - ); - return fillAmountWei; - } - default: - throw new Error(`Invalid Asset Proxy Id: ${assetProxyId}`); - } - } - private async _calculateMarketBuyERC20FillAmountAsync( - orders: SignedOrder[], - feeOrders: SignedOrder[], - feeProportion: number, - makerAssetFillAmount: BigNumber, - ): Promise<BigNumber> { - const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(orders[0].makerAssetData); - const makerAssetToken = makerAssetData.tokenAddress; - const params = formatters.createMarketBuyOrders(orders, makerAssetFillAmount); - - let fillAmountWei; - if (makerAssetToken === this._zrxAddress) { - // If buying ZRX we buy the tokens and fees from the ZRX order in one step - const expectedBuyFeeTokensFillResults = await this._forwarderContract.calculateMarketBuyZrxResults.callAsync( - params.orders, - makerAssetFillAmount, - ); - if (expectedBuyFeeTokensFillResults.makerAssetFilledAmount.lessThan(makerAssetFillAmount)) { - throw new Error(INSUFFICENT_ORDERS_FOR_MAKER_AMOUNT); - } - fillAmountWei = expectedBuyFeeTokensFillResults.takerAssetFilledAmount; - } else { - const expectedMarketBuyFillResults = await this._forwarderContract.calculateMarketBuyResults.callAsync( - params.orders, - makerAssetFillAmount, - ); - if (expectedMarketBuyFillResults.makerAssetFilledAmount.lessThan(makerAssetFillAmount)) { - throw new Error(INSUFFICENT_ORDERS_FOR_MAKER_AMOUNT); - } - fillAmountWei = expectedMarketBuyFillResults.takerAssetFilledAmount; - const expectedFeeAmount = expectedMarketBuyFillResults.takerFeePaid; - if (expectedFeeAmount.greaterThan(ZERO_AMOUNT)) { - const expectedFeeFillFillAmountWei = await this._calculateMarketBuyERC20FillAmountAsync( - feeOrders, - [], - DEFAULT_FEE_PROPORTION, - expectedFeeAmount, - ); - fillAmountWei = fillAmountWei.plus(expectedFeeFillFillAmountWei); - } - } - fillAmountWei = ForwarderWrapper._calculateAdditionalFeeProportionAmount(feeProportion, fillAmountWei); - return fillAmountWei; - } - private async _calculateMarketBuyERC721FillAmountAsync( - orders: SignedOrder[], - feeOrders: SignedOrder[], - feeProportion: number, - ): Promise<BigNumber> { - // Total cost when buying ERC721 is the total cost of all ERC721 orders + any fee abstraction - let fillAmountWei = _.reduce( - orders, - (totalAmount: BigNumber, order: SignedOrder) => { - return totalAmount.plus(order.takerAssetAmount); - }, - ZERO_AMOUNT, - ); - const totalFees = _.reduce( - orders, - (totalAmount: BigNumber, order: SignedOrder) => { - return totalAmount.plus(order.takerFee); - }, - ZERO_AMOUNT, - ); - if (totalFees.greaterThan(ZERO_AMOUNT)) { - // Calculate the ZRX fee abstraction cost - const emptyFeeOrders: SignedOrder[] = []; - const expectedFeeAmountWei = await this._calculateMarketBuyERC20FillAmountAsync( - feeOrders, - emptyFeeOrders, - DEFAULT_FEE_PROPORTION, - totalFees, - ); - fillAmountWei = fillAmountWei.plus(expectedFeeAmountWei); - } - fillAmountWei = ForwarderWrapper._calculateAdditionalFeeProportionAmount(feeProportion, fillAmountWei); - return fillAmountWei; + public async withdrawERC20Async( + tokenAddress: string, + amount: BigNumber, + txData: TxDataPayable, + ): Promise<TransactionReceiptWithDecodedLogs> { + const txHash = await this._forwarderContract.withdrawERC20.sendTransactionAsync(tokenAddress, amount, txData); + const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); + return tx; } } |