From 88ae8311c85f8e866aeae634c91a08101d450547 Mon Sep 17 00:00:00 2001 From: Xianny <8582774+xianny@users.noreply.github.com> Date: Fri, 12 Jul 2019 17:27:06 -0700 Subject: [PATCH] Copy dutch auction asset data utils to @0x/order-utils (#1943) * copy dutch auction assetdata utils to @0x/order-utils * encode/decode dutch auction asset data using order-utils --- packages/0x.js/src/index.ts | 2 +- packages/contract-wrappers/CHANGELOG.json | 8 +++ .../dutch_auction_wrapper.ts | 41 ++------------ packages/contract-wrappers/src/index.ts | 2 +- packages/contract-wrappers/src/types.ts | 8 +-- packages/order-utils/CHANGELOG.json | 19 +++---- packages/order-utils/src/asset_data_utils.ts | 56 +++++++++++++++++++ packages/order-utils/src/index.ts | 2 + .../order-utils/test/asset_data_utils_test.ts | 33 +++++++++++ packages/types/src/index.ts | 6 ++ 10 files changed, 122 insertions(+), 55 deletions(-) diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts index 650e82a0ac..6951f36b48 100644 --- a/packages/0x.js/src/index.ts +++ b/packages/0x.js/src/index.ts @@ -37,7 +37,6 @@ export { OrderAndTraderInfo, TraderInfo, ValidateOrderFillableOpts, - DutchAuctionData, } from '@0x/contract-wrappers'; export { @@ -85,6 +84,7 @@ export { ExchangeContractErrs, Order, SignedOrder, + DutchAuctionData, ECSignature, OrderStateValid, OrderStateInvalid, diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index a10fdc48c6..fa8c87afce 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,4 +1,12 @@ [ + { + "version": "9.1.5", + "changes": [ + { + "note": "Use assetDataUtils for encoding and decoding DutchAuctionData" + } + ] + }, { "timestamp": 1558712885, "version": "9.1.4", diff --git a/packages/contract-wrappers/src/contract_wrappers/dutch_auction_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/dutch_auction_wrapper.ts index fc18e510c0..774ec1d16d 100644 --- a/packages/contract-wrappers/src/contract_wrappers/dutch_auction_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/dutch_auction_wrapper.ts @@ -2,17 +2,15 @@ import { DutchAuctionContract } from '@0x/abi-gen-wrappers'; import { DutchAuction } from '@0x/contract-artifacts'; import { schemas } from '@0x/json-schemas'; import { assetDataUtils } from '@0x/order-utils'; -import { DutchAuctionDetails, SignedOrder } from '@0x/types'; +import { DutchAuctionData, DutchAuctionDetails, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { ContractAbi } from 'ethereum-types'; -import * as ethAbi from 'ethereumjs-abi'; -import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema'; import { txOptsSchema } from '../schemas/tx_opts_schema'; -import { DutchAuctionData, DutchAuctionWrapperError, OrderTransactionOpts } from '../types'; +import { DutchAuctionWrapperError, OrderTransactionOpts } from '../types'; import { assert } from '../utils/assert'; import { _getDefaultContractAddresses } from '../utils/contract_addresses'; @@ -36,14 +34,7 @@ export class DutchAuctionWrapper extends ContractWrapper { beginTimeSeconds: BigNumber, beginAmount: BigNumber, ): string { - const assetDataBuffer = ethUtil.toBuffer(assetData); - const abiEncodedAuctionData = (ethAbi as any).rawEncode( - ['uint256', 'uint256'], - [beginTimeSeconds.toString(), beginAmount.toString()], - ); - const abiEncodedAuctionDataBuffer = ethUtil.toBuffer(abiEncodedAuctionData); - const dutchAuctionDataBuffer = Buffer.concat([assetDataBuffer, abiEncodedAuctionDataBuffer]); - const dutchAuctionData = ethUtil.bufferToHex(dutchAuctionDataBuffer); + const dutchAuctionData = assetDataUtils.encodeDutchAuctionAssetData(assetData, beginTimeSeconds, beginAmount); return dutchAuctionData; } /** @@ -54,30 +45,8 @@ export class DutchAuctionWrapper extends ContractWrapper { * @return An object containing the auction asset, auction begin time and auction begin amount. */ public static decodeDutchAuctionData(dutchAuctionData: string): DutchAuctionData { - const dutchAuctionDataBuffer = ethUtil.toBuffer(dutchAuctionData); - // Decode asset data - const dutchAuctionDataLengthInBytes = 64; - const assetDataBuffer = dutchAuctionDataBuffer.slice( - 0, - dutchAuctionDataBuffer.byteLength - dutchAuctionDataLengthInBytes, - ); - const assetDataHex = ethUtil.bufferToHex(assetDataBuffer); - const assetData = assetDataUtils.decodeAssetDataOrThrow(assetDataHex); - // Decode auction details - const dutchAuctionDetailsBuffer = dutchAuctionDataBuffer.slice( - dutchAuctionDataBuffer.byteLength - dutchAuctionDataLengthInBytes, - ); - const [beginTimeSecondsAsBN, beginAmountAsBN] = ethAbi.rawDecode( - ['uint256', 'uint256'], - dutchAuctionDetailsBuffer, - ); - const beginTimeSeconds = new BigNumber(beginTimeSecondsAsBN.toString()); - const beginAmount = new BigNumber(beginAmountAsBN.toString()); - return { - assetData, - beginTimeSeconds, - beginAmount, - }; + const decoded = assetDataUtils.decodeDutchAuctionData(dutchAuctionData); + return decoded; } /** * Instantiate DutchAuctionWrapper diff --git a/packages/contract-wrappers/src/index.ts b/packages/contract-wrappers/src/index.ts index 5d4bdb2519..f179df21f9 100644 --- a/packages/contract-wrappers/src/index.ts +++ b/packages/contract-wrappers/src/index.ts @@ -58,7 +58,6 @@ export { OrderAndTraderInfo, TraderInfo, ValidateOrderFillableOpts, - DutchAuctionData, CoordinatorServerCancellationResponse, CoordinatorServerError, } from './types'; @@ -73,6 +72,7 @@ export { StaticCallAssetData, MultiAssetDataWithRecursiveDecoding, DutchAuctionDetails, + DutchAuctionData, Order, SignedOrder, AssetProxyId, diff --git a/packages/contract-wrappers/src/types.ts b/packages/contract-wrappers/src/types.ts index 7ae38a5f08..de190c62d1 100644 --- a/packages/contract-wrappers/src/types.ts +++ b/packages/contract-wrappers/src/types.ts @@ -9,7 +9,7 @@ import { WETH9Events, } from '@0x/abi-gen-wrappers'; import { ContractAddresses } from '@0x/contract-addresses'; -import { AssetData, OrderState, SignedOrder } from '@0x/types'; +import { OrderState, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { BlockParam, ContractEventArg, DecodedLogArgs, LogEntryEvent, LogWithDecodedArgs } from 'ethereum-types'; @@ -220,12 +220,6 @@ export enum DutchAuctionWrapperError { AssetDataMismatch = 'ASSET_DATA_MISMATCH', } -export interface DutchAuctionData { - assetData: AssetData; - beginTimeSeconds: BigNumber; - beginAmount: BigNumber; -} - export { CoordinatorServerCancellationResponse, CoordinatorServerError } from './utils/coordinator_server_types'; export interface CoordinatorTransaction { diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index 6f465672c5..7225960d49 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -1,13 +1,4 @@ [ - { - "version": "8.3.0", - "changes": [ - { - "note": "Add support for marketSell utils", - "pr": 1914 - } - ] - }, { "version": "8.2.0", "changes": [ @@ -15,9 +6,17 @@ "note": "Add support for encoding/decoding StaticCallProxy assetData", "pr": 1863 }, + { + "note": "Add support for marketSell utils", + "pr": 1914 + }, + { + "note": "Add support for encoding/decoding DutchAuction assetData", + "pr": 1943 + }, { "note": "Added `validateMakerTransferThrowIfInvalidAsync` to OrderValidationUtils", - "pr": "1937" + "pr": 1937 } ] }, diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts index 8aeb54646c..1a07944726 100644 --- a/packages/order-utils/src/asset_data_utils.ts +++ b/packages/order-utils/src/asset_data_utils.ts @@ -1,5 +1,6 @@ import { AssetProxyId, + DutchAuctionData, ERC1155AssetData, ERC1155AssetDataNoProxyId, ERC20AssetData, @@ -10,6 +11,8 @@ import { StaticCallAssetData, } from '@0x/types'; import { AbiEncoder, BigNumber } from '@0x/utils'; +import * as ethAbi from 'ethereumjs-abi'; +import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { constants } from './constants'; @@ -230,6 +233,59 @@ export const assetDataUtils = { staticCallData: decodedAssetData.staticCallData, }; }, + /** + * Dutch auction details are encoded with the asset data for a 0x order. This function produces a hex + * encoded assetData string, containing information both about the asset being traded and the + * dutch auction; which is usable in the makerAssetData or takerAssetData fields in a 0x order. + * @param assetData Hex encoded assetData string for the asset being auctioned. + * @param beginTimeSeconds Begin time of the dutch auction. + * @param beginAmount Starting amount being sold in the dutch auction. + * @return The hex encoded assetData string. + */ + encodeDutchAuctionAssetData(assetData: string, beginTimeSeconds: BigNumber, beginAmount: BigNumber): string { + const assetDataBuffer = ethUtil.toBuffer(assetData); + const abiEncodedAuctionData = (ethAbi as any).rawEncode( + ['uint256', 'uint256'], + [beginTimeSeconds.toString(), beginAmount.toString()], + ); + const abiEncodedAuctionDataBuffer = ethUtil.toBuffer(abiEncodedAuctionData); + const dutchAuctionDataBuffer = Buffer.concat([assetDataBuffer, abiEncodedAuctionDataBuffer]); + const dutchAuctionData = ethUtil.bufferToHex(dutchAuctionDataBuffer); + return dutchAuctionData; + }, + /** + * Dutch auction details are encoded with the asset data for a 0x order. This function decodes a hex + * encoded assetData string, containing information both about the asset being traded and the + * dutch auction. + * @param dutchAuctionData Hex encoded assetData string for the asset being auctioned. + * @return An object containing the auction asset, auction begin time and auction begin amount. + */ + decodeDutchAuctionData(dutchAuctionData: string): DutchAuctionData { + const dutchAuctionDataBuffer = ethUtil.toBuffer(dutchAuctionData); + // Decode asset data + const dutchAuctionDataLengthInBytes = 64; + const assetDataBuffer = dutchAuctionDataBuffer.slice( + 0, + dutchAuctionDataBuffer.byteLength - dutchAuctionDataLengthInBytes, + ); + const assetDataHex = ethUtil.bufferToHex(assetDataBuffer); + const assetData = assetDataUtils.decodeAssetDataOrThrow(assetDataHex); + // Decode auction details + const dutchAuctionDetailsBuffer = dutchAuctionDataBuffer.slice( + dutchAuctionDataBuffer.byteLength - dutchAuctionDataLengthInBytes, + ); + const [beginTimeSecondsAsBN, beginAmountAsBN] = ethAbi.rawDecode( + ['uint256', 'uint256'], + dutchAuctionDetailsBuffer, + ); + const beginTimeSeconds = new BigNumber(beginTimeSecondsAsBN.toString()); + const beginAmount = new BigNumber(beginAmountAsBN.toString()); + return { + assetData, + beginTimeSeconds, + beginAmount, + }; + }, /** * Decode and return the assetProxyId from the assetData * @param assetData Hex encoded assetData string to decode diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 90848a671c..46e5915d60 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -44,7 +44,9 @@ export { OrderRelevantState, OrderState, ECSignature, + AssetData, SingleAssetData, + DutchAuctionData, ERC20AssetData, ERC721AssetData, ERC1155AssetData, diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts index ab168e9fa9..f876fed308 100644 --- a/packages/order-utils/test/asset_data_utils_test.ts +++ b/packages/order-utils/test/asset_data_utils_test.ts @@ -40,6 +40,15 @@ const KNOWN_MULTI_ASSET_ENCODING = { '0x94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000204a7cb5fb70000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000003e90000000000000000000000000000000000000000000000000000000000002711000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000007d10000000000000000000000000000000000000000000000000000000000004e210000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c4800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', }; +const KNOWN_DUTCH_AUCTION_ENCODING = { + tokenAddress: '0x34d402f14d58e001d8efbe6585051bf9706aa064', + assetData: '0xf47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064', // ERC20 + beginTimeSeconds: new BigNumber(1562807905), + beginAmount: new BigNumber(5), + dutchAuctionAssetData: + '0xf47261b000000000000000000000000034d402f14d58e001d8efbe6585051bf9706aa064000000000000000000000000000000000000000000000000000000005d268e610000000000000000000000000000000000000000000000000000000000000005', +}; + describe('assetDataUtils', () => { it('should encode ERC20', () => { const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ERC20_ENCODING.address); @@ -175,4 +184,28 @@ describe('assetDataUtils', () => { expect(decodedErc1155AssetData2.tokenIds).to.be.deep.equal(KNOWN_ERC1155_ENCODING.tokenIds); expect(decodedErc1155AssetData2.callbackData).to.be.equal(KNOWN_ERC1155_ENCODING.callbackData); }); + it('should encode Dutch Auction', async () => { + const encodedAssetData = assetDataUtils.encodeDutchAuctionAssetData( + KNOWN_DUTCH_AUCTION_ENCODING.assetData, + KNOWN_DUTCH_AUCTION_ENCODING.beginTimeSeconds, + KNOWN_DUTCH_AUCTION_ENCODING.beginAmount, + ); + expect(encodedAssetData).to.be.equal(KNOWN_DUTCH_AUCTION_ENCODING.dutchAuctionAssetData); + }); + it('should decode Dutch Auction', async () => { + const { assetData, beginTimeSeconds, beginAmount } = assetDataUtils.decodeDutchAuctionData( + KNOWN_DUTCH_AUCTION_ENCODING.dutchAuctionAssetData, + ); + + const { assetProxyId, tokenAddress } = assetDataUtils.decodeERC20AssetData( + KNOWN_DUTCH_AUCTION_ENCODING.assetData, + ); + + // tslint:disable:no-unnecessary-type-assertion + expect((assetData as ERC20AssetData).assetProxyId).to.be.equal(assetProxyId); + expect((assetData as ERC20AssetData).tokenAddress).to.be.equal(tokenAddress); + // tslint:enable:no-unnecessary-type-assertion + expect(beginTimeSeconds).to.deep.equal(KNOWN_DUTCH_AUCTION_ENCODING.beginTimeSeconds); + expect(beginAmount).to.deep.equal(KNOWN_DUTCH_AUCTION_ENCODING.beginAmount); + }); }); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 7bc1ff4d9a..50bc0fc338 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -216,6 +216,12 @@ export interface MultiAssetDataWithRecursiveDecoding { nestedAssetData: SingleAssetData[]; } +export interface DutchAuctionData { + assetData: AssetData; + beginTimeSeconds: BigNumber; + beginAmount: BigNumber; +} + export type AssetData = SingleAssetData | MultiAssetData | MultiAssetDataWithRecursiveDecoding; // TODO: DRY. These should be extracted from contract code.