Add StaticCallAssetData encoding and decoding

This commit is contained in:
Amir Bandeali
2019-06-08 16:33:19 -07:00
parent 720d335b09
commit 570c1e1809
3 changed files with 60 additions and 14 deletions

View File

@@ -1,13 +1,13 @@
import { import {
AssetProxyId, AssetProxyId,
ERC1155AssetData, ERC1155AssetData,
ERC1155AssetDataAbi,
ERC1155AssetDataNoProxyId, ERC1155AssetDataNoProxyId,
ERC20AssetData, ERC20AssetData,
ERC721AssetData, ERC721AssetData,
MultiAssetData, MultiAssetData,
MultiAssetDataWithRecursiveDecoding, MultiAssetDataWithRecursiveDecoding,
SingleAssetData, SingleAssetData,
StaticCallAssetData,
} from '@0x/types'; } from '@0x/types';
import { AbiEncoder, BigNumber } from '@0x/utils'; import { AbiEncoder, BigNumber } from '@0x/utils';
import * as _ from 'lodash'; import * as _ from 'lodash';
@@ -31,7 +31,7 @@ export const assetDataUtils = {
return assetData; return assetData;
}, },
/** /**
* Decodes an ERC20 assetData hex string into it's corresponding ERC20 tokenAddress & assetProxyId * Decodes an ERC20 assetData hex string into its corresponding ERC20 tokenAddress & assetProxyId
* @param assetData Hex encoded assetData string to decode * @param assetData Hex encoded assetData string to decode
* @return An object containing the decoded tokenAddress & assetProxyId * @return An object containing the decoded tokenAddress & assetProxyId
*/ */
@@ -60,7 +60,7 @@ export const assetDataUtils = {
return assetData; return assetData;
}, },
/** /**
* Decodes an ERC721 assetData hex string into it's corresponding ERC721 tokenAddress, tokenId & assetProxyId * Decodes an ERC721 assetData hex string into its corresponding ERC721 tokenAddress, tokenId & assetProxyId
* @param assetData Hex encoded assetData string to decode * @param assetData Hex encoded assetData string to decode
* @return An object containing the decoded tokenAddress, tokenId & assetProxyId * @return An object containing the decoded tokenAddress, tokenId & assetProxyId
*/ */
@@ -91,13 +91,13 @@ export const assetDataUtils = {
tokenValues: BigNumber[], tokenValues: BigNumber[],
callbackData: string, callbackData: string,
): string { ): string {
const abiEncoder = AbiEncoder.createMethod('ERC1155Assets', ERC1155AssetDataAbi); const abiEncoder = AbiEncoder.createMethod('ERC1155Assets', constants.ERC1155_METHOD_ABI.inputs);
const args = [tokenAddress, tokenIds, tokenValues, callbackData]; const args = [tokenAddress, tokenIds, tokenValues, callbackData];
const assetData = abiEncoder.encode(args, encodingRules); const assetData = abiEncoder.encode(args, encodingRules);
return assetData; return assetData;
}, },
/** /**
* Decodes an ERC1155 assetData hex string into it's corresponding ERC1155 components. * Decodes an ERC1155 assetData hex string into its corresponding ERC1155 components.
* @param assetData Hex encoded assetData string to decode * @param assetData Hex encoded assetData string to decode
* @return An object containing the decoded tokenAddress, tokenIds, tokenValues, callbackData & assetProxyId * @return An object containing the decoded tokenAddress, tokenIds, tokenValues, callbackData & assetProxyId
*/ */
@@ -106,7 +106,7 @@ export const assetDataUtils = {
if (assetProxyId !== AssetProxyId.ERC1155) { if (assetProxyId !== AssetProxyId.ERC1155) {
throw new Error(`Invalid assetProxyId. Expected '${AssetProxyId.ERC1155}', got '${assetProxyId}'`); throw new Error(`Invalid assetProxyId. Expected '${AssetProxyId.ERC1155}', got '${assetProxyId}'`);
} }
const abiEncoder = AbiEncoder.createMethod('ERC1155Assets', ERC1155AssetDataAbi); const abiEncoder = AbiEncoder.createMethod('ERC1155Assets', constants.ERC1155_METHOD_ABI.inputs);
// tslint:disable-next-line:no-unnecessary-type-assertion // tslint:disable-next-line:no-unnecessary-type-assertion
const decodedAssetData = abiEncoder.decode(assetData, decodingRules) as ERC1155AssetDataNoProxyId; const decodedAssetData = abiEncoder.decode(assetData, decodingRules) as ERC1155AssetDataNoProxyId;
return { return {
@@ -139,7 +139,7 @@ export const assetDataUtils = {
return assetData; return assetData;
}, },
/** /**
* Decodes a MultiAsset assetData hex string into it's corresponding amounts and nestedAssetData * Decodes a MultiAsset assetData hex string into its corresponding amounts and nestedAssetData
* @param assetData Hex encoded assetData string to decode * @param assetData Hex encoded assetData string to decode
* @return An object containing the decoded amounts and nestedAssetData * @return An object containing the decoded amounts and nestedAssetData
*/ */
@@ -165,7 +165,7 @@ export const assetDataUtils = {
}; };
}, },
/** /**
* Decodes a MultiAsset assetData hex string into it's corresponding amounts and decoded nestedAssetData elements (all nested elements are flattened) * 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 * @param assetData Hex encoded assetData string to decode
* @return An object containing the decoded amounts and nestedAssetData * @return An object containing the decoded amounts and nestedAssetData
*/ */
@@ -201,6 +201,35 @@ export const assetDataUtils = {
nestedAssetData: flattenedDecodedNestedAssetData as SingleAssetData[], nestedAssetData: flattenedDecodedNestedAssetData as SingleAssetData[],
}; };
}, },
/**
* Encodes StaticCallProxy data into an assetData hex string
* @param callTarget Address of contract to call from StaticCallProxy
* @param staticCallData The function data that will be called on the callTarget contract
* @param callResultHash The keccak256 hash of the ABI encoded expected output of the static call
* @return The hex encoded assetData string
*/
encodeStaticCallAssetData(callTarget: string, staticCallData: string, callResultHash: string): string {
const abiEncoder = AbiEncoder.createMethod('StaticCall', constants.STATIC_CALL_METHOD_ABI.inputs);
const args = [callTarget, staticCallData, callResultHash];
const assetData = abiEncoder.encode(args, encodingRules);
return assetData;
},
/**
* Decoded StaticCall assetData into its corresponding callTarget, staticCallData, and expected callResultHash
* @param assetData Hex encoded assetData string to decode
* @return An object containing the decoded callTarget, staticCallData, and expected callResultHash
*/
decodeStaticCallAssetData(assetData: string): StaticCallAssetData {
const abiEncoder = AbiEncoder.createMethod('StaticCall', constants.STATIC_CALL_METHOD_ABI.inputs);
const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
const decodedAssetData = abiEncoder.decode(assetData, decodingRules) as any;
return {
assetProxyId,
callTarget: decodedAssetData.callTarget,
callResultHash: decodedAssetData.callResultHash,
staticCallData: decodedAssetData.staticCallData,
};
},
/** /**
* Decode and return the assetProxyId from the assetData * Decode and return the assetProxyId from the assetData
* @param assetData Hex encoded assetData string to decode * @param assetData Hex encoded assetData string to decode
@@ -219,6 +248,7 @@ export const assetDataUtils = {
assetProxyId !== AssetProxyId.ERC20 && assetProxyId !== AssetProxyId.ERC20 &&
assetProxyId !== AssetProxyId.ERC721 && assetProxyId !== AssetProxyId.ERC721 &&
assetProxyId !== AssetProxyId.ERC1155 && assetProxyId !== AssetProxyId.ERC1155 &&
assetProxyId !== AssetProxyId.StaticCall &&
assetProxyId !== AssetProxyId.MultiAsset assetProxyId !== AssetProxyId.MultiAsset
) { ) {
throw new Error(`Invalid assetProxyId: ${assetProxyId}`); throw new Error(`Invalid assetProxyId: ${assetProxyId}`);
@@ -253,6 +283,13 @@ export const assetDataUtils = {
isMultiAssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is MultiAssetData { isMultiAssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is MultiAssetData {
return decodedAssetData.assetProxyId === AssetProxyId.MultiAsset; return decodedAssetData.assetProxyId === AssetProxyId.MultiAsset;
}, },
/**
* Checks if the decoded asset data is valid StaticCall data
* @param decodedAssetData The decoded asset data to check
*/
isStaticCallAssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is StaticCallAssetData {
return decodedAssetData.assetProxyId === AssetProxyId.StaticCall;
},
/** /**
* Throws if the length or assetProxyId are invalid for the ERC20Proxy. * Throws if the length or assetProxyId are invalid for the ERC20Proxy.
* @param assetData Hex encoded assetData string * @param assetData Hex encoded assetData string
@@ -324,6 +361,13 @@ export const assetDataUtils = {
); );
} }
}, },
/**
* Throws if the assetData is not StaticCallData.
* @param assetData Hex encoded assetData string
*/
assertIsStaticCallAssetData(assetData: string): void {
assetDataUtils.decodeStaticCallAssetData(assetData);
},
/** /**
* Throws if the length or assetProxyId are invalid for the corresponding AssetProxy. * Throws if the length or assetProxyId are invalid for the corresponding AssetProxy.
* @param assetData Hex encoded assetData string * @param assetData Hex encoded assetData string
@@ -348,7 +392,7 @@ export const assetDataUtils = {
} }
}, },
/** /**
* Decode any assetData into it's corresponding assetData object * Decode any assetData into its corresponding assetData object
* @param assetData Hex encoded assetData string to decode * @param assetData Hex encoded assetData string to decode
* @return Either a ERC20, ERC721, ERC1155, or MultiAsset assetData object * @return Either a ERC20, ERC721, ERC1155, or MultiAsset assetData object
*/ */

