diff options
94 files changed, 1521 insertions, 845 deletions
diff --git a/CODEOWNERS b/CODEOWNERS index ba40af31a..3cf75fb2d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -10,20 +10,26 @@ packages/instant/ @BMillman19 @fragosti @steveklebanoff packages/website/ @BMillman19 @fragosti @fabioberger @steveklebanoff # Dev tools & setup +.circleci/ @LogvinovLeon packages/abi-gen/ @LogvinovLeon packages/base-contract/ @LogvinovLeon +packages/connect/ @fragosti packages/contract_templates/ @LogvinovLeon +packages/contract-addresses/ @albrow +packages/contract-artifacts/ @albrow packages/dev-utils/ @LogvinovLeon @fabioberger - +packages/devnet/ @albrow packages/ethereum-types/ @LogvinovLeon packages/metacoin/ @LogvinovLeon +packages/monorepo-scripts/ @fabioberger +packages/order-utils/ @fabioberger @LogvinovLeon packages/sol-compiler/ @LogvinovLeon packages/sol-cov/ @LogvinovLeon packages/sol-resolver/ @LogvinovLeon -packages/web3-wrapper/ @LogvinovLeon @fabioberger -.circleci/ @LogvinovLeon packages/subproviders/ @fabioberger @dekz -packages/connect/ @fragosti -packages/monorepo-scripts/ @fabioberger -packages/order-utils/ @fabioberger @LogvinovLeon -python-packages/ @feuGeneA
\ No newline at end of file +packages/verdaccio/ @albrow +packages/web3-wrapper/ @LogvinovLeon @fabioberger +python-packages/ @feuGeneA + +# Protocol/smart contracts +packages/contracts/test/ @albrow diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 56999d3e5..bfc29128c 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -18,7 +18,7 @@ "build": "yarn build:all", "build:ci": "yarn build:commonjs", "build:all": "run-p build:umd:prod build:commonjs", - "lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/*", + "lint": "tslint --format stylish --project .", "test:circleci": "run-s test:coverage", "rebuild_and_test": "run-s build test", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", diff --git a/packages/abi-gen-wrappers/package.json b/packages/abi-gen-wrappers/package.json index 313cc339d..b8010a8a7 100644 --- a/packages/abi-gen-wrappers/package.json +++ b/packages/abi-gen-wrappers/package.json @@ -12,7 +12,7 @@ "scripts": { "build": "yarn pre_build && tsc -b", "build:ci": "yarn build", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "pre_build": "yarn generate_contract_wrappers", "clean": "shx rm -rf lib wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/generated-wrappers --backend ethers" diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json index f32bb3a31..c78d4f269 100644 --- a/packages/abi-gen/package.json +++ b/packages/abi-gen/package.json @@ -8,7 +8,7 @@ "main": "lib/src/index.js", "types": "lib/src/index.d.ts", "scripts": { - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "clean": "shx rm -rf lib", "build": "tsc -b", "build:ci": "yarn build", diff --git a/packages/assert/package.json b/packages/assert/package.json index 37af8979d..a96f65258 100644 --- a/packages/assert/package.json +++ b/packages/assert/package.json @@ -11,7 +11,7 @@ "build": "tsc -b", "build:ci": "yarn build", "clean": "shx rm -rf lib test_temp", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --exit", "test": "yarn run_mocha", "rebuild_and_test": "run-s clean build test", diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json index e4b4d19c0..dd0668632 100644 --- a/packages/asset-buyer/package.json +++ b/packages/asset-buyer/package.json @@ -10,7 +10,7 @@ "scripts": { "build": "yarn tsc -b", "build:ci": "yarn build", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "test": "yarn run_mocha", "rebuild_and_test": "run-s clean build test", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", diff --git a/packages/asset-buyer/src/asset_buyer.ts b/packages/asset-buyer/src/asset_buyer.ts index 2f9a3108e..74f3cb471 100644 --- a/packages/asset-buyer/src/asset_buyer.ts +++ b/packages/asset-buyer/src/asset_buyer.ts @@ -135,9 +135,14 @@ export class AssetBuyer { assert.isBoolean('shouldForceOrderRefresh', shouldForceOrderRefresh); assert.isNumber('slippagePercentage', slippagePercentage); const zrxTokenAssetData = this._getZrxTokenAssetDataOrThrow(); + const isMakerAssetZrxToken = assetData === zrxTokenAssetData; + // get the relevant orders for the makerAsset and fees + // if the requested assetData is ZRX, don't get the fee info const [ordersAndFillableAmounts, feeOrdersAndFillableAmounts] = await Promise.all([ this._getOrdersAndFillableAmountsAsync(assetData, shouldForceOrderRefresh), - this._getOrdersAndFillableAmountsAsync(zrxTokenAssetData, shouldForceOrderRefresh), + isMakerAssetZrxToken + ? Promise.resolve(constants.EMPTY_ORDERS_AND_FILLABLE_AMOUNTS) + : this._getOrdersAndFillableAmountsAsync(zrxTokenAssetData, shouldForceOrderRefresh), shouldForceOrderRefresh, ]); if (ordersAndFillableAmounts.orders.length === 0) { @@ -149,6 +154,7 @@ export class AssetBuyer { assetBuyAmount, feePercentage, slippagePercentage, + isMakerAssetZrxToken, ); return buyQuote; } diff --git a/packages/asset-buyer/src/constants.ts b/packages/asset-buyer/src/constants.ts index 55effdb23..c912253bd 100644 --- a/packages/asset-buyer/src/constants.ts +++ b/packages/asset-buyer/src/constants.ts @@ -1,6 +1,7 @@ +import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; -import { AssetBuyerOpts, BuyQuoteExecutionOpts, BuyQuoteRequestOpts } from './types'; +import { AssetBuyerOpts, BuyQuoteExecutionOpts, BuyQuoteRequestOpts, OrdersAndFillableAmounts } from './types'; const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'; const MAINNET_NETWORK_ID = 1; @@ -22,6 +23,11 @@ const DEFAULT_BUY_QUOTE_EXECUTION_OPTS: BuyQuoteExecutionOpts = { feeRecipient: NULL_ADDRESS, }; +const EMPTY_ORDERS_AND_FILLABLE_AMOUNTS: OrdersAndFillableAmounts = { + orders: [] as SignedOrder[], + remainingFillableMakerAssetAmounts: [] as BigNumber[], +}; + export const constants = { ZERO_AMOUNT: new BigNumber(0), NULL_ADDRESS, @@ -31,4 +37,5 @@ export const constants = { DEFAULT_BUY_QUOTE_EXECUTION_OPTS, DEFAULT_BUY_QUOTE_REQUEST_OPTS, MAX_PER_PAGE: 10000, + EMPTY_ORDERS_AND_FILLABLE_AMOUNTS, }; diff --git a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts index 91682b089..41fd1fb30 100644 --- a/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts +++ b/packages/asset-buyer/src/order_providers/standard_relayer_api_order_provider.ts @@ -30,7 +30,7 @@ export class StandardRelayerAPIOrderProvider implements OrderProvider { 'remainingTakerAssetAmount', order.takerAssetAmount, ); - const remainingFillableMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( + const remainingFillableMakerAssetAmount = orderUtils.getRemainingMakerAmount( order, remainingFillableTakerAssetAmount, ); diff --git a/packages/asset-buyer/src/utils/buy_quote_calculator.ts b/packages/asset-buyer/src/utils/buy_quote_calculator.ts index b0cb4a547..f94ab3fa4 100644 --- a/packages/asset-buyer/src/utils/buy_quote_calculator.ts +++ b/packages/asset-buyer/src/utils/buy_quote_calculator.ts @@ -1,10 +1,12 @@ -import { marketUtils, rateUtils } from '@0x/order-utils'; +import { marketUtils, SignedOrder } from '@0x/order-utils'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; import { constants } from '../constants'; import { AssetBuyerError, BuyQuote, BuyQuoteInfo, OrdersAndFillableAmounts } from '../types'; +import { orderUtils } from './order_utils'; + // Calculates a buy quote for orders that have WETH as the takerAsset export const buyQuoteCalculator = { calculate( @@ -13,6 +15,7 @@ export const buyQuoteCalculator = { assetBuyAmount: BigNumber, feePercentage: number, slippagePercentage: number, + isMakerAssetZrxToken: boolean, ): BuyQuote { const orders = ordersAndFillableAmounts.orders; const remainingFillableMakerAssetAmounts = ordersAndFillableAmounts.remainingFillableMakerAssetAmounts; @@ -32,22 +35,31 @@ export const buyQuoteCalculator = { if (remainingFillAmount.gt(constants.ZERO_AMOUNT)) { throw new Error(AssetBuyerError.InsufficientAssetLiquidity); } + // if we are not buying ZRX: // given the orders calculated above, find the fee-orders that cover the desired assetBuyAmount (with slippage) // TODO(bmillman): optimization // update this logic to find the minimum amount of feeOrders to cover the worst case as opposed to // finding order that cover all fees, this will help with estimating ETH and minimizing gas usage - const { - resultFeeOrders, - remainingFeeAmount, - feeOrdersRemainingFillableMakerAssetAmounts, - } = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders(resultOrders, feeOrders, { - remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts, - remainingFillableFeeAmounts, - }); - // if we do not have enough feeOrders to cover the fees, throw - if (remainingFeeAmount.gt(constants.ZERO_AMOUNT)) { - throw new Error(AssetBuyerError.InsufficientZrxLiquidity); + let resultFeeOrders = [] as SignedOrder[]; + let feeOrdersRemainingFillableMakerAssetAmounts = [] as BigNumber[]; + if (!isMakerAssetZrxToken) { + const feeOrdersAndRemainingFeeAmount = marketUtils.findFeeOrdersThatCoverFeesForTargetOrders( + resultOrders, + feeOrders, + { + remainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts, + remainingFillableFeeAmounts, + }, + ); + // if we do not have enough feeOrders to cover the fees, throw + if (feeOrdersAndRemainingFeeAmount.remainingFeeAmount.gt(constants.ZERO_AMOUNT)) { + throw new Error(AssetBuyerError.InsufficientZrxLiquidity); + } + resultFeeOrders = feeOrdersAndRemainingFeeAmount.resultFeeOrders; + feeOrdersRemainingFillableMakerAssetAmounts = + feeOrdersAndRemainingFeeAmount.feeOrdersRemainingFillableMakerAssetAmounts; } + // assetData information for the result const assetData = orders[0].makerAssetData; // compile the resulting trimmed set of orders for makerAsset and feeOrders that are needed for assetBuyAmount @@ -64,6 +76,7 @@ export const buyQuoteCalculator = { trimmedFeeOrdersAndFillableAmounts, assetBuyAmount, feePercentage, + isMakerAssetZrxToken, ); // in order to calculate the maxRate, reverse the ordersAndFillableAmounts such that they are sorted from worst rate to best rate const worstCaseQuoteInfo = calculateQuoteInfo( @@ -71,6 +84,7 @@ export const buyQuoteCalculator = { reverseOrdersAndFillableAmounts(trimmedFeeOrdersAndFillableAmounts), assetBuyAmount, feePercentage, + isMakerAssetZrxToken, ); return { assetData, @@ -89,22 +103,30 @@ function calculateQuoteInfo( feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, assetBuyAmount: BigNumber, feePercentage: number, + isMakerAssetZrxToken: boolean, ): BuyQuoteInfo { // find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right - const [ethAmountToBuyAsset, zrxAmountToBuyAsset] = findEthAndZrxAmountNeededToBuyAsset( - ordersAndFillableAmounts, - assetBuyAmount, - ); - // find the total eth needed to buy fees - const ethAmountToBuyFees = findEthAmountNeededToBuyFees(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset); - const affiliateFeeEthAmount = ethAmountToBuyAsset.mul(feePercentage); - const totalEthAmountWithoutAffiliateFee = ethAmountToBuyAsset.plus(ethAmountToBuyFees); - const totalEthAmount = totalEthAmountWithoutAffiliateFee.plus(affiliateFeeEthAmount); + let ethAmountToBuyAsset = constants.ZERO_AMOUNT; + let ethAmountToBuyZrx = constants.ZERO_AMOUNT; + if (isMakerAssetZrxToken) { + ethAmountToBuyAsset = findEthAmountNeededToBuyZrx(ordersAndFillableAmounts, assetBuyAmount); + } else { + // find eth and zrx amounts needed to buy + const ethAndZrxAmountToBuyAsset = findEthAndZrxAmountNeededToBuyAsset(ordersAndFillableAmounts, assetBuyAmount); + ethAmountToBuyAsset = ethAndZrxAmountToBuyAsset[0]; + const zrxAmountToBuyAsset = ethAndZrxAmountToBuyAsset[1]; + // find eth amount needed to buy zrx + ethAmountToBuyZrx = findEthAmountNeededToBuyZrx(feeOrdersAndFillableAmounts, zrxAmountToBuyAsset); + } + /// find the eth amount needed to buy the affiliate fee + const ethAmountToBuyAffiliateFee = ethAmountToBuyAsset.mul(feePercentage); + const totalEthAmountWithoutAffiliateFee = ethAmountToBuyAsset.plus(ethAmountToBuyZrx); + const ethAmountTotal = totalEthAmountWithoutAffiliateFee.plus(ethAmountToBuyAffiliateFee); // divide into the assetBuyAmount in order to find rate of makerAsset / WETH const ethPerAssetPrice = totalEthAmountWithoutAffiliateFee.div(assetBuyAmount); return { - totalEthAmount, - feeEthAmount: affiliateFeeEthAmount, + totalEthAmount: ethAmountTotal, + feeEthAmount: ethAmountToBuyAffiliateFee, ethPerAssetPrice, }; } @@ -119,29 +141,38 @@ function reverseOrdersAndFillableAmounts(ordersAndFillableAmounts: OrdersAndFill }; } -function findEthAmountNeededToBuyFees( +function findEthAmountNeededToBuyZrx( feeOrdersAndFillableAmounts: OrdersAndFillableAmounts, - feeAmount: BigNumber, + zrxBuyAmount: BigNumber, ): BigNumber { const { orders, remainingFillableMakerAssetAmounts } = feeOrdersAndFillableAmounts; const result = _.reduce( orders, (acc, order, index) => { + const { totalEthAmount, remainingZrxBuyAmount } = acc; const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index]; - const amountToFill = BigNumber.min(acc.remainingFeeAmount, remainingFillableMakerAssetAmount); - const feeAdjustedRate = rateUtils.getFeeAdjustedRateOfFeeOrder(order); - const ethAmountForThisOrder = feeAdjustedRate.mul(amountToFill); + const makerFillAmount = BigNumber.min(remainingZrxBuyAmount, remainingFillableMakerAssetAmount); + const [takerFillAmount, adjustedMakerFillAmount] = orderUtils.getTakerFillAmountForFeeOrder( + order, + makerFillAmount, + ); + const extraFeeAmount = remainingFillableMakerAssetAmount.greaterThanOrEqualTo(adjustedMakerFillAmount) + ? constants.ZERO_AMOUNT + : adjustedMakerFillAmount.sub(makerFillAmount); return { - ethAmount: acc.ethAmount.plus(ethAmountForThisOrder), - remainingFeeAmount: BigNumber.max(constants.ZERO_AMOUNT, acc.remainingFeeAmount.minus(amountToFill)), + totalEthAmount: totalEthAmount.plus(takerFillAmount), + remainingZrxBuyAmount: BigNumber.max( + constants.ZERO_AMOUNT, + remainingZrxBuyAmount.minus(makerFillAmount).plus(extraFeeAmount), + ), }; }, { - ethAmount: constants.ZERO_AMOUNT, - remainingFeeAmount: feeAmount, + totalEthAmount: constants.ZERO_AMOUNT, + remainingZrxBuyAmount: zrxBuyAmount, }, ); - return result.ethAmount; + return result.totalEthAmount; } function findEthAndZrxAmountNeededToBuyAsset( @@ -152,28 +183,25 @@ function findEthAndZrxAmountNeededToBuyAsset( const result = _.reduce( orders, (acc, order, index) => { + const { totalEthAmount, totalZrxAmount, remainingAssetBuyAmount } = acc; const remainingFillableMakerAssetAmount = remainingFillableMakerAssetAmounts[index]; - const amountToFill = BigNumber.min(acc.remainingAssetBuyAmount, remainingFillableMakerAssetAmount); - // find the amount of eth required to fill amountToFill (amountToFill / makerAssetAmount) * takerAssetAmount - const ethAmountForThisOrder = amountToFill - .mul(order.takerAssetAmount) - .dividedToIntegerBy(order.makerAssetAmount); - // find the amount of zrx required to fill fees for amountToFill (amountToFill / makerAssetAmount) * takerFee - const zrxAmountForThisOrder = amountToFill.mul(order.takerFee).dividedToIntegerBy(order.makerAssetAmount); + const makerFillAmount = BigNumber.min(acc.remainingAssetBuyAmount, remainingFillableMakerAssetAmount); + const takerFillAmount = orderUtils.getTakerFillAmount(order, makerFillAmount); + const takerFeeAmount = orderUtils.getTakerFeeAmount(order, takerFillAmount); return { - ethAmount: acc.ethAmount.plus(ethAmountForThisOrder), - zrxAmount: acc.zrxAmount.plus(zrxAmountForThisOrder), + totalEthAmount: totalEthAmount.plus(takerFillAmount), + totalZrxAmount: totalZrxAmount.plus(takerFeeAmount), remainingAssetBuyAmount: BigNumber.max( constants.ZERO_AMOUNT, - acc.remainingAssetBuyAmount.minus(amountToFill), + remainingAssetBuyAmount.minus(makerFillAmount), ), }; }, { - ethAmount: constants.ZERO_AMOUNT, - zrxAmount: constants.ZERO_AMOUNT, + totalEthAmount: constants.ZERO_AMOUNT, + totalZrxAmount: constants.ZERO_AMOUNT, remainingAssetBuyAmount: assetBuyAmount, }, ); - return [result.ethAmount, result.zrxAmount]; + return [result.totalEthAmount, result.totalZrxAmount]; } diff --git a/packages/asset-buyer/src/utils/order_provider_response_processor.ts b/packages/asset-buyer/src/utils/order_provider_response_processor.ts index 81464d945..28f684f3c 100644 --- a/packages/asset-buyer/src/utils/order_provider_response_processor.ts +++ b/packages/asset-buyer/src/utils/order_provider_response_processor.ts @@ -110,10 +110,7 @@ function getValidOrdersWithRemainingFillableMakerAssetAmountsFromOnChain( traderInfo.makerZrxBalance, ]); const remainingTakerAssetAmount = order.takerAssetAmount.minus(orderInfo.orderTakerAssetFilledAmount); - const remainingMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount( - order, - remainingTakerAssetAmount, - ); + const remainingMakerAssetAmount = orderUtils.getRemainingMakerAmount(order, remainingTakerAssetAmount); const remainingFillableCalculator = new RemainingFillableCalculator( order.makerFee, order.makerAssetAmount, diff --git a/packages/asset-buyer/src/utils/order_utils.ts b/packages/asset-buyer/src/utils/order_utils.ts index ff47eb7c5..1cc2cf95f 100644 --- a/packages/asset-buyer/src/utils/order_utils.ts +++ b/packages/asset-buyer/src/utils/order_utils.ts @@ -12,19 +12,63 @@ export const orderUtils = { const currentUnixTimestampSec = new BigNumber(Date.now() / millisecondsInSecond).round(); return order.expirationTimeSeconds.lessThan(currentUnixTimestampSec.plus(secondsFromNow)); }, - calculateRemainingMakerAssetAmount(order: SignedOrder, remainingTakerAssetAmount: BigNumber): BigNumber { - if (remainingTakerAssetAmount.eq(0)) { - return constants.ZERO_AMOUNT; - } - return remainingTakerAssetAmount.times(order.makerAssetAmount).dividedToIntegerBy(order.takerAssetAmount); - }, - calculateRemainingTakerAssetAmount(order: SignedOrder, remainingMakerAssetAmount: BigNumber): BigNumber { - if (remainingMakerAssetAmount.eq(0)) { - return constants.ZERO_AMOUNT; - } - return remainingMakerAssetAmount.times(order.takerAssetAmount).dividedToIntegerBy(order.makerAssetAmount); - }, isOpenOrder(order: SignedOrder): boolean { return order.takerAddress === constants.NULL_ADDRESS; }, + // given a remaining amount of takerAsset, calculate how much makerAsset is available + getRemainingMakerAmount(order: SignedOrder, remainingTakerAmount: BigNumber): BigNumber { + const remainingMakerAmount = remainingTakerAmount + .times(order.makerAssetAmount) + .div(order.takerAssetAmount) + .floor(); + return remainingMakerAmount; + }, + // given a desired amount of makerAsset, calculate how much takerAsset is required to fill that amount + getTakerFillAmount(order: SignedOrder, makerFillAmount: BigNumber): BigNumber { + // Round up because exchange rate favors Maker + const takerFillAmount = makerFillAmount + .mul(order.takerAssetAmount) + .div(order.makerAssetAmount) + .ceil(); + return takerFillAmount; + }, + // given a desired amount of takerAsset to fill, calculate how much fee is required by the taker to fill that amount + getTakerFeeAmount(order: SignedOrder, takerFillAmount: BigNumber): BigNumber { + // Round down because Taker fee rate favors Taker + const takerFeeAmount = takerFillAmount + .mul(order.takerFee) + .div(order.takerAssetAmount) + .floor(); + return takerFeeAmount; + }, + // given a desired amount of takerAsset to fill, calculate how much makerAsset will be filled + getMakerFillAmount(order: SignedOrder, takerFillAmount: BigNumber): BigNumber { + // Round down because exchange rate favors Maker + const makerFillAmount = takerFillAmount + .mul(order.makerAssetAmount) + .div(order.takerAssetAmount) + .floor(); + return makerFillAmount; + }, + // given a desired amount of makerAsset, calculate how much fee is required by the maker to fill that amount + getMakerFeeAmount(order: SignedOrder, makerFillAmount: BigNumber): BigNumber { + // Round down because Maker fee rate favors Maker + const makerFeeAmount = makerFillAmount + .mul(order.makerFee) + .div(order.makerAssetAmount) + .floor(); + return makerFeeAmount; + }, + // given a desired amount of ZRX from a fee order, calculate how much takerAsset is required to fill that amount + // also calculate how much ZRX needs to be bought in order fill the desired amount + takerFee + getTakerFillAmountForFeeOrder(order: SignedOrder, makerFillAmount: BigNumber): [BigNumber, BigNumber] { + // For each unit of TakerAsset we buy (MakerAsset - TakerFee) + const adjustedTakerFillAmount = makerFillAmount + .mul(order.takerAssetAmount) + .div(order.makerAssetAmount.sub(order.takerFee)) + .ceil(); + // The amount that we buy will be greater than makerFillAmount, since we buy some amount for fees. + const adjustedMakerFillAmount = orderUtils.getMakerFillAmount(order, adjustedTakerFillAmount); + return [adjustedTakerFillAmount, adjustedMakerFillAmount]; + }, }; diff --git a/packages/asset-buyer/test/buy_quote_calculator_test.ts b/packages/asset-buyer/test/buy_quote_calculator_test.ts index 0f516a0f7..0ea371982 100644 --- a/packages/asset-buyer/test/buy_quote_calculator_test.ts +++ b/packages/asset-buyer/test/buy_quote_calculator_test.ts @@ -49,9 +49,9 @@ describe('buyQuoteCalculator', () => { remainingFillableMakerAssetAmounts: [smallFeeOrder.makerAssetAmount], }; const largeFeeOrder = orderFactory.createSignedOrderFromPartial({ - makerAssetAmount: new BigNumber(110), + makerAssetAmount: new BigNumber(113), takerAssetAmount: new BigNumber(200), - takerFee: new BigNumber(10), + takerFee: new BigNumber(11), }); allFeeOrdersAndFillableAmounts = { orders: [smallFeeOrder, largeFeeOrder], @@ -70,6 +70,7 @@ describe('buyQuoteCalculator', () => { new BigNumber(500), 0, 0, + false, ), ).to.throw(AssetBuyerError.InsufficientAssetLiquidity); }); @@ -82,6 +83,7 @@ describe('buyQuoteCalculator', () => { new BigNumber(300), 0, 0, + false, ), ).to.throw(AssetBuyerError.InsufficientZrxLiquidity); }); @@ -97,6 +99,7 @@ describe('buyQuoteCalculator', () => { assetBuyAmount, feePercentage, slippagePercentage, + false, ); // test if orders are correct expect(buyQuote.orders).to.deep.equal([ordersAndFillableAmounts.orders[0]]); @@ -134,6 +137,7 @@ describe('buyQuoteCalculator', () => { assetBuyAmount, feePercentage, slippagePercentage, + false, ); // test if orders are correct expect(buyQuote.orders).to.deep.equal(ordersAndFillableAmounts.orders); @@ -149,9 +153,9 @@ describe('buyQuoteCalculator', () => { expect(buyQuote.bestCaseQuoteInfo.feeEthAmount).to.bignumber.equal(expectedFeeEthAmount); expect(buyQuote.bestCaseQuoteInfo.totalEthAmount).to.bignumber.equal(expectedTotalEthAmount); expect(buyQuote.bestCaseQuoteInfo.ethPerAssetPrice).to.bignumber.equal(expectedEthPerAssetPrice); - // 100 eth to fill the first order + 200 eth for fees + // 100 eth to fill the first order + 208 eth for fees const expectedWorstEthAmountForAsset = new BigNumber(100); - const expectedWorstEthAmountForZrxFees = new BigNumber(200); + const expectedWorstEthAmountForZrxFees = new BigNumber(208); const expectedWorstFillEthAmount = expectedWorstEthAmountForAsset.plus(expectedWorstEthAmountForZrxFees); const expectedWorstFeeEthAmount = expectedWorstEthAmountForAsset.mul(feePercentage); const expectedWorstTotalEthAmount = expectedWorstFillEthAmount.plus(expectedWorstFeeEthAmount); diff --git a/packages/base-contract/package.json b/packages/base-contract/package.json index 676e8aab9..520dff5f9 100644 --- a/packages/base-contract/package.json +++ b/packages/base-contract/package.json @@ -17,7 +17,7 @@ "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", - "lint": "tslint --project . --exclude **/src/contract_wrappers/**/*" + "lint": "tslint --format stylish --project ." }, "license": "Apache-2.0", "repository": { diff --git a/packages/connect/package.json b/packages/connect/package.json index 95b1bbd7d..de846e58b 100644 --- a/packages/connect/package.json +++ b/packages/connect/package.json @@ -19,7 +19,7 @@ "build:ci": "yarn build", "clean": "shx rm -rf lib test_temp generated_docs", "copy_test_fixtures": "copyfiles -u 2 './test/fixtures/**/*.json' ./lib/test/fixtures", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --exit", "test": "run-s copy_test_fixtures run_mocha", "rebuild_and_test": "run-s clean build test", diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index c2b44ede8..2727cd42b 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "version": "1.1.0", + "changes": [ + { + "pr": 1192, + "note": "Update Forwarder addresses" + } + ] + }, + { "version": "1.0.1", "changes": [ { diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index 1ea8c61dd..f5fd8d0be 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -25,7 +25,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { etherToken: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', exchange: '0x4f833a24e1f95d70f028921e27040ca56e09ab0b', assetProxyOwner: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6', - forwarder: '0x7afc2d5107af94c462a194d2c21b5bdd238709d6', + forwarder: '0x5468a1dc173652ee28d249c271fa9933144746b1', orderValidator: '0x9463e518dea6810309563c81d5266c1b1d149138', }, 3: { @@ -35,7 +35,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { etherToken: '0xc778417e063141139fce010982780140aa0cd5ab', exchange: '0x4530c0483a1633c7a1c97d2c53721caff2caaaaf', assetProxyOwner: '0xf5fa5b5fed2727a0e44ac67f6772e97977aa358b', - forwarder: '0x3983e204b12b3c02fb0638caf2cd406a62e0ead3', + forwarder: '0x2240dab907db71e64d3e0dba4800c83b5c502d4e', orderValidator: '0x90431a90516ab49af23a0530e04e8c7836e7122f', }, 42: { @@ -45,7 +45,7 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { etherToken: '0xd0a1e359811322d97991e03f863a0c30c2cf029c', exchange: '0x35dd2932454449b14cee11a94d3674a936d5d7b2', assetProxyOwner: '0x2c824d2882baa668e0d5202b1e7f2922278703f8', - forwarder: '0xd85e2fa7e7e252b27b01bf0d65c946959d2f45b8', + forwarder: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6', orderValidator: '0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d', }, }; diff --git a/packages/contract-artifacts/CHANGELOG.json b/packages/contract-artifacts/CHANGELOG.json index c2b44ede8..e6a6d02c0 100644 --- a/packages/contract-artifacts/CHANGELOG.json +++ b/packages/contract-artifacts/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "version": "1.1.0", + "changes": [ + { + "pr": 1192, + "note": "Update Forwarder artifact" + } + ] + }, + { "version": "1.0.1", "changes": [ { diff --git a/packages/contract-artifacts/artifacts/Forwarder.json b/packages/contract-artifacts/artifacts/Forwarder.json index 4f93b231b..a7bd62f8e 100644 --- a/packages/contract-artifacts/artifacts/Forwarder.json +++ b/packages/contract-artifacts/artifacts/Forwarder.json @@ -1,448 +1,447 @@ { - "schemaVersion": "2.0.0", - "contractName": "Forwarder", - "compilerOutput": { - "abi": [ - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "orders", - "type": "tuple[]" - }, - { - "name": "makerAssetFillAmount", - "type": "uint256" - }, - { - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "feeOrders", - "type": "tuple[]" - }, - { - "name": "feeSignatures", - "type": "bytes[]" - }, - { - "name": "feePercentage", - "type": "uint256" - }, - { - "name": "feeRecipient", - "type": "address" - } - ], - "name": "marketBuyOrdersWithEth", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "orderFillResults", - "type": "tuple" - }, - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "feeOrderFillResults", - "type": "tuple" - } - ], - "payable": true, - "stateMutability": "payable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "assetData", - "type": "bytes" - }, - { - "name": "amount", - "type": "uint256" - } - ], - "name": "withdrawAsset", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "owner", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "orders", - "type": "tuple[]" - }, - { - "name": "signatures", - "type": "bytes[]" - }, - { - "components": [ - { - "name": "makerAddress", - "type": "address" - }, - { - "name": "takerAddress", - "type": "address" - }, - { - "name": "feeRecipientAddress", - "type": "address" - }, - { - "name": "senderAddress", - "type": "address" - }, - { - "name": "makerAssetAmount", - "type": "uint256" - }, - { - "name": "takerAssetAmount", - "type": "uint256" - }, - { - "name": "makerFee", - "type": "uint256" - }, - { - "name": "takerFee", - "type": "uint256" - }, - { - "name": "expirationTimeSeconds", - "type": "uint256" - }, - { - "name": "salt", - "type": "uint256" - }, - { - "name": "makerAssetData", - "type": "bytes" - }, - { - "name": "takerAssetData", - "type": "bytes" - } - ], - "name": "feeOrders", - "type": "tuple[]" - }, - { - "name": "feeSignatures", - "type": "bytes[]" - }, - { - "name": "feePercentage", - "type": "uint256" - }, - { - "name": "feeRecipient", - "type": "address" - } - ], - "name": "marketSellOrdersWithEth", - "outputs": [ - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "orderFillResults", - "type": "tuple" - }, - { - "components": [ - { - "name": "makerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "takerAssetFilledAmount", - "type": "uint256" - }, - { - "name": "makerFeePaid", - "type": "uint256" - }, - { - "name": "takerFeePaid", - "type": "uint256" - } - ], - "name": "feeOrderFillResults", - "type": "tuple" - } - ], - "payable": true, - "stateMutability": "payable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "name": "_exchange", - "type": "address" - }, - { - "name": "_zrxAssetData", - "type": "bytes" - }, - { - "name": "_wethAssetData", - "type": "bytes" - } - ], - "payable": false, - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "payable": true, - "stateMutability": "payable", - "type": "fallback" - } - ], - "evm": { - "bytecode": { - "linkReferences": {}, - "object": - "0x60806040523480156200001157600080fd5b5060405162002cdb38038062002cdb83398101806040526200003791908101906200051d565b6000805433600160a060020a031991821617825560018054909116600160a060020a0386161790558251849084908490849081906200007e906004906020870190620003d0565b50825162000094906005906020860190620003d0565b50620000b0836010640100000000620018f66200036f82021704565b9150620000cd846010640100000000620018f66200036f82021704565b60028054600160a060020a03948516600160a060020a031991821617909155600380549285169290911691909117905550600154604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130181207f6070410800000000000000000000000000000000000000000000000000000000825291909216945063607041089350620001739250906004016200068e565b602060405180830381600087803b1580156200018e57600080fd5b505af1158015620001a3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620001c99190810190620004f4565b9050600160a060020a038116151562000219576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200021090620006b0565b60405180910390fd5b6002546040517f095ea7b3000000000000000000000000000000000000000000000000000000008152600160a060020a039091169063095ea7b39062000268908490600019906004016200066f565b602060405180830381600087803b1580156200028357600080fd5b505af115801562000298573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620002be9190810190620005a1565b506003546040517f095ea7b3000000000000000000000000000000000000000000000000000000008152600160a060020a039091169063095ea7b3906200030e908490600019906004016200066f565b602060405180830381600087803b1580156200032957600080fd5b505af11580156200033e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620003649190810190620005a1565b50505050506200077a565b600081601401835110151515620003b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000210906200069e565b506014818301810151910190600160a060020a03165b92915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200041357805160ff191683800117855562000443565b8280016001018555821562000443579182015b828111156200044357825182559160200191906001019062000426565b506200045192915062000455565b5090565b6200047291905b808211156200045157600081556001016200045c565b90565b600062000483825162000711565b9392505050565b600062000483825162000742565b6000601f82018313620004aa57600080fd5b8151620004c1620004bb82620006e9565b620006c2565b91508082526020830160208301858383011115620004de57600080fd5b620004eb83828462000747565b50505092915050565b6000602082840312156200050757600080fd5b600062000515848462000475565b949350505050565b6000806000606084860312156200053357600080fd5b600062000541868662000475565b93505060208401516001604060020a038111156200055e57600080fd5b6200056c8682870162000498565b92505060408401516001604060020a038111156200058957600080fd5b620005978682870162000498565b9150509250925092565b600060208284031215620005b457600080fd5b60006200051584846200048a565b620005cd8162000711565b82525050565b620005cd816200071d565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601881527f554e524547495354455245445f41535345545f50524f58590000000000000000602082015260400190565b620005cd8162000472565b604081016200067f8285620005c2565b62000483602083018462000664565b60208101620003ca8284620005d3565b60208082528101620003ca81620005de565b60208082528101620003ca8162000634565b6040518181016001604060020a0381118282101715620006e157600080fd5b604052919050565b60006001604060020a038211156200070057600080fd5b506020601f91909101601f19160190565b600160a060020a031690565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b151590565b60005b83811015620007645781810151838201526020016200074a565b8381111562000774576000848401525b50505050565b612551806200078a6000396000f30060806040526004361061006c5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166318978e8281146100c8578063630f1e6c146100f25780638da5cb5b146101125780639395525c14610134578063f2fde38b14610147575b60025473ffffffffffffffffffffffffffffffffffffffff1633146100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612337565b60405180910390fd5b005b6100db6100d6366004611da0565b610167565b6040516100e9929190612437565b60405180910390f35b3480156100fe57600080fd5b506100c661010d366004611e9b565b6102f7565b34801561011e57600080fd5b50610127610388565b6040516100e991906122e6565b6100db610142366004611cba565b6103a4565b34801561015357600080fd5b506100c6610162366004611c94565b61050a565b61016f6119a9565b6101776119a9565b6000806101826105bb565b60048054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100600188161502019095169490940493840181900481028201810190925282815261025c939092909183018282801561022d5780601f106102025761010080835404028352916020019161022d565b820191906000526020600020905b81548152906001019060200180831161021057829003601f168201915b50505050508c600081518110151561024157fe5b6020908102909101015161014001519063ffffffff61069616565b156102875761026c8b8b8b6107c3565b935061028084600001518560600151610acb565b90506102ae565b6102928b8b8b610b0d565b9350836060015191506102a68883896107c3565b845190935090505b6102c2846020015184602001518888610d1f565b6102e98b60008151811015156102d457fe5b90602001906020020151610140015182610f33565b505097509795505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610348576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123e7565b61038383838080601f01602080910402602001604051908101604052809392919081815260200183838082843750879450610f339350505050565b505050565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b6103ac6119a9565b6103b46119a9565b60008060006103c16105bb565b60048054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152610441939092909183018282801561022d5780601f106102025761010080835404028352916020019161022d565b156104925761046a670de0b6b3a7640000610464670de0b6b3a76400008a61104f565b34611099565b92506104778b848c6110f1565b945061048b85600001518660600151610acb565b90506104d6565b6104ad670d2f13f7789f0000670de0b6b3a764000034611099565b92506104ba8b848c6110f1565b9450846060015191506104ce89838a6107c3565b855190945090505b6104ea856020015185602001518989610d1f565b6104fc8b60008151811015156102d457fe5b505050965096945050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461055b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123e7565b73ffffffffffffffffffffffffffffffffffffffff8116156105b857600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83161790555b50565b600034116105f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612347565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016000604051808303818588803b15801561067b57600080fd5b505af115801561068f573d6000803e3d6000fd5b5050505050565b6000815183511480156107ba5750816040518082805190602001908083835b602083106106f257805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016106b5565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0180199092169116179052604051919093018190038120885190955088945090928392508401908083835b6020831061078757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161074a565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916145b90505b92915050565b6107cb6119a9565b60608060008060008060006107de6119a9565b8a15156107ea57610abc565b6004805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561088e5780601f106108635761010080835404028352916020019161088e565b820191906000526020600020905b81548152906001019060200180831161087157829003601f168201915b505060058054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152969e509194509250840190508282801561093d5780601f106109125761010080835404028352916020019161093d565b820191906000526020600020905b81548152906001019060200180831161092057829003601f168201915b50505050509650600095508b519450600093505b838514610a8257878c8581518110151561096757fe5b6020908102909101015161014001528b5187908d908690811061098657fe5b60209081029091010151610160015261099f8b87610acb565b9250610a068c858151811015156109b257fe5b9060200190602002015160a00151610a008e878151811015156109d157fe5b90602001906020020151608001518f888151811015156109ed57fe5b9060200190602002015160e00151610acb565b85611099565b9150610a4b8c85815181101515610a1957fe5b90602001906020020151610a2e84600161104f565b8c87815181101515610a3c57fe5b90602001906020020151611295565b9050610a57898261130d565b610a6989600001518a60600151610acb565b95508a8610610a7757610a82565b600190930192610951565b8a861015610abc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123c7565b50505050505050509392505050565b600082821115610b07576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612367565b50900390565b610b156119a9565b606080600080600080610b266119a9565b60008b6000815181101515610b3757fe5b6020908102919091018101516101400151600580546040805160026001841615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190931692909204601f8101869004860283018601909152808252929b5092909190830182828015610bef5780601f10610bc457610100808354040283529160200191610bef565b820191906000526020600020905b815481529060010190602001808311610bd257829003601f168201915b505050505096508b519550600094505b848614610ce557878c86815181101515610c1557fe5b6020908102909101015161014001528b5187908d9087908110610c3457fe5b6020908102909101015161016001528851610c50908c90610acb565b9350610c938c86815181101515610c6357fe5b9060200190602002015160a001518d87815181101515610c7f57fe5b906020019060200201516080015186611099565b9250610cbf8c86815181101515610ca657fe5b90602001906020020151848c88815181101515610a3c57fe5b9150610ccb898361130d565b5087518a8110610cda57610ce5565b600190940193610bff565b8a811015610abc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123c7565b600080808066b1a2bc2ec50000861115610d65576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123f7565b610d6f888861104f565b935034841115610dab576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612357565b610db53485610acb565b9250610dca86670de0b6b3a76400008a611099565b915082821115610e06576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123d7565b6000831115610f29576002546040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690632e1a7d4d90610e65908690600401612453565b600060405180830381600087803b158015610e7f57600080fd5b505af1158015610e93573d6000803e3d6000fd5b505050506000821115610ee55760405173ffffffffffffffffffffffffffffffffffffffff86169083156108fc029084906000818181858888f19350505050158015610ee3573d6000803e3d6000fd5b505b610eef8383610acb565b90506000811115610f2957604051339082156108fc029083906000818181858888f19350505050158015610f27573d6000803e3d6000fd5b505b5050505050505050565b6000610f45838263ffffffff61136f16565b604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130190209091507fffffffff0000000000000000000000000000000000000000000000000000000080831691161415610fb557610fb083836113dc565b610383565b604080517f455243373231546f6b656e28616464726573732c75696e7432353629000000008152905190819003601c0190207fffffffff000000000000000000000000000000000000000000000000000000008281169116141561101d57610fb083836115ca565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123a7565b60008282018381101561108e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612397565b8091505b5092915050565b60008083116110d4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612387565b6110e76110e185846116b2565b8461170d565b90505b9392505050565b6110f96119a9565b60608060008060006111096119a9565b89600081518110151561111857fe5b6020908102919091018101516101400151600580546040805160026001841615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190931692909204601f8101869004860283018601909152808252929950929091908301828280156111d05780601f106111a5576101008083540402835291602001916111d0565b820191906000526020600020905b8154815290600101906020018083116111b357829003601f168201915b5050505050945089519350600092505b82841461128857858a848151811015156111f657fe5b602090810290910101516101400152895185908b908590811061121557fe5b906020019060200201516101600181905250611235898860200151610acb565b91506112618a8481518110151561124857fe5b90602001906020020151838a86815181101515610a3c57fe5b905061126d878261130d565b6020870151891161127d57611288565b6001909201916111e0565b5050505050509392505050565b61129d6119a9565b606060006112ac868686611724565b600154815191935073ffffffffffffffffffffffffffffffffffffffff1691506080908390602082016000855af1801561130357825184526020830151602085015260408301516040850152606083015160608501525b5050509392505050565b8151815161131b919061104f565b825260208083015190820151611331919061104f565b60208301526040808301519082015161134a919061104f565b604083015260608083015190820151611363919061104f565b60609092019190915250565b6000816004018351101515156113b1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612417565b5001602001517fffffffff000000000000000000000000000000000000000000000000000000001690565b6000806113f084601063ffffffff6118f616565b604080517f7472616e7366657228616464726573732c75696e7432353629000000000000008152905190819003601901812091935073ffffffffffffffffffffffffffffffffffffffff8416919061144e903390879060240161231c565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931783525181519192909182919080838360005b838110156114f25781810151838201526020016114da565b50505050905090810190601f16801561151f5780820380516001836020036101000a031916815260200191505b509150506000604051808303816000865af19250505080151561156e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123b7565b3d1561158b575060003d6020141561158b5760206000803e506000515b8015156115c4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123b7565b50505050565b60008060018314611607576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612427565b61161884601063ffffffff6118f616565b915061162b84602463ffffffff61195716565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff8316906323b872dd90611684903090339086906004016122f4565b600060405180830381600087803b15801561169e57600080fd5b505af1158015610f29573d6000803e3d6000fd5b6000808315156116c55760009150611092565b508282028284828115156116d557fe5b041461108e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612397565b600080828481151561171b57fe5b04949350505050565b604080517fb4be83d5000000000000000000000000000000000000000000000000000000006020808301919091526060602483018181528751608485019081528884015160a48601529488015160c48501529087015160e4840152608087015161010484015260a087015161012484015260c087015161014484015260e08701516101648401526101008701516101848401526101208701516101a4840152610140870180516101c485019081526101608901516101e4860152610180905251805161020485018190529394919384936044870192849261022489019291820191601f82010460005b8181101561182b57835185526020948501949093019260010161180d565b50505050818103610160808401919091528a0151805180835260209283019291820191601f82010460005b81811015611874578351855260209485019490930192600101611856565b50505089845250848103602093840190815288518083529093918201918981019190601f82010460005b818110156118bc57835185526020948501949093019260010161189e565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08883030188525060405250505050509392505050565b600081601401835110151515611938576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612407565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b60006107ba83836000816020018351101515156119a0576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612377565b50016020015190565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b60006107ba82356124ef565b6000601f820183136119ef57600080fd5b8135611a026119fd82612488565b612461565b81815260209384019390925082018360005b83811015611a405781358601611a2a8882611af0565b8452506020928301929190910190600101611a14565b5050505092915050565b6000601f82018313611a5b57600080fd5b8135611a696119fd82612488565b81815260209384019390925082018360005b83811015611a405781358601611a918882611b3f565b8452506020928301929190910190600101611a7b565b600080601f83018413611ab957600080fd5b50813567ffffffffffffffff811115611ad157600080fd5b602083019150836001820283011115611ae957600080fd5b9250929050565b6000601f82018313611b0157600080fd5b8135611b0f6119fd826124a9565b91508082526020830160208301858383011115611b2b57600080fd5b611b3683828461250b565b50505092915050565b60006101808284031215611b5257600080fd5b611b5d610180612461565b90506000611b6b84846119d2565b8252506020611b7c848483016119d2565b6020830152506040611b90848285016119d2565b6040830152506060611ba4848285016119d2565b6060830152506080611bb884828501611c88565b60808301525060a0611bcc84828501611c88565b60a08301525060c0611be084828501611c88565b60c08301525060e0611bf484828501611c88565b60e083015250610100611c0984828501611c88565b61010083015250610120611c1f84828501611c88565b6101208301525061014082013567ffffffffffffffff811115611c4157600080fd5b611c4d84828501611af0565b6101408301525061016082013567ffffffffffffffff811115611c6f57600080fd5b611c7b84828501611af0565b6101608301525092915050565b60006107ba8235612508565b600060208284031215611ca657600080fd5b6000611cb284846119d2565b949350505050565b60008060008060008060c08789031215611cd357600080fd5b863567ffffffffffffffff811115611cea57600080fd5b611cf689828a01611a4a565b965050602087013567ffffffffffffffff811115611d1357600080fd5b611d1f89828a016119de565b955050604087013567ffffffffffffffff811115611d3c57600080fd5b611d4889828a01611a4a565b945050606087013567ffffffffffffffff811115611d6557600080fd5b611d7189828a016119de565b9350506080611d8289828a01611c88565b92505060a0611d9389828a016119d2565b9150509295509295509295565b600080600080600080600060e0888a031215611dbb57600080fd5b873567ffffffffffffffff811115611dd257600080fd5b611dde8a828b01611a4a565b9750506020611def8a828b01611c88565b965050604088013567ffffffffffffffff811115611e0c57600080fd5b611e188a828b016119de565b955050606088013567ffffffffffffffff811115611e3557600080fd5b611e418a828b01611a4a565b945050608088013567ffffffffffffffff811115611e5e57600080fd5b611e6a8a828b016119de565b93505060a0611e7b8a828b01611c88565b92505060c0611e8c8a828b016119d2565b91505092959891949750929550565b600080600060408486031215611eb057600080fd5b833567ffffffffffffffff811115611ec757600080fd5b611ed386828701611aa7565b93509350506020611ee686828701611c88565b9150509250925092565b611ef9816124ef565b82525050565b602381527f44454641554c545f46554e4354494f4e5f574554485f434f4e54524143545f4f60208201527f4e4c590000000000000000000000000000000000000000000000000000000000604082015260600190565b601181527f494e56414c49445f4d53475f56414c5545000000000000000000000000000000602082015260400190565b600d81527f4f564552534f4c445f5745544800000000000000000000000000000000000000602082015260400190565b601181527f55494e543235365f554e444552464c4f57000000000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601081527f4449564953494f4e5f42595f5a45524f00000000000000000000000000000000602082015260400190565b601081527f55494e543235365f4f564552464c4f5700000000000000000000000000000000602082015260400190565b601781527f554e535550504f525445445f41535345545f50524f5859000000000000000000602082015260400190565b600f81527f5452414e534645525f4641494c45440000000000000000000000000000000000602082015260400190565b601481527f434f4d504c4554455f46494c4c5f4641494c4544000000000000000000000000602082015260400190565b601a81527f494e53554646494349454e545f4554485f52454d41494e494e47000000000000602082015260400190565b601381527f4f4e4c595f434f4e54524143545f4f574e455200000000000000000000000000602082015260400190565b601881527f4645455f50455243454e544147455f544f4f5f4c415247450000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b602581527f475245415445525f4f525f455155414c5f544f5f345f4c454e4754485f52455160208201527f5549524544000000000000000000000000000000000000000000000000000000604082015260600190565b600e81527f494e56414c49445f414d4f554e54000000000000000000000000000000000000602082015260400190565b805160808301906122a884826122dd565b5060208201516122bb60208501826122dd565b5060408201516122ce60408501826122dd565b5060608201516115c460608501825b611ef981612508565b602081016107bd8284611ef0565b606081016123028286611ef0565b61230f6020830185611ef0565b611cb260408301846122dd565b6040810161232a8285611ef0565b6110ea60208301846122dd565b602080825281016107bd81611eff565b602080825281016107bd81611f55565b602080825281016107bd81611f85565b602080825281016107bd81611fb5565b602080825281016107bd81611fe5565b602080825281016107bd8161203b565b602080825281016107bd8161206b565b602080825281016107bd8161209b565b602080825281016107bd816120cb565b602080825281016107bd816120fb565b602080825281016107bd8161212b565b602080825281016107bd8161215b565b602080825281016107bd8161218b565b602080825281016107bd816121bb565b602080825281016107bd81612211565b602080825281016107bd81612267565b61010081016124468285612297565b6110ea6080830184612297565b602081016107bd82846122dd565b60405181810167ffffffffffffffff8111828210171561248057600080fd5b604052919050565b600067ffffffffffffffff82111561249f57600080fd5b5060209081020190565b600067ffffffffffffffff8211156124c057600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b828183375060009101525600a265627a7a72305820185af4a5e8525b0c20983bf22c09773a1cab3f704716cba4c8da3b112601cd666c6578706572696d656e74616cf50037" + "schemaVersion": "2.0.0", + "contractName": "Forwarder", + "compilerOutput": { + "abi": [ + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "makerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signatures", + "type": "bytes[]" + }, + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "feeOrders", + "type": "tuple[]" + }, + { + "name": "feeSignatures", + "type": "bytes[]" + }, + { + "name": "feePercentage", + "type": "uint256" + }, + { + "name": "feeRecipient", + "type": "address" + } + ], + "name": "marketBuyOrdersWithEth", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "orderFillResults", + "type": "tuple" + }, + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "feeOrderFillResults", + "type": "tuple" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "assetData", + "type": "bytes" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "withdrawAsset", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "signatures", + "type": "bytes[]" + }, + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "feeOrders", + "type": "tuple[]" + }, + { + "name": "feeSignatures", + "type": "bytes[]" + }, + { + "name": "feePercentage", + "type": "uint256" + }, + { + "name": "feeRecipient", + "type": "address" + } + ], + "name": "marketSellOrdersWithEth", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "orderFillResults", + "type": "tuple" + }, + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "feeOrderFillResults", + "type": "tuple" + } + ], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_exchange", + "type": "address" + }, + { + "name": "_zrxAssetData", + "type": "bytes" + }, + { + "name": "_wethAssetData", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + } + ], + "evm": { + "bytecode": { + "linkReferences": {}, + "object": "0x60806040523480156200001157600080fd5b5060405162002d2c38038062002d2c83398101806040526200003791908101906200051d565b6000805433600160a060020a031991821617825560018054909116600160a060020a0386161790558251849084908490849081906200007e906004906020870190620003d0565b50825162000094906005906020860190620003d0565b50620000b0836010640100000000620019476200036f82021704565b9150620000cd846010640100000000620019476200036f82021704565b60028054600160a060020a03948516600160a060020a031991821617909155600380549285169290911691909117905550600154604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130181207f6070410800000000000000000000000000000000000000000000000000000000825291909216945063607041089350620001739250906004016200068e565b602060405180830381600087803b1580156200018e57600080fd5b505af1158015620001a3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620001c99190810190620004f4565b9050600160a060020a038116151562000219576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200021090620006b0565b60405180910390fd5b6002546040517f095ea7b3000000000000000000000000000000000000000000000000000000008152600160a060020a039091169063095ea7b39062000268908490600019906004016200066f565b602060405180830381600087803b1580156200028357600080fd5b505af115801562000298573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620002be9190810190620005a1565b506003546040517f095ea7b3000000000000000000000000000000000000000000000000000000008152600160a060020a039091169063095ea7b3906200030e908490600019906004016200066f565b602060405180830381600087803b1580156200032957600080fd5b505af11580156200033e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620003649190810190620005a1565b50505050506200077a565b600081601401835110151515620003b4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040162000210906200069e565b506014818301810151910190600160a060020a03165b92915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200041357805160ff191683800117855562000443565b8280016001018555821562000443579182015b828111156200044357825182559160200191906001019062000426565b506200045192915062000455565b5090565b6200047291905b808211156200045157600081556001016200045c565b90565b600062000483825162000711565b9392505050565b600062000483825162000742565b6000601f82018313620004aa57600080fd5b8151620004c1620004bb82620006e9565b620006c2565b91508082526020830160208301858383011115620004de57600080fd5b620004eb83828462000747565b50505092915050565b6000602082840312156200050757600080fd5b600062000515848462000475565b949350505050565b6000806000606084860312156200053357600080fd5b600062000541868662000475565b93505060208401516001604060020a038111156200055e57600080fd5b6200056c8682870162000498565b92505060408401516001604060020a038111156200058957600080fd5b620005978682870162000498565b9150509250925092565b600060208284031215620005b457600080fd5b60006200051584846200048a565b620005cd8162000711565b82525050565b620005cd816200071d565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601881527f554e524547495354455245445f41535345545f50524f58590000000000000000602082015260400190565b620005cd8162000472565b604081016200067f8285620005c2565b62000483602083018462000664565b60208101620003ca8284620005d3565b60208082528101620003ca81620005de565b60208082528101620003ca8162000634565b6040518181016001604060020a0381118282101715620006e157600080fd5b604052919050565b60006001604060020a038211156200070057600080fd5b506020601f91909101601f19160190565b600160a060020a031690565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b151590565b60005b83811015620007645781810151838201526020016200074a565b8381111562000774576000848401525b50505050565b6125a2806200078a6000396000f30060806040526004361061006c5763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166318978e8281146100c8578063630f1e6c146100f25780638da5cb5b146101125780639395525c14610134578063f2fde38b14610147575b60025473ffffffffffffffffffffffffffffffffffffffff1633146100c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612388565b60405180910390fd5b005b6100db6100d6366004611df1565b610167565b6040516100e9929190612488565b60405180910390f35b3480156100fe57600080fd5b506100c661010d366004611eec565b6102f7565b34801561011e57600080fd5b50610127610388565b6040516100e99190612337565b6100db610142366004611d0b565b6103a4565b34801561015357600080fd5b506100c6610162366004611ce5565b61050a565b61016f6119fa565b6101776119fa565b6000806101826105bb565b60048054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100600188161502019095169490940493840181900481028201810190925282815261025c939092909183018282801561022d5780601f106102025761010080835404028352916020019161022d565b820191906000526020600020905b81548152906001019060200180831161021057829003601f168201915b50505050508c600081518110151561024157fe5b6020908102909101015161014001519063ffffffff61069616565b156102875761026c8b8b8b6107c3565b935061028084600001518560600151610ac1565b90506102ae565b6102928b8b8b610b03565b9350836060015191506102a68883896107c3565b845190935090505b6102c2846020015184602001518888610d15565b6102e98b60008151811015156102d457fe5b90602001906020020151610140015182610f29565b505097509795505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610348576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612438565b61038383838080601f01602080910402602001604051908101604052809392919081815260200183838082843750879450610f299350505050565b505050565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b6103ac6119fa565b6103b46119fa565b60008060006103c16105bb565b60048054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152610441939092909183018282801561022d5780601f106102025761010080835404028352916020019161022d565b156104925761046a670de0b6b3a7640000610464670de0b6b3a76400008a611045565b3461108f565b92506104778b848c6110e7565b945061048b85600001518660600151610ac1565b90506104d6565b6104ad670d2f13f7789f0000670de0b6b3a76400003461108f565b92506104ba8b848c6110e7565b9450846060015191506104ce89838a6107c3565b855190945090505b6104ea856020015185602001518989610d15565b6104fc8b60008151811015156102d457fe5b505050965096945050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461055b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612438565b73ffffffffffffffffffffffffffffffffffffffff8116156105b857600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83161790555b50565b600034116105f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612398565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d0e30db0346040518263ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004016000604051808303818588803b15801561067b57600080fd5b505af115801561068f573d6000803e3d6000fd5b5050505050565b6000815183511480156107ba5750816040518082805190602001908083835b602083106106f257805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016106b5565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0180199092169116179052604051919093018190038120885190955088945090928392508401908083835b6020831061078757805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161074a565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916145b90505b92915050565b6107cb6119fa565b60608060008060008060006107de6119fa565b8a15156107ea57610ab2565b6004805460408051602060026001851615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190941693909304601f8101849004840282018401909252818152929183018282801561088e5780601f106108635761010080835404028352916020019161088e565b820191906000526020600020905b81548152906001019060200180831161087157829003601f168201915b505060058054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152969e509194509250840190508282801561093d5780601f106109125761010080835404028352916020019161093d565b820191906000526020600020905b81548152906001019060200180831161092057829003601f168201915b50505050509650600095508b519450600093505b838514610a7857878c8581518110151561096757fe5b6020908102909101015161014001528b5187908d908690811061098657fe5b60209081029091010151610160015261099f8b87610ac1565b9250610a068c858151811015156109b257fe5b9060200190602002015160a00151610a008e878151811015156109d157fe5b90602001906020020151608001518f888151811015156109ed57fe5b9060200190602002015160e00151610ac1565b8561128b565b9150610a418c85815181101515610a1957fe5b90602001906020020151838c87815181101515610a3257fe5b906020019060200201516112e6565b9050610a4d898261135e565b610a5f89600001518a60600151610ac1565b95508a8610610a6d57610a78565b600190930192610951565b8a861015610ab2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612418565b50505050505050509392505050565b600082821115610afd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123b8565b50900390565b610b0b6119fa565b606080600080600080610b1c6119fa565b60008b6000815181101515610b2d57fe5b6020908102919091018101516101400151600580546040805160026001841615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190931692909204601f8101869004860283018601909152808252929b5092909190830182828015610be55780601f10610bba57610100808354040283529160200191610be5565b820191906000526020600020905b815481529060010190602001808311610bc857829003601f168201915b505050505096508b519550600094505b848614610cdb57878c86815181101515610c0b57fe5b6020908102909101015161014001528b5187908d9087908110610c2a57fe5b6020908102909101015161016001528851610c46908c90610ac1565b9350610c898c86815181101515610c5957fe5b9060200190602002015160a001518d87815181101515610c7557fe5b90602001906020020151608001518661128b565b9250610cb58c86815181101515610c9c57fe5b90602001906020020151848c88815181101515610a3257fe5b9150610cc1898361135e565b5087518a8110610cd057610cdb565b600190940193610bf5565b8a811015610ab2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612418565b600080808066b1a2bc2ec50000861115610d5b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612448565b610d658888611045565b935034841115610da1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123a8565b610dab3485610ac1565b9250610dc086670de0b6b3a76400008a61108f565b915082821115610dfc576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612428565b6000831115610f1f576002546040517f2e1a7d4d00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690632e1a7d4d90610e5b9086906004016124a4565b600060405180830381600087803b158015610e7557600080fd5b505af1158015610e89573d6000803e3d6000fd5b505050506000821115610edb5760405173ffffffffffffffffffffffffffffffffffffffff86169083156108fc029084906000818181858888f19350505050158015610ed9573d6000803e3d6000fd5b505b610ee58383610ac1565b90506000811115610f1f57604051339082156108fc029083906000818181858888f19350505050158015610f1d573d6000803e3d6000fd5b505b5050505050505050565b6000610f3b838263ffffffff6113c016565b604080517f4552433230546f6b656e28616464726573732900000000000000000000000000815290519081900360130190209091507fffffffff0000000000000000000000000000000000000000000000000000000080831691161415610fab57610fa6838361142d565b610383565b604080517f455243373231546f6b656e28616464726573732c75696e7432353629000000008152905190819003601c0190207fffffffff000000000000000000000000000000000000000000000000000000008281169116141561101357610fa6838361161b565b6040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123f8565b600082820183811015611084576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123e8565b8091505b5092915050565b60008083116110ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123d8565b6110dd6110d78584611703565b8461175e565b90505b9392505050565b6110ef6119fa565b60608060008060006110ff6119fa565b89600081518110151561110e57fe5b6020908102919091018101516101400151600580546040805160026001841615610100027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190931692909204601f8101869004860283018601909152808252929950929091908301828280156111c65780601f1061119b576101008083540402835291602001916111c6565b820191906000526020600020905b8154815290600101906020018083116111a957829003601f168201915b5050505050945089519350600092505b82841461127e57858a848151811015156111ec57fe5b602090810290910101516101400152895185908b908590811061120b57fe5b90602001906020020151610160018190525061122b898860200151610ac1565b91506112578a8481518110151561123e57fe5b90602001906020020151838a86815181101515610a3257fe5b9050611263878261135e565b602087015189116112735761127e565b6001909201916111d6565b5050505050509392505050565b60008083116112c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123d8565b6110dd6110d76112d68685611703565b6112e1866001610ac1565b611045565b6112ee6119fa565b606060006112fd868686611775565b600154815191935073ffffffffffffffffffffffffffffffffffffffff1691506080908390602082016000855af1801561135457825184526020830151602085015260408301516040850152606083015160608501525b5050509392505050565b8151815161136c9190611045565b8252602080830151908201516113829190611045565b60208301526040808301519082015161139b9190611045565b6040830152606080830151908201516113b49190611045565b60609092019190915250565b600081600401835110151515611402576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612468565b5001602001517fffffffff000000000000000000000000000000000000000000000000000000001690565b60008061144184601063ffffffff61194716565b604080517f7472616e7366657228616464726573732c75696e7432353629000000000000008152905190819003601901812091935073ffffffffffffffffffffffffffffffffffffffff8416919061149f903390879060240161236d565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009094169390931783525181519192909182919080838360005b8381101561154357818101518382015260200161152b565b50505050905090810190601f1680156115705780820380516001836020036101000a031916815260200191505b509150506000604051808303816000865af1925050508015156115bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612408565b3d156115dc575060003d602014156115dc5760206000803e506000515b801515611615576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612408565b50505050565b60008060018314611658576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612478565b61166984601063ffffffff61194716565b915061167c84602463ffffffff6119a816565b6040517f23b872dd00000000000000000000000000000000000000000000000000000000815290915073ffffffffffffffffffffffffffffffffffffffff8316906323b872dd906116d590309033908690600401612345565b600060405180830381600087803b1580156116ef57600080fd5b505af1158015610f1f573d6000803e3d6000fd5b6000808315156117165760009150611088565b5082820282848281151561172657fe5b0414611084576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123e8565b600080828481151561176c57fe5b04949350505050565b604080517fb4be83d5000000000000000000000000000000000000000000000000000000006020808301919091526060602483018181528751608485019081528884015160a48601529488015160c48501529087015160e4840152608087015161010484015260a087015161012484015260c087015161014484015260e08701516101648401526101008701516101848401526101208701516101a4840152610140870180516101c485019081526101608901516101e4860152610180905251805161020485018190529394919384936044870192849261022489019291820191601f82010460005b8181101561187c57835185526020948501949093019260010161185e565b50505050818103610160808401919091528a0151805180835260209283019291820191601f82010460005b818110156118c55783518552602094850194909301926001016118a7565b50505089845250848103602093840190815288518083529093918201918981019190601f82010460005b8181101561190d5783518552602094850194909301926001016118ef565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08883030188525060405250505050509392505050565b600081601401835110151515611989576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd90612458565b50016014015173ffffffffffffffffffffffffffffffffffffffff1690565b60006107ba83836000816020018351101515156119f1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100bd906123c8565b50016020015190565b608060405190810160405280600081526020016000815260200160008152602001600081525090565b60006107ba8235612540565b6000601f82018313611a4057600080fd5b8135611a53611a4e826124d9565b6124b2565b81815260209384019390925082018360005b83811015611a915781358601611a7b8882611b41565b8452506020928301929190910190600101611a65565b5050505092915050565b6000601f82018313611aac57600080fd5b8135611aba611a4e826124d9565b81815260209384019390925082018360005b83811015611a915781358601611ae28882611b90565b8452506020928301929190910190600101611acc565b600080601f83018413611b0a57600080fd5b50813567ffffffffffffffff811115611b2257600080fd5b602083019150836001820283011115611b3a57600080fd5b9250929050565b6000601f82018313611b5257600080fd5b8135611b60611a4e826124fa565b91508082526020830160208301858383011115611b7c57600080fd5b611b8783828461255c565b50505092915050565b60006101808284031215611ba357600080fd5b611bae6101806124b2565b90506000611bbc8484611a23565b8252506020611bcd84848301611a23565b6020830152506040611be184828501611a23565b6040830152506060611bf584828501611a23565b6060830152506080611c0984828501611cd9565b60808301525060a0611c1d84828501611cd9565b60a08301525060c0611c3184828501611cd9565b60c08301525060e0611c4584828501611cd9565b60e083015250610100611c5a84828501611cd9565b61010083015250610120611c7084828501611cd9565b6101208301525061014082013567ffffffffffffffff811115611c9257600080fd5b611c9e84828501611b41565b6101408301525061016082013567ffffffffffffffff811115611cc057600080fd5b611ccc84828501611b41565b6101608301525092915050565b60006107ba8235612559565b600060208284031215611cf757600080fd5b6000611d038484611a23565b949350505050565b60008060008060008060c08789031215611d2457600080fd5b863567ffffffffffffffff811115611d3b57600080fd5b611d4789828a01611a9b565b965050602087013567ffffffffffffffff811115611d6457600080fd5b611d7089828a01611a2f565b955050604087013567ffffffffffffffff811115611d8d57600080fd5b611d9989828a01611a9b565b945050606087013567ffffffffffffffff811115611db657600080fd5b611dc289828a01611a2f565b9350506080611dd389828a01611cd9565b92505060a0611de489828a01611a23565b9150509295509295509295565b600080600080600080600060e0888a031215611e0c57600080fd5b873567ffffffffffffffff811115611e2357600080fd5b611e2f8a828b01611a9b565b9750506020611e408a828b01611cd9565b965050604088013567ffffffffffffffff811115611e5d57600080fd5b611e698a828b01611a2f565b955050606088013567ffffffffffffffff811115611e8657600080fd5b611e928a828b01611a9b565b945050608088013567ffffffffffffffff811115611eaf57600080fd5b611ebb8a828b01611a2f565b93505060a0611ecc8a828b01611cd9565b92505060c0611edd8a828b01611a23565b91505092959891949750929550565b600080600060408486031215611f0157600080fd5b833567ffffffffffffffff811115611f1857600080fd5b611f2486828701611af8565b93509350506020611f3786828701611cd9565b9150509250925092565b611f4a81612540565b82525050565b602381527f44454641554c545f46554e4354494f4e5f574554485f434f4e54524143545f4f60208201527f4e4c590000000000000000000000000000000000000000000000000000000000604082015260600190565b601181527f494e56414c49445f4d53475f56414c5545000000000000000000000000000000602082015260400190565b600d81527f4f564552534f4c445f5745544800000000000000000000000000000000000000602082015260400190565b601181527f55494e543235365f554e444552464c4f57000000000000000000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f33325f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b601081527f4449564953494f4e5f42595f5a45524f00000000000000000000000000000000602082015260400190565b601081527f55494e543235365f4f564552464c4f5700000000000000000000000000000000602082015260400190565b601781527f554e535550504f525445445f41535345545f50524f5859000000000000000000602082015260400190565b600f81527f5452414e534645525f4641494c45440000000000000000000000000000000000602082015260400190565b601481527f434f4d504c4554455f46494c4c5f4641494c4544000000000000000000000000602082015260400190565b601a81527f494e53554646494349454e545f4554485f52454d41494e494e47000000000000602082015260400190565b601381527f4f4e4c595f434f4e54524143545f4f574e455200000000000000000000000000602082015260400190565b601881527f4645455f50455243454e544147455f544f4f5f4c415247450000000000000000602082015260400190565b602681527f475245415445525f4f525f455155414c5f544f5f32305f4c454e4754485f524560208201527f5155495245440000000000000000000000000000000000000000000000000000604082015260600190565b602581527f475245415445525f4f525f455155414c5f544f5f345f4c454e4754485f52455160208201527f5549524544000000000000000000000000000000000000000000000000000000604082015260600190565b600e81527f494e56414c49445f414d4f554e54000000000000000000000000000000000000602082015260400190565b805160808301906122f9848261232e565b50602082015161230c602085018261232e565b50604082015161231f604085018261232e565b50606082015161161560608501825b611f4a81612559565b602081016107bd8284611f41565b606081016123538286611f41565b6123606020830185611f41565b611d03604083018461232e565b6040810161237b8285611f41565b6110e0602083018461232e565b602080825281016107bd81611f50565b602080825281016107bd81611fa6565b602080825281016107bd81611fd6565b602080825281016107bd81612006565b602080825281016107bd81612036565b602080825281016107bd8161208c565b602080825281016107bd816120bc565b602080825281016107bd816120ec565b602080825281016107bd8161211c565b602080825281016107bd8161214c565b602080825281016107bd8161217c565b602080825281016107bd816121ac565b602080825281016107bd816121dc565b602080825281016107bd8161220c565b602080825281016107bd81612262565b602080825281016107bd816122b8565b610100810161249782856122e8565b6110e060808301846122e8565b602081016107bd828461232e565b60405181810167ffffffffffffffff811182821017156124d157600080fd5b604052919050565b600067ffffffffffffffff8211156124f057600080fd5b5060209081020190565b600067ffffffffffffffff82111561251157600080fd5b506020601f919091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160190565b73ffffffffffffffffffffffffffffffffffffffff1690565b90565b828183375060009101525600a265627a7a72305820d9f418f11e0f91f06f6f9d22924be0add925495eeb76a6388b5417adb505eeb36c6578706572696d656e74616cf50037" } } }, - "networks": {} -} + "networks": {} +}
\ No newline at end of file diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index c3d986b4a..9ff372e33 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,5 +1,14 @@ [ { + "version": "3.0.1", + "changes": [ + { + "note": "Fix bug in `ForwarderWrapper` where `feeRecipientAddress` was not correctly normalized.", + "pr": 1178 + } + ] + }, + { "version": "3.0.0", "changes": [ { diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index d427bb628..178675763 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -13,7 +13,7 @@ "scripts": { "build": "tsc -b", "build:ci": "yarn build", - "lint": "tslint --project . --exclude **/src/contract_wrappers/**/* --exclude **/lib/**/*", + "lint": "tslint --format stylish --project . --exclude **/lib/**/*", "test:circleci": "run-s test:coverage", "test": "yarn run_mocha", "rebuild_and_test": "run-s build test", diff --git a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts index 7b11f35bc..749aaae10 100644 --- a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts @@ -28,10 +28,10 @@ export abstract class ContractWrapper { protected _networkId: number; protected _web3Wrapper: Web3Wrapper; private _blockAndLogStreamerIfExists: BlockAndLogStreamer<Block, Log> | undefined; - private _blockPollingIntervalMs: number; + private readonly _blockPollingIntervalMs: number; private _blockAndLogStreamIntervalIfExists?: NodeJS.Timer; - private _filters: { [filterToken: string]: FilterObject }; - private _filterCallbacks: { + private readonly _filters: { [filterToken: string]: FilterObject }; + private readonly _filterCallbacks: { [filterToken: string]: EventCallback<ContractEventArgs>; }; private _onLogAddedSubscriptionToken: string | undefined; diff --git a/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts index adf8c7614..45460bd6d 100644 --- a/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts @@ -34,6 +34,9 @@ export class ERC20ProxyWrapper extends ContractWrapper { */ public async getProxyIdAsync(): Promise<AssetProxyId> { const ERC20ProxyContractInstance = this._getERC20ProxyContract(); + // Note(albrow): Below is a TSLint false positive. Code won't compile if + // you remove the type assertion. + /* tslint:disable-next-line:no-unnecessary-type-assertion */ const proxyId = (await ERC20ProxyContractInstance.getProxyId.callAsync()) as AssetProxyId; return proxyId; } diff --git a/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts index f5fc63b42..5e0ec1951 100644 --- a/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts @@ -18,12 +18,11 @@ import { } from '../types'; import { assert } from '../utils/assert'; import { constants } from '../utils/constants'; +import { utils } from '../utils/utils'; import { ContractWrapper } from './contract_wrapper'; import { ERC20ProxyWrapper } from './erc20_proxy_wrapper'; -const removeUndefinedProperties = _.pickBy; - /** * This class includes all the functionality related to interacting with ERC20 token contracts. * All ERC20 method calls are supported, along with some convenience methods for getting/setting allowances @@ -32,8 +31,8 @@ const removeUndefinedProperties = _.pickBy; export class ERC20TokenWrapper extends ContractWrapper { public abi: ContractAbi = ERC20Token.compilerOutput.abi; public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - private _tokenContractsByAddress: { [address: string]: ERC20TokenContract }; - private _erc20ProxyWrapper: ERC20ProxyWrapper; + private readonly _tokenContractsByAddress: { [address: string]: ERC20TokenContract }; + private readonly _erc20ProxyWrapper: ERC20ProxyWrapper; /** * Instantiate ERC20TokenWrapper * @param web3Wrapper Web3Wrapper instance to use @@ -108,7 +107,7 @@ export class ERC20TokenWrapper extends ContractWrapper { const txHash = await tokenContract.approve.sendTransactionAsync( normalizedSpenderAddress, amountInBaseUnits, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ from: normalizedOwnerAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, @@ -278,7 +277,7 @@ export class ERC20TokenWrapper extends ContractWrapper { const txHash = await tokenContract.transfer.sendTransactionAsync( normalizedToAddress, amountInBaseUnits, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ from: normalizedFromAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, @@ -339,7 +338,7 @@ export class ERC20TokenWrapper extends ContractWrapper { normalizedFromAddress, normalizedToAddress, amountInBaseUnits, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ from: normalizedSenderAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, diff --git a/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts index 9f3a6930b..12758e191 100644 --- a/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts @@ -34,6 +34,9 @@ export class ERC721ProxyWrapper extends ContractWrapper { */ public async getProxyIdAsync(): Promise<AssetProxyId> { const ERC721ProxyContractInstance = await this._getERC721ProxyContract(); + // Note(albrow): Below is a TSLint false positive. Code won't compile if + // you remove the type assertion. + /* tslint:disable-next-line:no-unnecessary-type-assertion */ const proxyId = (await ERC721ProxyContractInstance.getProxyId.callAsync()) as AssetProxyId; return proxyId; } diff --git a/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts index 1c4b61c0b..1610af47b 100644 --- a/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts @@ -18,12 +18,11 @@ import { } from '../types'; import { assert } from '../utils/assert'; import { constants } from '../utils/constants'; +import { utils } from '../utils/utils'; import { ContractWrapper } from './contract_wrapper'; import { ERC721ProxyWrapper } from './erc721_proxy_wrapper'; -const removeUndefinedProperties = _.pickBy; - /** * This class includes all the functionality related to interacting with ERC721 token contracts. * All ERC721 method calls are supported, along with some convenience methods for getting/setting allowances @@ -31,8 +30,8 @@ const removeUndefinedProperties = _.pickBy; */ export class ERC721TokenWrapper extends ContractWrapper { public abi: ContractAbi = ERC721Token.compilerOutput.abi; - private _tokenContractsByAddress: { [address: string]: ERC721TokenContract }; - private _erc721ProxyWrapper: ERC721ProxyWrapper; + private readonly _tokenContractsByAddress: { [address: string]: ERC721TokenContract }; + private readonly _erc721ProxyWrapper: ERC721ProxyWrapper; /** * Instantiate ERC721TokenWrapper * @param web3Wrapper Web3Wrapper instance to use @@ -235,7 +234,7 @@ export class ERC721TokenWrapper extends ContractWrapper { const txHash = await tokenContract.setApprovalForAll.sendTransactionAsync( normalizedOperatorAddress, isApproved, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, from: normalizedOwnerAddress, @@ -295,7 +294,7 @@ export class ERC721TokenWrapper extends ContractWrapper { const txHash = await tokenContract.approve.sendTransactionAsync( normalizedApprovedAddress, tokenId, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, from: tokenOwnerAddress, @@ -366,7 +365,7 @@ export class ERC721TokenWrapper extends ContractWrapper { ownerAddress, normalizedReceiverAddress, tokenId, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, from: normalizedSenderAddress, diff --git a/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts index d4a08da86..913c47cf7 100644 --- a/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts @@ -8,22 +8,21 @@ import * as _ from 'lodash'; import { BlockRange, ContractWrappersError, EventCallback, IndexedFilterValues, TransactionOpts } from '../types'; import { assert } from '../utils/assert'; +import { utils } from '../utils/utils'; import { ContractWrapper } from './contract_wrapper'; import { ERC20TokenWrapper } from './erc20_token_wrapper'; -const removeUndefinedProperties = _.pickBy; - /** * This class includes all the functionality related to interacting with a wrapped Ether ERC20 token contract. * The caller can convert ETH into the equivalent number of wrapped ETH ERC20 tokens and back. */ export class EtherTokenWrapper extends ContractWrapper { public abi: ContractAbi = WETH9.compilerOutput.abi; - private _etherTokenContractsByAddress: { + private readonly _etherTokenContractsByAddress: { [address: string]: WETH9Contract; } = {}; - private _erc20TokenWrapper: ERC20TokenWrapper; + private readonly _erc20TokenWrapper: ERC20TokenWrapper; /** * Instantiate EtherTokenWrapper. * @param web3Wrapper Web3Wrapper instance to use @@ -67,7 +66,7 @@ export class EtherTokenWrapper extends ContractWrapper { const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress); const txHash = await wethContract.deposit.sendTransactionAsync( - removeUndefinedProperties({ + utils.removeUndefinedProperties({ from: normalizedDepositorAddress, value: amountInWei, gas: txOpts.gasLimit, @@ -109,7 +108,7 @@ export class EtherTokenWrapper extends ContractWrapper { const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress); const txHash = await wethContract.withdraw.sendTransactionAsync( amountInWei, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ from: normalizedWithdrawerAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, diff --git a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts index 907d25aa0..2e978f35b 100644 --- a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts @@ -46,8 +46,8 @@ export class ExchangeWrapper extends ContractWrapper { public address: string; public zrxTokenAddress: string; private _exchangeContractIfExists?: ExchangeContract; - private _erc721TokenWrapper: ERC721TokenWrapper; - private _erc20TokenWrapper: ERC20TokenWrapper; + private readonly _erc721TokenWrapper: ERC721TokenWrapper; + private readonly _erc20TokenWrapper: ERC20TokenWrapper; /** * Instantiate ExchangeWrapper * @param web3Wrapper Web3Wrapper instance to use. diff --git a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts index 9463a6849..80742e030 100644 --- a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts @@ -1,7 +1,7 @@ import { ForwarderContract } from '@0x/abi-gen-wrappers'; import { Forwarder } from '@0x/contract-artifacts'; import { schemas } from '@0x/json-schemas'; -import { AssetProxyId, SignedOrder } from '@0x/types'; +import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { ContractAbi } from 'ethereum-types'; @@ -118,7 +118,7 @@ export class ForwarderWrapper extends ContractWrapper { optimizedFeeOrders, feeSignatures, formattedFeePercentage, - feeRecipientAddress, + normalizedFeeRecipientAddress, { value: ethAmount, from: normalizedTakerAddress, @@ -207,7 +207,7 @@ export class ForwarderWrapper extends ContractWrapper { optimizedFeeOrders, feeSignatures, formattedFeePercentage, - feeRecipientAddress, + normalizedFeeRecipientAddress, { value: ethAmount, from: normalizedTakerAddress, diff --git a/packages/contract-wrappers/src/utils/utils.ts b/packages/contract-wrappers/src/utils/utils.ts index fbacdaa28..0b3270e78 100644 --- a/packages/contract-wrappers/src/utils/utils.ts +++ b/packages/contract-wrappers/src/utils/utils.ts @@ -1,5 +1,6 @@ import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as _ from 'lodash'; import { constants } from './constants'; @@ -14,4 +15,7 @@ export const utils = { numberPercentageToEtherTokenAmountPercentage(percentage: number): BigNumber { return Web3Wrapper.toBaseUnitAmount(constants.ONE_AMOUNT, constants.ETHER_TOKEN_DECIMALS).mul(percentage); }, + removeUndefinedProperties<T extends object>(obj: T): Partial<T> { + return _.pickBy(obj); + }, }; diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 3a5f12c74..4f24310e8 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -23,7 +23,7 @@ "compile": "sol-compiler --contracts-dir contracts", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", - "lint": "tslint --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", + "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", "coverage:report:text": "istanbul report text", "coverage:report:html": "istanbul report html && open coverage/index.html", "profiler:report:html": "istanbul report html && open coverage/index.html", diff --git a/packages/dev-tools-pages/package.json b/packages/dev-tools-pages/package.json index 3563b4bef..ca13c1cc8 100644 --- a/packages/dev-tools-pages/package.json +++ b/packages/dev-tools-pages/package.json @@ -11,7 +11,7 @@ "build:ci": "yarn build", "build:dev": "../../node_modules/.bin/webpack --mode development", "clean": "shx rm -f public/bundle*", - "lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'", + "lint": "tslint --format stylish --project . 'ts/**/*.ts' 'ts/**/*.tsx'", "dev": "webpack-dev-server --mode development --content-base public" }, "license": "Apache-2.0", diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index d1b1efc87..7c6bfde90 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -17,7 +17,7 @@ "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "clean": "shx rm -rf lib", - "lint": "tslint --project ." + "lint": "tslint --format stylish --project ." }, "license": "Apache-2.0", "repository": { diff --git a/packages/ethereum-types/package.json b/packages/ethereum-types/package.json index d24e39dfd..49b093bdd 100644 --- a/packages/ethereum-types/package.json +++ b/packages/ethereum-types/package.json @@ -11,7 +11,7 @@ "build": "tsc -b", "build:ci": "yarn build", "clean": "shx rm -rf lib generated_docs", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, "config": { diff --git a/packages/fill-scenarios/package.json b/packages/fill-scenarios/package.json index 0e1303259..ae7fa02ad 100644 --- a/packages/fill-scenarios/package.json +++ b/packages/fill-scenarios/package.json @@ -8,7 +8,7 @@ "build": "yarn tsc -b", "build:ci": "yarn build", "clean": "shx rm -rf lib src/generated_contract_wrappers", - "lint": "tslint --project ." + "lint": "tslint --format stylish --project ." }, "license": "Apache-2.0", "repository": { diff --git a/packages/instant/package.json b/packages/instant/package.json index 421802530..0329c3078 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -16,7 +16,7 @@ "build:ci": "yarn build", "watch_without_deps": "tsc -w", "dev": "webpack-dev-server --mode development", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "test": "jest", "test:coverage": "jest --coverage", "rebuild_and_test": "run-s clean build test", @@ -58,7 +58,7 @@ "react-redux": "^5.0.7", "redux": "^4.0.0", "redux-devtools-extension": "^2.13.5", - "styled-components": "^3.4.9", + "styled-components": "^4.0.2", "ts-optchain": "^0.1.1" }, "devDependencies": { @@ -73,6 +73,7 @@ "@types/react-dom": "^16.0.8", "@types/react-redux": "^6.0.9", "@types/redux": "^3.6.0", + "@types/styled-components": "^4.0.1", "awesome-typescript-loader": "^5.2.1", "enzyme": "^3.6.0", "enzyme-adapter-react-16": "^1.5.0", diff --git a/packages/instant/src/components/amount_input.tsx b/packages/instant/src/components/amount_input.tsx deleted file mode 100644 index c89fb05ad..000000000 --- a/packages/instant/src/components/amount_input.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { BigNumber } from '@0x/utils'; -import * as _ from 'lodash'; -import * as React from 'react'; - -import { ColorOption } from '../style/theme'; -import { util } from '../util/util'; - -import { Container, Input } from './ui'; - -export interface AmountInputProps { - fontColor?: ColorOption; - fontSize?: string; - value?: BigNumber; - onChange: (value?: BigNumber) => void; -} - -export class AmountInput extends React.Component<AmountInputProps> { - public static defaultProps = { - onChange: util.boundNoop, - }; - public render(): React.ReactNode { - const { fontColor, fontSize, value } = this.props; - return ( - <Container borderBottom="1px solid rgba(255,255,255,0.3)" display="inline-block"> - <Input - fontColor={fontColor} - fontSize={fontSize} - onChange={this._handleChange} - value={!_.isUndefined(value) ? value.toString() : ''} - placeholder="0.00" - width="2.2em" - /> - </Container> - ); - } - private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => { - const value = event.target.value; - let bigNumberValue; - if (!_.isEmpty(value)) { - try { - bigNumberValue = new BigNumber(event.target.value); - } catch { - // We don't want to allow values that can't be a BigNumber, so don't even call onChange. - return; - } - } - this.props.onChange(bigNumberValue); - }; -} diff --git a/packages/instant/src/components/animations/slide_animations.tsx b/packages/instant/src/components/animations/slide_animations.tsx index 1f10a2ed6..84280372b 100644 --- a/packages/instant/src/components/animations/slide_animations.tsx +++ b/packages/instant/src/components/animations/slide_animations.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; +import { Keyframes } from 'styled-components'; -import { keyframes, styled } from '../../style/theme'; +import { css, keyframes, styled } from '../../style/theme'; const slideKeyframeGenerator = (fromY: string, toY: string) => keyframes` from { @@ -15,7 +16,7 @@ const slideKeyframeGenerator = (fromY: string, toY: string) => keyframes` `; export interface SlideAnimationProps { - keyframes: string; + keyframes: Keyframes; animationType: string; animationDirection?: string; } @@ -24,7 +25,10 @@ export const SlideAnimation = styled.div < SlideAnimationProps > ` - animation-name: ${props => props.keyframes}; + animation-name: ${props => + css` + ${props.keyframes}; + `}; animation-duration: 0.3s; animation-timing-function: ${props => props.animationType}; animation-delay: 0s; diff --git a/packages/instant/src/components/asset_amount_input.tsx b/packages/instant/src/components/asset_amount_input.tsx deleted file mode 100644 index c03ef1cf3..000000000 --- a/packages/instant/src/components/asset_amount_input.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { BigNumber } from '@0x/utils'; -import * as _ from 'lodash'; -import * as React from 'react'; - -import { ColorOption } from '../style/theme'; -import { ERC20Asset } from '../types'; -import { assetUtils } from '../util/asset'; -import { util } from '../util/util'; - -import { AmountInput, AmountInputProps } from './amount_input'; -import { Container, Text } from './ui'; - -// Asset amounts only apply to ERC20 assets -export interface AssetAmountInputProps extends AmountInputProps { - asset?: ERC20Asset; - onChange: (value?: BigNumber, asset?: ERC20Asset) => void; -} - -export class AssetAmountInput extends React.Component<AssetAmountInputProps> { - public static defaultProps = { - onChange: util.boundNoop, - }; - public render(): React.ReactNode { - const { asset, onChange, ...rest } = this.props; - return ( - <Container> - <AmountInput {...rest} onChange={this._handleChange} /> - <Container display="inline-block" marginLeft="10px"> - <Text fontSize={rest.fontSize} fontColor={ColorOption.white} textTransform="uppercase"> - {assetUtils.bestNameForAsset(asset)} - </Text> - </Container> - </Container> - ); - } - private readonly _handleChange = (value?: BigNumber): void => { - this.props.onChange(value, this.props.asset); - }; -} diff --git a/packages/instant/src/components/erc20_asset_amount_input.tsx b/packages/instant/src/components/erc20_asset_amount_input.tsx new file mode 100644 index 000000000..583fad28b --- /dev/null +++ b/packages/instant/src/components/erc20_asset_amount_input.tsx @@ -0,0 +1,84 @@ +import * as _ from 'lodash'; +import * as React from 'react'; + +import { ColorOption, transparentWhite } from '../style/theme'; +import { ERC20Asset } from '../types'; +import { assetUtils } from '../util/asset'; +import { BigNumberInput } from '../util/big_number_input'; +import { util } from '../util/util'; + +import { ScalingAmountInput } from './scaling_amount_input'; +import { Container, Text } from './ui'; + +// Asset amounts only apply to ERC20 assets +export interface ERC20AssetAmountInputProps { + asset?: ERC20Asset; + value?: BigNumberInput; + onChange: (value?: BigNumberInput, asset?: ERC20Asset) => void; + startingFontSizePx: number; + fontColor?: ColorOption; +} + +export interface ERC20AssetAmountInputState { + currentFontSizePx: number; +} + +export class ERC20AssetAmountInput extends React.Component<ERC20AssetAmountInputProps, ERC20AssetAmountInputState> { + public static defaultProps = { + onChange: util.boundNoop, + }; + constructor(props: ERC20AssetAmountInputProps) { + super(props); + this.state = { + currentFontSizePx: props.startingFontSizePx, + }; + } + public render(): React.ReactNode { + const { asset, onChange, ...rest } = this.props; + return ( + <Container whiteSpace="nowrap"> + <Container borderBottom={`1px solid ${transparentWhite}`} display="inline-block"> + <ScalingAmountInput + {...rest} + textLengthThreshold={this._textLengthThresholdForAsset(asset)} + maxFontSizePx={this.props.startingFontSizePx} + onChange={this._handleChange} + onFontSizeChange={this._handleFontSizeChange} + /> + </Container> + <Container display="inline-flex" marginLeft="10px" title={assetUtils.bestNameForAsset(asset)}> + <Text + fontSize={`${this.state.currentFontSizePx}px`} + fontColor={ColorOption.white} + textTransform="uppercase" + > + {assetUtils.formattedSymbolForAsset(asset)} + </Text> + </Container> + </Container> + ); + } + private readonly _handleChange = (value?: BigNumberInput): void => { + this.props.onChange(value, this.props.asset); + }; + private readonly _handleFontSizeChange = (fontSizePx: number): void => { + this.setState({ + currentFontSizePx: fontSizePx, + }); + }; + // For assets with symbols of different length, + // start scaling the input at different character lengths + private readonly _textLengthThresholdForAsset = (asset?: ERC20Asset): number => { + if (_.isUndefined(asset)) { + return 3; + } + const symbol = asset.metaData.symbol; + if (symbol.length <= 3) { + return 5; + } + if (symbol.length === 5) { + return 3; + } + return 4; + }; +} diff --git a/packages/instant/src/components/instant_heading.tsx b/packages/instant/src/components/instant_heading.tsx index 17ac65429..1ef276ff3 100644 --- a/packages/instant/src/components/instant_heading.tsx +++ b/packages/instant/src/components/instant_heading.tsx @@ -2,7 +2,7 @@ import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; import * as React from 'react'; -import { SelectedAssetAmountInput } from '../containers/selected_asset_amount_input'; +import { SelectedERC20AssetAmountInput } from '../containers/selected_erc20_asset_amount_input'; import { ColorOption } from '../style/theme'; import { AsyncProcessState, OrderProcessState, OrderState } from '../types'; import { format } from '../util/format'; @@ -48,7 +48,9 @@ export class InstantHeading extends React.Component<InstantHeadingProps, {}> { </Text> </Container> <Flex direction="row" justify="space-between"> - <SelectedAssetAmountInput fontSize="45px" /> + <Flex height="60px"> + <SelectedERC20AssetAmountInput startingFontSizePx={38} /> + </Flex> <Flex direction="column" justify="space-between"> {iconOrAmounts} </Flex> diff --git a/packages/instant/src/components/scaling_amount_input.tsx b/packages/instant/src/components/scaling_amount_input.tsx new file mode 100644 index 000000000..655ae2b74 --- /dev/null +++ b/packages/instant/src/components/scaling_amount_input.tsx @@ -0,0 +1,52 @@ +import * as _ from 'lodash'; +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; +import { BigNumberInput } from '../util/big_number_input'; +import { util } from '../util/util'; + +import { ScalingInput } from './scaling_input'; + +export interface ScalingAmountInputProps { + maxFontSizePx: number; + textLengthThreshold: number; + fontColor?: ColorOption; + value?: BigNumberInput; + onChange: (value?: BigNumberInput) => void; + onFontSizeChange: (fontSizePx: number) => void; +} + +export class ScalingAmountInput extends React.Component<ScalingAmountInputProps> { + public static defaultProps = { + onChange: util.boundNoop, + onFontSizeChange: util.boundNoop, + }; + public render(): React.ReactNode { + const { textLengthThreshold, fontColor, maxFontSizePx, value, onFontSizeChange } = this.props; + return ( + <ScalingInput + maxFontSizePx={maxFontSizePx} + textLengthThreshold={textLengthThreshold} + onFontSizeChange={onFontSizeChange} + fontColor={fontColor} + onChange={this._handleChange} + value={!_.isUndefined(value) ? value.toDisplayString() : ''} + placeholder="0.00" + emptyInputWidthCh={3.5} + /> + ); + } + private readonly _handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => { + const value = event.target.value; + let bigNumberValue; + if (!_.isEmpty(value)) { + try { + bigNumberValue = new BigNumberInput(value); + } catch { + // We don't want to allow values that can't be a BigNumber, so don't even call onChange. + return; + } + } + this.props.onChange(bigNumberValue); + }; +} diff --git a/packages/instant/src/components/scaling_input.tsx b/packages/instant/src/components/scaling_input.tsx new file mode 100644 index 000000000..34cb0b5fd --- /dev/null +++ b/packages/instant/src/components/scaling_input.tsx @@ -0,0 +1,170 @@ +import * as _ from 'lodash'; +import * as React from 'react'; + +import { ColorOption } from '../style/theme'; +import { util } from '../util/util'; + +import { Input } from './ui'; + +export enum ScalingInputPhase { + FixedFontSize, + ScalingFontSize, +} + +export interface ScalingSettings { + percentageToReduceFontSizePerCharacter: number; + constantPxToIncreaseWidthPerCharacter: number; +} + +export interface ScalingInputProps { + textLengthThreshold: number; + maxFontSizePx: number; + value: string; + emptyInputWidthCh: number; + onChange: (event: React.ChangeEvent<HTMLInputElement>) => void; + onFontSizeChange: (fontSizePx: number) => void; + fontColor?: ColorOption; + placeholder?: string; + maxLength?: number; + scalingSettings: ScalingSettings; +} + +export interface ScalingInputState { + inputWidthPxAtPhaseChange?: number; +} + +export interface ScalingInputSnapshot { + inputWidthPx: number; +} + +// These are magic numbers that were determined experimentally. +const defaultScalingSettings: ScalingSettings = { + percentageToReduceFontSizePerCharacter: 0.125, + constantPxToIncreaseWidthPerCharacter: 4, +}; + +export class ScalingInput extends React.Component<ScalingInputProps, ScalingInputState> { + public static defaultProps = { + onChange: util.boundNoop, + onFontSizeChange: util.boundNoop, + maxLength: 7, + scalingSettings: defaultScalingSettings, + }; + public state: ScalingInputState = { + inputWidthPxAtPhaseChange: undefined, + }; + private readonly _inputRef = React.createRef<HTMLInputElement>(); + public static getPhase(textLengthThreshold: number, value: string): ScalingInputPhase { + if (value.length <= textLengthThreshold) { + return ScalingInputPhase.FixedFontSize; + } + return ScalingInputPhase.ScalingFontSize; + } + public static getPhaseFromProps(props: ScalingInputProps): ScalingInputPhase { + const { value, textLengthThreshold } = props; + return ScalingInput.getPhase(textLengthThreshold, value); + } + public static calculateFontSize( + textLengthThreshold: number, + maxFontSizePx: number, + phase: ScalingInputPhase, + value: string, + percentageToReduceFontSizePerCharacter: number, + ): number { + if (phase !== ScalingInputPhase.ScalingFontSize) { + return maxFontSizePx; + } + const charactersOverMax = value.length - textLengthThreshold; + const scalingFactor = (1 - percentageToReduceFontSizePerCharacter) ** charactersOverMax; + const fontSize = scalingFactor * maxFontSizePx; + return fontSize; + } + public static calculateFontSizeFromProps(props: ScalingInputProps, phase: ScalingInputPhase): number { + const { textLengthThreshold, value, maxFontSizePx, scalingSettings } = props; + return ScalingInput.calculateFontSize( + textLengthThreshold, + maxFontSizePx, + phase, + value, + scalingSettings.percentageToReduceFontSizePerCharacter, + ); + } + public getSnapshotBeforeUpdate(): ScalingInputSnapshot { + return { + inputWidthPx: this._getInputWidthInPx(), + }; + } + public componentDidUpdate( + prevProps: ScalingInputProps, + prevState: ScalingInputState, + snapshot: ScalingInputSnapshot, + ): void { + const prevPhase = ScalingInput.getPhaseFromProps(prevProps); + const curPhase = ScalingInput.getPhaseFromProps(this.props); + // if we went from fixed to scaling, save the width from the transition + if (prevPhase !== ScalingInputPhase.ScalingFontSize && curPhase === ScalingInputPhase.ScalingFontSize) { + this.setState({ + inputWidthPxAtPhaseChange: snapshot.inputWidthPx, + }); + } + // if we went from scaling to fixed, revert back to scaling using `ch` + if (prevPhase === ScalingInputPhase.ScalingFontSize && curPhase !== ScalingInputPhase.ScalingFontSize) { + this.setState({ + inputWidthPxAtPhaseChange: undefined, + }); + } + const prevFontSize = ScalingInput.calculateFontSizeFromProps(prevProps, prevPhase); + const curFontSize = ScalingInput.calculateFontSizeFromProps(this.props, curPhase); + // If font size has changed, notify. + if (prevFontSize !== curFontSize) { + this.props.onFontSizeChange(curFontSize); + } + } + public render(): React.ReactNode { + const { fontColor, onChange, placeholder, value, maxLength } = this.props; + const phase = ScalingInput.getPhaseFromProps(this.props); + return ( + <Input + ref={this._inputRef as any} + fontColor={fontColor} + onChange={onChange} + value={value} + placeholder={placeholder} + fontSize={`${this._calculateFontSize(phase)}px`} + width={this._calculateWidth(phase)} + maxLength={maxLength} + /> + ); + } + private readonly _calculateWidth = (phase: ScalingInputPhase): string => { + const { value, textLengthThreshold, scalingSettings } = this.props; + if (_.isEmpty(value)) { + return `${this.props.emptyInputWidthCh}ch`; + } + switch (phase) { + case ScalingInputPhase.FixedFontSize: + return `${value.length}ch`; + case ScalingInputPhase.ScalingFontSize: + const { inputWidthPxAtPhaseChange } = this.state; + if (!_.isUndefined(inputWidthPxAtPhaseChange)) { + const charactersOverMax = value.length - textLengthThreshold; + const scalingAmount = scalingSettings.constantPxToIncreaseWidthPerCharacter * charactersOverMax; + const width = inputWidthPxAtPhaseChange + scalingAmount; + return `${width}px`; + } + return `${textLengthThreshold}ch`; + default: + return '1ch'; + } + }; + private readonly _calculateFontSize = (phase: ScalingInputPhase): number => { + return ScalingInput.calculateFontSizeFromProps(this.props, phase); + }; + private readonly _getInputWidthInPx = (): number => { + const ref = this._inputRef.current; + if (!ref) { + return 0; + } + return ref.getBoundingClientRect().width; + }; +} diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx index 5e2218c68..76b570de7 100644 --- a/packages/instant/src/components/ui/container.tsx +++ b/packages/instant/src/components/ui/container.tsx @@ -1,5 +1,3 @@ -import * as React from 'react'; - import { ColorOption, styled } from '../../style/theme'; import { cssRuleIfExists } from '../../style/util'; @@ -11,6 +9,7 @@ export interface ContainerProps { bottom?: string; left?: string; width?: string; + height?: string; maxWidth?: string; margin?: string; marginTop?: string; @@ -27,14 +26,14 @@ export interface ContainerProps { backgroundColor?: ColorOption; hasBoxShadow?: boolean; zIndex?: number; + whiteSpace?: string; opacity?: number; } -const PlainContainer: React.StatelessComponent<ContainerProps> = ({ children, className }) => ( - <div className={className}>{children}</div> -); - -export const Container = styled(PlainContainer)` +export const Container = + styled.div < + ContainerProps > + ` box-sizing: border-box; ${props => cssRuleIfExists(props, 'display')} ${props => cssRuleIfExists(props, 'position')} @@ -43,6 +42,7 @@ export const Container = styled(PlainContainer)` ${props => cssRuleIfExists(props, 'bottom')} ${props => cssRuleIfExists(props, 'left')} ${props => cssRuleIfExists(props, 'width')} + ${props => cssRuleIfExists(props, 'height')} ${props => cssRuleIfExists(props, 'max-width')} ${props => cssRuleIfExists(props, 'margin')} ${props => cssRuleIfExists(props, 'margin-top')} @@ -55,6 +55,7 @@ export const Container = styled(PlainContainer)` ${props => cssRuleIfExists(props, 'border-top')} ${props => cssRuleIfExists(props, 'border-bottom')} ${props => cssRuleIfExists(props, 'z-index')} + ${props => cssRuleIfExists(props, 'white-space')} ${props => cssRuleIfExists(props, 'opacity')} ${props => (props.hasBoxShadow ? `box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1)` : '')}; background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')}; diff --git a/packages/instant/src/components/ui/flex.tsx b/packages/instant/src/components/ui/flex.tsx index 327e91926..5fa3fc95b 100644 --- a/packages/instant/src/components/ui/flex.tsx +++ b/packages/instant/src/components/ui/flex.tsx @@ -1,5 +1,3 @@ -import * as React from 'react'; - import { ColorOption, styled } from '../../style/theme'; import { cssRuleIfExists } from '../../style/util'; @@ -9,21 +7,22 @@ export interface FlexProps { justify?: 'flex-start' | 'center' | 'space-around' | 'space-between' | 'space-evenly' | 'flex-end'; align?: 'flex-start' | 'center' | 'space-around' | 'space-between' | 'space-evenly' | 'flex-end'; width?: string; + height?: string; backgroundColor?: ColorOption; className?: string; } -const PlainFlex: React.StatelessComponent<FlexProps> = ({ children, className }) => ( - <div className={className}>{children}</div> -); - -export const Flex = styled(PlainFlex)` +export const Flex = + styled.div < + FlexProps > + ` display: flex; flex-direction: ${props => props.direction}; flex-wrap: ${props => props.flexWrap}; justify-content: ${props => props.justify}; align-items: ${props => props.align}; ${props => cssRuleIfExists(props, 'width')} + ${props => cssRuleIfExists(props, 'height')} background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')}; `; diff --git a/packages/instant/src/components/ui/input.tsx b/packages/instant/src/components/ui/input.tsx index f8c6b6ef6..a884ff7cb 100644 --- a/packages/instant/src/components/ui/input.tsx +++ b/packages/instant/src/components/ui/input.tsx @@ -12,11 +12,10 @@ export interface InputProps { onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void; } -const PlainInput: React.StatelessComponent<InputProps> = ({ value, className, placeholder, onChange }) => ( - <input className={className} value={value} onChange={onChange} placeholder={placeholder} /> -); - -export const Input = styled(PlainInput)` +export const Input = + styled.input < + InputProps > + ` font-size: ${props => props.fontSize}; width: ${props => props.width}; padding: 0.1em 0em; diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx index 9fb8ea26f..fd72f6cc8 100644 --- a/packages/instant/src/components/ui/text.tsx +++ b/packages/instant/src/components/ui/text.tsx @@ -23,14 +23,11 @@ export interface TextProps { display?: string; } -const PlainText: React.StatelessComponent<TextProps> = ({ children, className, onClick }) => ( - <div className={className} onClick={onClick}> - {children} - </div> -); - const darkenOnHoverAmount = 0.3; -export const Text = styled(PlainText)` +export const Text = + styled.div < + TextProps > + ` font-family: ${props => props.fontFamily}; font-style: ${props => props.fontStyle}; font-weight: ${props => props.fontWeight}; diff --git a/packages/instant/src/containers/selected_asset_amount_input.ts b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts index e9dbc61ce..ee76e9d66 100644 --- a/packages/instant/src/containers/selected_asset_amount_input.ts +++ b/packages/instant/src/containers/selected_erc20_asset_amount_input.ts @@ -11,34 +11,35 @@ import { Action, actions } from '../redux/actions'; import { State } from '../redux/reducer'; import { ColorOption } from '../style/theme'; import { ERC20Asset, OrderProcessState } from '../types'; +import { BigNumberInput } from '../util/big_number_input'; import { errorUtil } from '../util/error'; -import { AssetAmountInput } from '../components/asset_amount_input'; +import { ERC20AssetAmountInput } from '../components/erc20_asset_amount_input'; -export interface SelectedAssetAmountInputProps { +export interface SelectedERC20AssetAmountInputProps { fontColor?: ColorOption; - fontSize?: string; + startingFontSizePx: number; } interface ConnectedState { assetBuyer?: AssetBuyer; - value?: BigNumber; + value?: BigNumberInput; asset?: ERC20Asset; } interface ConnectedDispatch { - updateBuyQuote: (assetBuyer?: AssetBuyer, value?: BigNumber, asset?: ERC20Asset) => void; + updateBuyQuote: (assetBuyer?: AssetBuyer, value?: BigNumberInput, asset?: ERC20Asset) => void; } interface ConnectedProps { - value?: BigNumber; + value?: BigNumberInput; asset?: ERC20Asset; - onChange: (value?: BigNumber, asset?: ERC20Asset) => void; + onChange: (value?: BigNumberInput, asset?: ERC20Asset) => void; } -type FinalProps = ConnectedProps & SelectedAssetAmountInputProps; +type FinalProps = ConnectedProps & SelectedERC20AssetAmountInputProps; -const mapStateToProps = (state: State, _ownProps: SelectedAssetAmountInputProps): ConnectedState => { +const mapStateToProps = (state: State, _ownProps: SelectedERC20AssetAmountInputProps): ConnectedState => { const selectedAsset = state.selectedAsset; if (_.isUndefined(selectedAsset) || selectedAsset.metaData.assetProxyId !== AssetProxyId.ERC20) { return { @@ -82,7 +83,7 @@ const debouncedUpdateBuyQuoteAsync = _.debounce(updateBuyQuoteAsync, 200, { trai const mapDispatchToProps = ( dispatch: Dispatch<Action>, - _ownProps: SelectedAssetAmountInputProps, + _ownProps: SelectedERC20AssetAmountInputProps, ): ConnectedDispatch => ({ updateBuyQuote: (assetBuyer, value, asset) => { // Update the input @@ -104,7 +105,7 @@ const mapDispatchToProps = ( const mergeProps = ( connectedState: ConnectedState, connectedDispatch: ConnectedDispatch, - ownProps: SelectedAssetAmountInputProps, + ownProps: SelectedERC20AssetAmountInputProps, ): FinalProps => { return { ...ownProps, @@ -116,8 +117,8 @@ const mergeProps = ( }; }; -export const SelectedAssetAmountInput: React.ComponentClass<SelectedAssetAmountInputProps> = connect( +export const SelectedERC20AssetAmountInput: React.ComponentClass<SelectedERC20AssetAmountInputProps> = connect( mapStateToProps, mapDispatchToProps, mergeProps, -)(AssetAmountInput); +)(ERC20AssetAmountInput); diff --git a/packages/instant/src/redux/actions.ts b/packages/instant/src/redux/actions.ts index 5a4099f15..46045024b 100644 --- a/packages/instant/src/redux/actions.ts +++ b/packages/instant/src/redux/actions.ts @@ -2,6 +2,8 @@ import { BuyQuote } from '@0x/asset-buyer'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; +import { BigNumberInput } from '../util/big_number_input'; + import { ActionsUnion, OrderState } from '../types'; export interface PlainAction<T extends string> { @@ -36,7 +38,8 @@ export enum ActionTypes { export const actions = { updateEthUsdPrice: (price?: BigNumber) => createAction(ActionTypes.UPDATE_ETH_USD_PRICE, price), - updateSelectedAssetAmount: (amount?: BigNumber) => createAction(ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, amount), + updateSelectedAssetAmount: (amount?: BigNumberInput) => + createAction(ActionTypes.UPDATE_SELECTED_ASSET_AMOUNT, amount), updateBuyOrderState: (orderState: OrderState) => createAction(ActionTypes.UPDATE_BUY_ORDER_STATE, orderState), updateLatestBuyQuote: (buyQuote?: BuyQuote) => createAction(ActionTypes.UPDATE_LATEST_BUY_QUOTE, buyQuote), updateSelectedAsset: (assetData?: string) => createAction(ActionTypes.UPDATE_SELECTED_ASSET, assetData), diff --git a/packages/instant/src/redux/reducer.ts b/packages/instant/src/redux/reducer.ts index 25d0092b2..614ed21ac 100644 --- a/packages/instant/src/redux/reducer.ts +++ b/packages/instant/src/redux/reducer.ts @@ -1,6 +1,7 @@ import { AssetBuyer, BuyQuote } from '@0x/asset-buyer'; -import { ObjectMap } from '@0x/types'; +import { AssetProxyId, ObjectMap } from '@0x/types'; import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import * as _ from 'lodash'; import { assetMetaDataMap } from '../data/asset_meta_data_map'; @@ -14,6 +15,7 @@ import { OrderState, } from '../types'; import { assetUtils } from '../util/asset'; +import { BigNumberInput } from '../util/big_number_input'; import { Action, ActionTypes } from './actions'; @@ -22,7 +24,7 @@ export interface State { assetBuyer?: AssetBuyer; assetMetaDataMap: ObjectMap<AssetMetaData>; selectedAsset?: Asset; - selectedAssetAmount?: BigNumber; + selectedAssetAmount?: BigNumberInput; buyOrderState: OrderState; ethUsdPrice?: BigNumber; latestBuyQuote?: BuyQuote; @@ -56,11 +58,19 @@ export const reducer = (state: State = INITIAL_STATE, action: Action): State => selectedAssetAmount: action.data, }; case ActionTypes.UPDATE_LATEST_BUY_QUOTE: - return { - ...state, - latestBuyQuote: action.data, - quoteRequestState: AsyncProcessState.SUCCESS, - }; + const newBuyQuoteIfExists = action.data; + const shouldUpdate = + _.isUndefined(newBuyQuoteIfExists) || doesBuyQuoteMatchState(newBuyQuoteIfExists, state); + if (shouldUpdate) { + return { + ...state, + latestBuyQuote: newBuyQuoteIfExists, + quoteRequestState: AsyncProcessState.SUCCESS, + }; + } else { + return state; + } + case ActionTypes.SET_QUOTE_REQUEST_STATE_PENDING: return { ...state, @@ -121,3 +131,29 @@ export const reducer = (state: State = INITIAL_STATE, action: Action): State => return state; } }; + +const doesBuyQuoteMatchState = (buyQuote: BuyQuote, state: State): boolean => { + const selectedAssetIfExists = state.selectedAsset; + const selectedAssetAmountIfExists = state.selectedAssetAmount; + // if no selectedAsset or selectedAssetAmount exists on the current state, return false + if (_.isUndefined(selectedAssetIfExists) || _.isUndefined(selectedAssetAmountIfExists)) { + return false; + } + // if buyQuote's assetData does not match that of the current selected asset, return false + if (selectedAssetIfExists.assetData !== buyQuote.assetData) { + return false; + } + // if ERC20 and buyQuote's assetBuyAmount does not match selectedAssetAmount, return false + // if ERC721, return true + const selectedAssetMetaData = selectedAssetIfExists.metaData; + if (selectedAssetMetaData.assetProxyId === AssetProxyId.ERC20) { + const selectedAssetAmountBaseUnits = Web3Wrapper.toBaseUnitAmount( + selectedAssetAmountIfExists, + selectedAssetMetaData.decimals, + ); + const doesAssetAmountMatch = selectedAssetAmountBaseUnits.eq(buyQuote.assetBuyAmount); + return doesAssetAmountMatch; + } else { + return true; + } +}; diff --git a/packages/instant/src/style/fonts.ts b/packages/instant/src/style/fonts.ts index 975a30a61..92450502d 100644 --- a/packages/instant/src/style/fonts.ts +++ b/packages/instant/src/style/fonts.ts @@ -1,10 +1,10 @@ -import { injectGlobal } from './theme'; - export const fonts = { include: () => { // Inject the inter-ui font into the page - return injectGlobal` - @import url('https://rsms.me/inter/inter-ui.css'); - `; + const appendTo = document.head || document.getElementsByTagName('head')[0] || document.body; + const style = document.createElement('style'); + style.type = 'text/css'; + style.appendChild(document.createTextNode(`@import url('https://rsms.me/inter/inter-ui.css')`)); + appendTo.appendChild(style); }, }; diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts index d26c816c1..6575ff9f4 100644 --- a/packages/instant/src/style/theme.ts +++ b/packages/instant/src/style/theme.ts @@ -1,6 +1,6 @@ import * as styledComponents from 'styled-components'; -const { default: styled, css, injectGlobal, keyframes, ThemeProvider } = styledComponents; +const { default: styled, css, keyframes, ThemeProvider } = styledComponents; export type Theme = { [key in ColorOption]: string }; @@ -28,4 +28,6 @@ export const theme: Theme = { darkOrange: '#F2994C', }; -export { styled, css, injectGlobal, keyframes, ThemeProvider }; +export const transparentWhite = 'rgba(255,255,255,0.3)'; + +export { styled, css, keyframes, ThemeProvider }; diff --git a/packages/instant/src/util/asset.ts b/packages/instant/src/util/asset.ts index 4e3b2b946..2c5b6325d 100644 --- a/packages/instant/src/util/asset.ts +++ b/packages/instant/src/util/asset.ts @@ -2,7 +2,7 @@ import { AssetProxyId, ObjectMap } from '@0x/types'; import * as _ from 'lodash'; import { assetDataNetworkMapping } from '../data/asset_data_network_mapping'; -import { Asset, AssetMetaData, Network, ZeroExInstantError } from '../types'; +import { Asset, AssetMetaData, ERC20Asset, Network, ZeroExInstantError } from '../types'; export const assetUtils = { createAssetFromAssetData: ( @@ -43,6 +43,16 @@ export const assetUtils = { return defaultName; } }, + formattedSymbolForAsset: (asset?: ERC20Asset, defaultName: string = '???'): string => { + if (_.isUndefined(asset)) { + return defaultName; + } + const symbol = asset.metaData.symbol; + if (symbol.length <= 5) { + return symbol; + } + return `${symbol.slice(0, 3)}…`; + }, getAssociatedAssetDataIfExists: (assetData: string, network: Network): string | undefined => { const assetDataGroupIfExists = _.find(assetDataNetworkMapping, value => value[network] === assetData); if (_.isUndefined(assetDataGroupIfExists)) { diff --git a/packages/instant/src/util/big_number_input.ts b/packages/instant/src/util/big_number_input.ts new file mode 100644 index 000000000..d2a9a8dc5 --- /dev/null +++ b/packages/instant/src/util/big_number_input.ts @@ -0,0 +1,29 @@ +import { BigNumber } from '@0x/utils'; +import * as _ from 'lodash'; + +/** + * A BigNumber extension that is more flexible about decimal strings. + * Such as allowing: + * new BigNumberInput('0.') => 0 + * new BigNumberInput('1.') => 1 + * new BigNumberInput('1..') => still throws + */ +export class BigNumberInput extends BigNumber { + private readonly _isEndingWithDecimal: boolean; + constructor(bigNumberString: string) { + const hasDecimalPeriod = _.endsWith(bigNumberString, '.'); + let internalString = bigNumberString; + if (hasDecimalPeriod) { + internalString = bigNumberString.slice(0, -1); + } + super(internalString); + this._isEndingWithDecimal = hasDecimalPeriod; + } + public toDisplayString(): string { + const internalString = super.toString(); + if (this._isEndingWithDecimal) { + return `${internalString}.`; + } + return internalString; + } +} diff --git a/packages/instant/src/util/format.ts b/packages/instant/src/util/format.ts index 8482b1526..ca7c01359 100644 --- a/packages/instant/src/util/format.ts +++ b/packages/instant/src/util/format.ts @@ -24,7 +24,7 @@ export const format = { if (_.isUndefined(ethUnitAmount)) { return defaultText; } - const roundedAmount = ethUnitAmount.round(decimalPlaces); + const roundedAmount = ethUnitAmount.round(decimalPlaces).toDigits(decimalPlaces); return `${roundedAmount} ETH`; }, ethBaseAmountInUsd: ( diff --git a/packages/instant/test/util/format.test.ts b/packages/instant/test/util/format.test.ts index 141df9275..2c9294c78 100644 --- a/packages/instant/test/util/format.test.ts +++ b/packages/instant/test/util/format.test.ts @@ -20,8 +20,8 @@ describe('format', () => { it('converts .432414 ETH in base units to the string `.4324 ETH`', () => { expect(format.ethBaseAmount(DECIMAL_ETH_IN_BASE_UNITS)).toBe('0.4324 ETH'); }); - it('converts 5.3014059295032 ETH in base units to the string `5.3014 ETH`', () => { - expect(format.ethBaseAmount(IRRATIONAL_ETH_IN_BASE_UNITS)).toBe('5.3014 ETH'); + it('converts 5.3014059295032 ETH in base units to the string `5.301 ETH`', () => { + expect(format.ethBaseAmount(IRRATIONAL_ETH_IN_BASE_UNITS)).toBe('5.301 ETH'); }); it('returns defaultText param when ethBaseAmount is not defined', () => { const defaultText = 'defaultText'; @@ -38,8 +38,8 @@ describe('format', () => { it('converts BigNumer(.432414) to the string `.4324 ETH`', () => { expect(format.ethUnitAmount(BIG_NUMBER_DECIMAL)).toBe('0.4324 ETH'); }); - it('converts BigNumber(5.3014059295032) to the string `5.3014 ETH`', () => { - expect(format.ethUnitAmount(BIG_NUMBER_IRRATIONAL)).toBe('5.3014 ETH'); + it('converts BigNumber(5.3014059295032) to the string `5.301 ETH`', () => { + expect(format.ethUnitAmount(BIG_NUMBER_IRRATIONAL)).toBe('5.301 ETH'); }); it('returns defaultText param when ethUnitAmount is not defined', () => { const defaultText = 'defaultText'; diff --git a/packages/json-schemas/package.json b/packages/json-schemas/package.json index 97cf607c1..57715c601 100644 --- a/packages/json-schemas/package.json +++ b/packages/json-schemas/package.json @@ -10,7 +10,7 @@ "scripts": { "build": "tsc -b", "build:ci": "yarn build", - "lint": "tslint --project . --exclude **/schemas/**/*", + "lint": "tslint --format stylish --project . --exclude **/schemas/**/*", "test": "yarn run_mocha", "rebuild_and_test": "run-s clean build test", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", diff --git a/packages/metacoin/package.json b/packages/metacoin/package.json index 04bdeba4c..e3432c010 100644 --- a/packages/metacoin/package.json +++ b/packages/metacoin/package.json @@ -7,7 +7,7 @@ "private": true, "description": "Example solidity project using 0x dev tools", "scripts": { - "lint": "tslint --project . --exclude **/src/contract_wrappers/**/*", + "lint": "tslint --format stylish --project . --exclude **/src/contract_wrappers/**/*", "build": "yarn pre_build && tsc -b", "build:ci": "yarn build", "pre_build": "run-s compile generate_contract_wrappers copy_artifacts", diff --git a/packages/migrations/README.md b/packages/migrations/README.md index 926654cd8..b90d730eb 100644 --- a/packages/migrations/README.md +++ b/packages/migrations/README.md @@ -50,33 +50,10 @@ yarn lint ### Migrate -#### V2-beta smart contracts +#### V2 smart contracts -In order to migrate the V2-beta 0x smart contracts to Kovan using a Ledger Nano S, run: - -```bash -yarn migrate:v2-beta-testnet -``` - -**Note:** Ledger settings `contract data` must be `on`, and `browser support` must be set to `off`. - -Post-publish steps: - -1. Since we don't re-deploy the `WETH9` nor `ZRXToken` contracts, manually copy over the artifacts for them from `2.0.0` into `2.0.0-beta-testnet` and add the Kovan & ganache addresses to both of their `networks` sections. -2. We now need to copy over the network `50` settings from the `2.0.0` artifacts to the `2.0.0-beta-testnet` artifacts for the newly deployed contracts (e.g `Exchange`, `ERC20Proxy`, `ERC721Proxy` and `AssetProxyOwner`) - -#### V2 (under development) smart contracts - -In order to migrate the V2 (under development) 0x smart contracts to TestRPC/Ganache running at `http://localhost:8545`, run: +In order to migrate the V2 0x smart contracts to TestRPC/Ganache running at `http://localhost:8545`, run: ```bash yarn migrate:v2 ``` - -#### V1 smart contracts - -In order to migrate the V1 0x smart contracts to TestRPC/Ganache running at `http://localhost:8545`, run: - -```bash -yarn migrate:v1 -``` diff --git a/packages/migrations/package.json b/packages/migrations/package.json index 353697bfc..b610d3c6a 100644 --- a/packages/migrations/package.json +++ b/packages/migrations/package.json @@ -11,7 +11,7 @@ "build": "tsc -b", "build:ci": "yarn build", "clean": "shx rm -rf lib", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "migrate:v2": "run-s build script:migrate:v2", "script:migrate:v2": "node ./lib/migrate.js --contracts-version 2.0.0" }, diff --git a/packages/monorepo-scripts/package.json b/packages/monorepo-scripts/package.json index ba5f9ca6a..a83f90516 100644 --- a/packages/monorepo-scripts/package.json +++ b/packages/monorepo-scripts/package.json @@ -11,7 +11,7 @@ "scripts": { "build": "tsc -b", "build:ci": "yarn build", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "clean": "shx rm -rf lib", "test:publish": "run-s build script:publish", "find_unused_deps": "run-s build script:find_unused_deps", diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index 69f14a79e..a6a84b940 100644 --- a/packages/order-utils/package.json +++ b/packages/order-utils/package.json @@ -17,7 +17,7 @@ "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "clean": "shx rm -rf lib generated_docs", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, "config": { diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts index f8b850604..f175b7a38 100644 --- a/packages/order-utils/test/asset_data_utils_test.ts +++ b/packages/order-utils/test/asset_data_utils_test.ts @@ -1,6 +1,7 @@ import * as chai from 'chai'; -import { ERC20AssetData } from '@0x/types'; +import { ERC20AssetData, ERC721AssetData } from '@0x/types'; +import { BigNumber } from '@0x/utils'; import { assetDataUtils } from '../src/asset_data_utils'; @@ -14,18 +15,36 @@ const KNOWN_ENCODINGS = [ address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48', }, + { + address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', + tokenId: new BigNumber(1), + assetData: + '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001', + }, ]; const ERC20_ASSET_PROXY_ID = '0xf47261b0'; +const ERC721_ASSET_PROXY_ID = '0x02571792'; describe('assetDataUtils', () => { - it('should encode', () => { + it('should encode ERC20', () => { const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ENCODINGS[0].address); expect(assetData).to.equal(KNOWN_ENCODINGS[0].assetData); }); - it('should decode', () => { + it('should decode ERC20', () => { const assetData: ERC20AssetData = assetDataUtils.decodeERC20AssetData(KNOWN_ENCODINGS[0].assetData); expect(assetData.tokenAddress).to.equal(KNOWN_ENCODINGS[0].address); expect(assetData.assetProxyId).to.equal(ERC20_ASSET_PROXY_ID); }); + it('should encode ERC721', () => { + const assetData = assetDataUtils.encodeERC721AssetData(KNOWN_ENCODINGS[1].address, KNOWN_ENCODINGS[1] + .tokenId as BigNumber); + expect(assetData).to.equal(KNOWN_ENCODINGS[1].assetData); + }); + it('should decode ERC721', () => { + const assetData: ERC721AssetData = assetDataUtils.decodeERC721AssetData(KNOWN_ENCODINGS[1].assetData); + expect(assetData.tokenAddress).to.equal(KNOWN_ENCODINGS[1].address); + expect(assetData.assetProxyId).to.equal(ERC721_ASSET_PROXY_ID); + expect(assetData.tokenId).to.be.bignumber.equal(KNOWN_ENCODINGS[1].tokenId); + }); }); diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json index acc70544e..42e3f572c 100644 --- a/packages/order-watcher/package.json +++ b/packages/order-watcher/package.json @@ -14,7 +14,7 @@ "scripts": { "build": "yarn tsc -b", "build:ci": "yarn build", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "test:circleci": "run-s test:coverage", "test": "yarn run_mocha", "rebuild_and_test": "run-s build test", diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index d3ccfa8da..e234b0479 100644 --- a/packages/react-docs/package.json +++ b/packages/react-docs/package.json @@ -8,7 +8,7 @@ "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "build": "tsc -b", "build:ci": "yarn build", "clean": "shx rm -rf lib" diff --git a/packages/react-shared/package.json b/packages/react-shared/package.json index 7a150bf35..45220e2d2 100644 --- a/packages/react-shared/package.json +++ b/packages/react-shared/package.json @@ -8,7 +8,7 @@ "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "build": "tsc", "build:ci": "yarn build", "watch_without_deps": "tsc -w", diff --git a/packages/sol-compiler/package.json b/packages/sol-compiler/package.json index 4c4370b1f..17ccb50b8 100644 --- a/packages/sol-compiler/package.json +++ b/packages/sol-compiler/package.json @@ -19,7 +19,7 @@ "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", "clean": "shx rm -rf lib generated_docs", "migrate": "npm run build; node lib/src/cli.js migrate", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "test:circleci": "yarn test:coverage", "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, diff --git a/packages/sol-cov/package.json b/packages/sol-cov/package.json index a56713d69..09119321d 100644 --- a/packages/sol-cov/package.json +++ b/packages/sol-cov/package.json @@ -11,7 +11,7 @@ "build": "yarn pre_build && tsc -b", "build:ci": "yarn build", "pre_build": "run-s copy_test_fixtures", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "test": "run-s compile_test run_mocha", "rebuild_and_test": "run-s clean build test", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", diff --git a/packages/sol-doc/package.json b/packages/sol-doc/package.json index de10b9e17..b1e887fa5 100644 --- a/packages/sol-doc/package.json +++ b/packages/sol-doc/package.json @@ -11,7 +11,7 @@ "test:circleci": "yarn test:coverage", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", - "lint": "tslint --project . --format stylish", + "lint": "tslint --format stylish --project .", "clean": "shx rm -rf lib", "generate-v1-protocol-docs": "(cd ../contracts/src/1.0.0; node ../../../../node_modules/.bin/sol-doc --contracts-dir . --contracts Exchange/Exchange_v1.sol TokenRegistry/TokenRegistry.sol TokenTransferProxy/TokenTransferProxy_v1.sol) > v1.0.0.json", "generate-v2-protocol-docs": "(cd ../contracts/src/2.0.0; node ../../../../node_modules/.bin/sol-doc --contracts-dir . --contracts Exchange/Exchange.sol AssetProxy/ERC20Proxy.sol AssetProxy/ERC721Proxy.sol OrderValidator/OrderValidator.sol Forwarder/Forwarder.sol AssetProxyOwner/AssetProxyOwner.sol) > v2.0.0.json", diff --git a/packages/sol-resolver/package.json b/packages/sol-resolver/package.json index 4bd63f406..4d4d0be0e 100644 --- a/packages/sol-resolver/package.json +++ b/packages/sol-resolver/package.json @@ -11,7 +11,7 @@ "build": "tsc -b", "build:ci": "yarn build", "clean": "shx rm -rf lib", - "lint": "tslint --project ." + "lint": "tslint --format stylish --project ." }, "repository": { "type": "git", diff --git a/packages/sra-spec/package.json b/packages/sra-spec/package.json index 0c55465d7..2d3e7decd 100644 --- a/packages/sra-spec/package.json +++ b/packages/sra-spec/package.json @@ -10,7 +10,7 @@ "scripts": { "serve": "redoc-cli serve lib/api.json --watch", "watch_without_deps": "run-p build:watch serve", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "test": "swagger-cli validate lib/api.json", "rebuild_and_test": "run-s clean build test", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index 4eef09fe6..32e2bae24 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -11,7 +11,7 @@ "build": "tsc -b", "build:ci": "yarn build", "clean": "shx rm -rf lib generated_docs", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "run_mocha_unit": "mocha --require source-map-support/register --require make-promises-safe lib/test/unit/**/*_test.js --timeout 10000 --bail --exit", "run_mocha_integration": "mocha --require source-map-support/register --require make-promises-safe lib/test/integration/**/*_test.js --timeout 10000 --bail --exit", "test": "npm run test:unit", diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json index e683ae25a..53f1b95df 100644 --- a/packages/testnet-faucets/package.json +++ b/packages/testnet-faucets/package.json @@ -12,7 +12,7 @@ "build:ci": "yarn build", "dev": "node ../../node_modules/gulp/bin/gulp.js run", "start": "node ./server/server.js", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "clean": "shx rm -rf server" }, "author": "Fabio Berger", diff --git a/packages/tslint-config/package.json b/packages/tslint-config/package.json index 0a8c1b245..a235532fb 100644 --- a/packages/tslint-config/package.json +++ b/packages/tslint-config/package.json @@ -10,7 +10,7 @@ "build": "tsc -b", "build:ci": "yarn build", "clean": "shx rm -rf lib", - "lint": "tslint --project ." + "lint": "tslint --format stylish --project ." }, "repository": { "type": "git", diff --git a/packages/types/package.json b/packages/types/package.json index fdd68a486..9b3ae6701 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -11,7 +11,7 @@ "build": "tsc -b", "build:ci": "yarn build", "clean": "shx rm -rf lib", - "lint": "tslint --project ." + "lint": "tslint --format stylish --project ." }, "license": "Apache-2.0", "repository": { diff --git a/packages/utils/package.json b/packages/utils/package.json index f89cfda17..4e0df9275 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -11,7 +11,7 @@ "build": "tsc -b", "build:ci": "yarn build", "clean": "shx rm -rf lib", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "test": "yarn run_mocha", "test:circleci": "yarn test:coverage", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index 579957747..e95245df8 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -11,7 +11,7 @@ "build": "tsc -b", "build:ci": "yarn build", "clean": "shx rm -rf lib generated_docs", - "lint": "tslint --project .", + "lint": "tslint --format stylish --project .", "test": "yarn run_mocha", "rebuild_and_test": "run-s clean build test", "test:circleci": "yarn test:coverage", diff --git a/packages/website/package.json b/packages/website/package.json index 771b1b908..efb97d309 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -10,7 +10,7 @@ "build": "node --max_old_space_size=8192 ../../node_modules/.bin/webpack --mode production", "build:dev": "../../node_modules/.bin/webpack --mode development", "clean": "shx rm -f public/bundle*", - "lint": "tslint --project . 'ts/**/*.ts' 'ts/**/*.tsx'", + "lint": "tslint --format stylish --project . 'ts/**/*.ts' 'ts/**/*.tsx'", "dev": "webpack-dev-server --mode development --content-base public --https", "deploy_dogfood": "npm run build; aws s3 sync ./public/. s3://dogfood.0xproject.com --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", "deploy_staging": "npm run build; aws s3 sync ./public/. s3://staging-0xproject --profile 0xproject --region us-east-1 --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers", diff --git a/packages/website/ts/components/fill_order.tsx b/packages/website/ts/components/fill_order.tsx index ec73e4199..3d0203573 100644 --- a/packages/website/ts/components/fill_order.tsx +++ b/packages/website/ts/components/fill_order.tsx @@ -203,7 +203,10 @@ export class FillOrder extends React.Component<FillOrderProps, FillOrderState> { .tokenAddress; const makerToken = this.props.tokenByAddress[makerTokenAddress]; const makerAssetToken = { - amount: orderMakerAmount.times(takerAssetToken.amount).div(orderTakerAmount), + amount: orderMakerAmount + .times(takerAssetToken.amount) + .div(orderTakerAmount) + .floor(), symbol: makerToken.symbol, }; const fillAssetToken = { diff --git a/python-packages/order_utils/LICENSE b/python-packages/order_utils/LICENSE new file mode 100644 index 000000000..9096fefaa --- /dev/null +++ b/python-packages/order_utils/LICENSE @@ -0,0 +1,13 @@ +Copyright 2017 ZeroEx Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.
\ No newline at end of file diff --git a/python-packages/order_utils/README.md b/python-packages/order_utils/README.md new file mode 100644 index 000000000..a266694db --- /dev/null +++ b/python-packages/order_utils/README.md @@ -0,0 +1,45 @@ +## 0x-order-utils + +0x order-related utilities for those developing on top of 0x protocol. + +Read the [documentation](https://0x.readthedocs.io/projects/order-utils/en/latest/) + +## Installing + +```bash +pip install 0x-order-utils +``` + +## Contributing + +We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. + +### Install Code and Dependencies + +Ensure that you have Python >=3.6 installed, then: + +```bash +pip install -e .[dev] +``` + +### Test + +`./setup.py test` + +### Clean + +`./setup.py clean --all` + +### Lint + +`./setup.py lint` + +### Build Documentation + +`./setup.py build_sphinx` + +### More + +See `./setup.py --help-commands` for more info. diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py index 1a094cfe1..22a5f4c41 100755 --- a/python-packages/order_utils/setup.py +++ b/python-packages/order_utils/setup.py @@ -4,7 +4,7 @@ import subprocess # nosec from shutil import rmtree -from os import environ, path, remove, walk +from os import environ, path from sys import argv from distutils.command.clean import clean @@ -20,13 +20,15 @@ class TestCommandExtension(TestCommand): """Invoke pytest.""" import pytest - pytest.main() + exit(pytest.main()) # pylint: disable=too-many-ancestors class LintCommand(distutils.command.build_py.build_py): """Custom setuptools command class for running linters.""" + description = "Run linters" + def run(self): """Run linter shell commands.""" lint_commands = [ @@ -73,31 +75,64 @@ class CleanCommandExtension(clean): def run(self): """Run the regular clean, followed by our custom commands.""" super().run() - rmtree("build", ignore_errors=True) + rmtree("dist", ignore_errors=True) rmtree(".mypy_cache", ignore_errors=True) rmtree(".tox", ignore_errors=True) rmtree(".pytest_cache", ignore_errors=True) rmtree("src/order_utils.egg-info", ignore_errors=True) - # delete all .pyc files - for root, _, files in walk("."): - for file in files: - (_, extension) = path.splitext(file) - if extension == ".pyc": - remove(path.join(root, file)) + + +# pylint: disable=too-many-ancestors +class TestPublishCommand(distutils.command.build_py.build_py): + """Custom command to publish to test.pypi.org.""" + + description = ( + "Publish dist/* to test.pypi.org. Run sdist & bdist_wheel first." + ) + + def run(self): + """Run twine to upload to test.pypi.org.""" + subprocess.check_call( # nosec + ( + "twine upload --repository-url https://test.pypi.org/legacy/" + + " --verbose dist/*" + ).split() + ) + + +# pylint: disable=too-many-ancestors +class PublishCommand(distutils.command.build_py.build_py): + """Custom command to publish to pypi.org.""" + + description = "Publish dist/* to pypi.org. Run sdist & bdist_wheel first." + + def run(self): + """Run twine to upload to pypi.org.""" + subprocess.check_call("twine upload dist/*".split()) # nosec + + +with open("README.md", "r") as file_handle: + README_MD = file_handle.read() setup( - name="order_utils", - version="1.0.0", + name="0x-order-utils", + version="0.1.0", description="Order utilities for 0x applications", + long_description=README_MD, + long_description_content_type="text/markdown", + url="https://github.com/0xproject/0x-monorepo/python-packages/order_utils", author="F. Eugene Aumson", + author_email="feuGeneA@users.noreply.github.com", cmdclass={ "clean": CleanCommandExtension, "lint": LintCommand, "test": TestCommandExtension, + "test_publish": TestPublishCommand, + "publish": PublishCommand, }, include_package_data=True, - install_requires=["eth-abi", "web3"], + install_requires=["eth-abi", "mypy_extensions", "web3"], extras_require={ "dev": [ "bandit", @@ -112,6 +147,7 @@ setup( "pytest", "sphinx", "tox", + "twine", ] }, python_requires=">=3.6, <4", @@ -121,9 +157,10 @@ setup( keywords=( "ethereum cryptocurrency 0x decentralized blockchain dex exchange" ), - packages=["zero_ex.order_utils"], + namespace_packages=["zero_ex"], + packages=["zero_ex.order_utils", "zero_ex.dev_utils"], classifiers=[ - "Development Status :: 1 - Planning", + "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "Intended Audience :: Financial and Insurance Industry", "License :: OSI Approved :: Apache Software License", @@ -133,7 +170,10 @@ setup( "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Topic :: Internet :: WWW/HTTP", "Topic :: Office/Business :: Financial", + "Topic :: Other/Nonlisted Topic", + "Topic :: Security :: Cryptography", "Topic :: Software Development :: Libraries", "Topic :: Utilities", ], diff --git a/python-packages/order_utils/src/conf.py b/python-packages/order_utils/src/conf.py index e74a29d00..606cd3b2a 100644 --- a/python-packages/order_utils/src/conf.py +++ b/python-packages/order_utils/src/conf.py @@ -8,11 +8,11 @@ from typing import List # pylint: disable=invalid-name # because these variables are not named in upper case, as globals should be. -project = "order_utils.py" +project = "0x-order-utils" # pylint: disable=redefined-builtin copyright = "2018, ZeroEx, Intl." author = "F. Eugene Aumson" -version = "" # The short X.Y version +version = "0.1.0" # The short X.Y version release = "" # The full version, including alpha/beta/rc tags extensions = [ diff --git a/python-packages/order_utils/src/doc_static/.gitkeep b/python-packages/order_utils/src/doc_static/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python-packages/order_utils/src/doc_static/.gitkeep diff --git a/python-packages/order_utils/src/doc_templates/.gitkeep b/python-packages/order_utils/src/doc_templates/.gitkeep new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/python-packages/order_utils/src/doc_templates/.gitkeep diff --git a/python-packages/order_utils/src/index.rst b/python-packages/order_utils/src/index.rst index e09abcca2..22d5b0ef9 100644 --- a/python-packages/order_utils/src/index.rst +++ b/python-packages/order_utils/src/index.rst @@ -1,7 +1,7 @@ .. source for the sphinx-generated build/docs/web/index.html -order_utils.py -============== +Python zero_ex.order_utils +========================== .. toctree:: :maxdepth: 2 @@ -15,7 +15,9 @@ order_utils.py .. autoclass:: zero_ex.order_utils.asset_data_utils.ERC20AssetData -See source for properties. Sphinx does not easily generate class property docs; pull requests welcome. +.. autoclass:: zero_ex.order_utils.asset_data_utils.ERC721AssetData + +See source for class properties. Sphinx does not easily generate class property docs; pull requests welcome. Indices and tables ================== diff --git a/python-packages/order_utils/src/zero_ex/__init__.py b/python-packages/order_utils/src/zero_ex/__init__.py index c3ed1562a..e90d833db 100644 --- a/python-packages/order_utils/src/zero_ex/__init__.py +++ b/python-packages/order_utils/src/zero_ex/__init__.py @@ -1 +1,2 @@ """0x Python API.""" +__import__("pkg_resources").declare_namespace(__name__) diff --git a/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py b/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py index 745d014e6..a100da567 100644 --- a/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py +++ b/python-packages/order_utils/src/zero_ex/dev_utils/type_assertions.py @@ -31,3 +31,18 @@ def assert_is_list(value: Any, name: str) -> None: f"expected variable '{name}', with value {str(value)}, to have" + f" type 'list', not '{type(value).__name__}'" ) + + +def assert_is_int(value: Any, name: str) -> None: + """If :param value: isn't of type int, raise a TypeError. + + >>> try: assert_is_int('asdf', 'var') + ... except TypeError as type_error: print(str(type_error)) + ... + expected variable 'var', with value asdf, to have type 'int', not 'str' + """ + if not isinstance(value, int): + raise TypeError( + f"expected variable '{name}', with value {str(value)}, to have" + + f" type 'int', not '{type(value).__name__}'" + ) diff --git a/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py b/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py index 451de39af..e6f9a07c1 100644 --- a/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py +++ b/python-packages/order_utils/src/zero_ex/order_utils/asset_data_utils.py @@ -5,10 +5,11 @@ from mypy_extensions import TypedDict import eth_abi from zero_ex.dev_utils import abi_utils -from zero_ex.dev_utils.type_assertions import assert_is_string +from zero_ex.dev_utils.type_assertions import assert_is_string, assert_is_int ERC20_ASSET_DATA_BYTE_LENGTH = 36 +ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH = 53 SELECTOR_LENGTH = 10 @@ -19,6 +20,14 @@ class ERC20AssetData(TypedDict): token_address: str +class ERC721AssetData(TypedDict): + """Object interface to ERC721 asset data.""" + + asset_proxy_id: str + token_address: str + token_id: int + + def encode_erc20_asset_data(token_address: str) -> str: """Encode an ERC20 token address into an asset data string. @@ -39,7 +48,7 @@ def encode_erc20_asset_data(token_address: str) -> str: def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData: # docstring considered all one line by pylint: disable=line-too-long - """Decode an ERC20 assetData hex string. + """Decode an ERC20 asset data hex string. :param asset_data: String produced by prior call to encode_erc20_asset_data() @@ -55,7 +64,7 @@ def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData: + f" Got {str(len(asset_data))}." ) - asset_proxy_id: str = asset_data[0:10] + asset_proxy_id: str = asset_data[0:SELECTOR_LENGTH] if asset_proxy_id != abi_utils.method_id("ERC20Token", ["address"]): raise ValueError( "Could not decode ERC20 Proxy Data. Expected Asset Proxy Id to be" @@ -70,3 +79,65 @@ def decode_erc20_asset_data(asset_data: str) -> ERC20AssetData: )[0] return {"asset_proxy_id": asset_proxy_id, "token_address": token_address} + + +def encode_erc721_asset_data(token_address: str, token_id: int) -> str: + # docstring considered all one line by pylint: disable=line-too-long + """Encode an ERC721 asset data hex string. + + :param token_address: the ERC721 token's contract address. + :param token_id: the identifier of the asset's instance of the token. + :rtype: hex encoded asset data string, usable in the makerAssetData or + takerAssetData fields in a 0x order. + + >>> encode_erc721_asset_data('0x1dc4c1cefef38a777b15aa20260a54e584b16c48', 1) + '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001' + """ # noqa: E501 (line too long) + assert_is_string(token_address, "token_address") + assert_is_int(token_id, "token_id") + + return ( + "0x" + + abi_utils.simple_encode( + "ERC721Token(address,uint256)", token_address, token_id + ).hex() + ) + + +def decode_erc721_asset_data(asset_data: str) -> ERC721AssetData: + # docstring considered all one line by pylint: disable=line-too-long + """Decode an ERC721 asset data hex string. + + >>> decode_erc721_asset_data('0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001') + {'asset_proxy_id': '0x02571792', 'token_address': '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', 'token_id': 1} + """ # noqa: E501 (line too long) + assert_is_string(asset_data, "asset_data") + + if len(asset_data) < ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH: + raise ValueError( + "Could not decode ERC721 Asset Data. Expected length of encoded" + + f"data to be at least {ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH}. " + + f"Got {len(asset_data)}." + ) + + asset_proxy_id: str = asset_data[0:SELECTOR_LENGTH] + # prefer `black` formatting. pylint: disable=C0330 + if asset_proxy_id != abi_utils.method_id( + "ERC721Token", ["address", "uint256"] + ): + raise ValueError( + "Could not decode ERC721 Asset Data. Expected Asset Proxy Id to be" + + f" ERC721 (" + + f"{abi_utils.method_id('ERC721Token', ['address', 'uint256'])}" + + f"), but got {asset_proxy_id}" + ) + + (token_address, token_id) = eth_abi.decode_abi( + ["address", "uint256"], bytes.fromhex(asset_data[SELECTOR_LENGTH:]) + ) + + return { + "asset_proxy_id": asset_proxy_id, + "token_address": token_address, + "token_id": token_id, + } diff --git a/python-packages/order_utils/test/test_asset_data_utils.py b/python-packages/order_utils/test/test_asset_data_utils.py index eeada5873..079368714 100644 --- a/python-packages/order_utils/test/test_asset_data_utils.py +++ b/python-packages/order_utils/test/test_asset_data_utils.py @@ -3,9 +3,12 @@ import pytest from zero_ex.order_utils.asset_data_utils import ( - encode_erc20_asset_data, decode_erc20_asset_data, + decode_erc721_asset_data, + encode_erc20_asset_data, + encode_erc721_asset_data, ERC20_ASSET_DATA_BYTE_LENGTH, + ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH, ) @@ -33,3 +36,37 @@ def test_decode_erc20_asset_data_invalid_proxy_id(): decode_erc20_asset_data( "0xffffffff" + (" " * ERC20_ASSET_DATA_BYTE_LENGTH) ) + + +def test_encode_erc721_asset_data_type_error_on_token_address(): + """Test that passing a non-string for token_address raises a TypeError.""" + with pytest.raises(TypeError): + encode_erc721_asset_data(123, 123) + + +def test_encode_erc721_asset_data_type_error_on_token_id(): + """Test that passing a non-int for token_id raises a TypeError.""" + with pytest.raises(TypeError): + encode_erc721_asset_data("asdf", "asdf") + + +def test_decode_erc721_asset_data_type_error(): + """Test that passing a non-string for asset_data raises a TypeError.""" + with pytest.raises(TypeError): + decode_erc721_asset_data(123) + + +def test_decode_erc721_asset_data_with_asset_data_too_short(): + """Test that passing in too short of a string raises a ValueError.""" + with pytest.raises(ValueError): + decode_erc721_asset_data( + " " * (ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH - 1) + ) + + +def test_decode_erc721_asset_data_invalid_proxy_id(): + """Test that passing in too short of a string raises a ValueError.""" + with pytest.raises(ValueError): + decode_erc721_asset_data( + "0xffffffff" + " " * (ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH - 1) + ) diff --git a/python-packages/order_utils/tox.ini b/python-packages/order_utils/tox.ini index 1cce32b5f..ba7d55b56 100644 --- a/python-packages/order_utils/tox.ini +++ b/python-packages/order_utils/tox.ini @@ -10,3 +10,16 @@ envlist = py37 commands = pip install -e .[dev] python setup.py test + +[testenv:run_tests_against_test_deployment] +commands = + # install dependencies from real PyPI + pip install eth-abi mypy_extensions web3 pytest + # install package-under-test from test PyPI + pip install --index-url https://test.pypi.org/legacy/ 0x-order-utils + pytest test + +[testenv:run_tests_against_deployment] +commands = + pip install 0x-order-utils + pytest test diff --git a/readthedocs.yaml b/readthedocs.yaml new file mode 100644 index 000000000..7023141da --- /dev/null +++ b/readthedocs.yaml @@ -0,0 +1,7 @@ +build: + image: latest + +python: + version: 3.6 + +requirements_file: requirements.readthedocs.txt diff --git a/requirements.readthedocs.txt b/requirements.readthedocs.txt new file mode 100644 index 000000000..4228f6562 --- /dev/null +++ b/requirements.readthedocs.txt @@ -0,0 +1 @@ +./python-packages/order_utils @@ -484,6 +484,12 @@ dependencies: "@babel/highlight" "^7.0.0" +"@babel/helper-annotate-as-pure@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32" + dependencies: + "@babel/types" "^7.0.0" + "@babel/highlight@^7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" @@ -504,6 +510,24 @@ dependencies: regenerator-runtime "^0.12.0" +"@babel/types@^7.0.0": + version "7.1.3" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.1.3.tgz#3a767004567060c2f40fca49a304712c525ee37d" + dependencies: + esutils "^2.0.2" + lodash "^4.17.10" + to-fast-properties "^2.0.0" + +"@emotion/is-prop-valid@^0.6.8": + version "0.6.8" + resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.6.8.tgz#68ad02831da41213a2089d2cab4e8ac8b30cbd85" + dependencies: + "@emotion/memoize" "^0.6.6" + +"@emotion/memoize@^0.6.6": + version "0.6.6" + resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz#004b98298d04c7ca3b4f50ca2035d4f60d2eed1b" + "@ledgerhq/hw-app-eth@^4.3.0": version "4.7.3" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-app-eth/-/hw-app-eth-4.7.3.tgz#d352e19658ae296532e522c53c8ec2a1a77b64e5" @@ -1579,6 +1603,13 @@ "@types/node" "*" "@types/react" "*" +"@types/styled-components@^4.0.1": + version "4.0.1" + resolved "https://registry.npmjs.org/@types/styled-components/-/styled-components-4.0.1.tgz#5eb9a5474dbde3becab2bcc8f04e0b8e8dcf8c06" + dependencies: + "@types/node" "*" + "@types/react" "*" + "@types/tmp@^0.0.33": version "0.0.33" resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d" @@ -1869,10 +1900,6 @@ aes-js@^0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-0.2.4.tgz#94b881ab717286d015fa219e08fb66709dda5a3d" -aes-js@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/aes-js/-/aes-js-3.1.1.tgz#89fd1f94ae51b4c72d62466adc1a7323ff52f072" - agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" @@ -2536,6 +2563,13 @@ babel-plugin-jest-hoist@^23.2.0: version "23.2.0" resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz#e61fae05a1ca8801aadee57a6d66b8cefaf44167" +"babel-plugin-styled-components@>= 1": + version "1.8.0" + resolved "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.8.0.tgz#9dd054c8e86825203449a852a5746f29f2dab857" + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + lodash "^4.17.10" + babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" @@ -3303,7 +3337,7 @@ bs-logger@0.x: dependencies: fast-json-stable-stringify "^2.0.0" -bs58@=4.0.1, bs58@^4.0.0: +bs58@=4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" dependencies: @@ -3326,14 +3360,6 @@ bs58check@^1.0.8: bs58 "^3.1.0" create-hash "^1.1.0" -bs58check@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" - dependencies: - bs58 "^4.0.0" - create-hash "^1.1.0" - safe-buffer "^5.1.2" - bser@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" @@ -4593,6 +4619,14 @@ css-to-react-native@^2.0.3: fbjs "^0.8.5" postcss-value-parser "^3.3.0" +css-to-react-native@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.2.2.tgz#c077d0f7bf3e6c915a539e7325821c9dd01f9965" + dependencies: + css-color-keywords "^1.0.0" + fbjs "^0.8.5" + postcss-value-parser "^3.3.0" + css-vendor@^0.3.8: version "0.3.8" resolved "https://registry.npmjs.org/css-vendor/-/css-vendor-0.3.8.tgz#6421cfd3034ce664fe7673972fd0119fc28941fa" @@ -5913,19 +5947,6 @@ ethereumjs-wallet@0.6.0: utf8 "^2.1.1" uuid "^2.0.1" -ethereumjs-wallet@~0.6.0: - version "0.6.2" - resolved "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-0.6.2.tgz#67244b6af3e8113b53d709124b25477b64aeccda" - dependencies: - aes-js "^3.1.1" - bs58check "^2.1.2" - ethereumjs-util "^5.2.0" - hdkey "^1.0.0" - safe-buffer "^5.1.2" - scrypt.js "^0.2.0" - utf8 "^3.0.0" - uuid "^3.3.2" - ethers@~4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65" @@ -6730,7 +6751,7 @@ ganache-core@0xProject/ganache-core#monorepo-dep: ethereumjs-tx "0xProject/ethereumjs-tx#fake-tx-include-signature-by-default" ethereumjs-util "^5.2.0" ethereumjs-vm "2.3.5" - ethereumjs-wallet "0.6.0" + ethereumjs-wallet "~0.6.0" fake-merkle-patricia-tree "~1.0.1" heap "~0.2.6" js-scrypt "^0.2.0" @@ -7455,14 +7476,6 @@ hdkey@^0.7.0, hdkey@^0.7.1: coinstring "^2.0.0" secp256k1 "^3.0.1" -hdkey@^1.0.0: - version "1.1.0" - resolved "https://registry.npmjs.org/hdkey/-/hdkey-1.1.0.tgz#e74e7b01d2c47f797fa65d1d839adb7a44639f29" - dependencies: - coinstring "^2.0.0" - safe-buffer "^5.1.1" - secp256k1 "^3.0.1" - he@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" @@ -12533,13 +12546,6 @@ react-scroll@0xproject/react-scroll#pr-330-and-replace-state: lodash.throttle "^4.1.1" prop-types "^15.5.8" -react-scroll@0xproject/react-scroll#similar-to-pr-330-but-with-replace-state: - version "1.7.10" - resolved "https://codeload.github.com/0xproject/react-scroll/tar.gz/0f625b270d7e966313cac8b811c0ae807b37e170" - dependencies: - lodash.throttle "^4.1.1" - prop-types "^15.5.8" - react-side-effect@^1.0.2, react-side-effect@^1.1.0: version "1.1.5" resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-1.1.5.tgz#f26059e50ed9c626d91d661b9f3c8bb38cd0ff2d" @@ -14434,19 +14440,18 @@ styled-components@^3.3.3: stylis-rule-sheet "^0.0.10" supports-color "^3.2.3" -styled-components@^3.4.9: - version "3.4.10" - resolved "https://registry.npmjs.org/styled-components/-/styled-components-3.4.10.tgz#9a654c50ea2b516c36ade57ddcfa296bf85c96e1" +styled-components@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/styled-components/-/styled-components-4.0.2.tgz#7d4409ada019cdd34c25ba68c4577ea95dbcf0c5" dependencies: - buffer "^5.0.3" - css-to-react-native "^2.0.3" - fbjs "^0.8.16" - hoist-non-react-statics "^2.5.0" + "@emotion/is-prop-valid" "^0.6.8" + babel-plugin-styled-components ">= 1" + css-to-react-native "^2.2.2" + memoize-one "^4.0.0" prop-types "^15.5.4" react-is "^16.3.1" stylis "^3.5.0" stylis-rule-sheet "^0.0.10" - supports-color "^3.2.3" stylis-rule-sheet@^0.0.10: version "0.0.10" @@ -14889,6 +14894,10 @@ to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + to-no-case@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/to-no-case/-/to-no-case-1.0.2.tgz#c722907164ef6b178132c8e69930212d1b4aa16a" @@ -15553,10 +15562,6 @@ utf8@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.2.tgz#1fa0d9270e9be850d9b05027f63519bf46457d96" -utf8@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" - util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" |