diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index 2b2c24dae1..627958b150 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -1,6 +1,6 @@ export { getContractAddressesForChainOrThrow, ChainId, ContractAddresses } from '@0x/contract-addresses'; -export { signatureUtils, generatePseudoRandomSalt, decodeAssetDataOrThrow } from '@0x/order-utils'; +export { signatureUtils, generatePseudoRandomSalt, assetDataUtils } from '@0x/order-utils'; export { ExchangeEventArgs, diff --git a/packages/monorepo-scripts/src/doc_gen_configs.ts b/packages/monorepo-scripts/src/doc_gen_configs.ts index 9fe2a7166a..a657c60693 100644 --- a/packages/monorepo-scripts/src/doc_gen_configs.ts +++ b/packages/monorepo-scripts/src/doc_gen_configs.ts @@ -58,6 +58,15 @@ export const docGenConfigs: DocGenConfigs = { 'GlobalStakeByStatus', 'OwnerStakeByStatus', 'StakingPoolById', + 'AssetData', + 'SingleAssetData', + 'ERC20AssetData', + 'ERC20BridgeAssetData', + 'ERC721AssetData', + 'ERC1155AssetData', + 'MultiAssetData', + 'StaticCallAssetData', + 'MultiAssetDataWithRecursiveDecoding', ], // Some libraries only export types. In those cases, we cannot check if the exported types are part of the // "exported public interface". Thus we add them here and skip those checks. diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index 148625faeb..7f964fe89e 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -3,12 +3,12 @@ "version": "10.0.0", "changes": [ { - "note": "Removed `assetDataUtils`", - "pr": 2373 + "note": "Removed from assetDataUtils: individual decoding functions and assert functions", + "pr": 2388 }, { - "note": "Exported function `decodeAssetDataOrThrow`", - "pr": 2373 + "note": "Add ERC20Bridge support to assetDataUtils", + "pr": 2388 } ] }, diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts new file mode 100644 index 0000000000..485758e15f --- /dev/null +++ b/packages/order-utils/src/asset_data_utils.ts @@ -0,0 +1,165 @@ +import { IAssetDataContract } from '@0x/contract-wrappers'; +import { + AssetData, + AssetProxyId, + MultiAssetData, + MultiAssetDataWithRecursiveDecoding, + SingleAssetData, +} from '@0x/types'; +import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils'; +import * as _ from 'lodash'; + +const fakeProvider = { isEIP1193: true } as any; +const assetDataEncoder = new IAssetDataContract(NULL_ADDRESS, fakeProvider); + +export const assetDataUtils = { + encodeERC20AssetData(tokenAddress: string): string { + return assetDataEncoder.ERC20Token(tokenAddress).getABIEncodedTransactionData(); + }, + encodeERC20BridgeAssetData(tokenAddress: string, bridgeAddress: string, bridgeData: string): string { + return assetDataEncoder.ERC20Bridge(tokenAddress, bridgeAddress, bridgeData).getABIEncodedTransactionData(); + }, + encodeERC721AssetData(tokenAddress: string, tokenId: BigNumber): string { + return assetDataEncoder.ERC721Token(tokenAddress, tokenId).getABIEncodedTransactionData(); + }, + encodeERC1155AssetData( + tokenAddress: string, + tokenIds: BigNumber[], + tokenValues: BigNumber[], + callbackData: string, + ): string { + return assetDataEncoder + .ERC1155Assets(tokenAddress, tokenIds, tokenValues, callbackData) + .getABIEncodedTransactionData(); + }, + encodeMultiAssetData(values: BigNumber[], nestedAssetData: string[]): string { + return assetDataEncoder.MultiAsset(values, nestedAssetData).getABIEncodedTransactionData(); + }, + encodeStaticCallAssetData( + staticCallTargetAddress: string, + staticCallData: string, + expectedReturnDataHash: string, + ): string { + return assetDataEncoder + .StaticCall(staticCallTargetAddress, staticCallData, expectedReturnDataHash) + .getABIEncodedTransactionData(); + }, + /** + * Decode any assetData into its corresponding assetData object + * @param assetData Hex encoded assetData string to decode + * @return Either a ERC20, ERC20Bridge, ERC721, ERC1155, StaticCall, or MultiAsset assetData object + */ + decodeAssetDataOrThrow(assetData: string): AssetData { + const assetProxyId = hexUtils.slice(assetData, 0, 4); // tslint:disable-line:custom-no-magic-numbers + switch (assetProxyId) { + case AssetProxyId.ERC20: { + const tokenAddress = assetDataEncoder.getABIDecodedTransactionData('ERC20Token', assetData); + return { + assetProxyId, + tokenAddress, + }; + } + case AssetProxyId.ERC20Bridge: { + const [tokenAddress, bridgeAddress, bridgeData] = assetDataEncoder.getABIDecodedTransactionData< + [string, string, string] + >('ERC20Bridge', assetData); + return { + assetProxyId, + tokenAddress, + bridgeAddress, + bridgeData, + }; + } + case AssetProxyId.ERC721: { + const [tokenAddress, tokenId] = assetDataEncoder.getABIDecodedTransactionData<[string, BigNumber]>( + 'ERC721Token', + assetData, + ); + return { + assetProxyId, + tokenAddress, + tokenId, + }; + } + case AssetProxyId.ERC1155: { + const [ + tokenAddress, + tokenIds, + tokenValues, + callbackData, + ] = assetDataEncoder.getABIDecodedTransactionData<[string, BigNumber[], BigNumber[], string]>( + 'ERC1155Assets', + assetData, + ); + return { + assetProxyId, + tokenAddress, + tokenIds, + tokenValues, + callbackData, + }; + } + case AssetProxyId.MultiAsset: { + const [amounts, nestedAssetData] = assetDataEncoder.getABIDecodedTransactionData< + [BigNumber[], string[]] + >('MultiAsset', assetData); + + const multiAssetData: MultiAssetData = { + assetProxyId, + amounts, + nestedAssetData, + }; + return multiAssetData; + } + case AssetProxyId.StaticCall: + const [callTarget, staticCallData, callResultHash] = assetDataEncoder.getABIDecodedTransactionData< + [string, string, string] + >('StaticCall', assetData); + return { + assetProxyId, + callTarget, + staticCallData, + callResultHash, + }; + default: + throw new Error(`Unhandled asset proxy ID: ${assetProxyId}`); + } + }, + /** + * Decodes a MultiAsset assetData hex string into its 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.decodeAssetDataOrThrow(assetData) as MultiAssetData; // tslint:disable-line:no-unnecessary-type-assertion + if (decodedAssetData.assetProxyId !== AssetProxyId.MultiAsset) { + throw new Error(`Not a MultiAssetData. Use 'decodeAssetDataOrThrow' instead`); + } + const amounts: any[] = []; + const decodedNestedAssetData = decodedAssetData.nestedAssetData.map((nestedAssetDataElement, index) => { + const decodedNestedAssetDataElement = assetDataUtils.decodeAssetDataOrThrow(nestedAssetDataElement); + if (decodedNestedAssetDataElement.assetProxyId === AssetProxyId.MultiAsset) { + const recursivelyDecodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively( + nestedAssetDataElement, + ); + amounts.push( + recursivelyDecodedAssetData.amounts.map(amountElement => + amountElement.times(decodedAssetData.amounts[index]), + ), + ); + return recursivelyDecodedAssetData.nestedAssetData; + } else { + amounts.push(decodedAssetData.amounts[index]); + return decodedNestedAssetDataElement; + } + }); + const flattenedAmounts = _.flattenDeep(amounts); + const flattenedDecodedNestedAssetData = _.flattenDeep(decodedNestedAssetData); + 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/decode_asset_data.ts b/packages/order-utils/src/decode_asset_data.ts deleted file mode 100644 index 74517b273a..0000000000 --- a/packages/order-utils/src/decode_asset_data.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { IAssetDataContract } from '@0x/contract-wrappers'; -import { AssetData, AssetProxyId } from '@0x/types'; -import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils'; - -const fakeProvider = { isEIP1193: true } as any; -const assetDataDecoder = new IAssetDataContract(NULL_ADDRESS, fakeProvider); - -/** - * Decode any assetData into its corresponding assetData object - * @param assetData Hex encoded assetData string to decode - * @return Either a ERC20, ERC20Bridge, ERC721, ERC1155, StaticCall, or MultiAsset assetData object - */ -export function decodeAssetDataOrThrow(assetData: string): AssetData { - const assetProxyId = hexUtils.slice(assetData, 0, 4); // tslint:disable-line:custom-no-magic-numbers - switch (assetProxyId) { - case AssetProxyId.ERC20: { - const tokenAddress = assetDataDecoder.getABIDecodedTransactionData('ERC20Token', assetData); - return { - assetProxyId, - tokenAddress, - }; - } - case AssetProxyId.ERC20Bridge: { - const [tokenAddress, bridgeAddress, bridgeData] = assetDataDecoder.getABIDecodedTransactionData< - [string, string, string] - >('ERC20Bridge', assetData); - return { - assetProxyId, - tokenAddress, - bridgeAddress, - bridgeData, - }; - } - case AssetProxyId.ERC721: { - const [tokenAddress, tokenId] = assetDataDecoder.getABIDecodedTransactionData<[string, BigNumber]>( - 'ERC721Token', - assetData, - ); - return { - assetProxyId, - tokenAddress, - tokenId, - }; - } - case AssetProxyId.ERC1155: { - const [tokenAddress, tokenIds, tokenValues] = assetDataDecoder.getABIDecodedTransactionData< - [string, BigNumber[], BigNumber[]] - >('ERC1155Assets', assetData); - return { - assetProxyId, - tokenAddress, - tokenIds, - tokenValues, - }; - } - case AssetProxyId.MultiAsset: { - const [amounts, nestedAssetData] = assetDataDecoder.getABIDecodedTransactionData<[BigNumber[], string[]]>( - 'MultiAsset', - assetData, - ); - return { - assetProxyId, - amounts, - nestedAssetData, - }; - } - case AssetProxyId.StaticCall: - const [callTarget, staticCallData, callResultHash] = assetDataDecoder.getABIDecodedTransactionData< - [string, string, string] - >('StaticCall', assetData); - return { - assetProxyId, - callTarget, - staticCallData, - callResultHash, - }; - default: - throw new Error(`Unhandled asset proxy ID: ${assetProxyId}`); - } -} diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index eada7f5faf..76367af928 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -5,7 +5,7 @@ export { rateUtils } from './rate_utils'; export { sortingUtils } from './sorting_utils'; export { orderCalculationUtils } from './order_calculation_utils'; export { orderHashUtils } from './order_hash_utils'; -export { decodeAssetDataOrThrow } from './decode_asset_data'; +export { assetDataUtils } from './asset_data_utils'; export { eip712Utils } from './eip712_utils'; diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts new file mode 100644 index 0000000000..a989377580 --- /dev/null +++ b/packages/order-utils/test/asset_data_utils_test.ts @@ -0,0 +1,186 @@ +import * as chai from 'chai'; + +import { AssetProxyId, ERC1155AssetData, ERC20AssetData, ERC721AssetData, MultiAssetData } from '@0x/types'; +import { BigNumber } from '@0x/utils'; + +import { assetDataUtils } from '../src/asset_data_utils'; + +import { chaiSetup } from './utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +const KNOWN_ERC20_ENCODING = { + address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', + assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48', +}; +const KNOWN_ERC721_ENCODING = { + address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', + tokenId: new BigNumber(1), + assetData: + '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001', +}; +const KNOWN_ERC1155_ENCODING = { + tokenAddress: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', + tokenIds: [new BigNumber(100), new BigNumber(1001), new BigNumber(10001)], + tokenValues: [new BigNumber(200), new BigNumber(2001), new BigNumber(20001)], + callbackData: + '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001', + assetData: + '0xa7cb5fb70000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000003e90000000000000000000000000000000000000000000000000000000000002711000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000007d10000000000000000000000000000000000000000000000000000000000004e210000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000', +}; +const KNOWN_MULTI_ASSET_ENCODING = { + amounts: [new BigNumber(70), new BigNumber(1), new BigNumber(18)], + nestedAssetData: [ + KNOWN_ERC20_ENCODING.assetData, + KNOWN_ERC721_ENCODING.assetData, + KNOWN_ERC1155_ENCODING.assetData, + ], + assetData: + '0x94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000204a7cb5fb70000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000003e90000000000000000000000000000000000000000000000000000000000002711000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000007d10000000000000000000000000000000000000000000000000000000000004e210000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c4800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', +}; + +describe('assetDataUtils', () => { + it('should encode ERC20', () => { + const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ERC20_ENCODING.address); + expect(assetData).to.equal(KNOWN_ERC20_ENCODING.assetData); + }); + it('should decode ERC20', () => { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow( + KNOWN_ERC20_ENCODING.assetData, + ) as ERC20AssetData; // tslint:disable-line:no-unnecessary-type-assertion + 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_ERC721_ENCODING.address, + KNOWN_ERC721_ENCODING.tokenId, + ); + expect(assetData).to.equal(KNOWN_ERC721_ENCODING.assetData); + }); + it('should decode ERC721', () => { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow( + KNOWN_ERC721_ENCODING.assetData, + ) as ERC721AssetData; // tslint:disable-line:no-unnecessary-type-assertion + 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 ERC1155', () => { + const assetData = assetDataUtils.encodeERC1155AssetData( + KNOWN_ERC1155_ENCODING.tokenAddress, + KNOWN_ERC1155_ENCODING.tokenIds, + KNOWN_ERC1155_ENCODING.tokenValues, + KNOWN_ERC1155_ENCODING.callbackData, + ); + expect(assetData).to.equal(KNOWN_ERC1155_ENCODING.assetData); + }); + it('should decode ERC1155', () => { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow( + KNOWN_ERC1155_ENCODING.assetData, + ) as ERC1155AssetData; // tslint:disable-line:no-unnecessary-type-assertion + expect(decodedAssetData.assetProxyId).to.be.equal(AssetProxyId.ERC1155); + expect(decodedAssetData.tokenAddress).to.be.equal(KNOWN_ERC1155_ENCODING.tokenAddress); + expect(decodedAssetData.tokenValues).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenValues); + expect(decodedAssetData.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds); + expect(decodedAssetData.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData); + }); + it('should encode ERC20, ERC721 and ERC1155 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, ERC721 and ERC1155 multiAssetData', () => { + const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow( + KNOWN_MULTI_ASSET_ENCODING.assetData, + ) as MultiAssetData; // tslint:disable-line:no-unnecessary-type-assertion + 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); + }); + 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); + expect(decodedAssetData.nestedAssetData.length).to.equal(3); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc20AssetData = decodedAssetData.nestedAssetData[0] as ERC20AssetData; + expect(decodedErc20AssetData.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); + expect(decodedErc20AssetData.assetProxyId).to.equal(AssetProxyId.ERC20); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc721AssetData = decodedAssetData.nestedAssetData[1] as ERC721AssetData; + 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); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc1155AssetData = decodedAssetData.nestedAssetData[2] as ERC1155AssetData; + expect(decodedErc1155AssetData.tokenAddress).to.be.equal(KNOWN_ERC1155_ENCODING.tokenAddress); + expect(decodedErc1155AssetData.tokenValues).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenValues); + expect(decodedErc1155AssetData.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds); + expect(decodedErc1155AssetData.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData); + }); + it('should recursively decode nested assetData within multiAssetData', () => { + // setup test parameters + const erc20Amount = new BigNumber(1); + const erc721Amount = new BigNumber(1); + const erc1155Amount = new BigNumber(15); + const nestedAssetsAmount = new BigNumber(2); + const amounts = [erc20Amount, erc721Amount, erc1155Amount, nestedAssetsAmount]; + const nestedAssetData = [ + KNOWN_ERC20_ENCODING.assetData, + KNOWN_ERC721_ENCODING.assetData, + KNOWN_ERC1155_ENCODING.assetData, + KNOWN_MULTI_ASSET_ENCODING.assetData, + ]; + const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData); + // execute test + const decodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively(assetData); + // validate asset data + expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); + const expectedAmounts = [ + erc20Amount, + erc721Amount, + erc1155Amount, + KNOWN_MULTI_ASSET_ENCODING.amounts[0].times(nestedAssetsAmount), + KNOWN_MULTI_ASSET_ENCODING.amounts[1].times(nestedAssetsAmount), + KNOWN_MULTI_ASSET_ENCODING.amounts[2].times(nestedAssetsAmount), + ]; + expect(decodedAssetData.amounts).to.deep.equal(expectedAmounts); + const expectedNestedAssetDataLength = 6; + expect(decodedAssetData.nestedAssetData.length).to.be.equal(expectedNestedAssetDataLength); + // validate nested asset data (outer) + let nestedAssetDataIndex = 0; + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc20AssetData1 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++] as ERC20AssetData; + expect(decodedErc20AssetData1.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); + expect(decodedErc20AssetData1.assetProxyId).to.equal(AssetProxyId.ERC20); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc721AssetData1 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++] as ERC721AssetData; + expect(decodedErc721AssetData1.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); + expect(decodedErc721AssetData1.assetProxyId).to.equal(AssetProxyId.ERC721); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc1155AssetData1 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++] as ERC1155AssetData; + expect(decodedErc1155AssetData1.tokenAddress).to.be.equal(KNOWN_ERC1155_ENCODING.tokenAddress); + expect(decodedErc1155AssetData1.tokenValues).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenValues); + expect(decodedErc1155AssetData1.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds); + expect(decodedErc1155AssetData1.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData); + // validate nested asset data (inner) + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc20AssetData2 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++] as ERC20AssetData; + expect(decodedErc20AssetData2.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address); + expect(decodedErc20AssetData2.assetProxyId).to.equal(AssetProxyId.ERC20); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc721AssetData2 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++] as ERC721AssetData; + expect(decodedErc721AssetData2.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address); + expect(decodedErc721AssetData2.assetProxyId).to.equal(AssetProxyId.ERC721); + // tslint:disable-next-line:no-unnecessary-type-assertion + const decodedErc1155AssetData2 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++] as ERC1155AssetData; + expect(decodedErc1155AssetData2.tokenAddress).to.be.equal(KNOWN_ERC1155_ENCODING.tokenAddress); + expect(decodedErc1155AssetData2.tokenValues).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenValues); + expect(decodedErc1155AssetData2.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds); + expect(decodedErc1155AssetData2.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData); + }); +});