View File

@@ -48,7 +48,6 @@ export {
ERC20AssetData, ERC20AssetData,
ERC721AssetData, ERC721AssetData,
ERC1155AssetData, ERC1155AssetData,
ERC1155AssetDataAbi,
MultiAssetData, MultiAssetData,
MultiAssetDataWithRecursiveDecoding, MultiAssetDataWithRecursiveDecoding,
AssetProxyId, AssetProxyId,

View File

@@ -1,6 +1,6 @@
import * as chai from 'chai'; import * as chai from 'chai';
import { AssetProxyId, ERC1155AssetData, ERC721AssetData } from '@0x/types'; import { AssetProxyId, ERC1155AssetData, ERC20AssetData, ERC721AssetData } from '@0x/types';
import { BigNumber } from '@0x/utils'; import { BigNumber } from '@0x/utils';
import { assetDataUtils } from '../src/asset_data_utils'; import { assetDataUtils } from '../src/asset_data_utils';
@@ -98,7 +98,8 @@ describe('assetDataUtils', () => {
expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset); expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset);
expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts); expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts);
expect(decodedAssetData.nestedAssetData.length).to.equal(3); expect(decodedAssetData.nestedAssetData.length).to.equal(3);
const decodedErc20AssetData = decodedAssetData.nestedAssetData[0]; // 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.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address);
expect(decodedErc20AssetData.assetProxyId).to.equal(AssetProxyId.ERC20); expect(decodedErc20AssetData.assetProxyId).to.equal(AssetProxyId.ERC20);
// tslint:disable-next-line:no-unnecessary-type-assertion // tslint:disable-next-line:no-unnecessary-type-assertion
@@ -144,7 +145,8 @@ describe('assetDataUtils', () => {
expect(decodedAssetData.nestedAssetData.length).to.be.equal(expectedNestedAssetDataLength); expect(decodedAssetData.nestedAssetData.length).to.be.equal(expectedNestedAssetDataLength);
// validate nested asset data (outer) // validate nested asset data (outer)
let nestedAssetDataIndex = 0; let nestedAssetDataIndex = 0;
const decodedErc20AssetData1 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++]; // 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.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address);
expect(decodedErc20AssetData1.assetProxyId).to.equal(AssetProxyId.ERC20); expect(decodedErc20AssetData1.assetProxyId).to.equal(AssetProxyId.ERC20);
// tslint:disable-next-line:no-unnecessary-type-assertion // tslint:disable-next-line:no-unnecessary-type-assertion
@@ -158,7 +160,8 @@ describe('assetDataUtils', () => {
expect(decodedErc1155AssetData1.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds); expect(decodedErc1155AssetData1.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds);
expect(decodedErc1155AssetData1.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData); expect(decodedErc1155AssetData1.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData);
// validate nested asset data (inner) // validate nested asset data (inner)
const decodedErc20AssetData2 = decodedAssetData.nestedAssetData[nestedAssetDataIndex++]; // 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.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address);
expect(decodedErc20AssetData2.assetProxyId).to.equal(AssetProxyId.ERC20); expect(decodedErc20AssetData2.assetProxyId).to.equal(AssetProxyId.ERC20);
// tslint:disable-next-line:no-unnecessary-type-assertion // tslint:disable-next-line:no-unnecessary-type-assertion