From 4d5fcf8985be9f06509ad0b3c05ead066b24bf5a Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 30 Nov 2018 15:33:52 -0800 Subject: feat: Add MultiAsset types --- packages/types/src/index.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 4470dd501..846c030ee 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -155,6 +155,7 @@ export enum SignatureType { export enum AssetProxyId { ERC20 = '0xf47261b0', ERC721 = '0x02571792', + MultiAsset = '0x94cfcdd7', } export interface ERC20AssetData { @@ -168,7 +169,13 @@ export interface ERC721AssetData { tokenId: BigNumber; } -export type AssetData = ERC20AssetData | ERC721AssetData; +export interface MultiAssetData { + assetProxyId: string; + amounts: BigNumber[]; + nestedAssetData: string[]; +} + +export type AssetData = ERC20AssetData | ERC721AssetData | MultiAssetData; // TODO: DRY. These should be extracted from contract code. export enum RevertReason { -- cgit v1.2.3 From 354f7053dc3f322412da64460ae8295a6076b3e0 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 30 Nov 2018 15:35:48 -0800 Subject: Use new AbiEncoder, add logic for encoding/decoding MultiAsset assetData --- packages/order-utils/package.json | 6 +- packages/order-utils/src/asset_data_utils.ts | 221 +++++++++++++++------ packages/order-utils/src/constants.ts | 58 +++++- .../balance_and_proxy_allowance_lazy_store.ts | 4 +- 4 files changed, 217 insertions(+), 72 deletions(-) diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index 400c9b66f..8a5150a48 100644 --- a/packages/order-utils/package.json +++ b/packages/order-utils/package.json @@ -13,12 +13,14 @@ "test": "yarn run_mocha", "rebuild_and_test": "run-s build test", "test:circleci": "yarn test:coverage", - "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", + "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", "clean": "shx rm -rf lib generated_docs", "lint": "tslint --format stylish --project .", - "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" + "docs:json": + "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, "config": { "postpublish": { diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index 9bbef3a23..b5cfe698e 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -1,10 +1,13 @@ -import { AssetData, AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import ethAbi = require('ethereumjs-abi'); -import ethUtil = require('ethereumjs-util'); +import { AssetData, AssetProxyId, ERC20AssetData, ERC721AssetData, MultiAssetData } from '@0x/types'; +import { AbiEncoder, BigNumber } from '@0x/utils'; +import { MethodAbi } from 'ethereum-types'; +import * as _ from 'lodash'; import { constants } from './constants'; +const encodingRules: AbiEncoder.EncodingRules = { optimize: true }; +const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + export const assetDataUtils = { /** * Encodes an ERC20 token address into a hex encoded assetData string, usable in the makerAssetData or @@ -13,7 +16,10 @@ export const assetDataUtils = { * @return The hex encoded assetData string */ encodeERC20AssetData(tokenAddress: string): string { - return ethUtil.bufferToHex(ethAbi.simpleEncode('ERC20Token(address)', tokenAddress)); + const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI as MethodAbi); + const args = [tokenAddress]; + const assetData = abiEncoder.encode(args, encodingRules); + return assetData; }, /** * Decodes an ERC20 assetData hex string into it's corresponding ERC20 tokenAddress & assetProxyId @@ -21,26 +27,13 @@ export const assetDataUtils = { * @return An object containing the decoded tokenAddress & assetProxyId */ decodeERC20AssetData(assetData: string): ERC20AssetData { - const data = ethUtil.toBuffer(assetData); - if (data.byteLength < constants.ERC20_ASSET_DATA_BYTE_LENGTH) { - throw new Error( - `Could not decode ERC20 Proxy Data. Expected length of encoded data to be at least ${ - constants.ERC20_ASSET_DATA_BYTE_LENGTH - }. Got ${data.byteLength}`, - ); - } - const assetProxyId = ethUtil.bufferToHex(data.slice(0, constants.SELECTOR_LENGTH)); - if (assetProxyId !== AssetProxyId.ERC20) { - throw new Error( - `Could not decode ERC20 Proxy Data. Expected Asset Proxy Id to be ERC20 (${ - AssetProxyId.ERC20 - }), but got ${assetProxyId}`, - ); - } - const [tokenAddress] = ethAbi.rawDecode(['address'], data.slice(constants.SELECTOR_LENGTH)); + assetDataUtils.validateERC20AssetDataThrow(assetData); + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI as MethodAbi); + const [tokenAddress] = abiEncoder.decode(assetData, decodingRules); return { assetProxyId, - tokenAddress: ethUtil.addHexPrefix(tokenAddress), + tokenAddress, }; }, /** @@ -51,14 +44,10 @@ export const assetDataUtils = { * @return The hex encoded assetData string */ encodeERC721AssetData(tokenAddress: string, tokenId: BigNumber): string { - // TODO: Pass `tokendId` as a BigNumber. - return ethUtil.bufferToHex( - ethAbi.simpleEncode( - 'ERC721Token(address,uint256)', - tokenAddress, - `0x${tokenId.toString(constants.BASE_16)}`, - ), - ); + const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI as MethodAbi); + const args = [tokenAddress, tokenId]; + const assetData = abiEncoder.encode(args, encodingRules); + return assetData; }, /** * Decodes an ERC721 assetData hex string into it's corresponding ERC721 tokenAddress, tokenId & assetProxyId @@ -66,27 +55,51 @@ export const assetDataUtils = { * @return An object containing the decoded tokenAddress, tokenId & assetProxyId */ decodeERC721AssetData(assetData: string): ERC721AssetData { - const data = ethUtil.toBuffer(assetData); - if (data.byteLength < constants.ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH) { - throw new Error( - `Could not decode ERC721 Asset Data. Expected length of encoded data to be at least ${ - constants.ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH - }. Got ${data.byteLength}`, - ); - } - const assetProxyId = ethUtil.bufferToHex(data.slice(0, constants.SELECTOR_LENGTH)); - if (assetProxyId !== AssetProxyId.ERC721) { + assetDataUtils.validateERC721AssetDataOrThrow(assetData); + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI as MethodAbi); + const [tokenAddress, tokenId] = abiEncoder.decode(assetData, decodingRules); + return { + assetProxyId, + tokenAddress, + tokenId, + }; + }, + /** + * Encodes assetData for multiple AssetProxies into a single hex encoded assetData string, usable in the makerAssetData or + * takerAssetData fields in a 0x order. + * @param amounts Amounts of each asset that correspond to a ginle unit within an order. + * @param nestedAssetData assetData strings that correspond to a valid assetProxyId. + * @return The hex encoded assetData string + */ + encodeMultiAssetData(amounts: BigNumber[], nestedAssetData: string[]): string { + _.forEach(nestedAssetData, assetDataElement => assetDataUtils.validateAssetDataOrThrow(assetDataElement)); + const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI as MethodAbi); + const args = [amounts, nestedAssetData]; + const assetData = abiEncoder.encode(args, encodingRules); + return assetData; + }, + /** + * Decodes a MultiAsset assetData hex string into it's corresponding amounts and nestedAssetData + * @param assetData Hex encoded assetData string to decode + * @return An object containing the decoded amounts and nestedAssetData + */ + decodeMultiAssetData(assetData: string): MultiAssetData { + assetDataUtils.validateMultiAssetDataOrThrow(assetData); + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI as MethodAbi); + const [amounts, nestedAssetData] = abiEncoder.decode(assetData, decodingRules); + if (amounts.length !== nestedAssetData.length) { throw new Error( - `Could not decode ERC721 Asset Data. Expected Asset Proxy Id to be ERC721 (${ - AssetProxyId.ERC721 - }), but got ${assetProxyId}`, + `Invalid MultiAsset assetData. Expected length of 'amounts' (${ + amounts.length + }) to equal length of 'nestedAssetData' (${nestedAssetData.length})`, ); } - const [tokenAddress, tokenId] = ethAbi.rawDecode(['address', 'uint256'], data.slice(constants.SELECTOR_LENGTH)); return { assetProxyId, - tokenAddress: ethUtil.addHexPrefix(tokenAddress), - tokenId: new BigNumber(tokenId.toString()), + amounts, + nestedAssetData, }; }, /** @@ -95,18 +108,106 @@ export const assetDataUtils = { * @return The assetProxyId */ decodeAssetProxyId(assetData: string): AssetProxyId { - const encodedAssetData = ethUtil.toBuffer(assetData); - if (encodedAssetData.byteLength < constants.SELECTOR_LENGTH) { + if (assetData.length < constants.SELECTOR_CHAR_LENGTH_WITH_PREFIX) { throw new Error( - `Could not decode assetData. Expected length of encoded data to be at least 4. Got ${ - encodedAssetData.byteLength + `Could not decode assetData. Expected length of encoded data to be at least 10. Got ${ + assetData.length }`, ); } - const encodedAssetProxyId = encodedAssetData.slice(0, constants.SELECTOR_LENGTH); - const assetProxyId = decodeAssetProxyId(encodedAssetProxyId); + const assetProxyId = assetData.slice(0, constants.SELECTOR_CHAR_LENGTH_WITH_PREFIX); + if ( + assetProxyId !== AssetProxyId.ERC20 && + assetProxyId !== AssetProxyId.ERC721 && + assetProxyId !== AssetProxyId.MultiAsset + ) { + throw new Error(`Invalid assetProxyId: ${assetProxyId}`); + } return assetProxyId; }, + /** + * Throws if the length or assetProxyId are invalid for the ERC20Proxy. + * @param assetData Hex encoded assetData string + */ + validateERC20AssetDataThrow(assetData: string): void { + if (assetData.length < constants.ERC20_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) { + throw new Error( + `Could not decode ERC20 Proxy Data. Expected length of encoded data to be at least ${ + constants.ERC20_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX + }. Got ${assetData.length}`, + ); + } + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + if (assetProxyId !== AssetProxyId.ERC20) { + throw new Error( + `Could not decode ERC20 assetData. Expected assetProxyId to be ERC20 (${ + AssetProxyId.ERC20 + }), but got ${assetProxyId}`, + ); + } + }, + /** + * Throws if the length or assetProxyId are invalid for the ERC721Proxy. + * @param assetData Hex encoded assetData string + */ + validateERC721AssetDataOrThrow(assetData: string): void { + if (assetData.length < constants.ERC721_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) { + throw new Error( + `Could not decode ERC721 assetData. Expected length of encoded data to be at least ${ + constants.ERC721_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX + }. Got ${assetData.length}`, + ); + } + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + if (assetProxyId !== AssetProxyId.ERC721) { + throw new Error( + `Could not decode ERC721 assetData. Expected assetProxyId to be ERC721 (${ + AssetProxyId.ERC721 + }), but got ${assetProxyId}`, + ); + } + }, + /** + * Throws if the length or assetProxyId are invalid for the MultiAssetProxy. + * @param assetData Hex encoded assetData string + */ + validateMultiAssetDataOrThrow(assetData: string): void { + if (assetData.length < constants.MULTI_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) { + throw new Error( + `Could not decode MultiAsset assetData. Expected length of encoded data to be at least ${ + constants.MULTI_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX + }. Got ${assetData.length}`, + ); + } + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + if (assetProxyId !== AssetProxyId.MultiAsset) { + throw new Error( + `Could not decode MultiAsset assetData. Expected assetProxyId to be MultiAsset (${ + AssetProxyId.MultiAsset + }), but got ${assetProxyId}`, + ); + } + }, + /** + * Throws if the length or assetProxyId are invalid for the corresponding AssetProxy. + * @param assetData Hex encoded assetData string + */ + validateAssetDataOrThrow(assetData: string): void { + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + switch (assetProxyId) { + case AssetProxyId.ERC20: + assetDataUtils.validateERC20AssetDataThrow(assetData); + break; + case AssetProxyId.ERC721: + assetDataUtils.validateERC721AssetDataOrThrow(assetData); + break; + case AssetProxyId.MultiAsset: + assetDataUtils.validateMultiAssetDataOrThrow(assetData); + break; + default: + throw new Error(`Unrecognized asset proxy id: ${assetProxyId}`); + } + }, /** * Decode any assetData into it's corresponding assetData object * @param assetData Hex encoded assetData string to decode @@ -121,19 +222,11 @@ export const assetDataUtils = { case AssetProxyId.ERC721: const erc721AssetData = assetDataUtils.decodeERC721AssetData(assetData); return erc721AssetData; + case AssetProxyId.MultiAsset: + const multiAssetData = assetDataUtils.decodeMultiAssetData(assetData); + return multiAssetData; default: throw new Error(`Unrecognized asset proxy id: ${assetProxyId}`); } }, }; - -function decodeAssetProxyId(encodedAssetProxyId: Buffer): AssetProxyId { - const hexString = ethUtil.bufferToHex(encodedAssetProxyId); - if (hexString === AssetProxyId.ERC20) { - return AssetProxyId.ERC20; - } - if (hexString === AssetProxyId.ERC721) { - return AssetProxyId.ERC721; - } - throw new Error(`Invalid ProxyId: ${hexString}`); -} diff --git a/packages/order-utils/src/constants.ts b/packages/order-utils/src/constants.ts index 10029dcc3..be7f3a885 100644 --- a/packages/order-utils/src/constants.ts +++ b/packages/order-utils/src/constants.ts @@ -7,10 +7,10 @@ export const constants = { UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1), TESTRPC_NETWORK_ID: 50, ADDRESS_LENGTH: 20, - ERC20_ASSET_DATA_BYTE_LENGTH: 36, - ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH: 53, - SELECTOR_LENGTH: 4, - BASE_16: 16, + ERC20_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX: 74, + ERC721_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX: 136, + MULTI_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX: 266, + SELECTOR_CHAR_LENGTH_WITH_PREFIX: 10, INFINITE_TIMESTAMP_SEC: new BigNumber(2524604400), // Close to infinite ZERO_AMOUNT: new BigNumber(0), EIP712_DOMAIN_NAME: '0x Protocol', @@ -48,4 +48,54 @@ export const constants = { { name: 'data', type: 'bytes' }, ], }, + ERC20_METHOD_ABI: { + constant: false, + inputs: [ + { + name: 'tokenContract', + type: 'address', + }, + ], + name: 'ERC20Token', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + ERC721_METHOD_ABI: { + constant: false, + inputs: [ + { + name: 'tokenContract', + type: 'address', + }, + { + name: 'tokenId', + type: 'uint256', + }, + ], + name: 'ERC721Token', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, + MULTI_ASSET_METHOD_ABI: { + constant: false, + inputs: [ + { + name: 'amounts', + type: 'uint256[]', + }, + { + name: 'nestedAssetData', + type: 'bytes[]', + }, + ], + name: 'MultiAsset', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', + }, }; diff --git a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts index f42a76d0c..0bbaa844a 100644 --- a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts +++ b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts @@ -119,10 +119,10 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx public deleteAllERC721ProxyAllowance(tokenAddress: string, userAddress: string): void { for (const assetData in this._proxyAllowance) { if (this._proxyAllowance.hasOwnProperty(assetData)) { - const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + const decodedAssetData = assetDataUtils.decodeERC721AssetData(assetData); if ( decodedAssetData.assetProxyId === AssetProxyId.ERC721 && - decodedAssetData.tokenAddress === tokenAddress && + !_.isUndefined(decodedAssetData.tokenAddress) && !_.isUndefined(this._proxyAllowance[assetData][userAddress]) ) { delete this._proxyAllowance[assetData][userAddress]; -- cgit v1.2.3 From d957cd53441cf08b3210d24751fdbc70fbfd29a4 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 30 Nov 2018 15:53:07 -0800 Subject: Use assetDataUtils instead of IAssetData for encoding MultiAsset values --- contracts/protocol/test/asset_proxy/proxies.ts | 32 +++++++++++++------------- contracts/protocol/test/exchange/core.ts | 30 ++++-------------------- 2 files changed, 21 insertions(+), 41 deletions(-) diff --git a/contracts/protocol/test/asset_proxy/proxies.ts b/contracts/protocol/test/asset_proxy/proxies.ts index 89c8b390c..bbb44f402 100644 --- a/contracts/protocol/test/asset_proxy/proxies.ts +++ b/contracts/protocol/test/asset_proxy/proxies.ts @@ -715,7 +715,7 @@ describe('Asset Transfer Proxies', () => { const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); const amounts = [erc20Amount]; const nestedAssetData = [erc20AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -778,7 +778,7 @@ describe('Asset Transfer Proxies', () => { const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); const amounts = [erc20Amount1, erc20Amount2]; const nestedAssetData = [erc20AssetData1, erc20AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -811,7 +811,7 @@ describe('Asset Transfer Proxies', () => { const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address); const amounts = [erc20Amount1, erc20Amount2]; const nestedAssetData = [erc20AssetData1, erc20AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -849,7 +849,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); const amounts = [erc721Amount]; const nestedAssetData = [erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -881,7 +881,7 @@ describe('Asset Transfer Proxies', () => { const erc721Amount = new BigNumber(1); const amounts = [erc721Amount, erc721Amount]; const nestedAssetData = [erc721AssetData1, erc721AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -913,7 +913,7 @@ describe('Asset Transfer Proxies', () => { const erc721Amount = new BigNumber(1); const amounts = [erc721Amount, erc721Amount]; const nestedAssetData = [erc721AssetData1, erc721AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -946,7 +946,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -984,10 +984,7 @@ describe('Asset Transfer Proxies', () => { const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; const extraData = '0102030405060708'; - const assetData = `${assetDataInterface.MultiAsset.getABIEncodedTransactionData( - amounts, - nestedAssetData, - )}${extraData}`; + const assetData = `${assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData)}${extraData}`; const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -1024,7 +1021,7 @@ describe('Asset Transfer Proxies', () => { const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address); const amounts = [erc20Amount1, erc20Amount2]; const nestedAssetData = [erc20AssetData1, erc20AssetData2]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -1085,7 +1082,7 @@ describe('Asset Transfer Proxies', () => { erc721AssetData3, erc721AssetData4, ]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -1143,7 +1140,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -1169,6 +1166,7 @@ describe('Asset Transfer Proxies', () => { const invalidErc721AssetData = `${invalidProxyId}${erc721AssetData.slice(10)}`; const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, invalidErc721AssetData]; + // HACK: This is used to get around validation built into assetDataUtils const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, @@ -1192,6 +1190,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); const amounts = [erc20Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; + // HACK: This is used to get around validation built into assetDataUtils const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, @@ -1214,7 +1213,7 @@ describe('Asset Transfer Proxies', () => { const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); const amounts = [erc20Amount]; const nestedAssetData = [erc20AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, @@ -1238,6 +1237,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = '0x123456'; const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; + // HACK: This is used to get around validation built into assetDataUtils const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, @@ -1262,7 +1262,7 @@ describe('Asset Transfer Proxies', () => { const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); const amounts = [erc20Amount, erc721Amount]; const nestedAssetData = [erc20AssetData, erc721AssetData]; - const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( assetData, fromAddress, diff --git a/contracts/protocol/test/exchange/core.ts b/contracts/protocol/test/exchange/core.ts index 9b948f991..8367898de 100644 --- a/contracts/protocol/test/exchange/core.ts +++ b/contracts/protocol/test/exchange/core.ts @@ -43,11 +43,6 @@ import { ExchangeWrapper } from '../utils/exchange_wrapper'; chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -const assetDataInterface = new IAssetDataContract( - interfacesArtifacts.IAssetData.compilerOutput.abi, - constants.NULL_ADDRESS, - provider, -); // tslint:disable:no-unnecessary-type-assertion describe('Exchange core', () => { let makerAddress: string; @@ -777,10 +772,7 @@ describe('Exchange core', () => { assetDataUtils.encodeERC20AssetData(erc20TokenA.address), assetDataUtils.encodeERC20AssetData(erc20TokenB.address), ]; - const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - makerAmounts, - makerNestedAssetData, - ); + const makerAssetData = assetDataUtils.encodeMultiAssetData(makerAmounts, makerNestedAssetData); const makerAssetAmount = new BigNumber(1); const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); const takerAssetAmount = new BigNumber(10); @@ -830,10 +822,7 @@ describe('Exchange core', () => { assetDataUtils.encodeERC20AssetData(erc20TokenA.address), assetDataUtils.encodeERC20AssetData(erc20TokenB.address), ]; - const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - makerAmounts, - makerNestedAssetData, - ); + const makerAssetData = assetDataUtils.encodeMultiAssetData(makerAmounts, makerNestedAssetData); const makerAssetAmount = new BigNumber(1); const takerAmounts = [new BigNumber(10), new BigNumber(1)]; const takerAssetId = erc721TakerAssetIds[0]; @@ -841,10 +830,7 @@ describe('Exchange core', () => { assetDataUtils.encodeERC20AssetData(zrxToken.address), assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), ]; - const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - takerAmounts, - takerNestedAssetData, - ); + const takerAssetData = assetDataUtils.encodeMultiAssetData(takerAmounts, takerNestedAssetData); const takerAssetAmount = new BigNumber(1); signedOrder = await orderFactory.newSignedOrderAsync({ makerAssetData, @@ -900,10 +886,7 @@ describe('Exchange core', () => { assetDataUtils.encodeERC20AssetData(erc20TokenA.address), assetDataUtils.encodeERC20AssetData(erc20TokenB.address), ]; - const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - makerAmounts, - makerNestedAssetData, - ); + const makerAssetData = assetDataUtils.encodeMultiAssetData(makerAmounts, makerNestedAssetData); const makerAssetAmount = new BigNumber(30); const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); const takerAssetAmount = new BigNumber(10); @@ -980,10 +963,7 @@ describe('Exchange core', () => { assetDataUtils.encodeERC20AssetData(erc20TokenA.address), assetDataUtils.encodeERC20AssetData(erc20TokenB.address), ]; - const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( - takerAmounts, - takerNestedAssetData, - ); + const takerAssetData = assetDataUtils.encodeMultiAssetData(takerAmounts, takerNestedAssetData); const takerAssetAmount = new BigNumber(30); const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); const makerAssetAmount = new BigNumber(10); -- cgit v1.2.3 From 1553b366da410fbf00338a3207d326e5d57f54b2 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 30 Nov 2018 15:54:46 -0800 Subject: Update CHANGELOGs --- packages/order-utils/CHANGELOG.json | 9 +++++++++ packages/types/CHANGELOG.json | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index 11889b92e..a6fb91b83 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "3.1.0", + "changes": [ + { + "note": "Use new ABI encoder and add encoding/decoding logic for MultiAsset assetData", + "pr": 1363 + } + ] + }, { "version": "3.0.7", "changes": [ diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index f1cd2f18e..fdb421e79 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -18,6 +18,10 @@ { "note": "Add RevertReasons for DutchAuction contract", "pr": 1225 + }, + { + "note": "Add MultiAsset types", + "pr": 1363 } ], "timestamp": 1544570656 -- cgit v1.2.3 From 0cb5209266b979bcee094f3863efca0a1fa63697 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 30 Nov 2018 15:57:30 -0800 Subject: Add length check to MultiAsset encoding --- packages/order-utils/src/asset_data_utils.ts | 7 +++++++ .../src/store/balance_and_proxy_allowance_lazy_store.ts | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index b5cfe698e..0b32cf216 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -73,6 +73,13 @@ export const assetDataUtils = { * @return The hex encoded assetData string */ encodeMultiAssetData(amounts: BigNumber[], nestedAssetData: string[]): string { + if (amounts.length !== nestedAssetData.length) { + throw new Error( + `Invalid MultiAsset arguments. Expected length of 'amounts' (${ + amounts.length + }) to equal length of 'nestedAssetData' (${nestedAssetData.length})`, + ); + } _.forEach(nestedAssetData, assetDataElement => assetDataUtils.validateAssetDataOrThrow(assetDataElement)); const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI as MethodAbi); const args = [amounts, nestedAssetData]; diff --git a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts index 0bbaa844a..ae3e36238 100644 --- a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts +++ b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts @@ -122,7 +122,7 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx const decodedAssetData = assetDataUtils.decodeERC721AssetData(assetData); if ( decodedAssetData.assetProxyId === AssetProxyId.ERC721 && - !_.isUndefined(decodedAssetData.tokenAddress) && + decodedAssetData.tokenAddress === tokenAddress && !_.isUndefined(this._proxyAllowance[assetData][userAddress]) ) { delete this._proxyAllowance[assetData][userAddress]; -- cgit v1.2.3 From 19e93440b39161e8b1ecb79f29548faae1be4b1b Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 30 Nov 2018 16:13:35 -0800 Subject: fix: Add error message if assetProxyId is not ERC20 or ERC721 --- packages/fill-scenarios/CHANGELOG.json | 9 +++++++++ packages/fill-scenarios/src/fill_scenarios.ts | 25 ++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json index ca256399a..176267df9 100644 --- a/packages/fill-scenarios/CHANGELOG.json +++ b/packages/fill-scenarios/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.0.17", + "changes": [ + { + "note": "Add error message if assetProxyId is not ERC20 or ERC721", + "pr": 1363 + } + ] + }, { "version": "1.0.16", "changes": [ diff --git a/packages/fill-scenarios/src/fill_scenarios.ts b/packages/fill-scenarios/src/fill_scenarios.ts index 0154bcd0a..e08c6ae5f 100644 --- a/packages/fill-scenarios/src/fill_scenarios.ts +++ b/packages/fill-scenarios/src/fill_scenarios.ts @@ -2,7 +2,14 @@ import { DummyERC20TokenContract, DummyERC721TokenContract, ExchangeContract } f import * as artifacts from '@0x/contract-artifacts'; import { assetDataUtils } from '@0x/order-utils'; import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; -import { AssetProxyId, ERC721AssetData, OrderWithoutExchangeAddress, SignedOrder } from '@0x/types'; +import { + AssetData, + AssetProxyId, + ERC20AssetData, + ERC721AssetData, + OrderWithoutExchangeAddress, + SignedOrder, +} from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider } from 'ethereum-types'; @@ -153,35 +160,39 @@ export class FillScenarios { const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(makerAssetData); if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) { await this._increaseERC20BalanceAndAllowanceAsync( - decodedMakerAssetData.tokenAddress, + (decodedMakerAssetData as ERC20AssetData).tokenAddress, makerAddress, makerFillableAmount, ); - } else { + } else if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC721) { if (!makerFillableAmount.equals(1)) { throw new Error(`ERC721 makerFillableAmount should be equal 1. Found: ${makerFillableAmount}`); } await this._increaseERC721BalanceAndAllowanceAsync( - decodedMakerAssetData.tokenAddress, + (decodedMakerAssetData as ERC721AssetData).tokenAddress, makerAddress, // tslint:disable-next-line:no-unnecessary-type-assertion (decodedMakerAssetData as ERC721AssetData).tokenId, ); + } else { + throw new Error(`Proxy with id ${decodedMakerAssetData.assetProxyId} not supported`); } const decodedTakerAssetData = assetDataUtils.decodeAssetDataOrThrow(takerAssetData); if (decodedTakerAssetData.assetProxyId === AssetProxyId.ERC20) { - const takerTokenAddress = decodedTakerAssetData.tokenAddress; + const takerTokenAddress = (decodedTakerAssetData as ERC20AssetData).tokenAddress; await this._increaseERC20BalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount); - } else { + } else if (decodedTakerAssetData.assetProxyId === AssetProxyId.ERC721) { if (!takerFillableAmount.equals(1)) { throw new Error(`ERC721 takerFillableAmount should be equal 1. Found: ${takerFillableAmount}`); } await this._increaseERC721BalanceAndAllowanceAsync( - decodedTakerAssetData.tokenAddress, + (decodedTakerAssetData as ERC721AssetData).tokenAddress, takerAddress, // tslint:disable-next-line:no-unnecessary-type-assertion (decodedMakerAssetData as ERC721AssetData).tokenId, ); + } else { + throw new Error(`Proxy with id ${decodedTakerAssetData.assetProxyId} not supported`); } // Fees await Promise.all([ -- cgit v1.2.3 From 67dfe6030c3d193e8ccf9411b114a44e2ed11c73 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sat, 1 Dec 2018 22:30:32 -0800 Subject: feat: Add support for MultiAssetProxy --- .../dependent_order_hashes_tracker.ts | 76 +++++++++----- .../src/order_watcher/order_watcher.ts | 110 +++++++++++++++------ 2 files changed, 131 insertions(+), 55 deletions(-) diff --git a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts index a956a94db..eed1d055b 100644 --- a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts +++ b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts @@ -1,6 +1,6 @@ // tslint:disable:no-unnecessary-type-assertion import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { AssetProxyId, ERC20AssetData, ERC721AssetData, SignedOrder } from '@0x/types'; +import { AssetProxyId, ERC20AssetData, ERC721AssetData, MultiAssetData, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; @@ -62,39 +62,20 @@ export class DependentOrderHashesTracker { return dependentOrderHashes; } public addToDependentOrderHashes(signedOrder: SignedOrder): void { - const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(signedOrder.makerAssetData); - if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) { - this._addToERC20DependentOrderHashes(signedOrder, (decodedMakerAssetData as ERC20AssetData).tokenAddress); - } else { - this._addToERC721DependentOrderHashes( - signedOrder, - (decodedMakerAssetData as ERC721AssetData).tokenAddress, - (decodedMakerAssetData as ERC721AssetData).tokenId, - ); - } + this._addAssetDataToDependentOrderHashes(signedOrder, signedOrder.makerAssetData); this._addToERC20DependentOrderHashes(signedOrder, this._zrxTokenAddress); this._addToMakerDependentOrderHashes(signedOrder); } public removeFromDependentOrderHashes(signedOrder: SignedOrder): void { - const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(signedOrder.makerAssetData); - if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) { - this._removeFromERC20DependentOrderhashes( - signedOrder, - (decodedMakerAssetData as ERC20AssetData).tokenAddress, - ); - } else { - this._removeFromERC721DependentOrderhashes( - signedOrder, - (decodedMakerAssetData as ERC721AssetData).tokenAddress, - (decodedMakerAssetData as ERC721AssetData).tokenId, - ); - } + this._removeAssetDataFromDependentOrderHashes(signedOrder, signedOrder.makerAssetData); // If makerToken === ZRX then we already removed it and we don't need to remove it again. + const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(signedOrder.makerAssetData); if ((decodedMakerAssetData as ERC20AssetData).tokenAddress !== this._zrxTokenAddress) { this._removeFromERC20DependentOrderhashes(signedOrder, this._zrxTokenAddress); } this._removeFromMakerDependentOrderhashes(signedOrder); } + private _getDependentOrderHashesByERC20AssetData(makerAddress: string, erc20AssetData: string): string[] { const tokenAddress = assetDataUtils.decodeERC20AssetData(erc20AssetData).tokenAddress; let dependentOrderHashes: string[] = []; @@ -167,6 +148,28 @@ export class DependentOrderHashesTracker { tokenId.toString() ].add(orderHash); } + private _addAssetDataToDependentOrderHashes(signedOrder: SignedOrder, assetData: string): void { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + switch (decodedAssetData.assetProxyId) { + case AssetProxyId.ERC20: + this._addToERC20DependentOrderHashes(signedOrder, (decodedAssetData as ERC20AssetData).tokenAddress); + break; + case AssetProxyId.ERC721: + this._addToERC721DependentOrderHashes( + signedOrder, + (decodedAssetData as ERC721AssetData).tokenAddress, + (decodedAssetData as ERC721AssetData).tokenId, + ); + break; + case AssetProxyId.MultiAsset: + _.each((decodedAssetData as MultiAssetData).nestedAssetData, nestedAssetDataElement => + this._addAssetDataToDependentOrderHashes(signedOrder, nestedAssetDataElement), + ); + break; + default: + break; + } + } private _addToMakerDependentOrderHashes(signedOrder: SignedOrder): void { const orderHash = orderHashUtils.getOrderHashHex(signedOrder); if (_.isUndefined(this._orderHashesByMakerAddress[signedOrder.makerAddress])) { @@ -230,4 +233,29 @@ export class DependentOrderHashesTracker { delete this._orderHashesByMakerAddress[signedOrder.makerAddress]; } } + private _removeAssetDataFromDependentOrderHashes(signedOrder: SignedOrder, assetData: string): void { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + switch (decodedAssetData.assetProxyId) { + case AssetProxyId.ERC20: + this._removeFromERC20DependentOrderhashes( + signedOrder, + (decodedAssetData as ERC20AssetData).tokenAddress, + ); + break; + case AssetProxyId.ERC721: + this._removeFromERC721DependentOrderhashes( + signedOrder, + (decodedAssetData as ERC721AssetData).tokenAddress, + (decodedAssetData as ERC721AssetData).tokenId, + ); + break; + case AssetProxyId.MultiAsset: + _.each((decodedAssetData as MultiAssetData).nestedAssetData, nestedAssetDataElement => + this._removeAssetDataFromDependentOrderHashes(signedOrder, nestedAssetDataElement), + ); + break; + default: + break; + } + } } diff --git a/packages/order-watcher/src/order_watcher/order_watcher.ts b/packages/order-watcher/src/order_watcher/order_watcher.ts index 96c5ca7b4..bf890f0e6 100644 --- a/packages/order-watcher/src/order_watcher/order_watcher.ts +++ b/packages/order-watcher/src/order_watcher/order_watcher.ts @@ -32,7 +32,16 @@ import { orderHashUtils, OrderStateUtils, } from '@0x/order-utils'; -import { AssetProxyId, ExchangeContractErrs, OrderState, SignedOrder, Stats } from '@0x/types'; +import { + AssetProxyId, + ERC20AssetData, + ERC721AssetData, + ExchangeContractErrs, + MultiAssetData, + OrderState, + SignedOrder, + Stats, +} from '@0x/types'; import { errorUtils, intervalUtils } from '@0x/utils'; import { BlockParamLiteral, LogEntryEvent, LogWithDecodedArgs, Provider } from 'ethereum-types'; import * as _ from 'lodash'; @@ -161,14 +170,7 @@ export class OrderWatcher { this._dependentOrderHashesTracker.addToDependentOrderHashes(signedOrder); const orderAssetDatas = [signedOrder.makerAssetData, signedOrder.takerAssetData]; - _.each(orderAssetDatas, assetData => { - const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); - if (decodedAssetData.assetProxyId === AssetProxyId.ERC20) { - this._collisionResistantAbiDecoder.addERC20Token(decodedAssetData.tokenAddress); - } else if (decodedAssetData.assetProxyId === AssetProxyId.ERC721) { - this._collisionResistantAbiDecoder.addERC721Token(decodedAssetData.tokenAddress); - } - }); + _.each(orderAssetDatas, assetData => this._addAssetDataToAbiDecoder(assetData)); } /** * Removes an order from the orderWatcher @@ -236,31 +238,77 @@ export class OrderWatcher { await this._emitRevalidateOrdersAsync([orderHash]); } } + private _addAssetDataToAbiDecoder(assetData: string): void { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + switch (decodedAssetData.assetProxyId) { + case AssetProxyId.ERC20: + this._collisionResistantAbiDecoder.addERC20Token((decodedAssetData as ERC20AssetData).tokenAddress); + break; + case AssetProxyId.ERC721: + this._collisionResistantAbiDecoder.addERC721Token((decodedAssetData as ERC721AssetData).tokenAddress); + break; + case AssetProxyId.MultiAsset: + _.each((decodedAssetData as MultiAssetData).nestedAssetData, nestedAssetDataElement => + this._addAssetDataToAbiDecoder(nestedAssetDataElement), + ); + break; + default: + break; + } + } + private _deleteLazyStoreBalance(assetData: string, userAddress: string): void { + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + switch (assetProxyId) { + case AssetProxyId.ERC20: + case AssetProxyId.ERC721: + this._balanceAndProxyAllowanceLazyStore.deleteBalance(assetData, userAddress); + break; + case AssetProxyId.MultiAsset: + const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData); + _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => + this._deleteLazyStoreBalance(nestedAssetDataElement, userAddress), + ); + break; + default: + break; + } + } + private _deleteLazyStoreProxyAllowance(assetData: string, userAddress: string): void { + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + switch (assetProxyId) { + case AssetProxyId.ERC20: + case AssetProxyId.ERC721: + this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(assetData, userAddress); + break; + case AssetProxyId.MultiAsset: + const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData); + _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => + this._deleteLazyStoreProxyAllowance(nestedAssetDataElement, userAddress), + ); + break; + default: + break; + } + } private _cleanupOrderRelatedState(orderHash: string): void { const signedOrder = this._orderByOrderHash[orderHash]; this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(orderHash); this._orderFilledCancelledLazyStore.deleteIsCancelled(orderHash); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.makerAssetData, signedOrder.makerAddress); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance( - signedOrder.makerAssetData, - signedOrder.makerAddress, - ); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.takerAssetData, signedOrder.takerAddress); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance( - signedOrder.takerAssetData, - signedOrder.takerAddress, - ); + this._deleteLazyStoreBalance(signedOrder.makerAssetData, signedOrder.makerAddress); + this._deleteLazyStoreProxyAllowance(signedOrder.makerAssetData, signedOrder.makerAddress); + this._deleteLazyStoreBalance(signedOrder.takerAssetData, signedOrder.takerAddress); + this._deleteLazyStoreProxyAllowance(signedOrder.takerAssetData, signedOrder.takerAddress); const zrxAssetData = this._orderFilledCancelledLazyStore.getZRXAssetData(); if (!signedOrder.makerFee.isZero()) { - this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxAssetData, signedOrder.makerAddress); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxAssetData, signedOrder.makerAddress); + this._deleteLazyStoreBalance(zrxAssetData, signedOrder.makerAddress); + this._deleteLazyStoreProxyAllowance(zrxAssetData, signedOrder.makerAddress); } if (!signedOrder.takerFee.isZero()) { - this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxAssetData, signedOrder.takerAddress); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxAssetData, signedOrder.takerAddress); + this._deleteLazyStoreBalance(zrxAssetData, signedOrder.takerAddress); + this._deleteLazyStoreProxyAllowance(zrxAssetData, signedOrder.takerAddress); } } private _onOrderExpired(orderHash: string): void { @@ -302,7 +350,7 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as ERC20TokenApprovalEventArgs; const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(tokenAssetData, args._owner); + this._deleteLazyStoreProxyAllowance(tokenAssetData, args._owner); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._owner, @@ -315,7 +363,7 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as ERC721TokenApprovalEventArgs; const tokenAssetData = assetDataUtils.encodeERC721AssetData(decodedLog.address, args._tokenId); - this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(tokenAssetData, args._owner); + this._deleteLazyStoreProxyAllowance(tokenAssetData, args._owner); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._owner, @@ -333,8 +381,8 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as ERC20TokenTransferEventArgs; const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._from); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._to); + this._deleteLazyStoreBalance(tokenAssetData, args._from); + this._deleteLazyStoreBalance(tokenAssetData, args._to); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._from, @@ -347,8 +395,8 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as ERC721TokenTransferEventArgs; const tokenAssetData = assetDataUtils.encodeERC721AssetData(decodedLog.address, args._tokenId); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._from); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._to); + this._deleteLazyStoreBalance(tokenAssetData, args._from); + this._deleteLazyStoreBalance(tokenAssetData, args._to); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._from, @@ -375,7 +423,7 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as WETH9DepositEventArgs; const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._owner); + this._deleteLazyStoreBalance(tokenAssetData, args._owner); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._owner, @@ -388,7 +436,7 @@ export class OrderWatcher { // Invalidate cache const args = decodedLog.args as WETH9WithdrawalEventArgs; const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address); - this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._owner); + this._deleteLazyStoreBalance(tokenAssetData, args._owner); // Revalidate orders const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker( args._owner, -- cgit v1.2.3 From fc71d87d2bf38f59bd68d491f6eb6acbc04a9ece Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sat, 1 Dec 2018 22:32:16 -0800 Subject: Update CHANGELOG --- packages/order-watcher/CHANGELOG.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json index 304dc45fd..b4807dc89 100644 --- a/packages/order-watcher/CHANGELOG.json +++ b/packages/order-watcher/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "2.4.0", + "changes": [ + { + "note": "Add support for MultiAssetProxy", + "pr": 1363 + } + ] + }, { "version": "2.3.0", "changes": [ -- cgit v1.2.3 From 46359c098b0f6f3b0377f9426c78aa2ce2e4cf5a Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sat, 1 Dec 2018 23:32:50 -0800 Subject: Add support for MAP in fill-scenarios --- packages/fill-scenarios/CHANGELOG.json | 4 +- packages/fill-scenarios/src/fill_scenarios.ts | 81 ++++++++++++++------------- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json index 176267df9..2c3e261cb 100644 --- a/packages/fill-scenarios/CHANGELOG.json +++ b/packages/fill-scenarios/CHANGELOG.json @@ -1,9 +1,9 @@ [ { - "version": "1.0.17", + "version": "1.1.0", "changes": [ { - "note": "Add error message if assetProxyId is not ERC20 or ERC721", + "note": "Add support for MultiAssetProxy", "pr": 1363 } ] diff --git a/packages/fill-scenarios/src/fill_scenarios.ts b/packages/fill-scenarios/src/fill_scenarios.ts index e08c6ae5f..b11b01a22 100644 --- a/packages/fill-scenarios/src/fill_scenarios.ts +++ b/packages/fill-scenarios/src/fill_scenarios.ts @@ -3,10 +3,10 @@ import * as artifacts from '@0x/contract-artifacts'; import { assetDataUtils } from '@0x/order-utils'; import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; import { - AssetData, AssetProxyId, ERC20AssetData, ERC721AssetData, + MultiAssetData, OrderWithoutExchangeAddress, SignedOrder, } from '@0x/types'; @@ -157,43 +157,8 @@ export class FillScenarios { feeRecipientAddress: string, expirationTimeSeconds?: BigNumber, ): Promise { - const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(makerAssetData); - if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) { - await this._increaseERC20BalanceAndAllowanceAsync( - (decodedMakerAssetData as ERC20AssetData).tokenAddress, - makerAddress, - makerFillableAmount, - ); - } else if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC721) { - if (!makerFillableAmount.equals(1)) { - throw new Error(`ERC721 makerFillableAmount should be equal 1. Found: ${makerFillableAmount}`); - } - await this._increaseERC721BalanceAndAllowanceAsync( - (decodedMakerAssetData as ERC721AssetData).tokenAddress, - makerAddress, - // tslint:disable-next-line:no-unnecessary-type-assertion - (decodedMakerAssetData as ERC721AssetData).tokenId, - ); - } else { - throw new Error(`Proxy with id ${decodedMakerAssetData.assetProxyId} not supported`); - } - const decodedTakerAssetData = assetDataUtils.decodeAssetDataOrThrow(takerAssetData); - if (decodedTakerAssetData.assetProxyId === AssetProxyId.ERC20) { - const takerTokenAddress = (decodedTakerAssetData as ERC20AssetData).tokenAddress; - await this._increaseERC20BalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount); - } else if (decodedTakerAssetData.assetProxyId === AssetProxyId.ERC721) { - if (!takerFillableAmount.equals(1)) { - throw new Error(`ERC721 takerFillableAmount should be equal 1. Found: ${takerFillableAmount}`); - } - await this._increaseERC721BalanceAndAllowanceAsync( - (decodedTakerAssetData as ERC721AssetData).tokenAddress, - takerAddress, - // tslint:disable-next-line:no-unnecessary-type-assertion - (decodedMakerAssetData as ERC721AssetData).tokenId, - ); - } else { - throw new Error(`Proxy with id ${decodedTakerAssetData.assetProxyId} not supported`); - } + await this._increaseBalanceAndAllowanceWithAssetDataAsync(makerAssetData, makerAddress, makerFillableAmount); + await this._increaseBalanceAndAllowanceWithAssetDataAsync(takerAssetData, takerAddress, takerFillableAmount); // Fees await Promise.all([ this._increaseERC20BalanceAndAllowanceAsync(this._zrxTokenAddress, makerAddress, makerFee), @@ -309,4 +274,44 @@ export class FillScenarios { }); await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); } + private async _increaseBalanceAndAllowanceWithAssetDataAsync( + assetData: string, + userAddress: string, + amount: BigNumber, + ): Promise { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + switch (decodedAssetData.assetProxyId) { + case AssetProxyId.ERC20: + await this._increaseERC20BalanceAndAllowanceAsync( + (decodedAssetData as ERC20AssetData).tokenAddress, + userAddress, + amount, + ); + break; + case AssetProxyId.ERC721: + await this._increaseERC721BalanceAndAllowanceAsync( + (decodedAssetData as ERC721AssetData).tokenAddress, + userAddress, + // tslint:disable-next-line:no-unnecessary-type-assertion + (decodedAssetData as ERC721AssetData).tokenId, + ); + break; + case AssetProxyId.MultiAsset: + for (const [ + index, + nestedAssetDataElement, + ] of (decodedAssetData as MultiAssetData).nestedAssetData.entries()) { + const amountsElement = (decodedAssetData as MultiAssetData).amounts[index]; + const totalAmount = amount.times(amountsElement); + await this._increaseBalanceAndAllowanceWithAssetDataAsync( + nestedAssetDataElement, + userAddress, + totalAmount, + ); + } + break; + default: + throw new Error(`Proxy with id ${decodedAssetData.assetProxyId} not supported`); + } + } } -- cgit v1.2.3 From aae32037da737e77b3ffaf0115494a1d7966a972 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sat, 1 Dec 2018 23:40:12 -0800 Subject: Run linter --- packages/fill-scenarios/src/fill_scenarios.ts | 4 ++++ packages/order-watcher/src/order_watcher/order_watcher.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/fill-scenarios/src/fill_scenarios.ts b/packages/fill-scenarios/src/fill_scenarios.ts index b11b01a22..2e87e60cc 100644 --- a/packages/fill-scenarios/src/fill_scenarios.ts +++ b/packages/fill-scenarios/src/fill_scenarios.ts @@ -283,6 +283,7 @@ export class FillScenarios { switch (decodedAssetData.assetProxyId) { case AssetProxyId.ERC20: await this._increaseERC20BalanceAndAllowanceAsync( + // tslint:disable-next-line:no-unnecessary-type-assertion (decodedAssetData as ERC20AssetData).tokenAddress, userAddress, amount, @@ -290,6 +291,7 @@ export class FillScenarios { break; case AssetProxyId.ERC721: await this._increaseERC721BalanceAndAllowanceAsync( + // tslint:disable-next-line:no-unnecessary-type-assertion (decodedAssetData as ERC721AssetData).tokenAddress, userAddress, // tslint:disable-next-line:no-unnecessary-type-assertion @@ -300,7 +302,9 @@ export class FillScenarios { for (const [ index, nestedAssetDataElement, + // tslint:disable-next-line:no-unnecessary-type-assertion ] of (decodedAssetData as MultiAssetData).nestedAssetData.entries()) { + // tslint:disable-next-line:no-unnecessary-type-assertion const amountsElement = (decodedAssetData as MultiAssetData).amounts[index]; const totalAmount = amount.times(amountsElement); await this._increaseBalanceAndAllowanceWithAssetDataAsync( diff --git a/packages/order-watcher/src/order_watcher/order_watcher.ts b/packages/order-watcher/src/order_watcher/order_watcher.ts index bf890f0e6..c48606555 100644 --- a/packages/order-watcher/src/order_watcher/order_watcher.ts +++ b/packages/order-watcher/src/order_watcher/order_watcher.ts @@ -502,4 +502,4 @@ export class OrderWatcher { this._callbackIfExists(null, orderState); } } -} +} // tslint:disable:max-file-line-count -- cgit v1.2.3 From 8549252a2c0d12ec736eea72b702924353a9daaf Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sun, 2 Dec 2018 13:16:07 -0800 Subject: Fix decoding and add tests --- packages/order-utils/src/asset_data_utils.ts | 17 +++-- packages/order-utils/test/asset_data_utils_test.ts | 72 +++++++++++++--------- 2 files changed, 55 insertions(+), 34 deletions(-) diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index 0b32cf216..1b416abd1 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -30,10 +30,11 @@ export const assetDataUtils = { assetDataUtils.validateERC20AssetDataThrow(assetData); const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI as MethodAbi); - const [tokenAddress] = abiEncoder.decode(assetData, decodingRules); + const decodedAssetData = abiEncoder.decode(assetData, decodingRules); return { assetProxyId, - tokenAddress, + // TODO(abandeali1): fix decode return types + tokenAddress: (decodedAssetData as any).tokenContract, }; }, /** @@ -58,11 +59,12 @@ export const assetDataUtils = { assetDataUtils.validateERC721AssetDataOrThrow(assetData); const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI as MethodAbi); - const [tokenAddress, tokenId] = abiEncoder.decode(assetData, decodingRules); + const decodedAssetData = abiEncoder.decode(assetData, decodingRules); return { assetProxyId, - tokenAddress, - tokenId, + // TODO(abandeali1): fix decode return types + tokenAddress: (decodedAssetData as any).tokenContract, + tokenId: (decodedAssetData as any).tokenId, }; }, /** @@ -95,7 +97,10 @@ export const assetDataUtils = { assetDataUtils.validateMultiAssetDataOrThrow(assetData); const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI as MethodAbi); - const [amounts, nestedAssetData] = abiEncoder.decode(assetData, decodingRules); + const decodedAssetData = abiEncoder.decode(assetData, decodingRules); + // TODO(abandeali1): fix decode return types + const amounts = (decodedAssetData as any).amounts; + const nestedAssetData = (decodedAssetData as any).nestedAssetData; if (amounts.length !== nestedAssetData.length) { throw new Error( `Invalid MultiAsset assetData. Expected length of 'amounts' (${ diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts index f175b7a38..a070aaf16 100644 --- a/packages/order-utils/test/asset_data_utils_test.ts +++ b/packages/order-utils/test/asset_data_utils_test.ts @@ -1,6 +1,6 @@ import * as chai from 'chai'; -import { ERC20AssetData, ERC721AssetData } from '@0x/types'; +import { AssetProxyId } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { assetDataUtils } from '../src/asset_data_utils'; @@ -10,41 +10,57 @@ import { chaiSetup } from './utils/chai_setup'; chaiSetup.configure(); const expect = chai.expect; -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'; +const KNOWN_ERC20_ENCODING = { + address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', + assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48', +}; +const KNOWN_ERC721_ENCODING = { + address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', + tokenId: new BigNumber(1), + assetData: + '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001', +}; +const KNOWN_MULTI_ASSET_ENCODING = { + amounts: [new BigNumber(1), new BigNumber(1)], + nestedAssetData: [KNOWN_ERC20_ENCODING.assetData, KNOWN_ERC721_ENCODING.assetData], + assetData: + '0x94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000', +}; describe('assetDataUtils', () => { it('should encode ERC20', () => { - const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ENCODINGS[0].address); - expect(assetData).to.equal(KNOWN_ENCODINGS[0].assetData); + const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ERC20_ENCODING.address); + expect(assetData).to.equal(KNOWN_ERC20_ENCODING.assetData); }); 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); + const decodedAssetData = assetDataUtils.decodeERC20AssetData(KNOWN_ERC20_ENCODING.assetData); + expect(decodedAssetData.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); + expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.ERC20); }); 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); + const assetData = assetDataUtils.encodeERC721AssetData( + KNOWN_ERC721_ENCODING.address, + KNOWN_ERC721_ENCODING.tokenId, + ); + expect(assetData).to.equal(KNOWN_ERC721_ENCODING.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); + const decodedAssetData = assetDataUtils.decodeERC721AssetData(KNOWN_ERC721_ENCODING.assetData); + expect(decodedAssetData.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); + expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.ERC721); + expect(decodedAssetData.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); + }); + it('should encode ERC20 and ERC721', () => { + const assetData = assetDataUtils.encodeMultiAssetData( + KNOWN_MULTI_ASSET_ENCODING.amounts, + KNOWN_MULTI_ASSET_ENCODING.nestedAssetData, + ); + expect(assetData).to.equal(KNOWN_MULTI_ASSET_ENCODING.assetData); + }); + it('should decode ERC20 and ERC21', () => { + const decodedAssetData = assetDataUtils.decodeMultiAssetData(KNOWN_MULTI_ASSET_ENCODING.assetData); + expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); + expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts); + expect(decodedAssetData.nestedAssetData).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.nestedAssetData); }); }); -- cgit v1.2.3 From 492e0ae34546e0020c2c6a694ce60feef5ebf695 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sun, 2 Dec 2018 14:43:33 -0800 Subject: feat: Export MultiAssetData in types --- packages/0x.js/CHANGELOG.json | 9 +++++++++ packages/0x.js/src/index.ts | 1 + packages/order-utils/src/index.ts | 1 + 3 files changed, 11 insertions(+) diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index 32351ad82..0024052b3 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "2.0.9", + "changes": [ + { + "note": "Export `MultiAssetData` from types", + "pr": 1363 + } + ] + }, { "version": "2.0.8", "changes": [ diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index 2df360b96..0ccb0f768 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -82,6 +82,7 @@ export { AssetData, ERC20AssetData, ERC721AssetData, + MultiAssetData, SignatureType, OrderRelevantState, Stats, diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index e70d43efb..aae439e68 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -37,6 +37,7 @@ export { AssetData, ERC20AssetData, ERC721AssetData, + MultiAssetData, AssetProxyId, SignatureType, OrderStateValid, -- cgit v1.2.3 From c6ff77bdd8ea04e04a9be4ae2e5dc30afc89665b Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 3 Dec 2018 16:53:01 -0800 Subject: Add prefix to names of properties in EncodingRules and DecodingRules --- packages/utils/CHANGELOG.json | 9 +++++++++ .../src/abi_encoder/abstract_data_types/types/set.ts | 2 +- packages/utils/src/abi_encoder/calldata/calldata.ts | 6 ++++-- packages/utils/src/abi_encoder/utils/constants.ts | 4 ++-- packages/utils/src/abi_encoder/utils/rules.ts | 6 +++--- packages/utils/test/abi_encoder/evm_data_types_test.ts | 16 ++++++++-------- packages/utils/test/abi_encoder/methods_test.ts | 4 ++-- packages/utils/test/abi_encoder/optimizer_test.ts | 4 ++-- packages/utils/test/abi_encoder/return_values_test.ts | 2 +- 9 files changed, 32 insertions(+), 21 deletions(-) diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index 605151fb6..eb94da8d6 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "2.1.1", + "changes": [ + { + "note": "Add `should` prefix to names of properties in EncodingRules and DecodingRules", + "pr": 1363 + } + ] + }, { "version": "2.1.0", "changes": [ diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts index 089d04659..00059a4b6 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts @@ -62,7 +62,7 @@ export abstract class AbstractSetDataType extends DataType { // Create a new scope in the calldata, before descending into the members of this set. calldata.startScope(); let value: any[] | object; - if (rules.structsAsObjects && !this._isArray) { + if (rules.shouldConvertStructsToObjects && !this._isArray) { // Construct an object with values for each member of the set. value = {}; _.each(this._memberIndexByName, (idx: number, key: string) => { diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index 5f3eee94a..b08fb71ce 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -49,7 +49,7 @@ export class Calldata { throw new Error('expected root'); } // Optimize, if flag set - if (this._rules.optimize) { + if (this._rules.shouldOptimize) { this._optimize(); } // Set offsets @@ -60,7 +60,9 @@ export class Calldata { offset += block.getSizeInBytes(); } // Generate hex string - const hexString = this._rules.annotate ? this._toHumanReadableCallData() : this._toEvmCompatibeCallDataHex(); + const hexString = this._rules.shouldAnnotate + ? this._toHumanReadableCallData() + : this._toEvmCompatibeCallDataHex(); return hexString; } /** diff --git a/packages/utils/src/abi_encoder/utils/constants.ts b/packages/utils/src/abi_encoder/utils/constants.ts index 2f43ba04d..36de2dd4f 100644 --- a/packages/utils/src/abi_encoder/utils/constants.ts +++ b/packages/utils/src/abi_encoder/utils/constants.ts @@ -11,7 +11,7 @@ export const constants = { HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA: 0, // Disable no-object-literal-type-assertion so we can enforce cast /* tslint:disable no-object-literal-type-assertion */ - DEFAULT_DECODING_RULES: { structsAsObjects: false } as DecodingRules, - DEFAULT_ENCODING_RULES: { optimize: true, annotate: false } as EncodingRules, + DEFAULT_DECODING_RULES: { shouldConvertStructsToObjects: false } as DecodingRules, + DEFAULT_ENCODING_RULES: { shouldOptimize: true, shouldAnnotate: false } as EncodingRules, /* tslint:enable no-object-literal-type-assertion */ }; diff --git a/packages/utils/src/abi_encoder/utils/rules.ts b/packages/utils/src/abi_encoder/utils/rules.ts index 31471e97a..c8d83c3ba 100644 --- a/packages/utils/src/abi_encoder/utils/rules.ts +++ b/packages/utils/src/abi_encoder/utils/rules.ts @@ -1,8 +1,8 @@ export interface DecodingRules { - structsAsObjects: boolean; + shouldConvertStructsToObjects: boolean; } export interface EncodingRules { - optimize?: boolean; - annotate?: boolean; + shouldOptimize?: boolean; + shouldAnnotate?: boolean; } diff --git a/packages/utils/test/abi_encoder/evm_data_types_test.ts b/packages/utils/test/abi_encoder/evm_data_types_test.ts index 7185851a8..55d582d10 100644 --- a/packages/utils/test/abi_encoder/evm_data_types_test.ts +++ b/packages/utils/test/abi_encoder/evm_data_types_test.ts @@ -10,7 +10,7 @@ chaiSetup.configure(); const expect = chai.expect; describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { - const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately. + const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately. describe('Array', () => { it('Fixed size; Static elements', async () => { // Create DataType object @@ -194,7 +194,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000001'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -214,7 +214,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -234,7 +234,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -254,7 +254,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -276,7 +276,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0x0102030400000000000000000000000000000000000000000000000000000000050607080000000000000000000000000000000000000000000000000000000009101112000000000000000000000000000000000000000000000000000000001314151600000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -298,7 +298,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040506070800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041314151600000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); @@ -328,7 +328,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); expect(decodedArgs).to.be.deep.equal(args); }); diff --git a/packages/utils/test/abi_encoder/methods_test.ts b/packages/utils/test/abi_encoder/methods_test.ts index 837020883..a0525967e 100644 --- a/packages/utils/test/abi_encoder/methods_test.ts +++ b/packages/utils/test/abi_encoder/methods_test.ts @@ -10,7 +10,7 @@ chaiSetup.configure(); const expect = chai.expect; describe('ABI Encoder: Method Encoding / Decoding', () => { - const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately. + const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately. it('Types with default widths', async () => { // Generate calldata const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); @@ -360,7 +360,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const decodedValue = method.decode(calldata, { structsAsObjects: true }); + const decodedValue = method.decode(calldata, { shouldConvertStructsToObjects: true }); expect(decodedValue).to.be.deep.equal(args); }); }); diff --git a/packages/utils/test/abi_encoder/optimizer_test.ts b/packages/utils/test/abi_encoder/optimizer_test.ts index 18aa6549a..ee0654ec3 100644 --- a/packages/utils/test/abi_encoder/optimizer_test.ts +++ b/packages/utils/test/abi_encoder/optimizer_test.ts @@ -10,7 +10,7 @@ chaiSetup.configure(); const expect = chai.expect; describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { - const encodingRules: AbiEncoder.EncodingRules = { optimize: true }; + const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: true }; it('Duplicate Dynamic Arrays with Static Elements', async () => { // Generate calldata const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithStaticElements); @@ -206,7 +206,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const twoDimArray2 = twoDimArray1; const args = [twoDimArray1, twoDimArray2]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: false }); + const optimizedCalldata = method.encode(args, { shouldOptimize: false }); const expectedOptimizedCalldata = '0x0d28c4f9000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); diff --git a/packages/utils/test/abi_encoder/return_values_test.ts b/packages/utils/test/abi_encoder/return_values_test.ts index a8cdd6ca3..104c7f5db 100644 --- a/packages/utils/test/abi_encoder/return_values_test.ts +++ b/packages/utils/test/abi_encoder/return_values_test.ts @@ -10,7 +10,7 @@ chaiSetup.configure(); const expect = chai.expect; describe('ABI Encoder: Return Value Encoding/Decoding', () => { - const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately. + const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately. it('No Return Value', async () => { // Decode return value const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues); -- cgit v1.2.3 From 0abace337c8309b22092519091d10e3474095878 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 3 Dec 2018 16:58:51 -0800 Subject: Rename functions --- packages/order-utils/package.json | 6 ++---- packages/order-utils/src/asset_data_utils.ts | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index 8a5150a48..400c9b66f 100644 --- a/packages/order-utils/package.json +++ b/packages/order-utils/package.json @@ -13,14 +13,12 @@ "test": "yarn run_mocha", "rebuild_and_test": "run-s build test", "test:circleci": "yarn test:coverage", - "run_mocha": - "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js --bail --exit", + "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", "clean": "shx rm -rf lib generated_docs", "lint": "tslint --format stylish --project .", - "docs:json": - "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" + "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES" }, "config": { "postpublish": { diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index 1b416abd1..2adaf31b8 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -5,8 +5,8 @@ import * as _ from 'lodash'; import { constants } from './constants'; -const encodingRules: AbiEncoder.EncodingRules = { optimize: true }; -const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; +const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: true }; +const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true }; export const assetDataUtils = { /** @@ -27,13 +27,13 @@ export const assetDataUtils = { * @return An object containing the decoded tokenAddress & assetProxyId */ decodeERC20AssetData(assetData: string): ERC20AssetData { - assetDataUtils.validateERC20AssetDataThrow(assetData); + assetDataUtils.assertIsERC20AssetData(assetData); const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI as MethodAbi); const decodedAssetData = abiEncoder.decode(assetData, decodingRules); return { assetProxyId, - // TODO(abandeali1): fix decode return types + // TODO(abandeali1): fix return types for `AbiEncoder.Method.decode` so that we can remove type assertion tokenAddress: (decodedAssetData as any).tokenContract, }; }, @@ -56,13 +56,13 @@ export const assetDataUtils = { * @return An object containing the decoded tokenAddress, tokenId & assetProxyId */ decodeERC721AssetData(assetData: string): ERC721AssetData { - assetDataUtils.validateERC721AssetDataOrThrow(assetData); + assetDataUtils.assertIsERC721AssetData(assetData); const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI as MethodAbi); const decodedAssetData = abiEncoder.decode(assetData, decodingRules); return { assetProxyId, - // TODO(abandeali1): fix decode return types + // TODO(abandeali1): fix return types for `AbiEncoder.Method.decode` so that we can remove type assertion tokenAddress: (decodedAssetData as any).tokenContract, tokenId: (decodedAssetData as any).tokenId, }; @@ -94,11 +94,11 @@ export const assetDataUtils = { * @return An object containing the decoded amounts and nestedAssetData */ decodeMultiAssetData(assetData: string): MultiAssetData { - assetDataUtils.validateMultiAssetDataOrThrow(assetData); + assetDataUtils.assertIsMultiAssetData(assetData); const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI as MethodAbi); const decodedAssetData = abiEncoder.decode(assetData, decodingRules); - // TODO(abandeali1): fix decode return types + // TODO(abandeali1): fix return types for `AbiEncoder.Method.decode` so that we can remove type assertion const amounts = (decodedAssetData as any).amounts; const nestedAssetData = (decodedAssetData as any).nestedAssetData; if (amounts.length !== nestedAssetData.length) { @@ -141,7 +141,7 @@ export const assetDataUtils = { * Throws if the length or assetProxyId are invalid for the ERC20Proxy. * @param assetData Hex encoded assetData string */ - validateERC20AssetDataThrow(assetData: string): void { + assertIsERC20AssetData(assetData: string): void { if (assetData.length < constants.ERC20_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) { throw new Error( `Could not decode ERC20 Proxy Data. Expected length of encoded data to be at least ${ @@ -162,7 +162,7 @@ export const assetDataUtils = { * Throws if the length or assetProxyId are invalid for the ERC721Proxy. * @param assetData Hex encoded assetData string */ - validateERC721AssetDataOrThrow(assetData: string): void { + assertIsERC721AssetData(assetData: string): void { if (assetData.length < constants.ERC721_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) { throw new Error( `Could not decode ERC721 assetData. Expected length of encoded data to be at least ${ @@ -183,7 +183,7 @@ export const assetDataUtils = { * Throws if the length or assetProxyId are invalid for the MultiAssetProxy. * @param assetData Hex encoded assetData string */ - validateMultiAssetDataOrThrow(assetData: string): void { + assertIsMultiAssetData(assetData: string): void { if (assetData.length < constants.MULTI_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) { throw new Error( `Could not decode MultiAsset assetData. Expected length of encoded data to be at least ${ @@ -208,13 +208,13 @@ export const assetDataUtils = { const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); switch (assetProxyId) { case AssetProxyId.ERC20: - assetDataUtils.validateERC20AssetDataThrow(assetData); + assetDataUtils.assertIsERC20AssetData(assetData); break; case AssetProxyId.ERC721: - assetDataUtils.validateERC721AssetDataOrThrow(assetData); + assetDataUtils.assertIsERC721AssetData(assetData); break; case AssetProxyId.MultiAsset: - assetDataUtils.validateMultiAssetDataOrThrow(assetData); + assetDataUtils.assertIsMultiAssetData(assetData); break; default: throw new Error(`Unrecognized asset proxy id: ${assetProxyId}`); -- cgit v1.2.3 From eeb07d76fc81b786d83c4ab848cc538e5896e240 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Tue, 4 Dec 2018 15:44:46 -0800 Subject: Fix transferFrom to work with MAP --- .../order-utils/src/exchange_transfer_simulator.ts | 64 ++++++++++++++++------ packages/order-utils/test/asset_data_utils_test.ts | 4 +- .../dependent_order_hashes_tracker.ts | 1 - 3 files changed, 48 insertions(+), 21 deletions(-) diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts index 7a38b35df..06621fd9e 100644 --- a/packages/order-utils/src/exchange_transfer_simulator.ts +++ b/packages/order-utils/src/exchange_transfer_simulator.ts @@ -1,7 +1,8 @@ -import { ExchangeContractErrs } from '@0x/types'; +import { AssetProxyId, ExchangeContractErrs } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store'; +import { assetDataUtils } from './asset_data_utils'; import { constants } from './constants'; import { TradeSide, TransferType } from './types'; @@ -74,24 +75,51 @@ export class ExchangeTransferSimulator { tradeSide: TradeSide, transferType: TransferType, ): Promise { - // HACK: When simulating an open order (e.g taker is NULL_ADDRESS), we don't want to adjust balances/ - // allowances for the taker. We do however, want to increase the balance of the maker since the maker - // might be relying on those funds to fill subsequent orders or pay the order's fees. - if (from === constants.NULL_ADDRESS && tradeSide === TradeSide.Taker) { - await this._increaseBalanceAsync(assetData, to, amountInBaseUnits); - return; + const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); + switch (assetProxyId) { + case AssetProxyId.ERC20: + case AssetProxyId.ERC721: + // HACK: When simulating an open order (e.g taker is NULL_ADDRESS), we don't want to adjust balances/ + // allowances for the taker. We do however, want to increase the balance of the maker since the maker + // might be relying on those funds to fill subsequent orders or pay the order's fees. + if (from === constants.NULL_ADDRESS && tradeSide === TradeSide.Taker) { + await this._increaseBalanceAsync(assetData, to, amountInBaseUnits); + return; + } + const balance = await this._store.getBalanceAsync(assetData, from); + const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, from); + if (proxyAllowance.lessThan(amountInBaseUnits)) { + ExchangeTransferSimulator._throwValidationError( + FailureReason.ProxyAllowance, + tradeSide, + transferType, + ); + } + if (balance.lessThan(amountInBaseUnits)) { + ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType); + } + await this._decreaseProxyAllowanceAsync(assetData, from, amountInBaseUnits); + await this._decreaseBalanceAsync(assetData, from, amountInBaseUnits); + await this._increaseBalanceAsync(assetData, to, amountInBaseUnits); + break; + case AssetProxyId.MultiAsset: + const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData); + for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) { + const amountsElement = decodedAssetData.amounts[index]; + const totalAmount = amountInBaseUnits.times(amountsElement); + await this.transferFromAsync( + nestedAssetDataElement, + from, + to, + totalAmount, + tradeSide, + transferType, + ); + } + break; + default: + break; } - const balance = await this._store.getBalanceAsync(assetData, from); - const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, from); - if (proxyAllowance.lessThan(amountInBaseUnits)) { - ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType); - } - if (balance.lessThan(amountInBaseUnits)) { - ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType); - } - await this._decreaseProxyAllowanceAsync(assetData, from, amountInBaseUnits); - await this._decreaseBalanceAsync(assetData, from, amountInBaseUnits); - await this._increaseBalanceAsync(assetData, to, amountInBaseUnits); } private async _decreaseProxyAllowanceAsync( assetData: string, diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts index a070aaf16..6835b6e30 100644 --- a/packages/order-utils/test/asset_data_utils_test.ts +++ b/packages/order-utils/test/asset_data_utils_test.ts @@ -50,14 +50,14 @@ describe('assetDataUtils', () => { expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.ERC721); expect(decodedAssetData.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); }); - it('should encode ERC20 and ERC721', () => { + it('should encode ERC20 and ERC721 multiAssetData', () => { const assetData = assetDataUtils.encodeMultiAssetData( KNOWN_MULTI_ASSET_ENCODING.amounts, KNOWN_MULTI_ASSET_ENCODING.nestedAssetData, ); expect(assetData).to.equal(KNOWN_MULTI_ASSET_ENCODING.assetData); }); - it('should decode ERC20 and ERC21', () => { + it('should decode ERC20 and ERC721 multiAssetData', () => { const decodedAssetData = assetDataUtils.decodeMultiAssetData(KNOWN_MULTI_ASSET_ENCODING.assetData); expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts); diff --git a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts index eed1d055b..12723f73e 100644 --- a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts +++ b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts @@ -75,7 +75,6 @@ export class DependentOrderHashesTracker { } this._removeFromMakerDependentOrderhashes(signedOrder); } - private _getDependentOrderHashesByERC20AssetData(makerAddress: string, erc20AssetData: string): string[] { const tokenAddress = assetDataUtils.decodeERC20AssetData(erc20AssetData).tokenAddress; let dependentOrderHashes: string[] = []; -- cgit v1.2.3 From b797a45d4a67ffd3fa21678e9bcce93e774bfd66 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sat, 22 Dec 2018 18:11:40 -0800 Subject: Do not attempt to parse tokenAddress if decoding data from multiAssetProxy --- .../pipeline/src/parsers/events/exchange_events.ts | 40 ++++++++++++++-------- packages/pipeline/src/parsers/sra_orders/index.ts | 22 +++++++----- packages/pipeline/src/types.ts | 2 +- .../src/utils/transformers/asset_proxy_id_types.ts | 16 +++++++++ packages/pipeline/src/utils/transformers/index.ts | 1 + 5 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts diff --git a/packages/pipeline/src/parsers/events/exchange_events.ts b/packages/pipeline/src/parsers/events/exchange_events.ts index e18106c75..16b05260b 100644 --- a/packages/pipeline/src/parsers/events/exchange_events.ts +++ b/packages/pipeline/src/parsers/events/exchange_events.ts @@ -1,11 +1,11 @@ import { ExchangeCancelEventArgs, ExchangeCancelUpToEventArgs, ExchangeFillEventArgs } from '@0x/contract-wrappers'; import { assetDataUtils } from '@0x/order-utils'; -import { AssetProxyId, ERC721AssetData } from '@0x/types'; +import { AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0x/types'; import { LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; import { ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeFillEvent } from '../../entities'; -import { bigNumbertoStringOrNull } from '../../utils'; +import { bigNumbertoStringOrNull, convertAssetProxyIdToType } from '../../utils'; /** * Parses raw event logs for a fill event and returns an array of @@ -40,9 +40,7 @@ export const parseExchangeCancelUpToEvents: ( */ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs): ExchangeFillEvent { const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const exchangeFillEvent = new ExchangeFillEvent(); exchangeFillEvent.contractAddress = eventLog.address as string; exchangeFillEvent.blockNumber = eventLog.blockNumber as number; @@ -59,16 +57,24 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs, ): ExchangeCancelEvent { const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const exchangeCancelEvent = new ExchangeCancelEvent(); exchangeCancelEvent.contractAddress = eventLog.address as string; exchangeCancelEvent.blockNumber = eventLog.blockNumber as number; @@ -98,15 +102,23 @@ export function _convertToExchangeCancelEvent( exchangeCancelEvent.senderAddress = eventLog.args.senderAddress; exchangeCancelEvent.orderHash = eventLog.args.orderHash; exchangeCancelEvent.rawMakerAssetData = eventLog.args.makerAssetData; - exchangeCancelEvent.makerAssetType = makerAssetType; + exchangeCancelEvent.makerAssetType = convertAssetProxyIdToType(makerAssetData.assetProxyId as AssetProxyId); exchangeCancelEvent.makerAssetProxyId = makerAssetData.assetProxyId; - exchangeCancelEvent.makerTokenAddress = makerAssetData.tokenAddress; + // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store a null byte array when decoding assetData from the MultiAssetProxy + exchangeCancelEvent.makerTokenAddress = + makerAssetData.assetProxyId === AssetProxyId.MultiAsset + ? '0x' + : (makerAssetData as ERC20AssetData | ERC721AssetData).tokenAddress; // tslint:disable-next-line:no-unnecessary-type-assertion exchangeCancelEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); exchangeCancelEvent.rawTakerAssetData = eventLog.args.takerAssetData; - exchangeCancelEvent.takerAssetType = takerAssetType; + exchangeCancelEvent.takerAssetType = convertAssetProxyIdToType(takerAssetData.assetProxyId as AssetProxyId); exchangeCancelEvent.takerAssetProxyId = takerAssetData.assetProxyId; - exchangeCancelEvent.takerTokenAddress = takerAssetData.tokenAddress; + // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store a null byte array when decoding assetData from the MultiAssetProxy + exchangeCancelEvent.takerTokenAddress = + makerAssetData.assetProxyId === AssetProxyId.MultiAsset + ? '0x' + : (takerAssetData as ERC20AssetData | ERC721AssetData).tokenAddress; // tslint:disable-next-line:no-unnecessary-type-assertion exchangeCancelEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); return exchangeCancelEvent; diff --git a/packages/pipeline/src/parsers/sra_orders/index.ts b/packages/pipeline/src/parsers/sra_orders/index.ts index ef8901e40..2d3e5381d 100644 --- a/packages/pipeline/src/parsers/sra_orders/index.ts +++ b/packages/pipeline/src/parsers/sra_orders/index.ts @@ -1,10 +1,10 @@ import { APIOrder, OrdersResponse } from '@0x/connect'; import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { AssetProxyId, ERC721AssetData } from '@0x/types'; +import { AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0x/types'; import * as R from 'ramda'; import { SraOrder } from '../../entities'; -import { bigNumbertoStringOrNull } from '../../utils'; +import { bigNumbertoStringOrNull, convertAssetProxyIdToType } from '../../utils'; /** * Parses a raw order response from an SRA endpoint and returns an array of @@ -22,9 +22,7 @@ export function parseSraOrders(rawOrdersResponse: OrdersResponse): SraOrder[] { export function _convertToEntity(apiOrder: APIOrder): SraOrder { // TODO(albrow): refactor out common asset data decoding code. const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.makerAssetData); - const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.takerAssetData); - const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721'; const sraOrder = new SraOrder(); sraOrder.exchangeAddress = apiOrder.order.exchangeAddress; @@ -43,16 +41,24 @@ export function _convertToEntity(apiOrder: APIOrder): SraOrder { sraOrder.signature = apiOrder.order.signature; sraOrder.rawMakerAssetData = apiOrder.order.makerAssetData; - sraOrder.makerAssetType = makerAssetType; + sraOrder.makerAssetType = convertAssetProxyIdToType(makerAssetData.assetProxyId as AssetProxyId); sraOrder.makerAssetProxyId = makerAssetData.assetProxyId; - sraOrder.makerTokenAddress = makerAssetData.tokenAddress; + // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store a null byte array when decoding assetData from the MultiAssetProxy + sraOrder.makerTokenAddress = + makerAssetData.assetProxyId === AssetProxyId.MultiAsset + ? '0x' + : (makerAssetData as ERC20AssetData | ERC721AssetData).tokenAddress; // tslint has a false positive here. Type assertion is required. // tslint:disable-next-line:no-unnecessary-type-assertion sraOrder.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId); sraOrder.rawTakerAssetData = apiOrder.order.takerAssetData; - sraOrder.takerAssetType = takerAssetType; + sraOrder.takerAssetType = convertAssetProxyIdToType(takerAssetData.assetProxyId as AssetProxyId); sraOrder.takerAssetProxyId = takerAssetData.assetProxyId; - sraOrder.takerTokenAddress = takerAssetData.tokenAddress; + // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store a null byte array when decoding assetData from the MultiAssetProxy + sraOrder.takerTokenAddress = + makerAssetData.assetProxyId === AssetProxyId.MultiAsset + ? '0x' + : (takerAssetData as ERC20AssetData | ERC721AssetData).tokenAddress; // tslint:disable-next-line:no-unnecessary-type-assertion sraOrder.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId); diff --git a/packages/pipeline/src/types.ts b/packages/pipeline/src/types.ts index e02b42a40..cd649426b 100644 --- a/packages/pipeline/src/types.ts +++ b/packages/pipeline/src/types.ts @@ -1,2 +1,2 @@ -export type AssetType = 'erc20' | 'erc721'; +export type AssetType = 'erc20' | 'erc721' | 'multiAsset'; export type OrderType = 'bid' | 'ask'; diff --git a/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts b/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts new file mode 100644 index 000000000..bc83ad283 --- /dev/null +++ b/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts @@ -0,0 +1,16 @@ +import { AssetProxyId } from '@0x/types'; + +import { AssetType } from '../../types'; + +export function convertAssetProxyIdToType(assetProxyId: AssetProxyId): AssetType { + switch (assetProxyId) { + case AssetProxyId.ERC20: + return 'erc20'; + case AssetProxyId.ERC721: + return 'erc721'; + case AssetProxyId.MultiAsset: + return 'multiAsset'; + default: + throw new Error(`${assetProxyId} not a supported assetProxyId`); + } +} diff --git a/packages/pipeline/src/utils/transformers/index.ts b/packages/pipeline/src/utils/transformers/index.ts index 232c1c5de..31a4c9223 100644 --- a/packages/pipeline/src/utils/transformers/index.ts +++ b/packages/pipeline/src/utils/transformers/index.ts @@ -1,2 +1,3 @@ export * from './big_number'; export * from './number_to_bigint'; +export * from './asset_proxy_id_types'; -- cgit v1.2.3 From de4916ccb811e6324692e1b7fccec0940ef0c44f Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 2 Jan 2019 14:05:51 -0800 Subject: Add individual balances and allowances to OrderRelevantState --- packages/order-utils/src/asset_data_utils.ts | 4 +- packages/order-utils/src/index.ts | 2 +- packages/order-utils/src/order_state_utils.ts | 71 +++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index 2adaf31b8..18c1f93b6 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -1,4 +1,4 @@ -import { AssetData, AssetProxyId, ERC20AssetData, ERC721AssetData, MultiAssetData } from '@0x/types'; +import { AssetProxyId, ERC20AssetData, ERC721AssetData, MultiAssetData, SingleAssetData } from '@0x/types'; import { AbiEncoder, BigNumber } from '@0x/utils'; import { MethodAbi } from 'ethereum-types'; import * as _ from 'lodash'; @@ -225,7 +225,7 @@ export const assetDataUtils = { * @param assetData Hex encoded assetData string to decode * @return Either a ERC20 or ERC721 assetData object */ - decodeAssetDataOrThrow(assetData: string): AssetData { + decodeAssetDataOrThrow(assetData: string): SingleAssetData | MultiAssetData { const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); switch (assetProxyId) { case AssetProxyId.ERC20: diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index aae439e68..e098f23c3 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -34,7 +34,7 @@ export { OrderRelevantState, OrderState, ECSignature, - AssetData, + SingleAssetData, ERC20AssetData, ERC721AssetData, MultiAssetData, diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index fe0d6c773..9e3e228ba 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -1,15 +1,21 @@ import { + AssetProxyId, ExchangeContractErrs, + MultiAssetData, + ObjectMap, OrderRelevantState, OrderState, OrderStateInvalid, OrderStateValid, SignedOrder, + SingleAssetData, } from '@0x/types'; import { BigNumber } from '@0x/utils'; +import * as _ from 'lodash'; import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; +import { assetDataUtils } from './asset_data_utils'; import { orderHashUtils } from './order_hash'; import { OrderValidationUtils } from './order_validation_utils'; import { RemainingFillableCalculator } from './remaining_fillable_calculator'; @@ -18,7 +24,9 @@ import { utils } from './utils'; interface SidedOrderRelevantState { isMakerSide: boolean; traderBalance: BigNumber; + traderIndividualBalances: ObjectMap; traderProxyAllowance: BigNumber; + traderIndividualProxyAllowances: ObjectMap; traderFeeBalance: BigNumber; traderFeeProxyAllowance: BigNumber; filledTakerAssetAmount: BigNumber; @@ -121,7 +129,9 @@ export class OrderStateUtils { const sidedOrderRelevantState = { isMakerSide: true, traderBalance: orderRelevantState.makerBalance, + traderIndividualBalances: orderRelevantState.makerIndividualBalances, traderProxyAllowance: orderRelevantState.makerProxyAllowance, + traderIndividualProxyAllowances: orderRelevantState.makerIndividualProxyAllowances, traderFeeBalance: orderRelevantState.makerFeeBalance, traderFeeProxyAllowance: orderRelevantState.makerFeeProxyAllowance, filledTakerAssetAmount: orderRelevantState.filledTakerAssetAmount, @@ -165,7 +175,9 @@ export class OrderStateUtils { const orderRelevantState = { makerBalance: sidedOrderRelevantState.traderBalance, + makerIndividualBalances: sidedOrderRelevantState.traderIndividualBalances, makerProxyAllowance: sidedOrderRelevantState.traderProxyAllowance, + makerIndividualProxyAllowances: sidedOrderRelevantState.traderIndividualProxyAllowances, makerFeeBalance: sidedOrderRelevantState.traderFeeBalance, makerFeeProxyAllowance: sidedOrderRelevantState.traderFeeProxyAllowance, filledTakerAssetAmount: sidedOrderRelevantState.filledTakerAssetAmount, @@ -236,10 +248,12 @@ export class OrderStateUtils { const isAssetZRX = assetData === zrxAssetData; const traderBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress); + const traderIndividualBalances = await this._getAssetBalancesAsync(assetData, traderAddress); const traderProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( assetData, traderAddress, ); + const traderIndividualProxyAllowances = await this._getAssetProxyAllowancesAsync(assetData, traderAddress); const traderFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync( zrxAssetData, traderAddress, @@ -278,7 +292,9 @@ export class OrderStateUtils { const sidedOrderRelevantState = { isMakerSide, traderBalance, + traderIndividualBalances, traderProxyAllowance, + traderIndividualProxyAllowances, traderFeeBalance, traderFeeProxyAllowance, filledTakerAssetAmount, @@ -287,4 +303,59 @@ export class OrderStateUtils { }; return sidedOrderRelevantState; } + private async _getAssetBalancesAsync( + assetData: string, + traderAddress: string, + initialBalances: ObjectMap = {}, + ): Promise> { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + let balances: ObjectMap = { ...initialBalances }; + switch (decodedAssetData.assetProxyId) { + case AssetProxyId.ERC20: + case AssetProxyId.ERC721: + const balance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress); + const tokenAddress = (decodedAssetData as SingleAssetData).tokenAddress; + balances[tokenAddress] = _.isUndefined(initialBalances[tokenAddress]) + ? balance + : balances[tokenAddress].add(balance); + break; + case AssetProxyId.MultiAsset: + for (const assetDataElement of (decodedAssetData as MultiAssetData).nestedAssetData) { + balances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, balances); + } + break; + default: + throw new Error(`Proxy with id ${decodedAssetData.assetProxyId} not supported`); + } + return balances; + } + private async _getAssetProxyAllowancesAsync( + assetData: string, + traderAddress: string, + initialAllowances: ObjectMap = {}, + ): Promise> { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); + let allowances: ObjectMap = { ...initialAllowances }; + switch (decodedAssetData.assetProxyId) { + case AssetProxyId.ERC20: + case AssetProxyId.ERC721: + const allowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( + assetData, + traderAddress, + ); + const tokenAddress = (decodedAssetData as SingleAssetData).tokenAddress; + allowances[tokenAddress] = _.isUndefined(initialAllowances[tokenAddress]) + ? allowance + : allowances[tokenAddress].add(allowance); + break; + case AssetProxyId.MultiAsset: + for (const assetDataElement of (decodedAssetData as MultiAssetData).nestedAssetData) { + allowances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, allowances); + } + break; + default: + throw new Error(`Proxy with id ${decodedAssetData.assetProxyId} not supported`); + } + return allowances; + } } -- cgit v1.2.3 From df4f3d9eacd370b56913f6b85ab7aa705cbed8fe Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 2 Jan 2019 14:07:17 -0800 Subject: feat: Add fields to OrderRelevantState and change AssetData to SingleAssetData --- packages/0x.js/src/index.ts | 2 +- packages/types/src/index.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index 0ccb0f768..375843741 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -79,7 +79,7 @@ export { OrderStateInvalid, OrderState, AssetProxyId, - AssetData, + SingleAssetData, ERC20AssetData, ERC721AssetData, MultiAssetData, diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 846c030ee..2e8ce6629 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -110,7 +110,9 @@ export type DoneCallback = (err?: Error) => void; export interface OrderRelevantState { makerBalance: BigNumber; + makerIndividualBalances: ObjectMap; makerProxyAllowance: BigNumber; + makerIndividualProxyAllowances: ObjectMap; makerFeeBalance: BigNumber; makerFeeProxyAllowance: BigNumber; filledTakerAssetAmount: BigNumber; @@ -175,7 +177,7 @@ export interface MultiAssetData { nestedAssetData: string[]; } -export type AssetData = ERC20AssetData | ERC721AssetData | MultiAssetData; +export type SingleAssetData = ERC20AssetData | ERC721AssetData; // TODO: DRY. These should be extracted from contract code. export enum RevertReason { -- cgit v1.2.3 From 0fba0b1a1bbe1192802ebcf4b88437b94d6a1c18 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 2 Jan 2019 14:08:21 -0800 Subject: feat: Add balance and allowance checks for MultiAssetProxy --- .../asset_balance_and_proxy_allowance_fetcher.ts | 132 ++++++++++++++------- 1 file changed, 87 insertions(+), 45 deletions(-) diff --git a/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts b/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts index d10cffe57..376004f52 100644 --- a/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts +++ b/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts @@ -1,8 +1,9 @@ // tslint:disable:no-unnecessary-type-assertion import { AbstractBalanceAndProxyAllowanceFetcher, assetDataUtils } from '@0x/order-utils'; -import { AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0x/types'; +import { AssetProxyId, ERC20AssetData, ERC721AssetData, MultiAssetData } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { BlockParamLiteral } from 'ethereum-types'; +import * as _ from 'lodash'; import { ERC20TokenWrapper } from '../contract_wrappers/erc20_token_wrapper'; import { ERC721TokenWrapper } from '../contract_wrappers/erc721_token_wrapper'; @@ -18,60 +19,101 @@ export class AssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndP } public async getBalanceAsync(assetData: string, userAddress: string): Promise { const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); - if (decodedAssetData.assetProxyId === AssetProxyId.ERC20) { - const decodedERC20AssetData = decodedAssetData as ERC20AssetData; - const balance = await this._erc20Token.getBalanceAsync(decodedERC20AssetData.tokenAddress, userAddress, { - defaultBlock: this._stateLayer, - }); - return balance; - } else { - const decodedERC721AssetData = decodedAssetData as ERC721AssetData; - const tokenOwner = await this._erc721Token.getOwnerOfAsync( - decodedERC721AssetData.tokenAddress, - decodedERC721AssetData.tokenId, - { + let balance: BigNumber | undefined; + switch (decodedAssetData.assetProxyId) { + case AssetProxyId.ERC20: + const decodedERC20AssetData = decodedAssetData as ERC20AssetData; + balance = await this._erc20Token.getBalanceAsync(decodedERC20AssetData.tokenAddress, userAddress, { defaultBlock: this._stateLayer, - }, - ); - const balance = tokenOwner === userAddress ? new BigNumber(1) : new BigNumber(0); - return balance; + }); + break; + case AssetProxyId.ERC721: + const decodedERC721AssetData = decodedAssetData as ERC721AssetData; + const tokenOwner = await this._erc721Token.getOwnerOfAsync( + decodedERC721AssetData.tokenAddress, + decodedERC721AssetData.tokenId, + { + defaultBlock: this._stateLayer, + }, + ); + balance = tokenOwner === userAddress ? new BigNumber(1) : new BigNumber(0); + break; + case AssetProxyId.MultiAsset: + // The `balance` for MultiAssetData is the total units of the entire `assetData` that are held by the `userAddress`. + for (const [ + index, + nestedAssetDataElement, + ] of (decodedAssetData as MultiAssetData).nestedAssetData.entries()) { + const nestedAmountElement = (decodedAssetData as MultiAssetData).amounts[index]; + const nestedAssetBalance = (await this.getBalanceAsync( + nestedAssetDataElement, + userAddress, + )).dividedToIntegerBy(nestedAmountElement); + if (_.isUndefined(balance) || nestedAssetBalance.lessThan(balance)) { + balance = nestedAssetBalance; + } + } + break; + default: + throw new Error(`Proxy with id ${decodedAssetData.assetProxyId} not supported`); } + return balance as BigNumber; } public async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise { const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); - if (decodedAssetData.assetProxyId === AssetProxyId.ERC20) { - const decodedERC20AssetData = decodedAssetData as ERC20AssetData; - const proxyAllowance = await this._erc20Token.getProxyAllowanceAsync( - decodedERC20AssetData.tokenAddress, - userAddress, - { - defaultBlock: this._stateLayer, - }, - ); - return proxyAllowance; - } else { - const decodedERC721AssetData = decodedAssetData as ERC721AssetData; - - const isApprovedForAll = await this._erc721Token.isProxyApprovedForAllAsync( - decodedERC721AssetData.tokenAddress, - userAddress, - { - defaultBlock: this._stateLayer, - }, - ); - if (isApprovedForAll) { - return new BigNumber(this._erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); - } else { - const isApproved = await this._erc721Token.isProxyApprovedAsync( + let proxyAllowance: BigNumber | undefined; + switch (decodedAssetData.assetProxyId) { + case AssetProxyId.ERC20: + const decodedERC20AssetData = decodedAssetData as ERC20AssetData; + proxyAllowance = await this._erc20Token.getProxyAllowanceAsync( + decodedERC20AssetData.tokenAddress, + userAddress, + { + defaultBlock: this._stateLayer, + }, + ); + break; + case AssetProxyId.ERC721: + const decodedERC721AssetData = decodedAssetData as ERC721AssetData; + const isApprovedForAll = await this._erc721Token.isProxyApprovedForAllAsync( decodedERC721AssetData.tokenAddress, - decodedERC721AssetData.tokenId, + userAddress, { defaultBlock: this._stateLayer, }, ); - const proxyAllowance = isApproved ? new BigNumber(1) : new BigNumber(0); - return proxyAllowance; - } + if (isApprovedForAll) { + return new BigNumber(this._erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + } else { + const isApproved = await this._erc721Token.isProxyApprovedAsync( + decodedERC721AssetData.tokenAddress, + decodedERC721AssetData.tokenId, + { + defaultBlock: this._stateLayer, + }, + ); + proxyAllowance = isApproved ? new BigNumber(1) : new BigNumber(0); + } + break; + case AssetProxyId.MultiAsset: + // The `proxyAllowance` for MultiAssetData is the total units of the entire `assetData` that the proxies have been approved to spend by the `userAddress`. + for (const [ + index, + nestedAssetDataElement, + ] of (decodedAssetData as MultiAssetData).nestedAssetData.entries()) { + const nestedAmountElement = (decodedAssetData as MultiAssetData).amounts[index]; + const nestedAssetAllowance = (await this.getProxyAllowanceAsync( + nestedAssetDataElement, + userAddress, + )).dividedToIntegerBy(nestedAmountElement); + if (_.isUndefined(proxyAllowance) || nestedAssetAllowance.lessThan(proxyAllowance)) { + proxyAllowance = nestedAssetAllowance; + } + } + break; + default: + throw new Error(`Proxy with id ${decodedAssetData.assetProxyId} not supported`); } + return proxyAllowance as BigNumber; } } -- cgit v1.2.3 From 1dda10b4f34eb19b731e58c26f13ecc08e367bbe Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 2 Jan 2019 16:52:15 -0800 Subject: Add recursive MultiAsset decoding, store first address in pipeline --- packages/0x.js/src/index.ts | 1 + packages/order-utils/src/asset_data_utils.ts | 29 +++++++++++++++++++++- .../order-utils/src/exchange_transfer_simulator.ts | 2 +- packages/order-utils/src/index.ts | 1 + packages/pipeline/package.json | 2 +- .../pipeline/src/parsers/events/exchange_events.ts | 26 +++++++++++-------- packages/pipeline/src/parsers/sra_orders/index.ts | 14 ++++++----- packages/types/src/index.ts | 8 +++++- yarn.lock | 4 +-- 9 files changed, 64 insertions(+), 23 deletions(-) diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index 375843741..01742f1b9 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -83,6 +83,7 @@ export { ERC20AssetData, ERC721AssetData, MultiAssetData, + MultiAssetDataWithRecursiveDecoding, SignatureType, OrderRelevantState, Stats, diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index 18c1f93b6..3741a683e 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -1,4 +1,11 @@ -import { AssetProxyId, ERC20AssetData, ERC721AssetData, MultiAssetData, SingleAssetData } from '@0x/types'; +import { + AssetProxyId, + ERC20AssetData, + ERC721AssetData, + MultiAssetData, + MultiAssetDataWithRecursiveDecoding, + SingleAssetData, +} from '@0x/types'; import { AbiEncoder, BigNumber } from '@0x/utils'; import { MethodAbi } from 'ethereum-types'; import * as _ from 'lodash'; @@ -114,6 +121,26 @@ export const assetDataUtils = { nestedAssetData, }; }, + /** + * Decodes a MultiAsset assetData hex string into it's corresponding amounts and decoded nestedAssetData elements (all nested elements are flattened) + * @param assetData Hex encoded assetData string to decode + * @return An object containing the decoded amounts and nestedAssetData + */ + decodeMultiAssetDataRecursively(assetData: string): MultiAssetDataWithRecursiveDecoding { + const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData); + const decodedNestedAssetData = _.map(decodedAssetData.nestedAssetData as string[], nestedAssetDataElement => { + const decodedNestedAssetDataElement = assetDataUtils.decodeAssetDataOrThrow(nestedAssetDataElement); + return decodedNestedAssetDataElement.assetProxyId === AssetProxyId.MultiAsset + ? assetDataUtils.decodeMultiAssetDataRecursively(nestedAssetDataElement).nestedAssetData + : (decodedNestedAssetDataElement as SingleAssetData); + }); + const flattenedDecodedNestedAssetData = _.flattenDeep(decodedNestedAssetData); + return { + assetProxyId: decodedAssetData.assetProxyId, + amounts: decodedAssetData.amounts, + nestedAssetData: flattenedDecodedNestedAssetData as SingleAssetData[], + }; + }, /** * Decode and return the assetProxyId from the assetData * @param assetData Hex encoded assetData string to decode diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts index 06621fd9e..0a948fd1f 100644 --- a/packages/order-utils/src/exchange_transfer_simulator.ts +++ b/packages/order-utils/src/exchange_transfer_simulator.ts @@ -108,7 +108,7 @@ export class ExchangeTransferSimulator { const amountsElement = decodedAssetData.amounts[index]; const totalAmount = amountInBaseUnits.times(amountsElement); await this.transferFromAsync( - nestedAssetDataElement, + nestedAssetDataElement as string, from, to, totalAmount, diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index e098f23c3..398188433 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -38,6 +38,7 @@ export { ERC20AssetData, ERC721AssetData, MultiAssetData, + MultiAssetDataWithRecursiveDecoding, AssetProxyId, SignatureType, OrderStateValid, diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json index a40f3d21c..ab73642ec 100644 --- a/packages/pipeline/package.json +++ b/packages/pipeline/package.json @@ -44,7 +44,7 @@ "@0x/contract-artifacts": "^1.0.1", "@0x/contract-wrappers": "^3.0.0", "@0x/dev-utils": "^1.0.21", - "@0x/order-utils": "^2.0.0", + "@0x/order-utils": "^3.0.7", "@0x/subproviders": "^2.1.8", "@0x/types": "^1.4.1", "@0x/utils": "^2.0.8", diff --git a/packages/pipeline/src/parsers/events/exchange_events.ts b/packages/pipeline/src/parsers/events/exchange_events.ts index 16b05260b..50fad7e00 100644 --- a/packages/pipeline/src/parsers/events/exchange_events.ts +++ b/packages/pipeline/src/parsers/events/exchange_events.ts @@ -1,6 +1,6 @@ import { ExchangeCancelEventArgs, ExchangeCancelUpToEventArgs, ExchangeFillEventArgs } from '@0x/contract-wrappers'; import { assetDataUtils } from '@0x/order-utils'; -import { AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0x/types'; +import { AssetProxyId, ERC721AssetData, SingleAssetData } from '@0x/types'; import { LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; @@ -62,8 +62,9 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs Date: Wed, 2 Jan 2019 17:05:32 -0800 Subject: FIx linting errors --- contracts/protocol/test/exchange/core.ts | 1 - contracts/tokens/src/artifacts/index.ts | 1 + packages/pipeline/src/parsers/events/exchange_events.ts | 2 ++ packages/pipeline/src/parsers/sra_orders/index.ts | 2 ++ packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts | 4 ++++ 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/contracts/protocol/test/exchange/core.ts b/contracts/protocol/test/exchange/core.ts index 8367898de..42db8623d 100644 --- a/contracts/protocol/test/exchange/core.ts +++ b/contracts/protocol/test/exchange/core.ts @@ -1,4 +1,3 @@ -import { artifacts as interfacesArtifacts, IAssetDataContract } from '@0x/contracts-interfaces'; import { chaiSetup, constants, diff --git a/contracts/tokens/src/artifacts/index.ts b/contracts/tokens/src/artifacts/index.ts index 63b808748..977d01c3e 100644 --- a/contracts/tokens/src/artifacts/index.ts +++ b/contracts/tokens/src/artifacts/index.ts @@ -22,6 +22,7 @@ import * as UnlimitedAllowanceToken_v1 from '../../generated-artifacts/Unlimited import * as WETH9 from '../../generated-artifacts/WETH9.json'; import * as ZRXToken from '../../generated-artifacts/ZRXToken.json'; +// tslint:disable:no-unnecessary-type-assertion export const artifacts = { DummyERC20Token: DummyERC20Token as ContractArtifact, DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact, diff --git a/packages/pipeline/src/parsers/events/exchange_events.ts b/packages/pipeline/src/parsers/events/exchange_events.ts index 50fad7e00..8d2e7dec7 100644 --- a/packages/pipeline/src/parsers/events/exchange_events.ts +++ b/packages/pipeline/src/parsers/events/exchange_events.ts @@ -7,6 +7,8 @@ import * as R from 'ramda'; import { ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeFillEvent } from '../../entities'; import { bigNumbertoStringOrNull, convertAssetProxyIdToType } from '../../utils'; +// tslint:disable:no-unnecessary-type-assertion + /** * Parses raw event logs for a fill event and returns an array of * ExchangeFillEvent entities. diff --git a/packages/pipeline/src/parsers/sra_orders/index.ts b/packages/pipeline/src/parsers/sra_orders/index.ts index 663b8f509..10546921d 100644 --- a/packages/pipeline/src/parsers/sra_orders/index.ts +++ b/packages/pipeline/src/parsers/sra_orders/index.ts @@ -6,6 +6,8 @@ import * as R from 'ramda'; import { SraOrder } from '../../entities'; import { bigNumbertoStringOrNull, convertAssetProxyIdToType } from '../../utils'; +// tslint:disable:no-unnecessary-type-assertion + /** * Parses a raw order response from an SRA endpoint and returns an array of * SraOrder entities. diff --git a/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts b/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts index bc83ad283..ffeaaca52 100644 --- a/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts +++ b/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts @@ -2,6 +2,10 @@ import { AssetProxyId } from '@0x/types'; import { AssetType } from '../../types'; +/** + * Converts an assetProxyId to its string equivalent + * @param assetProxyId Id of AssetProxy + */ export function convertAssetProxyIdToType(assetProxyId: AssetProxyId): AssetType { switch (assetProxyId) { case AssetProxyId.ERC20: -- cgit v1.2.3 From 70508f52a1dd6bfcc891f145c3a06f1e66c3cda5 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 2 Jan 2019 17:17:40 -0800 Subject: Update CHANGELOGs --- packages/0x.js/CHANGELOG.json | 5 +++-- packages/0x.js/src/index.ts | 2 ++ packages/contract-wrappers/CHANGELOG.json | 6 +++++- packages/order-utils/CHANGELOG.json | 3 ++- packages/types/src/index.ts | 2 ++ 5 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index 0024052b3..29dbf12f5 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -1,9 +1,10 @@ [ { - "version": "2.0.9", + "version": "2.1.0", "changes": [ { - "note": "Export `MultiAssetData` from types", + "note": + "Export `MultiAssetData`, `MultiAssetDataWithRecursiveDecoding`, `ObjectMap`, and `SingleAssetData` from types", "pr": 1363 } ] diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index 01742f1b9..c8978466d 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -71,6 +71,7 @@ export { AbiDecoder } from '@0x/utils'; export { BigNumber } from '@0x/utils'; export { + AssetData, ExchangeContractErrs, Order, SignedOrder, @@ -85,6 +86,7 @@ export { MultiAssetData, MultiAssetDataWithRecursiveDecoding, SignatureType, + ObjectMap, OrderRelevantState, Stats, } from '@0x/types'; diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index d39027797..a8c74fa24 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,9 +1,13 @@ [ { - "version": "4.1.4", + "version": "4.2.0", "changes": [ { "note": "Add support for Trust Wallet signature denial error" + }, + { + "note": "Add balance and allowance queries for MultiAssetProxy", + "pr": 1363 } ] }, diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index a6fb91b83..292f7a57b 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -3,7 +3,8 @@ "version": "3.1.0", "changes": [ { - "note": "Use new ABI encoder and add encoding/decoding logic for MultiAsset assetData", + "note": + "Use new ABI encoder, add encoding/decoding logic for MultiAsset assetData, and add information to return values in orderStateUtils", "pr": 1363 } ] diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 71fcbf9db..472b56dc2 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -185,6 +185,8 @@ export interface MultiAssetDataWithRecursiveDecoding { nestedAssetData: SingleAssetData[]; } +export type AssetData = SingleAssetData | MultiAssetData | MultiAssetDataWithRecursiveDecoding; + // TODO: DRY. These should be extracted from contract code. export enum RevertReason { OrderUnfillable = 'ORDER_UNFILLABLE', -- cgit v1.2.3 From 5720589515d321e2d2db87c8337ee3f6340a0ef8 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Thu, 3 Jan 2019 11:18:33 -0800 Subject: Add tests for recursive MAP decoding --- packages/order-utils/src/asset_data_utils.ts | 30 +++++++++++---- packages/order-utils/src/constants.ts | 1 + packages/order-utils/test/asset_data_utils_test.ts | 43 +++++++++++++++++++++- .../order-utils/test/utils/test_order_factory.ts | 4 +- 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index 3741a683e..b5ce87e3e 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -128,16 +128,32 @@ export const assetDataUtils = { */ decodeMultiAssetDataRecursively(assetData: string): MultiAssetDataWithRecursiveDecoding { const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData); - const decodedNestedAssetData = _.map(decodedAssetData.nestedAssetData as string[], nestedAssetDataElement => { - const decodedNestedAssetDataElement = assetDataUtils.decodeAssetDataOrThrow(nestedAssetDataElement); - return decodedNestedAssetDataElement.assetProxyId === AssetProxyId.MultiAsset - ? assetDataUtils.decodeMultiAssetDataRecursively(nestedAssetDataElement).nestedAssetData - : (decodedNestedAssetDataElement as SingleAssetData); - }); + const amounts: any[] = []; + const decodedNestedAssetData = _.map( + decodedAssetData.nestedAssetData as string[], + (nestedAssetDataElement, index) => { + const decodedNestedAssetDataElement = assetDataUtils.decodeAssetDataOrThrow(nestedAssetDataElement); + if (decodedNestedAssetDataElement.assetProxyId === AssetProxyId.MultiAsset) { + const recursivelyDecodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively( + nestedAssetDataElement, + ); + amounts.push( + _.map(recursivelyDecodedAssetData.amounts as BigNumber[], amountElement => + amountElement.times(decodedAssetData.amounts[index]), + ), + ); + return recursivelyDecodedAssetData.nestedAssetData; + } else { + amounts.push(decodedAssetData.amounts[index]); + return decodedNestedAssetDataElement as SingleAssetData; + } + }, + ); + const flattenedAmounts = _.flattenDeep(amounts); const flattenedDecodedNestedAssetData = _.flattenDeep(decodedNestedAssetData); return { assetProxyId: decodedAssetData.assetProxyId, - amounts: decodedAssetData.amounts, + amounts: flattenedAmounts, nestedAssetData: flattenedDecodedNestedAssetData as SingleAssetData[], }; }, diff --git a/packages/order-utils/src/constants.ts b/packages/order-utils/src/constants.ts index be7f3a885..1248a5576 100644 --- a/packages/order-utils/src/constants.ts +++ b/packages/order-utils/src/constants.ts @@ -3,6 +3,7 @@ import { BigNumber } from '@0x/utils'; export const constants = { NULL_ADDRESS: '0x0000000000000000000000000000000000000000', NULL_BYTES: '0x', + NULL_ERC20_ASSET_DATA: '0xf47261b00000000000000000000000000000000000000000000000000000000000000000', // tslint:disable-next-line:custom-no-magic-numbers UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1), TESTRPC_NETWORK_ID: 50, diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts index 6835b6e30..d3d265b5a 100644 --- a/packages/order-utils/test/asset_data_utils_test.ts +++ b/packages/order-utils/test/asset_data_utils_test.ts @@ -1,6 +1,6 @@ import * as chai from 'chai'; -import { AssetProxyId } from '@0x/types'; +import { AssetProxyId, ERC721AssetData } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { assetDataUtils } from '../src/asset_data_utils'; @@ -63,4 +63,45 @@ describe('assetDataUtils', () => { expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts); expect(decodedAssetData.nestedAssetData).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.nestedAssetData); }); + it('should recursively decode ERC20 and ERC721 multiAssetData', () => { + const decodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively(KNOWN_MULTI_ASSET_ENCODING.assetData); + expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); + expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts); + const decodedErc20AssetData = decodedAssetData.nestedAssetData[0]; + const decodedErc721AssetData = decodedAssetData.nestedAssetData[1] as ERC721AssetData; + expect(decodedErc20AssetData.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); + expect(decodedErc20AssetData.assetProxyId).to.equal(AssetProxyId.ERC20); + expect(decodedErc721AssetData.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); + expect(decodedErc721AssetData.assetProxyId).to.equal(AssetProxyId.ERC721); + expect(decodedErc721AssetData.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); + }); + it('should recursively decode nested assetData within multiAssetData', () => { + const amounts = [new BigNumber(1), new BigNumber(1), new BigNumber(2)]; + const nestedAssetData = [ + KNOWN_ERC20_ENCODING.assetData, + KNOWN_ERC721_ENCODING.assetData, + KNOWN_MULTI_ASSET_ENCODING.assetData, + ]; + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); + const decodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively(assetData); + expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); + const expectedAmounts = [new BigNumber(1), new BigNumber(1), new BigNumber(2), new BigNumber(2)]; + expect(decodedAssetData.amounts).to.deep.equal(expectedAmounts); + const expectedLength = 4; + expect(decodedAssetData.nestedAssetData.length).to.be.equal(expectedLength); + const decodedErc20AssetData1 = decodedAssetData.nestedAssetData[0]; + const decodedErc721AssetData1 = decodedAssetData.nestedAssetData[1] as ERC721AssetData; + const decodedErc20AssetData2 = decodedAssetData.nestedAssetData[2]; + const decodedErc721AssetData2 = decodedAssetData.nestedAssetData[3] as ERC721AssetData; + expect(decodedErc20AssetData1.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); + expect(decodedErc20AssetData1.assetProxyId).to.equal(AssetProxyId.ERC20); + expect(decodedErc721AssetData1.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); + expect(decodedErc721AssetData1.assetProxyId).to.equal(AssetProxyId.ERC721); + expect(decodedErc721AssetData1.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); + expect(decodedErc20AssetData2.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); + expect(decodedErc20AssetData2.assetProxyId).to.equal(AssetProxyId.ERC20); + expect(decodedErc721AssetData2.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); + expect(decodedErc721AssetData2.assetProxyId).to.equal(AssetProxyId.ERC721); + expect(decodedErc721AssetData2.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId); + }); }); diff --git a/packages/order-utils/test/utils/test_order_factory.ts b/packages/order-utils/test/utils/test_order_factory.ts index 145332674..4efe0b38e 100644 --- a/packages/order-utils/test/utils/test_order_factory.ts +++ b/packages/order-utils/test/utils/test_order_factory.ts @@ -7,9 +7,9 @@ import { orderFactory } from '../../src/order_factory'; const BASE_TEST_ORDER: Order = orderFactory.createOrder( constants.NULL_ADDRESS, constants.ZERO_AMOUNT, - constants.NULL_ADDRESS, + constants.NULL_ERC20_ASSET_DATA, constants.ZERO_AMOUNT, - constants.NULL_ADDRESS, + constants.NULL_ERC20_ASSET_DATA, constants.NULL_ADDRESS, ); const BASE_TEST_SIGNED_ORDER: SignedOrder = { -- cgit v1.2.3 From d7f9e3f0a0a82f70d8fdca67abf7f8a4f09e9ec8 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Thu, 3 Jan 2019 11:19:10 -0800 Subject: Do not export AssetData in 0x.js --- packages/0x.js/CHANGELOG.json | 4 ++-- packages/0x.js/src/index.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index 29dbf12f5..bd6301441 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -1,10 +1,10 @@ [ { - "version": "2.1.0", + "version": "3.0.0", "changes": [ { "note": - "Export `MultiAssetData`, `MultiAssetDataWithRecursiveDecoding`, `ObjectMap`, and `SingleAssetData` from types", + "Export `MultiAssetData`, `MultiAssetDataWithRecursiveDecoding`, `ObjectMap`, and `SingleAssetData` from types. No longer export AssetData.", "pr": 1363 } ] diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index c8978466d..d21df0c01 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -71,7 +71,6 @@ export { AbiDecoder } from '@0x/utils'; export { BigNumber } from '@0x/utils'; export { - AssetData, ExchangeContractErrs, Order, SignedOrder, -- cgit v1.2.3 From ccdb81ac400308276bd6996c1a6b86baa01b9ee5 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Thu, 3 Jan 2019 11:49:56 -0800 Subject: Add tests for MAP in order watcher --- packages/order-watcher/test/order_watcher_test.ts | 208 +++++++++++++++++++++ .../test/order_watcher_web_socket_server_test.ts | 2 +- 2 files changed, 209 insertions(+), 1 deletion(-) diff --git a/packages/order-watcher/test/order_watcher_test.ts b/packages/order-watcher/test/order_watcher_test.ts index 271e5dec5..dfa36f2fd 100644 --- a/packages/order-watcher/test/order_watcher_test.ts +++ b/packages/order-watcher/test/order_watcher_test.ts @@ -675,5 +675,213 @@ describe('OrderWatcher', () => { })().catch(done); }); }); + describe('multiAsset', async () => { + const tokenId = new BigNumber(42); + const [makerErc721TokenAddress] = tokenUtils.getDummyERC721TokenAddresses(); + const makerErc721AssetData = assetDataUtils.encodeERC721AssetData(makerErc721TokenAddress, tokenId); + const fillableErc721Amount = new BigNumber(1); + const [makerErc20TokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); + const makerErc20AssetData = assetDataUtils.encodeERC20AssetData(makerErc20TokenAddress); + const fillableErc20Amount = new BigNumber(2); + const multiAssetAmounts = [fillableErc721Amount, fillableErc20Amount]; + const nestedAssetData = [makerErc721AssetData, makerErc20AssetData]; + const makerMultiAssetData = assetDataUtils.encodeMultiAssetData(multiAssetAmounts, nestedAssetData); + it('should emit orderStateInvalid when maker allowance of ERC721 token set to 0 for watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableErc721Amount, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance); + }); + orderWatcher.subscribe(callback); + await contractWrappers.erc721Token.setApprovalAsync( + makerErc721TokenAddress, + constants.NULL_ADDRESS, + tokenId, + ); + })().catch(done); + }); + it('should emit orderStateInvalid when maker allowance for all of ERC721 token set to 0 for watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableErc721Amount, + ); + await contractWrappers.erc721Token.setApprovalAsync( + makerErc721TokenAddress, + constants.NULL_ADDRESS, + tokenId, + ); + let isApproved = true; + await contractWrappers.erc721Token.setProxyApprovalForAllAsync( + makerErc721TokenAddress, + makerAddress, + isApproved, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance); + }); + orderWatcher.subscribe(callback); + isApproved = false; + await contractWrappers.erc721Token.setProxyApprovalForAllAsync( + makerErc721TokenAddress, + makerAddress, + isApproved, + ); + })().catch(done); + }); + it('should emit orderStateInvalid when maker moves ERC721 backing watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableErc721Amount, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance); + }); + orderWatcher.subscribe(callback); + await contractWrappers.erc721Token.transferFromAsync( + makerErc721TokenAddress, + coinbase, + makerAddress, + tokenId, + ); + })().catch(done); + }); + it('should emit orderStateInvalid when maker allowance of ERC20 token set to 0 for watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableErc721Amount, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance); + }); + orderWatcher.subscribe(callback); + await contractWrappers.erc20Token.setProxyAllowanceAsync( + makerErc20TokenAddress, + makerAddress, + new BigNumber(0), + ); + })().catch(done); + }); + it('should not emit an orderState event when irrelevant ERC20 Transfer event received', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableAmount, + ); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((_orderState: OrderState) => { + throw new Error('OrderState callback fired for irrelevant order'); + }); + orderWatcher.subscribe(callback); + const notTheMaker = userAddresses[0]; + const anyRecipient = takerAddress; + const transferAmount = new BigNumber(2); + await contractWrappers.erc20Token.transferAsync( + makerTokenAddress, + notTheMaker, + anyRecipient, + transferAmount, + ); + setTimeout(() => { + done(); + }, TIMEOUT_MS); + })().catch(done); + }); + it('should emit orderStateInvalid when makerAddress moves ERC20 balance backing watched order', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableAmount, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance); + }); + orderWatcher.subscribe(callback); + const anyRecipient = takerAddress; + const makerBalance = await contractWrappers.erc20Token.getBalanceAsync( + makerTokenAddress, + makerAddress, + ); + await contractWrappers.erc20Token.transferAsync( + makerTokenAddress, + makerAddress, + anyRecipient, + makerBalance, + ); + })().catch(done); + }); + // TODO(abandeali1): The following test will fail until the MAP has been deployed and activated. + // it('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => { + // (async () => { + // signedOrder = await fillScenarios.createFillableSignedOrderAsync( + // makerMultiAssetData, + // takerAssetData, + // makerAddress, + // takerAddress, + // fillableAmount, + // ); + // const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + // await orderWatcher.addOrderAsync(signedOrder); + + // const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + // expect(orderState.isValid).to.be.false(); + // const invalidOrderState = orderState as OrderStateInvalid; + // expect(invalidOrderState.orderHash).to.be.equal(orderHash); + // expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero); + // }); + // orderWatcher.subscribe(callback); + + // await contractWrappers.exchange.fillOrderAsync(signedOrder, fillableAmount, takerAddress); + // })().catch(done); + // }); + }); }); }); // tslint:disable:max-file-line-count diff --git a/packages/order-watcher/test/order_watcher_web_socket_server_test.ts b/packages/order-watcher/test/order_watcher_web_socket_server_test.ts index 578e0de61..6894f42fb 100644 --- a/packages/order-watcher/test/order_watcher_web_socket_server_test.ts +++ b/packages/order-watcher/test/order_watcher_web_socket_server_test.ts @@ -26,7 +26,7 @@ interface WsMessage { data: string; } -describe.only('OrderWatcherWebSocketServer', async () => { +describe('OrderWatcherWebSocketServer', async () => { let contractWrappers: ContractWrappers; let wsServer: OrderWatcherWebSocketServer; let wsClient: WebSocket.w3cwebsocket; -- cgit v1.2.3 From a91bc415ed3c9dc94dda2661c0d7ac13bcc58e08 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Thu, 3 Jan 2019 14:29:56 -0800 Subject: Fix linting errors --- packages/order-utils/src/asset_data_utils.ts | 1 + packages/order-utils/src/index.ts | 1 + packages/order-utils/test/asset_data_utils_test.ts | 1 + packages/order-watcher/src/index.ts | 1 + 4 files changed, 4 insertions(+) diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index b5ce87e3e..c2929d426 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -154,6 +154,7 @@ export const assetDataUtils = { return { assetProxyId: decodedAssetData.assetProxyId, amounts: flattenedAmounts, + // tslint:disable-next-line:no-unnecessary-type-assertion nestedAssetData: flattenedDecodedNestedAssetData as SingleAssetData[], }; }, diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 398188433..2150a02e4 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -41,6 +41,7 @@ export { MultiAssetDataWithRecursiveDecoding, AssetProxyId, SignatureType, + ObjectMap, OrderStateValid, OrderStateInvalid, ExchangeContractErrs, diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts index d3d265b5a..ebc5bdeac 100644 --- a/packages/order-utils/test/asset_data_utils_test.ts +++ b/packages/order-utils/test/asset_data_utils_test.ts @@ -27,6 +27,7 @@ const KNOWN_MULTI_ASSET_ENCODING = { '0x94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000', }; +// tslint:disable:no-unnecessary-type-assertion describe('assetDataUtils', () => { it('should encode ERC20', () => { const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ERC20_ENCODING.address); diff --git a/packages/order-watcher/src/index.ts b/packages/order-watcher/src/index.ts index e275a0c6a..1f4e5eff1 100644 --- a/packages/order-watcher/src/index.ts +++ b/packages/order-watcher/src/index.ts @@ -7,6 +7,7 @@ export { OrderStateInvalid, OrderState, ExchangeContractErrs, + ObjectMap, OrderRelevantState, Stats, } from '@0x/types'; -- cgit v1.2.3 From 24564b986daa703f66e54f85abf4782d99a40f94 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 4 Jan 2019 14:31:25 -0800 Subject: Minimize unnecessary type assertions --- .../asset_balance_and_proxy_allowance_fetcher.ts | 132 ++++++++------------- packages/fill-scenarios/src/fill_scenarios.ts | 59 +++------ packages/order-utils/src/asset_data_utils.ts | 36 ++++-- packages/order-utils/src/constants.ts | 107 +++++++++-------- .../order-utils/src/exchange_transfer_simulator.ts | 2 +- packages/order-utils/src/order_state_utils.ts | 61 ++++------ packages/order-utils/test/asset_data_utils_test.ts | 4 +- .../dependent_order_hashes_tracker.ts | 67 ++++------- .../src/order_watcher/order_watcher.ts | 33 ++---- .../pipeline/src/parsers/events/exchange_events.ts | 48 ++++---- packages/pipeline/src/parsers/sra_orders/index.ts | 26 ++-- yarn.lock | 39 +----- 12 files changed, 248 insertions(+), 366 deletions(-) diff --git a/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts b/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts index 376004f52..1ff130a48 100644 --- a/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts +++ b/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts @@ -1,6 +1,4 @@ -// tslint:disable:no-unnecessary-type-assertion import { AbstractBalanceAndProxyAllowanceFetcher, assetDataUtils } from '@0x/order-utils'; -import { AssetProxyId, ERC20AssetData, ERC721AssetData, MultiAssetData } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { BlockParamLiteral } from 'ethereum-types'; import * as _ from 'lodash'; @@ -20,99 +18,73 @@ export class AssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndP public async getBalanceAsync(assetData: string, userAddress: string): Promise { const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); let balance: BigNumber | undefined; - switch (decodedAssetData.assetProxyId) { - case AssetProxyId.ERC20: - const decodedERC20AssetData = decodedAssetData as ERC20AssetData; - balance = await this._erc20Token.getBalanceAsync(decodedERC20AssetData.tokenAddress, userAddress, { + if (assetDataUtils.isERC20AssetData(decodedAssetData)) { + balance = await this._erc20Token.getBalanceAsync(decodedAssetData.tokenAddress, userAddress, { + defaultBlock: this._stateLayer, + }); + } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { + const tokenOwner = await this._erc721Token.getOwnerOfAsync( + decodedAssetData.tokenAddress, + decodedAssetData.tokenId, + { defaultBlock: this._stateLayer, - }); - break; - case AssetProxyId.ERC721: - const decodedERC721AssetData = decodedAssetData as ERC721AssetData; - const tokenOwner = await this._erc721Token.getOwnerOfAsync( - decodedERC721AssetData.tokenAddress, - decodedERC721AssetData.tokenId, - { - defaultBlock: this._stateLayer, - }, - ); - balance = tokenOwner === userAddress ? new BigNumber(1) : new BigNumber(0); - break; - case AssetProxyId.MultiAsset: - // The `balance` for MultiAssetData is the total units of the entire `assetData` that are held by the `userAddress`. - for (const [ - index, + }, + ); + balance = tokenOwner === userAddress ? new BigNumber(1) : new BigNumber(0); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + // The `balance` for MultiAssetData is the total units of the entire `assetData` that are held by the `userAddress`. + for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) { + const nestedAmountElement = decodedAssetData.amounts[index]; + const nestedAssetBalance = (await this.getBalanceAsync( nestedAssetDataElement, - ] of (decodedAssetData as MultiAssetData).nestedAssetData.entries()) { - const nestedAmountElement = (decodedAssetData as MultiAssetData).amounts[index]; - const nestedAssetBalance = (await this.getBalanceAsync( - nestedAssetDataElement, - userAddress, - )).dividedToIntegerBy(nestedAmountElement); - if (_.isUndefined(balance) || nestedAssetBalance.lessThan(balance)) { - balance = nestedAssetBalance; - } + userAddress, + )).dividedToIntegerBy(nestedAmountElement); + if (_.isUndefined(balance) || nestedAssetBalance.lessThan(balance)) { + balance = nestedAssetBalance; } - break; - default: - throw new Error(`Proxy with id ${decodedAssetData.assetProxyId} not supported`); + } } return balance as BigNumber; } public async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise { const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); let proxyAllowance: BigNumber | undefined; - switch (decodedAssetData.assetProxyId) { - case AssetProxyId.ERC20: - const decodedERC20AssetData = decodedAssetData as ERC20AssetData; - proxyAllowance = await this._erc20Token.getProxyAllowanceAsync( - decodedERC20AssetData.tokenAddress, - userAddress, - { - defaultBlock: this._stateLayer, - }, - ); - break; - case AssetProxyId.ERC721: - const decodedERC721AssetData = decodedAssetData as ERC721AssetData; - const isApprovedForAll = await this._erc721Token.isProxyApprovedForAllAsync( - decodedERC721AssetData.tokenAddress, - userAddress, + if (assetDataUtils.isERC20AssetData(decodedAssetData)) { + proxyAllowance = await this._erc20Token.getProxyAllowanceAsync(decodedAssetData.tokenAddress, userAddress, { + defaultBlock: this._stateLayer, + }); + } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { + const isApprovedForAll = await this._erc721Token.isProxyApprovedForAllAsync( + decodedAssetData.tokenAddress, + userAddress, + { + defaultBlock: this._stateLayer, + }, + ); + if (isApprovedForAll) { + return new BigNumber(this._erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + } else { + const isApproved = await this._erc721Token.isProxyApprovedAsync( + decodedAssetData.tokenAddress, + decodedAssetData.tokenId, { defaultBlock: this._stateLayer, }, ); - if (isApprovedForAll) { - return new BigNumber(this._erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); - } else { - const isApproved = await this._erc721Token.isProxyApprovedAsync( - decodedERC721AssetData.tokenAddress, - decodedERC721AssetData.tokenId, - { - defaultBlock: this._stateLayer, - }, - ); - proxyAllowance = isApproved ? new BigNumber(1) : new BigNumber(0); - } - break; - case AssetProxyId.MultiAsset: - // The `proxyAllowance` for MultiAssetData is the total units of the entire `assetData` that the proxies have been approved to spend by the `userAddress`. - for (const [ - index, + proxyAllowance = isApproved ? new BigNumber(1) : new BigNumber(0); + } + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + // The `proxyAllowance` for MultiAssetData is the total units of the entire `assetData` that the proxies have been approved to spend by the `userAddress`. + for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) { + const nestedAmountElement = decodedAssetData.amounts[index]; + const nestedAssetAllowance = (await this.getProxyAllowanceAsync( nestedAssetDataElement, - ] of (decodedAssetData as MultiAssetData).nestedAssetData.entries()) { - const nestedAmountElement = (decodedAssetData as MultiAssetData).amounts[index]; - const nestedAssetAllowance = (await this.getProxyAllowanceAsync( - nestedAssetDataElement, - userAddress, - )).dividedToIntegerBy(nestedAmountElement); - if (_.isUndefined(proxyAllowance) || nestedAssetAllowance.lessThan(proxyAllowance)) { - proxyAllowance = nestedAssetAllowance; - } + userAddress, + )).dividedToIntegerBy(nestedAmountElement); + if (_.isUndefined(proxyAllowance) || nestedAssetAllowance.lessThan(proxyAllowance)) { + proxyAllowance = nestedAssetAllowance; } - break; - default: - throw new Error(`Proxy with id ${decodedAssetData.assetProxyId} not supported`); + } } return proxyAllowance as BigNumber; } diff --git a/packages/fill-scenarios/src/fill_scenarios.ts b/packages/fill-scenarios/src/fill_scenarios.ts index 2e87e60cc..ce1f7f9ff 100644 --- a/packages/fill-scenarios/src/fill_scenarios.ts +++ b/packages/fill-scenarios/src/fill_scenarios.ts @@ -2,14 +2,7 @@ import { DummyERC20TokenContract, DummyERC721TokenContract, ExchangeContract } f import * as artifacts from '@0x/contract-artifacts'; import { assetDataUtils } from '@0x/order-utils'; import { orderFactory } from '@0x/order-utils/lib/src/order_factory'; -import { - AssetProxyId, - ERC20AssetData, - ERC721AssetData, - MultiAssetData, - OrderWithoutExchangeAddress, - SignedOrder, -} from '@0x/types'; +import { OrderWithoutExchangeAddress, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider } from 'ethereum-types'; @@ -280,42 +273,24 @@ export class FillScenarios { amount: BigNumber, ): Promise { const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); - switch (decodedAssetData.assetProxyId) { - case AssetProxyId.ERC20: - await this._increaseERC20BalanceAndAllowanceAsync( - // tslint:disable-next-line:no-unnecessary-type-assertion - (decodedAssetData as ERC20AssetData).tokenAddress, - userAddress, - amount, - ); - break; - case AssetProxyId.ERC721: - await this._increaseERC721BalanceAndAllowanceAsync( - // tslint:disable-next-line:no-unnecessary-type-assertion - (decodedAssetData as ERC721AssetData).tokenAddress, + if (assetDataUtils.isERC20AssetData(decodedAssetData)) { + await this._increaseERC20BalanceAndAllowanceAsync(decodedAssetData.tokenAddress, userAddress, amount); + } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { + await this._increaseERC721BalanceAndAllowanceAsync( + decodedAssetData.tokenAddress, + userAddress, + decodedAssetData.tokenId, + ); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) { + const amountsElement = decodedAssetData.amounts[index]; + const totalAmount = amount.times(amountsElement); + await this._increaseBalanceAndAllowanceWithAssetDataAsync( + nestedAssetDataElement, userAddress, - // tslint:disable-next-line:no-unnecessary-type-assertion - (decodedAssetData as ERC721AssetData).tokenId, + totalAmount, ); - break; - case AssetProxyId.MultiAsset: - for (const [ - index, - nestedAssetDataElement, - // tslint:disable-next-line:no-unnecessary-type-assertion - ] of (decodedAssetData as MultiAssetData).nestedAssetData.entries()) { - // tslint:disable-next-line:no-unnecessary-type-assertion - const amountsElement = (decodedAssetData as MultiAssetData).amounts[index]; - const totalAmount = amount.times(amountsElement); - await this._increaseBalanceAndAllowanceWithAssetDataAsync( - nestedAssetDataElement, - userAddress, - totalAmount, - ); - } - break; - default: - throw new Error(`Proxy with id ${decodedAssetData.assetProxyId} not supported`); + } } } } diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index c2929d426..0526c3d00 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -7,7 +7,6 @@ import { SingleAssetData, } from '@0x/types'; import { AbiEncoder, BigNumber } from '@0x/utils'; -import { MethodAbi } from 'ethereum-types'; import * as _ from 'lodash'; import { constants } from './constants'; @@ -23,7 +22,7 @@ export const assetDataUtils = { * @return The hex encoded assetData string */ encodeERC20AssetData(tokenAddress: string): string { - const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI as MethodAbi); + const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI); const args = [tokenAddress]; const assetData = abiEncoder.encode(args, encodingRules); return assetData; @@ -36,7 +35,7 @@ export const assetDataUtils = { decodeERC20AssetData(assetData: string): ERC20AssetData { assetDataUtils.assertIsERC20AssetData(assetData); const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); - const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI as MethodAbi); + const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI); const decodedAssetData = abiEncoder.decode(assetData, decodingRules); return { assetProxyId, @@ -52,7 +51,7 @@ export const assetDataUtils = { * @return The hex encoded assetData string */ encodeERC721AssetData(tokenAddress: string, tokenId: BigNumber): string { - const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI as MethodAbi); + const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI); const args = [tokenAddress, tokenId]; const assetData = abiEncoder.encode(args, encodingRules); return assetData; @@ -65,7 +64,7 @@ export const assetDataUtils = { decodeERC721AssetData(assetData: string): ERC721AssetData { assetDataUtils.assertIsERC721AssetData(assetData); const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); - const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI as MethodAbi); + const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI); const decodedAssetData = abiEncoder.decode(assetData, decodingRules); return { assetProxyId, @@ -90,7 +89,7 @@ export const assetDataUtils = { ); } _.forEach(nestedAssetData, assetDataElement => assetDataUtils.validateAssetDataOrThrow(assetDataElement)); - const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI as MethodAbi); + const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI); const args = [amounts, nestedAssetData]; const assetData = abiEncoder.encode(args, encodingRules); return assetData; @@ -103,7 +102,7 @@ export const assetDataUtils = { decodeMultiAssetData(assetData: string): MultiAssetData { assetDataUtils.assertIsMultiAssetData(assetData); const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData); - const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI as MethodAbi); + const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI); const decodedAssetData = abiEncoder.decode(assetData, decodingRules); // TODO(abandeali1): fix return types for `AbiEncoder.Method.decode` so that we can remove type assertion const amounts = (decodedAssetData as any).amounts; @@ -138,7 +137,7 @@ export const assetDataUtils = { nestedAssetDataElement, ); amounts.push( - _.map(recursivelyDecodedAssetData.amounts as BigNumber[], amountElement => + _.map(recursivelyDecodedAssetData.amounts, amountElement => amountElement.times(decodedAssetData.amounts[index]), ), ); @@ -181,6 +180,27 @@ export const assetDataUtils = { } return assetProxyId; }, + /** + * Checks if the decoded asset data is valid ERC20 data + * @param decodedAssetData The decoded asset data to check + */ + isERC20AssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is ERC20AssetData { + return decodedAssetData.assetProxyId === AssetProxyId.ERC20; + }, + /** + * Checks if the decoded asset data is valid ERC721 data + * @param decodedAssetData The decoded asset data to check + */ + isERC721AssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is ERC721AssetData { + return decodedAssetData.assetProxyId === AssetProxyId.ERC721; + }, + /** + * Checks if the decoded asset data is valid MultiAsset data + * @param decodedAssetData The decoded asset data to check + */ + isMultiAssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is MultiAssetData { + return decodedAssetData.assetProxyId === AssetProxyId.MultiAsset; + }, /** * Throws if the length or assetProxyId are invalid for the ERC20Proxy. * @param assetData Hex encoded assetData string diff --git a/packages/order-utils/src/constants.ts b/packages/order-utils/src/constants.ts index 1248a5576..a9a687719 100644 --- a/packages/order-utils/src/constants.ts +++ b/packages/order-utils/src/constants.ts @@ -1,4 +1,58 @@ import { BigNumber } from '@0x/utils'; +import { MethodAbi } from 'ethereum-types'; + +const ERC20_METHOD_ABI: MethodAbi = { + constant: false, + inputs: [ + { + name: 'tokenContract', + type: 'address', + }, + ], + name: 'ERC20Token', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +const ERC721_METHOD_ABI: MethodAbi = { + constant: false, + inputs: [ + { + name: 'tokenContract', + type: 'address', + }, + { + name: 'tokenId', + type: 'uint256', + }, + ], + name: 'ERC721Token', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +const MULTI_ASSET_METHOD_ABI: MethodAbi = { + constant: false, + inputs: [ + { + name: 'amounts', + type: 'uint256[]', + }, + { + name: 'nestedAssetData', + type: 'bytes[]', + }, + ], + name: 'MultiAsset', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; export const constants = { NULL_ADDRESS: '0x0000000000000000000000000000000000000000', @@ -49,54 +103,7 @@ export const constants = { { name: 'data', type: 'bytes' }, ], }, - ERC20_METHOD_ABI: { - constant: false, - inputs: [ - { - name: 'tokenContract', - type: 'address', - }, - ], - name: 'ERC20Token', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - ERC721_METHOD_ABI: { - constant: false, - inputs: [ - { - name: 'tokenContract', - type: 'address', - }, - { - name: 'tokenId', - type: 'uint256', - }, - ], - name: 'ERC721Token', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, - MULTI_ASSET_METHOD_ABI: { - constant: false, - inputs: [ - { - name: 'amounts', - type: 'uint256[]', - }, - { - name: 'nestedAssetData', - type: 'bytes[]', - }, - ], - name: 'MultiAsset', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', - }, + ERC20_METHOD_ABI, + ERC721_METHOD_ABI, + MULTI_ASSET_METHOD_ABI, }; diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts index 0a948fd1f..06621fd9e 100644 --- a/packages/order-utils/src/exchange_transfer_simulator.ts +++ b/packages/order-utils/src/exchange_transfer_simulator.ts @@ -108,7 +108,7 @@ export class ExchangeTransferSimulator { const amountsElement = decodedAssetData.amounts[index]; const totalAmount = amountInBaseUnits.times(amountsElement); await this.transferFromAsync( - nestedAssetDataElement as string, + nestedAssetDataElement, from, to, totalAmount, diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index 9e3e228ba..389419587 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -1,14 +1,11 @@ import { - AssetProxyId, ExchangeContractErrs, - MultiAssetData, ObjectMap, OrderRelevantState, OrderState, OrderStateInvalid, OrderStateValid, SignedOrder, - SingleAssetData, } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; @@ -310,22 +307,16 @@ export class OrderStateUtils { ): Promise> { const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); let balances: ObjectMap = { ...initialBalances }; - switch (decodedAssetData.assetProxyId) { - case AssetProxyId.ERC20: - case AssetProxyId.ERC721: - const balance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress); - const tokenAddress = (decodedAssetData as SingleAssetData).tokenAddress; - balances[tokenAddress] = _.isUndefined(initialBalances[tokenAddress]) - ? balance - : balances[tokenAddress].add(balance); - break; - case AssetProxyId.MultiAsset: - for (const assetDataElement of (decodedAssetData as MultiAssetData).nestedAssetData) { - balances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, balances); - } - break; - default: - throw new Error(`Proxy with id ${decodedAssetData.assetProxyId} not supported`); + if (assetDataUtils.isERC20AssetData(decodedAssetData) || assetDataUtils.isERC721AssetData(decodedAssetData)) { + const balance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress); + const tokenAddress = decodedAssetData.tokenAddress; + balances[tokenAddress] = _.isUndefined(initialBalances[tokenAddress]) + ? balance + : balances[tokenAddress].add(balance); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + for (const assetDataElement of decodedAssetData.nestedAssetData) { + balances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, balances); + } } return balances; } @@ -336,25 +327,19 @@ export class OrderStateUtils { ): Promise> { const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); let allowances: ObjectMap = { ...initialAllowances }; - switch (decodedAssetData.assetProxyId) { - case AssetProxyId.ERC20: - case AssetProxyId.ERC721: - const allowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( - assetData, - traderAddress, - ); - const tokenAddress = (decodedAssetData as SingleAssetData).tokenAddress; - allowances[tokenAddress] = _.isUndefined(initialAllowances[tokenAddress]) - ? allowance - : allowances[tokenAddress].add(allowance); - break; - case AssetProxyId.MultiAsset: - for (const assetDataElement of (decodedAssetData as MultiAssetData).nestedAssetData) { - allowances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, allowances); - } - break; - default: - throw new Error(`Proxy with id ${decodedAssetData.assetProxyId} not supported`); + if (assetDataUtils.isERC20AssetData(decodedAssetData) || assetDataUtils.isERC721AssetData(decodedAssetData)) { + const allowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync( + assetData, + traderAddress, + ); + const tokenAddress = decodedAssetData.tokenAddress; + allowances[tokenAddress] = _.isUndefined(initialAllowances[tokenAddress]) + ? allowance + : allowances[tokenAddress].add(allowance); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + for (const assetDataElement of decodedAssetData.nestedAssetData) { + allowances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, allowances); + } } return allowances; } diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts index ebc5bdeac..c498c5a00 100644 --- a/packages/order-utils/test/asset_data_utils_test.ts +++ b/packages/order-utils/test/asset_data_utils_test.ts @@ -27,7 +27,6 @@ const KNOWN_MULTI_ASSET_ENCODING = { '0x94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000', }; -// tslint:disable:no-unnecessary-type-assertion describe('assetDataUtils', () => { it('should encode ERC20', () => { const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ERC20_ENCODING.address); @@ -69,6 +68,7 @@ describe('assetDataUtils', () => { expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts); const decodedErc20AssetData = decodedAssetData.nestedAssetData[0]; + // tslint:disable-next-line:no-unnecessary-type-assertion const decodedErc721AssetData = decodedAssetData.nestedAssetData[1] as ERC721AssetData; expect(decodedErc20AssetData.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); expect(decodedErc20AssetData.assetProxyId).to.equal(AssetProxyId.ERC20); @@ -91,8 +91,10 @@ describe('assetDataUtils', () => { const expectedLength = 4; expect(decodedAssetData.nestedAssetData.length).to.be.equal(expectedLength); const decodedErc20AssetData1 = decodedAssetData.nestedAssetData[0]; + // tslint:disable-next-line:no-unnecessary-type-assertion const decodedErc721AssetData1 = decodedAssetData.nestedAssetData[1] as ERC721AssetData; const decodedErc20AssetData2 = decodedAssetData.nestedAssetData[2]; + // tslint:disable-next-line:no-unnecessary-type-assertion const decodedErc721AssetData2 = decodedAssetData.nestedAssetData[3] as ERC721AssetData; expect(decodedErc20AssetData1.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); expect(decodedErc20AssetData1.assetProxyId).to.equal(AssetProxyId.ERC20); diff --git a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts index 12723f73e..d1085014c 100644 --- a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts +++ b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts @@ -1,6 +1,5 @@ -// tslint:disable:no-unnecessary-type-assertion import { assetDataUtils, orderHashUtils } from '@0x/order-utils'; -import { AssetProxyId, ERC20AssetData, ERC721AssetData, MultiAssetData, SignedOrder } from '@0x/types'; +import { AssetProxyId, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; @@ -70,7 +69,10 @@ export class DependentOrderHashesTracker { this._removeAssetDataFromDependentOrderHashes(signedOrder, signedOrder.makerAssetData); // If makerToken === ZRX then we already removed it and we don't need to remove it again. const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(signedOrder.makerAssetData); - if ((decodedMakerAssetData as ERC20AssetData).tokenAddress !== this._zrxTokenAddress) { + if ( + assetDataUtils.isERC20AssetData(decodedMakerAssetData) && + decodedMakerAssetData.tokenAddress !== this._zrxTokenAddress + ) { this._removeFromERC20DependentOrderhashes(signedOrder, this._zrxTokenAddress); } this._removeFromMakerDependentOrderhashes(signedOrder); @@ -149,24 +151,14 @@ export class DependentOrderHashesTracker { } private _addAssetDataToDependentOrderHashes(signedOrder: SignedOrder, assetData: string): void { const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); - switch (decodedAssetData.assetProxyId) { - case AssetProxyId.ERC20: - this._addToERC20DependentOrderHashes(signedOrder, (decodedAssetData as ERC20AssetData).tokenAddress); - break; - case AssetProxyId.ERC721: - this._addToERC721DependentOrderHashes( - signedOrder, - (decodedAssetData as ERC721AssetData).tokenAddress, - (decodedAssetData as ERC721AssetData).tokenId, - ); - break; - case AssetProxyId.MultiAsset: - _.each((decodedAssetData as MultiAssetData).nestedAssetData, nestedAssetDataElement => - this._addAssetDataToDependentOrderHashes(signedOrder, nestedAssetDataElement), - ); - break; - default: - break; + if (assetDataUtils.isERC20AssetData(decodedAssetData)) { + this._addToERC20DependentOrderHashes(signedOrder, decodedAssetData.tokenAddress); + } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { + this._addToERC721DependentOrderHashes(signedOrder, decodedAssetData.tokenAddress, decodedAssetData.tokenId); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => + this._addAssetDataToDependentOrderHashes(signedOrder, nestedAssetDataElement), + ); } } private _addToMakerDependentOrderHashes(signedOrder: SignedOrder): void { @@ -234,27 +226,18 @@ export class DependentOrderHashesTracker { } private _removeAssetDataFromDependentOrderHashes(signedOrder: SignedOrder, assetData: string): void { const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); - switch (decodedAssetData.assetProxyId) { - case AssetProxyId.ERC20: - this._removeFromERC20DependentOrderhashes( - signedOrder, - (decodedAssetData as ERC20AssetData).tokenAddress, - ); - break; - case AssetProxyId.ERC721: - this._removeFromERC721DependentOrderhashes( - signedOrder, - (decodedAssetData as ERC721AssetData).tokenAddress, - (decodedAssetData as ERC721AssetData).tokenId, - ); - break; - case AssetProxyId.MultiAsset: - _.each((decodedAssetData as MultiAssetData).nestedAssetData, nestedAssetDataElement => - this._removeAssetDataFromDependentOrderHashes(signedOrder, nestedAssetDataElement), - ); - break; - default: - break; + if (assetDataUtils.isERC20AssetData(decodedAssetData)) { + this._removeFromERC20DependentOrderhashes(signedOrder, decodedAssetData.tokenAddress); + } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { + this._removeFromERC721DependentOrderhashes( + signedOrder, + decodedAssetData.tokenAddress, + decodedAssetData.tokenId, + ); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => + this._removeAssetDataFromDependentOrderHashes(signedOrder, nestedAssetDataElement), + ); } } } diff --git a/packages/order-watcher/src/order_watcher/order_watcher.ts b/packages/order-watcher/src/order_watcher/order_watcher.ts index c48606555..656f21a53 100644 --- a/packages/order-watcher/src/order_watcher/order_watcher.ts +++ b/packages/order-watcher/src/order_watcher/order_watcher.ts @@ -32,16 +32,7 @@ import { orderHashUtils, OrderStateUtils, } from '@0x/order-utils'; -import { - AssetProxyId, - ERC20AssetData, - ERC721AssetData, - ExchangeContractErrs, - MultiAssetData, - OrderState, - SignedOrder, - Stats, -} from '@0x/types'; +import { AssetProxyId, ExchangeContractErrs, OrderState, SignedOrder, Stats } from '@0x/types'; import { errorUtils, intervalUtils } from '@0x/utils'; import { BlockParamLiteral, LogEntryEvent, LogWithDecodedArgs, Provider } from 'ethereum-types'; import * as _ from 'lodash'; @@ -240,20 +231,14 @@ export class OrderWatcher { } private _addAssetDataToAbiDecoder(assetData: string): void { const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData); - switch (decodedAssetData.assetProxyId) { - case AssetProxyId.ERC20: - this._collisionResistantAbiDecoder.addERC20Token((decodedAssetData as ERC20AssetData).tokenAddress); - break; - case AssetProxyId.ERC721: - this._collisionResistantAbiDecoder.addERC721Token((decodedAssetData as ERC721AssetData).tokenAddress); - break; - case AssetProxyId.MultiAsset: - _.each((decodedAssetData as MultiAssetData).nestedAssetData, nestedAssetDataElement => - this._addAssetDataToAbiDecoder(nestedAssetDataElement), - ); - break; - default: - break; + if (assetDataUtils.isERC20AssetData(decodedAssetData)) { + this._collisionResistantAbiDecoder.addERC20Token(decodedAssetData.tokenAddress); + } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) { + this._collisionResistantAbiDecoder.addERC721Token(decodedAssetData.tokenAddress); + } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) { + _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement => + this._addAssetDataToAbiDecoder(nestedAssetDataElement), + ); } } private _deleteLazyStoreBalance(assetData: string, userAddress: string): void { diff --git a/packages/pipeline/src/parsers/events/exchange_events.ts b/packages/pipeline/src/parsers/events/exchange_events.ts index 8d2e7dec7..9c4a5f89a 100644 --- a/packages/pipeline/src/parsers/events/exchange_events.ts +++ b/packages/pipeline/src/parsers/events/exchange_events.ts @@ -1,14 +1,12 @@ import { ExchangeCancelEventArgs, ExchangeCancelUpToEventArgs, ExchangeFillEventArgs } from '@0x/contract-wrappers'; import { assetDataUtils } from '@0x/order-utils'; -import { AssetProxyId, ERC721AssetData, SingleAssetData } from '@0x/types'; +import { AssetProxyId, ERC721AssetData } from '@0x/types'; import { LogWithDecodedArgs } from 'ethereum-types'; import * as R from 'ramda'; import { ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeFillEvent } from '../../entities'; import { bigNumbertoStringOrNull, convertAssetProxyIdToType } from '../../utils'; -// tslint:disable:no-unnecessary-type-assertion - /** * Parses raw event logs for a fill event and returns an array of * ExchangeFillEvent entities. @@ -59,26 +57,24 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs Date: Mon, 7 Jan 2019 11:11:19 -0800 Subject: Use enums in pipeline --- packages/0x.js/CHANGELOG.json | 2 +- packages/contract-wrappers/CHANGELOG.json | 2 +- packages/order-utils/src/asset_data_utils.ts | 2 +- packages/order-watcher/CHANGELOG.json | 2 +- .../src/order_watcher/order_watcher.ts | 2 +- packages/order-watcher/test/order_watcher_test.ts | 42 +++++++++++----------- packages/pipeline/src/entities/token_order.ts | 3 +- packages/pipeline/src/parsers/ddex_orders/index.ts | 8 +++-- packages/pipeline/src/parsers/idex_orders/index.ts | 12 ++++--- .../pipeline/src/parsers/oasis_orders/index.ts | 8 ++--- .../pipeline/src/parsers/paradex_orders/index.ts | 4 +-- packages/pipeline/src/types.ts | 11 ++++-- .../src/utils/transformers/asset_proxy_id_types.ts | 6 ++-- .../test/parsers/ddex_orders/index_test.ts | 4 +-- .../test/parsers/events/exchange_events_test.ts | 5 +-- .../test/parsers/idex_orders/index_test.ts | 8 ++--- .../test/parsers/oasis_orders/index_test.ts | 4 +-- .../test/parsers/paradex_orders/index_test.ts | 4 +-- .../pipeline/test/parsers/sra_orders/index_test.ts | 5 +-- 19 files changed, 75 insertions(+), 59 deletions(-) diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index bd6301441..f73de96aa 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -4,7 +4,7 @@ "changes": [ { "note": - "Export `MultiAssetData`, `MultiAssetDataWithRecursiveDecoding`, `ObjectMap`, and `SingleAssetData` from types. No longer export AssetData.", + "Export `MultiAssetData`, `MultiAssetDataWithRecursiveDecoding`, `ObjectMap`, and `SingleAssetData` from types. No longer export `AssetData`.", "pr": 1363 } ] diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index a8c74fa24..581fbdee1 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -6,7 +6,7 @@ "note": "Add support for Trust Wallet signature denial error" }, { - "note": "Add balance and allowance queries for MultiAssetProxy", + "note": "Add balance and allowance queries for `MultiAssetProxy`", "pr": 1363 } ] diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index 0526c3d00..f314891e2 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -76,7 +76,7 @@ export const assetDataUtils = { /** * Encodes assetData for multiple AssetProxies into a single hex encoded assetData string, usable in the makerAssetData or * takerAssetData fields in a 0x order. - * @param amounts Amounts of each asset that correspond to a ginle unit within an order. + * @param amounts Amounts of each asset that correspond to a single unit within an order. * @param nestedAssetData assetData strings that correspond to a valid assetProxyId. * @return The hex encoded assetData string */ diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json index b4807dc89..4cfecd034 100644 --- a/packages/order-watcher/CHANGELOG.json +++ b/packages/order-watcher/CHANGELOG.json @@ -3,7 +3,7 @@ "version": "2.4.0", "changes": [ { - "note": "Add support for MultiAssetProxy", + "note": "Add support for `MultiAssetProxy`", "pr": 1363 } ] diff --git a/packages/order-watcher/src/order_watcher/order_watcher.ts b/packages/order-watcher/src/order_watcher/order_watcher.ts index 656f21a53..a06fd0cfe 100644 --- a/packages/order-watcher/src/order_watcher/order_watcher.ts +++ b/packages/order-watcher/src/order_watcher/order_watcher.ts @@ -487,4 +487,4 @@ export class OrderWatcher { this._callbackIfExists(null, orderState); } } -} // tslint:disable:max-file-line-count +} diff --git a/packages/order-watcher/test/order_watcher_test.ts b/packages/order-watcher/test/order_watcher_test.ts index dfa36f2fd..41dc884d5 100644 --- a/packages/order-watcher/test/order_watcher_test.ts +++ b/packages/order-watcher/test/order_watcher_test.ts @@ -859,29 +859,29 @@ describe('OrderWatcher', () => { })().catch(done); }); // TODO(abandeali1): The following test will fail until the MAP has been deployed and activated. - // it('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => { - // (async () => { - // signedOrder = await fillScenarios.createFillableSignedOrderAsync( - // makerMultiAssetData, - // takerAssetData, - // makerAddress, - // takerAddress, - // fillableAmount, - // ); - // const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - // await orderWatcher.addOrderAsync(signedOrder); + it.skip('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => { + (async () => { + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerMultiAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableAmount, + ); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + await orderWatcher.addOrderAsync(signedOrder); - // const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { - // expect(orderState.isValid).to.be.false(); - // const invalidOrderState = orderState as OrderStateInvalid; - // expect(invalidOrderState.orderHash).to.be.equal(orderHash); - // expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero); - // }); - // orderWatcher.subscribe(callback); + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => { + expect(orderState.isValid).to.be.false(); + const invalidOrderState = orderState as OrderStateInvalid; + expect(invalidOrderState.orderHash).to.be.equal(orderHash); + expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero); + }); + orderWatcher.subscribe(callback); - // await contractWrappers.exchange.fillOrderAsync(signedOrder, fillableAmount, takerAddress); - // })().catch(done); - // }); + await contractWrappers.exchange.fillOrderAsync(signedOrder, fillableAmount, takerAddress); + })().catch(done); + }); }); }); }); // tslint:disable:max-file-line-count diff --git a/packages/pipeline/src/entities/token_order.ts b/packages/pipeline/src/entities/token_order.ts index 4b8f0abc3..2709747cb 100644 --- a/packages/pipeline/src/entities/token_order.ts +++ b/packages/pipeline/src/entities/token_order.ts @@ -1,7 +1,6 @@ import { BigNumber } from '@0x/utils'; import { Column, Entity, PrimaryColumn } from 'typeorm'; -import { OrderType } from '../types'; import { bigNumberTransformer, numberToBigIntTransformer } from '../utils'; @Entity({ name: 'token_orderbook_snapshots', schema: 'raw' }) @@ -11,7 +10,7 @@ export class TokenOrderbookSnapshot { @PrimaryColumn({ name: 'source' }) public source!: string; @PrimaryColumn({ name: 'order_type' }) - public orderType!: OrderType; + public orderType!: string; @PrimaryColumn({ name: 'price', type: 'numeric', transformer: bigNumberTransformer }) public price!: BigNumber; @PrimaryColumn({ name: 'base_asset_symbol' }) diff --git a/packages/pipeline/src/parsers/ddex_orders/index.ts b/packages/pipeline/src/parsers/ddex_orders/index.ts index d7b97efbe..eeb9c9d5b 100644 --- a/packages/pipeline/src/parsers/ddex_orders/index.ts +++ b/packages/pipeline/src/parsers/ddex_orders/index.ts @@ -23,8 +23,12 @@ export function parseDdexOrders( ): TokenOrder[] { const aggregatedBids = aggregateOrders(ddexOrderbook.bids); const aggregatedAsks = aggregateOrders(ddexOrderbook.asks); - const parsedBids = aggregatedBids.map(order => parseDdexOrder(ddexMarket, observedTimestamp, 'bid', source, order)); - const parsedAsks = aggregatedAsks.map(order => parseDdexOrder(ddexMarket, observedTimestamp, 'ask', source, order)); + const parsedBids = aggregatedBids.map(order => + parseDdexOrder(ddexMarket, observedTimestamp, OrderType.Bid, source, order), + ); + const parsedAsks = aggregatedAsks.map(order => + parseDdexOrder(ddexMarket, observedTimestamp, OrderType.Ask, source, order), + ); return parsedBids.concat(parsedAsks); } diff --git a/packages/pipeline/src/parsers/idex_orders/index.ts b/packages/pipeline/src/parsers/idex_orders/index.ts index dfe27455c..14b871195 100644 --- a/packages/pipeline/src/parsers/idex_orders/index.ts +++ b/packages/pipeline/src/parsers/idex_orders/index.ts @@ -2,7 +2,7 @@ import { BigNumber } from '@0x/utils'; import { aggregateOrders } from '../utils'; -import { IdexOrder, IdexOrderbook, IdexOrderParam } from '../../data_sources/idex'; +import { IdexOrderbook, IdexOrderParam } from '../../data_sources/idex'; import { TokenOrderbookSnapshot as TokenOrder } from '../../entities'; import { OrderType } from '../../types'; @@ -21,7 +21,9 @@ export function parseIdexOrders(idexOrderbook: IdexOrderbook, observedTimestamp: const idexBidOrder = idexOrderbook.bids[0]; const parsedBids = aggregatedBids.length > 0 - ? aggregatedBids.map(order => parseIdexOrder(idexBidOrder.params, observedTimestamp, 'bid', source, order)) + ? aggregatedBids.map(order => + parseIdexOrder(idexBidOrder.params, observedTimestamp, OrderType.Bid, source, order), + ) : []; const aggregatedAsks = aggregateOrders(idexOrderbook.asks); @@ -29,7 +31,9 @@ export function parseIdexOrders(idexOrderbook: IdexOrderbook, observedTimestamp: const idexAskOrder = idexOrderbook.asks[0]; const parsedAsks = aggregatedAsks.length > 0 - ? aggregatedAsks.map(order => parseIdexOrder(idexAskOrder.params, observedTimestamp, 'ask', source, order)) + ? aggregatedAsks.map(order => + parseIdexOrder(idexAskOrder.params, observedTimestamp, OrderType.Ask, source, order), + ) : []; return parsedBids.concat(parsedAsks); } @@ -62,7 +66,7 @@ export function parseIdexOrder( tokenOrder.baseVolume = amount; tokenOrder.quoteVolume = price.times(amount); - if (orderType === 'bid') { + if (orderType === OrderType.Bid) { tokenOrder.baseAssetSymbol = idexOrderParam.buySymbol; tokenOrder.baseAssetAddress = idexOrderParam.tokenBuy; tokenOrder.quoteAssetSymbol = idexOrderParam.sellSymbol; diff --git a/packages/pipeline/src/parsers/oasis_orders/index.ts b/packages/pipeline/src/parsers/oasis_orders/index.ts index 13997f31b..b71fb65b9 100644 --- a/packages/pipeline/src/parsers/oasis_orders/index.ts +++ b/packages/pipeline/src/parsers/oasis_orders/index.ts @@ -23,13 +23,13 @@ export function parseOasisOrders( observedTimestamp: number, source: string, ): TokenOrder[] { - const aggregatedBids = aggregateOrders(R.filter(R.propEq('act', 'bid'), oasisOrderbook)); - const aggregatedAsks = aggregateOrders(R.filter(R.propEq('act', 'ask'), oasisOrderbook)); + const aggregatedBids = aggregateOrders(R.filter(R.propEq('act', OrderType.Bid), oasisOrderbook)); + const aggregatedAsks = aggregateOrders(R.filter(R.propEq('act', OrderType.Ask), oasisOrderbook)); const parsedBids = aggregatedBids.map(order => - parseOasisOrder(oasisMarket, observedTimestamp, 'bid', source, order), + parseOasisOrder(oasisMarket, observedTimestamp, OrderType.Bid, source, order), ); const parsedAsks = aggregatedAsks.map(order => - parseOasisOrder(oasisMarket, observedTimestamp, 'ask', source, order), + parseOasisOrder(oasisMarket, observedTimestamp, OrderType.Ask, source, order), ); return parsedBids.concat(parsedAsks); } diff --git a/packages/pipeline/src/parsers/paradex_orders/index.ts b/packages/pipeline/src/parsers/paradex_orders/index.ts index 5ceeb64a4..85990dae4 100644 --- a/packages/pipeline/src/parsers/paradex_orders/index.ts +++ b/packages/pipeline/src/parsers/paradex_orders/index.ts @@ -21,10 +21,10 @@ export function parseParadexOrders( source: string, ): TokenOrder[] { const parsedBids = paradexOrderbookResponse.bids.map(order => - parseParadexOrder(paradexMarket, observedTimestamp, 'bid', source, order), + parseParadexOrder(paradexMarket, observedTimestamp, OrderType.Bid, source, order), ); const parsedAsks = paradexOrderbookResponse.asks.map(order => - parseParadexOrder(paradexMarket, observedTimestamp, 'ask', source, order), + parseParadexOrder(paradexMarket, observedTimestamp, OrderType.Ask, source, order), ); return parsedBids.concat(parsedAsks); } diff --git a/packages/pipeline/src/types.ts b/packages/pipeline/src/types.ts index cd649426b..5f2121807 100644 --- a/packages/pipeline/src/types.ts +++ b/packages/pipeline/src/types.ts @@ -1,2 +1,9 @@ -export type AssetType = 'erc20' | 'erc721' | 'multiAsset'; -export type OrderType = 'bid' | 'ask'; +export enum AssetType { + ERC20 = 'erc20', + ERC721 = 'erc721', + MultiAsset = 'multiAsset', +} +export enum OrderType { + Bid = 'bid', + Ask = 'ask', +} diff --git a/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts b/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts index ffeaaca52..2cd05a616 100644 --- a/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts +++ b/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts @@ -9,11 +9,11 @@ import { AssetType } from '../../types'; export function convertAssetProxyIdToType(assetProxyId: AssetProxyId): AssetType { switch (assetProxyId) { case AssetProxyId.ERC20: - return 'erc20'; + return AssetType.ERC20; case AssetProxyId.ERC721: - return 'erc721'; + return AssetType.ERC721; case AssetProxyId.MultiAsset: - return 'multiAsset'; + return AssetType.MultiAsset; default: throw new Error(`${assetProxyId} not a supported assetProxyId`); } diff --git a/packages/pipeline/test/parsers/ddex_orders/index_test.ts b/packages/pipeline/test/parsers/ddex_orders/index_test.ts index 4a4a86bf8..f30e86b02 100644 --- a/packages/pipeline/test/parsers/ddex_orders/index_test.ts +++ b/packages/pipeline/test/parsers/ddex_orders/index_test.ts @@ -31,13 +31,13 @@ describe('ddex_orders', () => { amountDecimals: 0, }; const observedTimestamp: number = Date.now(); - const orderType: OrderType = 'bid'; + const orderType: OrderType = OrderType.Bid; const source: string = 'ddex'; const expected = new TokenOrder(); expected.source = 'ddex'; expected.observedTimestamp = observedTimestamp; - expected.orderType = 'bid'; + expected.orderType = OrderType.Bid; expected.price = new BigNumber(0.5); // ddex currently confuses base and quote assets. // Switch them to maintain our internal consistency. diff --git a/packages/pipeline/test/parsers/events/exchange_events_test.ts b/packages/pipeline/test/parsers/events/exchange_events_test.ts index 5d4b185a5..956ad9ef8 100644 --- a/packages/pipeline/test/parsers/events/exchange_events_test.ts +++ b/packages/pipeline/test/parsers/events/exchange_events_test.ts @@ -6,6 +6,7 @@ import 'mocha'; import { ExchangeFillEvent } from '../../../src/entities'; import { _convertToExchangeFillEvent } from '../../../src/parsers/events/exchange_events'; +import { AssetType } from '../../../src/types'; import { chaiSetup } from '../../utils/chai_setup'; chaiSetup.configure(); @@ -62,12 +63,12 @@ describe('exchange_events', () => { expected.takerFeePaid = new BigNumber('12345'); expected.orderHash = '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a'; expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - expected.makerAssetType = 'erc20'; + expected.makerAssetType = AssetType.ERC20; expected.makerAssetProxyId = '0xf47261b0'; expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; expected.makerTokenId = null; expected.rawTakerAssetData = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; - expected.takerAssetType = 'erc20'; + expected.takerAssetType = AssetType.ERC20; expected.takerAssetProxyId = '0xf47261b0'; expected.takerTokenAddress = '0xe41d2489571d322189246dafa5ebde1f4699f498'; expected.takerTokenId = null; diff --git a/packages/pipeline/test/parsers/idex_orders/index_test.ts b/packages/pipeline/test/parsers/idex_orders/index_test.ts index d54ecb9a8..48b019732 100644 --- a/packages/pipeline/test/parsers/idex_orders/index_test.ts +++ b/packages/pipeline/test/parsers/idex_orders/index_test.ts @@ -31,13 +31,13 @@ describe('idex_orders', () => { user: '0x212345667543456435324564345643453453333', }; const observedTimestamp: number = Date.now(); - const orderType: OrderType = 'bid'; + const orderType: OrderType = OrderType.Bid; const source: string = 'idex'; const expected = new TokenOrder(); expected.source = 'idex'; expected.observedTimestamp = observedTimestamp; - expected.orderType = 'bid'; + expected.orderType = OrderType.Bid; expected.price = new BigNumber(0.5); expected.baseAssetSymbol = 'ABC'; expected.baseAssetAddress = '0x0000000000000000000000000000000000000000'; @@ -65,13 +65,13 @@ describe('idex_orders', () => { user: '0x212345667543456435324564345643453453333', }; const observedTimestamp: number = Date.now(); - const orderType: OrderType = 'ask'; + const orderType: OrderType = OrderType.Ask; const source: string = 'idex'; const expected = new TokenOrder(); expected.source = 'idex'; expected.observedTimestamp = observedTimestamp; - expected.orderType = 'ask'; + expected.orderType = OrderType.Ask; expected.price = new BigNumber(0.5); expected.baseAssetSymbol = 'ABC'; expected.baseAssetAddress = '0x0000000000000000000000000000000000000000'; diff --git a/packages/pipeline/test/parsers/oasis_orders/index_test.ts b/packages/pipeline/test/parsers/oasis_orders/index_test.ts index 433bfb665..401fedff8 100644 --- a/packages/pipeline/test/parsers/oasis_orders/index_test.ts +++ b/packages/pipeline/test/parsers/oasis_orders/index_test.ts @@ -27,13 +27,13 @@ describe('oasis_orders', () => { low: 0, }; const observedTimestamp: number = Date.now(); - const orderType: OrderType = 'bid'; + const orderType: OrderType = OrderType.Bid; const source: string = 'oasis'; const expected = new TokenOrder(); expected.source = 'oasis'; expected.observedTimestamp = observedTimestamp; - expected.orderType = 'bid'; + expected.orderType = OrderType.Bid; expected.price = new BigNumber(0.5); expected.baseAssetSymbol = 'DEF'; expected.baseAssetAddress = null; diff --git a/packages/pipeline/test/parsers/paradex_orders/index_test.ts b/packages/pipeline/test/parsers/paradex_orders/index_test.ts index 6b811b90d..c5dd8751b 100644 --- a/packages/pipeline/test/parsers/paradex_orders/index_test.ts +++ b/packages/pipeline/test/parsers/paradex_orders/index_test.ts @@ -32,13 +32,13 @@ describe('paradex_orders', () => { quoteTokenAddress: '0x0000000000000000000000000000000000000000', }; const observedTimestamp: number = Date.now(); - const orderType: OrderType = 'bid'; + const orderType: OrderType = OrderType.Bid; const source: string = 'paradex'; const expected = new TokenOrder(); expected.source = 'paradex'; expected.observedTimestamp = observedTimestamp; - expected.orderType = 'bid'; + expected.orderType = OrderType.Bid; expected.price = new BigNumber(0.1245); expected.baseAssetSymbol = 'DEF'; expected.baseAssetAddress = '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81'; diff --git a/packages/pipeline/test/parsers/sra_orders/index_test.ts b/packages/pipeline/test/parsers/sra_orders/index_test.ts index ee2842ef3..838171a72 100644 --- a/packages/pipeline/test/parsers/sra_orders/index_test.ts +++ b/packages/pipeline/test/parsers/sra_orders/index_test.ts @@ -5,6 +5,7 @@ import 'mocha'; import { SraOrder } from '../../../src/entities'; import { _convertToEntity } from '../../../src/parsers/sra_orders'; +import { AssetType } from '../../../src/types'; import { chaiSetup } from '../../utils/chai_setup'; chaiSetup.configure(); @@ -50,12 +51,12 @@ describe('sra_orders', () => { expected.signature = '0x1b5a5d672b0d647b5797387ccbb89d822d5d2e873346b014f4ff816ff0783f2a7a0d2824d2d7042ec8ea375bc7f870963e1cb8248f1db03ddf125e27b5963aa11f03'; expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; - expected.makerAssetType = 'erc20'; + expected.makerAssetType = AssetType.ERC20; expected.makerAssetProxyId = '0xf47261b0'; expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'; expected.makerTokenId = null; expected.rawTakerAssetData = '0xf47261b000000000000000000000000042d6622dece394b54999fbd73d108123806f6a18'; - expected.takerAssetType = 'erc20'; + expected.takerAssetType = AssetType.ERC20; expected.takerAssetProxyId = '0xf47261b0'; expected.takerTokenAddress = '0x42d6622dece394b54999fbd73d108123806f6a18'; expected.takerTokenId = null; -- cgit v1.2.3