From b50187f59c09d4ecf7a840fd22f96bcecd14bb1b Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Tue, 27 Nov 2018 11:25:51 -0800 Subject: Track install wallet clicked --- .../containers/connected_account_payment_method.ts | 37 ++++++++++++---------- packages/instant/src/types.ts | 5 +++ packages/instant/src/util/analytics.ts | 5 ++- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/packages/instant/src/containers/connected_account_payment_method.ts b/packages/instant/src/containers/connected_account_payment_method.ts index cdeb49a25..e9327a288 100644 --- a/packages/instant/src/containers/connected_account_payment_method.ts +++ b/packages/instant/src/containers/connected_account_payment_method.ts @@ -11,7 +11,7 @@ import { import { Action, actions } from '../redux/actions'; import { asyncData } from '../redux/async_data'; import { State } from '../redux/reducer'; -import { Network, Omit, OperatingSystem, ProviderState, StandardSlidingPanelContent } from '../types'; +import { Network, Omit, OperatingSystem, ProviderState, StandardSlidingPanelContent, WalletSuggestion } from '../types'; import { analytics } from '../util/analytics'; import { envUtil } from '../util/env'; @@ -60,23 +60,28 @@ const mergeProps = ( onUnlockWalletClick: () => connectedDispatch.unlockWalletAndDispatchToStore(connectedState.providerState), onInstallWalletClick: () => { const isMobile = envUtil.isMobileOperatingSystem(); - if (!isMobile) { + const walletSuggestion: WalletSuggestion = isMobile + ? WalletSuggestion.CoinbaseWallet + : WalletSuggestion.MetaMask; + + analytics.trackInstallWalletClicked(walletSuggestion); + if (walletSuggestion === WalletSuggestion.MetaMask) { connectedDispatch.openInstallWalletPanel(); - return; - } - const operatingSystem = envUtil.getOperatingSystem(); - let url = COINBASE_WALLET_SITE_URL; - switch (operatingSystem) { - case OperatingSystem.Android: - url = COINBASE_WALLET_ANDROID_APP_STORE_URL; - break; - case OperatingSystem.iOS: - url = COINBASE_WALLET_IOS_APP_STORE_URL; - break; - default: - break; + } else { + const operatingSystem = envUtil.getOperatingSystem(); + let url = COINBASE_WALLET_SITE_URL; + switch (operatingSystem) { + case OperatingSystem.Android: + url = COINBASE_WALLET_ANDROID_APP_STORE_URL; + break; + case OperatingSystem.iOS: + url = COINBASE_WALLET_IOS_APP_STORE_URL; + break; + default: + break; + } + window.open(url, '_blank'); } - window.open(url, '_blank'); }, }); diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index 999d50fed..4ad9c9d4f 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -149,6 +149,11 @@ export enum Browser { Other = 'OTHER', } +export enum WalletSuggestion { + CoinbaseWallet = 'Coinbase Wallet', + MetaMask = 'MetaMask', +} + export enum OperatingSystem { Android = 'ANDROID', iOS = 'IOS', diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts index 2bb974254..e35a9e13f 100644 --- a/packages/instant/src/util/analytics.ts +++ b/packages/instant/src/util/analytics.ts @@ -1,7 +1,7 @@ import { BuyQuote } from '@0x/asset-buyer'; import * as _ from 'lodash'; -import { AffiliateInfo, Asset, Network, OrderSource, ProviderState } from '../types'; +import { AffiliateInfo, Asset, Network, OrderSource, ProviderState, WalletSuggestion } from '../types'; import { EventProperties, heapUtil } from './heap'; @@ -30,6 +30,7 @@ enum EventNames { BUY_TX_SUBMITTED = 'Buy - Tx Submitted', BUY_TX_SUCCEEDED = 'Buy - Tx Succeeded', BUY_TX_FAILED = 'Buy - Tx Failed', + INSTALL_WALLET_CLICKED = 'Install Wallet - Clicked', TOKEN_SELECTOR_OPENED = 'Token Selector - Opened', TOKEN_SELECTOR_CLOSED = 'Token Selector - Closed', TOKEN_SELECTOR_CHOSE = 'Token Selector - Chose', @@ -164,6 +165,8 @@ export const analytics = { expectedTxTimeMs: expectedEndTimeUnix - startTimeUnix, actualTxTimeMs: new Date().getTime() - startTimeUnix, }), + trackInstallWalletClicked: (walletSuggestion: WalletSuggestion) => + trackingEventFnWithPayload(EventNames.INSTALL_WALLET_CLICKED)({ walletSuggestion }), trackTokenSelectorOpened: trackingEventFnWithoutPayload(EventNames.TOKEN_SELECTOR_OPENED), trackTokenSelectorClosed: (closedVia: TokenSelectorClosedVia) => trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_CLOSED)({ closedVia }), -- cgit v1.2.3 From 462a5face9b2b24c1e9d20a6b894809faa512232 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Tue, 27 Nov 2018 13:30:28 -0800 Subject: feat(instant): Install Wallet analytics --- .../src/components/install_wallet_panel_content.tsx | 9 ++++++++- packages/instant/src/components/standard_panel_content.tsx | 13 ++++++++++++- packages/instant/src/redux/analytics_middleware.ts | 14 +++++++++++++- packages/instant/src/util/analytics.ts | 10 ++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/packages/instant/src/components/install_wallet_panel_content.tsx b/packages/instant/src/components/install_wallet_panel_content.tsx index 88c26f59c..481d82da0 100644 --- a/packages/instant/src/components/install_wallet_panel_content.tsx +++ b/packages/instant/src/components/install_wallet_panel_content.tsx @@ -8,7 +8,9 @@ import { } from '../constants'; import { ColorOption } from '../style/theme'; import { Browser } from '../types'; +import { analytics } from '../util/analytics'; import { envUtil } from '../util/env'; +import { util } from '../util/util'; import { MetaMaskLogo } from './meta_mask_logo'; import { StandardPanelContent, StandardPanelContentProps } from './standard_panel_content'; @@ -45,6 +47,10 @@ export class InstallWalletPanelContent extends React.Component { + analytics.trackInstallWalletModalClickedGet(); + util.createOpenUrlInNewWindow(actionUrl)(); + }; return { image: , title: 'Install MetaMask', @@ -52,10 +58,11 @@ export class InstallWalletPanelContent extends React.Component void; } export interface StandardPanelContentProps { @@ -21,6 +23,15 @@ export interface StandardPanelContentProps { const SPACING_BETWEEN_PX = '20px'; +const onMoreInfoClick = (href: string, onClick?: () => void) => { + return () => { + if (onClick) { + onClick(); + } + util.createOpenUrlInNewWindow(href)(); + }; +}; + export const StandardPanelContent: React.StatelessComponent = ({ image, title, @@ -50,7 +61,7 @@ export const StandardPanelContent: React.StatelessComponent {moreInfoSettings.text} diff --git a/packages/instant/src/redux/analytics_middleware.ts b/packages/instant/src/redux/analytics_middleware.ts index 8aa76eb77..3dc5fe924 100644 --- a/packages/instant/src/redux/analytics_middleware.ts +++ b/packages/instant/src/redux/analytics_middleware.ts @@ -3,7 +3,7 @@ import * as _ from 'lodash'; import { Middleware } from 'redux'; import { ETH_DECIMALS } from '../constants'; -import { Account, AccountState } from '../types'; +import { Account, AccountState, StandardSlidingPanelContent } from '../types'; import { analytics } from '../util/analytics'; import { Action, ActionTypes } from './actions'; @@ -77,6 +77,18 @@ export const analyticsMiddleware: Middleware = store => next => middlewareAction }); } break; + case ActionTypes.OPEN_STANDARD_SLIDING_PANEL: + const openSlidingContent = curState.standardSlidingPanelSettings.content; + if (openSlidingContent === StandardSlidingPanelContent.InstallWallet) { + analytics.trackInstallWalletModalOpened(); + } + break; + case ActionTypes.CLOSE_STANDARD_SLIDING_PANEL: + const closeSlidingContent = curState.standardSlidingPanelSettings.content; + if (closeSlidingContent === StandardSlidingPanelContent.InstallWallet) { + analytics.trackInstallWalletModalClosed(); + } + break; } return nextAction; diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts index e35a9e13f..f1ce15805 100644 --- a/packages/instant/src/util/analytics.ts +++ b/packages/instant/src/util/analytics.ts @@ -31,6 +31,10 @@ enum EventNames { BUY_TX_SUCCEEDED = 'Buy - Tx Succeeded', BUY_TX_FAILED = 'Buy - Tx Failed', INSTALL_WALLET_CLICKED = 'Install Wallet - Clicked', + INSTALL_WALLET_MODAL_OPENED = 'Install Wallet - Modal - Opened', + INSTALL_WALLET_MODAL_CLICKED_EXPLANATION = 'Install Wallet - Modal - Clicked Explanation', + INSTALL_WALLET_MODAL_CLICKED_GET = 'Install Wallet - Modal - Clicked Get', + INSTALL_WALLET_MODAL_CLOSED = 'Install Wallet - Modal - Closed', TOKEN_SELECTOR_OPENED = 'Token Selector - Opened', TOKEN_SELECTOR_CLOSED = 'Token Selector - Closed', TOKEN_SELECTOR_CHOSE = 'Token Selector - Chose', @@ -167,6 +171,12 @@ export const analytics = { }), trackInstallWalletClicked: (walletSuggestion: WalletSuggestion) => trackingEventFnWithPayload(EventNames.INSTALL_WALLET_CLICKED)({ walletSuggestion }), + trackInstallWalletModalClickedExplanation: trackingEventFnWithoutPayload( + EventNames.INSTALL_WALLET_MODAL_CLICKED_EXPLANATION, + ), + trackInstallWalletModalClickedGet: trackingEventFnWithoutPayload(EventNames.INSTALL_WALLET_MODAL_CLICKED_GET), + trackInstallWalletModalOpened: trackingEventFnWithoutPayload(EventNames.INSTALL_WALLET_MODAL_OPENED), + trackInstallWalletModalClosed: trackingEventFnWithoutPayload(EventNames.INSTALL_WALLET_MODAL_CLOSED), trackTokenSelectorOpened: trackingEventFnWithoutPayload(EventNames.TOKEN_SELECTOR_OPENED), trackTokenSelectorClosed: (closedVia: TokenSelectorClosedVia) => trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_CLOSED)({ closedVia }), -- cgit v1.2.3 From c5d6b925e4e04d5b6cfd0623d0a1449ba69d3a30 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Tue, 27 Nov 2018 15:10:40 -0800 Subject: feat(instant): Quote fetch tracking --- packages/instant/src/components/zero_ex_instant_provider.tsx | 7 +++++-- .../src/containers/selected_erc20_asset_amount_input.ts | 3 ++- packages/instant/src/redux/async_data.ts | 11 ++++++++--- packages/instant/src/types.ts | 5 +++++ packages/instant/src/util/analytics.ts | 9 ++++++++- packages/instant/src/util/buy_quote_updater.ts | 11 +++++++++-- packages/instant/src/util/heartbeater_factory.ts | 2 ++ 7 files changed, 39 insertions(+), 9 deletions(-) diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index e006a5a5f..9f2c95e36 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -11,7 +11,7 @@ import { asyncData } from '../redux/async_data'; import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer'; import { store, Store } from '../redux/store'; import { fonts } from '../style/fonts'; -import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource } from '../types'; +import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource, QuoteFetchedVia } from '../types'; import { analytics, disableAnalytics } from '../util/analytics'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; @@ -115,7 +115,10 @@ export class ZeroExInstantProvider extends React.Component { const { buyOrderState, providerState, selectedAsset, selectedAssetUnitAmount, affiliateInfo } = state; const assetBuyer = providerState.assetBuyer; @@ -99,7 +99,12 @@ export const asyncData = { dispatch, selectedAsset as ERC20Asset, selectedAssetUnitAmount, - { setPending: !options.updateSilently, dispatchErrors: !options.updateSilently, affiliateInfo }, + { + setPending: !options.updateSilently, + dispatchErrors: !options.updateSilently, + fetchedVia: options.fetchedVia, + affiliateInfo, + }, ); } }, diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index 999d50fed..a8139c185 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -21,6 +21,11 @@ export enum OrderProcessState { Failure = 'FAILURE', } +export enum QuoteFetchedVia { + Manual = 'Manual', + Heartbeat = 'Heartbeat', +} + export interface SimulatedProgress { startTimeUnix: number; expectedEndTimeUnix: number; diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts index 5bc9bb385..204b6921d 100644 --- a/packages/instant/src/util/analytics.ts +++ b/packages/instant/src/util/analytics.ts @@ -1,7 +1,7 @@ import { BuyQuote } from '@0x/asset-buyer'; import * as _ from 'lodash'; -import { AffiliateInfo, Asset, Network, OrderSource, ProviderState } from '../types'; +import { AffiliateInfo, Asset, Network, OrderSource, ProviderState, QuoteFetchedVia } from '../types'; import { EventProperties, heapUtil } from './heap'; @@ -37,6 +37,8 @@ enum EventNames { TOKEN_SELECTOR_CLOSED = 'Token Selector - Closed', TOKEN_SELECTOR_CHOSE = 'Token Selector - Chose', TOKEN_SELECTOR_SEARCHED = 'Token Selector - Searched', + QUOTE_FETCHED = 'Quote - Fetched', + QUOTE_ERROR = 'Quote - Error', } const track = (eventName: EventNames, eventProperties: EventProperties = {}): void => { @@ -177,4 +179,9 @@ export const analytics = { trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_CHOSE)(payload), trackTokenSelectorSearched: (searchText: string) => trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_SEARCHED)({ searchText }), + trackQuoteFetched: (buyQuote: BuyQuote, fetchedVia: QuoteFetchedVia) => + trackingEventFnWithPayload(EventNames.QUOTE_FETCHED)({ + ...buyQuoteEventProperties(buyQuote), + fetchedVia, + }), }; diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index 2fd16d781..59d3a85af 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -6,7 +6,8 @@ import { Dispatch } from 'redux'; import { oc } from 'ts-optchain'; import { Action, actions } from '../redux/actions'; -import { AffiliateInfo, ERC20Asset } from '../types'; +import { AffiliateInfo, ERC20Asset, QuoteFetchedVia } from '../types'; +import { analytics } from '../util/analytics'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; @@ -16,7 +17,12 @@ export const buyQuoteUpdater = { dispatch: Dispatch, asset: ERC20Asset, assetUnitAmount: BigNumber, - options: { setPending: boolean; dispatchErrors: boolean; affiliateInfo?: AffiliateInfo }, + options: { + setPending: boolean; + dispatchErrors: boolean; + fetchedVia: QuoteFetchedVia; + affiliateInfo?: AffiliateInfo; + }, ): Promise => { // get a new buy quote. const baseUnitValue = Web3Wrapper.toBaseUnitAmount(assetUnitAmount, asset.metaData.decimals); @@ -58,5 +64,6 @@ export const buyQuoteUpdater = { errorFlasher.clearError(dispatch); // invalidate the last buy quote. dispatch(actions.updateLatestBuyQuote(newBuyQuote)); + analytics.trackQuoteFetched(newBuyQuote, options.fetchedVia); }, }; diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts index 2b852fb0d..bf9e4291f 100644 --- a/packages/instant/src/util/heartbeater_factory.ts +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -1,5 +1,6 @@ import { asyncData } from '../redux/async_data'; import { Store } from '../redux/store'; +import { QuoteFetchedVia } from '../types'; import { Heartbeater } from './heartbeater'; @@ -19,6 +20,7 @@ export const generateBuyQuoteHeartbeater = (options: HeartbeatFactoryOptions): H return new Heartbeater(async () => { await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(store.getState(), store.dispatch, { updateSilently: true, + fetchedVia: QuoteFetchedVia.Heartbeat, }); }, shouldPerformImmediatelyOnStart); }; -- cgit v1.2.3 From f0ed0c6a351203bd5dece3469b0223829f7db8b8 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 26 Nov 2018 12:14:31 +1100 Subject: chore: Redeploy Rinkeby using testnet Exchange contract. Add Ganache contract addresses back --- packages/contract-addresses/src/index.ts | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index 57358dd38..b36997f23 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -40,14 +40,14 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { orderValidator: '0x90431a90516ab49af23a0530e04e8c7836e7122f', }, 4: { - erc20Proxy: '0x3e809c563c15a295e832e37053798ddc8d6c8dab', - erc721Proxy: '0x8e1ff02637cb5e39f2fa36c14706aa348b065b09', - zrxToken: '0x2727e688b8fd40b198cd5fe6e408e00494a06f07', + exchange: '0xbce0b5f6eb618c565c3e5f5cd69652bbc279f44e', + erc20Proxy: '0x2f5ae4f6106e89b4147651688a92256885c5f410', + erc721Proxy: '0x7656d773e11ff7383a14dcf09a9c50990481cd10', + zrxToken: '0x8080c7e4b81ecf23aa6f877cfbfd9b0c228c6ffa', etherToken: '0xc778417e063141139fce010982780140aa0cd5ab', - exchange: '0x22ebc052f43a88efa06379426120718170f2204e', - assetProxyOwner: '0x1da52d1d3a3acfa0a1836b737393b4e9931268fc', - forwarder: '0xd2dbf3250a764eaaa94fa0c84ed87c0edc8ed04e', - orderValidator: '0x39c3fc9f4d8430af2713306ce80c584752d9e1c7', + assetProxyOwner: '0xe1703da878afcebff5b7624a826902af475b9c03', + forwarder: '0x2d40589abbdee84961f3a7656b9af7adb0ee5ab4', + orderValidator: '0x0c5173a51e26b29d6126c686756fb9fbef71f762', }, 42: { erc20Proxy: '0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e', @@ -59,6 +59,17 @@ const networkToAddresses: { [networkId: number]: ContractAddresses } = { forwarder: '0x17992e4ffb22730138e4b62aaa6367fa9d3699a6', orderValidator: '0xb389da3d204b412df2f75c6afb3d0a7ce0bc283d', }, + // NetworkId 50 represents our Ganache snapshot generated from migrations. + 50: { + exchange: '0x48bacb9266a570d521063ef5dd96e61686dbe788', + erc20Proxy: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48', + erc721Proxy: '0x1d7022f5b17d2f8b695918fb48fa1089c9f85401', + zrxToken: '0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c', + etherToken: '0x0b1ba0af832d7c05fd64161e0db78e85978e8082', + assetProxyOwner: '0x34d402f14d58e001d8efbe6585051bf9706aa064', + forwarder: '0xb69e673309512a9d726f87304c6984054f87a93b', + orderValidator: '0xe86bb98fcf9bff3512c74589b78fb168200cc546', + }, }; /** -- cgit v1.2.3 From 494adedeef0e0649c1177756d4f1be3cd18f6eb0 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Tue, 27 Nov 2018 08:18:28 +1100 Subject: chore: Update CHANGELOG --- packages/contract-addresses/CHANGELOG.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index e65351c7e..a671247e2 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -1,4 +1,17 @@ [ + { + "version": "2.0.0", + "changes": [ + { + "note": "Redeployed Rinkeby with testnet Exchange artifact", + "pr": 1318 + }, + { + "note": "Added Ganache snapshot addresses for network 50", + "pr": 1318 + } + ] + }, { "version": "1.2.0", "changes": [ -- cgit v1.2.3 From d3739488aed89ce98e805d48ec14a0d2608f85d7 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Tue, 27 Nov 2018 15:28:38 -0800 Subject: Tracking quote errors --- packages/instant/src/util/analytics.ts | 16 ++++++++++++++++ packages/instant/src/util/buy_quote_updater.ts | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts index 204b6921d..dd6021453 100644 --- a/packages/instant/src/util/analytics.ts +++ b/packages/instant/src/util/analytics.ts @@ -1,4 +1,5 @@ import { BuyQuote } from '@0x/asset-buyer'; +import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; import { AffiliateInfo, Asset, Network, OrderSource, ProviderState, QuoteFetchedVia } from '../types'; @@ -184,4 +185,19 @@ export const analytics = { ...buyQuoteEventProperties(buyQuote), fetchedVia, }), + trackQuoteError: ( + errorMessage: string, + assetName: string, + assetData: string, + assetAmount: BigNumber, + fetchedVia: QuoteFetchedVia, + ) => { + trackingEventFnWithPayload(EventNames.QUOTE_ERROR)({ + errorMessage, + assetName, + assetData, + assetAmount: assetAmount.toString(), + fetchedVia, + }); + }, }; diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index 59d3a85af..3c982ed1f 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -37,6 +37,13 @@ export const buyQuoteUpdater = { } catch (error) { if (options.dispatchErrors) { dispatch(actions.setQuoteRequestStateFailure()); + analytics.trackQuoteError( + error.message ? error.message : 'other', + asset.metaData.name, + asset.assetData, + assetUnitAmount, + options.fetchedVia, + ); let errorMessage; if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); -- cgit v1.2.3 From 8b80d28029c6d840c7401b06a15652523f5faa60 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Wed, 7 Nov 2018 10:32:49 -0800 Subject: feat: Implement MultiAssetProxy --- packages/contracts/compiler.json | 1 + .../protocol/AssetProxy/MultiAssetProxy.sol | 249 +++++++++++++++++++++ .../protocol/AssetProxy/interfaces/IAssetData.sol | 11 +- 3 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol diff --git a/packages/contracts/compiler.json b/packages/contracts/compiler.json index af3980b4e..c824e4645 100644 --- a/packages/contracts/compiler.json +++ b/packages/contracts/compiler.json @@ -38,6 +38,7 @@ "IValidator", "IWallet", "MixinAuthorizable", + "MultiAssetProxy", "MultiSigWallet", "MultiSigWalletWithTimeLock", "OrderValidator", diff --git a/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol new file mode 100644 index 000000000..9260ead8f --- /dev/null +++ b/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol @@ -0,0 +1,249 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "../../utils/SafeMath/SafeMath.sol"; +import "../Exchange/MixinAssetProxyDispatcher.sol"; +import "./MixinAuthorizable.sol"; + + +contract MultiAssetProxy is + SafeMath, + MixinAssetProxyDispatcher, + MixinAuthorizable +{ + // Id of this proxy. + bytes4 constant internal PROXY_ID = bytes4(keccak256("MultiAsset(uint256[],bytes[])")); + + // solhint-disable-next-line payable-fallback + function () + external + { + assembly { + // The first 4 bytes of calldata holds the function selector + let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) + + // `transferFrom` will be called with the following parameters: + // assetData Encoded byte array. + // from Address to transfer asset from. + // to Address to transfer asset to. + // amount Amount of asset to transfer. + // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4 + if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) { + + // To lookup a value in a mapping, we load from the storage location keccak256(k, p), + // where k is the key left padded to 32 bytes and p is the storage slot + mstore(0, and(caller, 0xffffffffffffffffffffffffffffffffffffffff)) + mstore(32, authorized_slot) + + // Revert if authorized[msg.sender] == false + if iszero(sload(keccak256(0, 64))) { + // Revert with `Error("SENDER_NOT_AUTHORIZED")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000) + mstore(96, 0) + revert(0, 100) + } + + // `transferFrom`. + // The function is marked `external`, so no abi decodeding is done for + // us. Instead, we expect the `calldata` memory to contain the + // following: + // + // | Area | Offset | Length | Contents | + // |----------|--------|---------|-------------------------------------| + // | Header | 0 | 4 | function selector | + // | Params | | 4 * 32 | function parameters: | + // | | 4 | | 1. offset to assetData (*) | + // | | 36 | | 2. from | + // | | 68 | | 3. to | + // | | 100 | | 4. amount | + // | Data | | | assetData: | + // | | 132 | 32 | assetData Length | + // | | 164 | ** | assetData Contents | + // + // (*): offset is computed from start of function parameters, so offset + // by an additional 4 bytes in the calldata. + // + // (**): see table below to compute length of assetData Contents + // + // WARNING: The ABIv2 specification allows additional padding between + // the Params and Data section. This will result in a larger + // offset to assetData. + + // Load offset to `assetData` + let assetDataOffset := calldataload(4) + + // Asset data itself is encoded as follows: + // + // | Area | Offset | Length | Contents | + // |----------|-------------|---------|-------------------------------------| + // | Header | 0 | 4 | assetProxyId | + // | Params | | 2 * 32 | function parameters: | + // | | 4 | | 1. offset to amounts (*) | + // | | 36 | | 2. offset to nestedAssetData (*) | + // | Data | | | amounts: | + // | | 68 | 32 | amounts Length | + // | | 100 | a | amounts Contents | + // | | | | nestedAssetData: | + // | | 100 + a | 32 | nestedAssetData Length | + // | | 132 + a | b | nestedAssetData Contents (offsets) | + // | | 132 + a + b | | nestedAssetData[0, ..., len] | + + // Load offset to `amounts` + // We must add 4 (function selector) + 32 (assetData len) + 4 (assetProxyId) to the assetData offset + let amountsOffset := calldataload(add(assetDataOffset, 40)) + + // Load offsets to `nestedAssetData` + let nestedAssetDataOffset := calldataload(add(assetDataOffset, 72)) + + // Load number of elements in `amounts` + // We must add 4 (function selector) + 128 (4 params * 32) + 32 (assetData len) + 4 (assetProxyId) + 32 (amounts len) to the amounts offset + let amountsContentsStart := add(amountsOffset, 200) + let amountsLen := calldataload(sub(amountsContentsStart, 32)) + + // Load number of elements in `nestedAssetData` + let nestedAssetDataContentsStart := add(nestedAssetDataOffset, 200) + let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32)) + + // Revert if number of elements in `amounts` differs from number of elements in `nestedAssetData` + if iszero(eq(amountsLen, nestedAssetDataLen)) { + // Revert with `Error("LENGTH_MISMATCH")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000000f4c454e4754485f4d49534d4154434800000000000000000000000000) + mstore(96, 0) + revert(0, 100) + } + + // Copy `transferFrom` selector, offset to `assetData`, `from`, and `to` from calldata to memory + calldatacopy( + 0, // memory can safely be overwritten from beginning + 0, // start of calldata + 100 // length of selector (4) and 3 params (32 * 3) + ) + + // Overwrite existing offset to `assetData` with our own + mstore(4, 128) + + // Load `amount` + let amount := calldataload(100) + + // Calculate number of bytes in `amounts` contents + let amountsByteLen := mul(amountsLen, 32) + + // Loop through `amounts` and `nestedAssetData`, calling `transferFrom` for each respective element + for {let i := 0} lt(i, amountsByteLen) {i := add(i, 32)} { + + // Calculate the total amount + let amountsElement := calldataload(add(amountsContentsStart, i)) + let totalAmount := mul(amountsElement, amount) + + // Revert if multiplication resulted in an overflow + if iszero(eq(div(totalAmount, amount), amountsElement)) { + // Revert with `Error("UINT256_OVERFLOW")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000001055494e543235365f4f564552464c4f57000000000000000000000000) + mstore(96, 0) + revert(0, 100) + } + + // Write `totalAmount` to memory + mstore(100, totalAmount) + + // Load offset to `nestedAssetData[i]` + let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i)) + + // Load length of `nestedAssetData[i]` + let nestedAssetDataElementContentsStart := add(nestedAssetDataElementOffset, 264) + let nestedAssetDataElementLen := calldataload(sub(nestedAssetDataElementContentsStart, 32)) + + // Revert if the `nestedAssetData` does not contain a 4 byte `assetProxyId` + if lt(nestedAssetDataElementLen, 4) { + // Revert with `Error("LENGTH_GREATER_THAN_3_REQUIRED")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000001e4c454e4754485f475245415445525f5448414e5f335f524551554952) + mstore(96, 0x4544000000000000000000000000000000000000000000000000000000000000) + revert(0, 100) + } + + // Load AssetProxy id + let assetProxyId := and(calldataload(nestedAssetDataElementContentsStart), 0xffffffff00000000000000000000000000000000000000000000000000000000) + + // To lookup a value in a mapping, we load from the storage location keccak256(k, p), + // where k is the key left padded to 32 bytes and p is the storage slot + mstore(132, assetProxyId) + mstore(164, assetProxies_slot) + let assetProxy := sload(keccak256(132, 64)) + + // Revert if AssetProxy with given id does not exist + if iszero(assetProxy) { + // Revert with `Error("ASSET_PROXY_DOES_NOT_EXIST")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x0000001a41535345545f50524f58595f444f45535f4e4f545f45584953540000) + mstore(96, 0) + revert(0, 100) + } + + // Copy `nestedAssetData[i]` from calldata to memory + calldatacopy( + 132, // memory slot after `amounts[i]` + nestedAssetDataElementOffset, // location of `nestedAssetData[i]` in calldata + add(nestedAssetDataElementLen, 32) // `nestedAssetData[i].length` plus 32 byte length + ) + + // call `assetProxy.transferFrom` + let success := call( + gas, // forward all gas + assetProxy, // call address of asset proxy + 0, // don't send any ETH + 0, // pointer to start of input + add(164, nestedAssetDataElementLen), // length of input + 100, // write output over memory that won't be reused + 512 // reserve 512 bytes for output + ) + + if iszero(success) { + revert(100, returndatasize()) + } + } + + // Return if no `transferFrom` calls reverted + return(0, 0) + } + + // Revert if undefined function is called + revert(0, 0) + } + } + + /// @dev Gets the proxy id associated with the proxy address. + /// @return Proxy id. + function getProxyId() + external + pure + returns (bytes4) + { + return PROXY_ID; + } +} \ No newline at end of file diff --git a/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetData.sol b/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetData.sol index 3e76e38dd..21130a480 100644 --- a/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetData.sol +++ b/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetData.sol @@ -18,6 +18,7 @@ // solhint-disable pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; // @dev Interface of the asset proxy's assetData. @@ -31,8 +32,14 @@ interface IAssetData { function ERC721Token( address tokenContract, - uint256 tokenId, - bytes receiverData + uint256 tokenId + ) + external + pure; + + function MultiAsset( + uint256[] amounts, + bytes[] nestedAssetData ) external pure; -- cgit v1.2.3 From 037e63ab49aba623c9878be3d39b8435fa3b987e Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sun, 11 Nov 2018 20:08:24 -0800 Subject: Factor offsets into calldata locations --- .../protocol/AssetProxy/MultiAssetProxy.sol | 68 +++++++++++++++++----- .../protocol/AssetProxy/interfaces/IAssetData.sol | 9 +-- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol index 9260ead8f..531ee2264 100644 --- a/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol +++ b/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol @@ -49,7 +49,7 @@ contract MultiAssetProxy is // To lookup a value in a mapping, we load from the storage location keccak256(k, p), // where k is the key left padded to 32 bytes and p is the storage slot - mstore(0, and(caller, 0xffffffffffffffffffffffffffffffffffffffff)) + mstore(0, caller) mstore(32, authorized_slot) // Revert if authorized[msg.sender] == false @@ -63,7 +63,7 @@ contract MultiAssetProxy is } // `transferFrom`. - // The function is marked `external`, so no abi decodeding is done for + // The function is marked `external`, so no abi decoding is done for // us. Instead, we expect the `calldata` memory to contain the // following: // @@ -107,20 +107,43 @@ contract MultiAssetProxy is // | | 132 + a | b | nestedAssetData Contents (offsets) | // | | 132 + a + b | | nestedAssetData[0, ..., len] | - // Load offset to `amounts` - // We must add 4 (function selector) + 32 (assetData len) + 4 (assetProxyId) to the assetData offset + // In order to find the offset to `amounts`, we must add: + // 4 (function selector) + // + assetDataOffset + // + 32 (assetData len) + // + 4 (assetProxyId) let amountsOffset := calldataload(add(assetDataOffset, 40)) - // Load offsets to `nestedAssetData` + // In order to find the offset to `nestedAssetData`, we must add: + // 4 (function selector) + // + assetDataOffset + // + 32 (assetData len) + // + 4 (assetProxyId) + // + 32 (amounts offset) let nestedAssetDataOffset := calldataload(add(assetDataOffset, 72)) + // In order to find the start of the `amounts` contents, we must add: + // 4 (function selector) + // + assetDataOffset + // + 32 (assetData len) + // + 4 (assetProxyId) + // + amountsOffset + // + 32 (amounts len) + let amountsContentsStart := add(assetDataOffset, add(amountsOffset, 72)) + // Load number of elements in `amounts` - // We must add 4 (function selector) + 128 (4 params * 32) + 32 (assetData len) + 4 (assetProxyId) + 32 (amounts len) to the amounts offset - let amountsContentsStart := add(amountsOffset, 200) let amountsLen := calldataload(sub(amountsContentsStart, 32)) + // In order to find the start of the `nestedAssetData` contents, we must add: + // 4 (function selector) + // + assetDataOffset + // + 32 (assetData len) + // + 4 (assetProxyId) + // + nestedAssetDataOffset + // + 32 (nestedAssetData len) + let nestedAssetDataContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, 72)) + // Load number of elements in `nestedAssetData` - let nestedAssetDataContentsStart := add(nestedAssetDataOffset, 200) let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32)) // Revert if number of elements in `amounts` differs from number of elements in `nestedAssetData` @@ -172,9 +195,20 @@ contract MultiAssetProxy is // Load offset to `nestedAssetData[i]` let nestedAssetDataElementOffset := calldataload(add(nestedAssetDataContentsStart, i)) + // In order to find the start of the `nestedAssetData[i]` contents, we must add: + // 4 (function selector) + // + assetDataOffset + // + 32 (assetData len) + // + 4 (assetProxyId) + // + nestedAssetDataOffset + // + 32 (nestedAssetData len) + // + nestedAssetDataElementOffset + // + 32 (nestedAssetDataElement len) + let nestedAssetDataElementContentsStart := add(assetDataOffset, add(nestedAssetDataOffset, add(nestedAssetDataElementOffset, 104))) + // Load length of `nestedAssetData[i]` - let nestedAssetDataElementContentsStart := add(nestedAssetDataElementOffset, 264) - let nestedAssetDataElementLen := calldataload(sub(nestedAssetDataElementContentsStart, 32)) + let nestedAssetDataElementLenStart := sub(nestedAssetDataElementContentsStart, 32) + let nestedAssetDataElementLen := calldataload(nestedAssetDataElementLenStart) // Revert if the `nestedAssetData` does not contain a 4 byte `assetProxyId` if lt(nestedAssetDataElementLen, 4) { @@ -208,7 +242,7 @@ contract MultiAssetProxy is // Copy `nestedAssetData[i]` from calldata to memory calldatacopy( 132, // memory slot after `amounts[i]` - nestedAssetDataElementOffset, // location of `nestedAssetData[i]` in calldata + nestedAssetDataElementLenStart, // location of `nestedAssetData[i]` in calldata add(nestedAssetDataElementLen, 32) // `nestedAssetData[i].length` plus 32 byte length ) @@ -219,12 +253,18 @@ contract MultiAssetProxy is 0, // don't send any ETH 0, // pointer to start of input add(164, nestedAssetDataElementLen), // length of input - 100, // write output over memory that won't be reused - 512 // reserve 512 bytes for output + 0, // write output over memory that won't be reused + 0 // reserve 512 bytes for output ) + // Revert with reason given by AssetProxy if `transferFrom` call failed if iszero(success) { - revert(100, returndatasize()) + returndatacopy( + 0, // copy to memory at 0 + 0, // copy from return data at 0 + returndatasize() // copy all return data + ) + revert(0, returndatasize()) } } diff --git a/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetData.sol b/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetData.sol index 21130a480..e2da68919 100644 --- a/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetData.sol +++ b/packages/contracts/contracts/protocol/AssetProxy/interfaces/IAssetData.sol @@ -27,21 +27,18 @@ pragma experimental ABIEncoderV2; interface IAssetData { function ERC20Token(address tokenContract) - external - pure; + external; function ERC721Token( address tokenContract, uint256 tokenId ) - external - pure; + external; function MultiAsset( uint256[] amounts, bytes[] nestedAssetData ) - external - pure; + external; } -- cgit v1.2.3 From d146e15ff3741dfd310b4e0b25166bb526b533f0 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sun, 11 Nov 2018 20:08:58 -0800 Subject: Fix async loops in erc721Wrapper --- packages/contracts/test/utils/constants.ts | 2 +- packages/contracts/test/utils/erc721_wrapper.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/contracts/test/utils/constants.ts b/packages/contracts/test/utils/constants.ts index cd21330e9..d2c3ab512 100644 --- a/packages/contracts/test/utils/constants.ts +++ b/packages/contracts/test/utils/constants.ts @@ -35,7 +35,7 @@ export const constants = { DUMMY_TOKEN_TOTAL_SUPPLY: new BigNumber(0), NULL_BYTES: '0x', NUM_DUMMY_ERC20_TO_DEPLOY: 3, - NUM_DUMMY_ERC721_TO_DEPLOY: 1, + NUM_DUMMY_ERC721_TO_DEPLOY: 2, NUM_ERC721_TOKENS_TO_MINT: 2, NULL_ADDRESS: '0x0000000000000000000000000000000000000000', UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1), diff --git a/packages/contracts/test/utils/erc721_wrapper.ts b/packages/contracts/test/utils/erc721_wrapper.ts index 3ef4e701d..9bd8fad01 100644 --- a/packages/contracts/test/utils/erc721_wrapper.ts +++ b/packages/contracts/test/utils/erc721_wrapper.ts @@ -29,7 +29,7 @@ export class ERC721Wrapper { this._contractOwnerAddress = contractOwnerAddress; } public async deployDummyTokensAsync(): Promise { - for (let i = 0; i < constants.NUM_DUMMY_ERC721_TO_DEPLOY; i++) { + for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) { this._dummyTokenContracts.push( await DummyERC721TokenContract.deployFrom0xArtifactAsync( artifacts.DummyERC721Token, @@ -61,7 +61,7 @@ export class ERC721Wrapper { this._initialTokenIdsByOwner = {}; for (const dummyTokenContract of this._dummyTokenContracts) { for (const tokenOwnerAddress of this._tokenOwnerAddresses) { - for (let i = 0; i < constants.NUM_ERC721_TOKENS_TO_MINT; i++) { + for (const i of _.times(constants.NUM_ERC721_TOKENS_TO_MINT)) { const tokenId = generatePseudoRandomSalt(); await this.mintAsync(dummyTokenContract.address, tokenId, tokenOwnerAddress); if (_.isUndefined(this._initialTokenIdsByOwner[tokenOwnerAddress])) { -- cgit v1.2.3 From a0bc97b589db46adcc4e2275aa61022c9e224a3f Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sun, 11 Nov 2018 20:09:13 -0800 Subject: Add initial MultiAssetProxy tests --- packages/contracts/package.json | 6 +- packages/contracts/src/artifacts/index.ts | 2 + packages/contracts/test/asset_proxy/proxies.ts | 667 ++++++++++++++++++------- packages/contracts/tsconfig.json | 1 + 4 files changed, 485 insertions(+), 191 deletions(-) diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 25445c4f8..a6380a467 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -19,7 +19,8 @@ "test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov", "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", - "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", + "run_mocha": + "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", "compile": "sol-compiler --contracts-dir contracts", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", @@ -32,7 +33,8 @@ "lint-contracts": "solhint contracts/**/**/**/**/*.sol" }, "config": { - "abis": "generated-artifacts/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|ERC20Proxy|ERC721Token|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist|WETH9|ZRXToken).json" + "abis": + "generated-artifacts/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyMultipleReturnERC20Token|DummyNoReturnERC20Token|ERC20Token|ERC20Proxy|ERC721Token|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiAssetProxy|MultiSigWallet|MultiSigWalletWithTimeLock|OrderValidator|ReentrantERC20Token|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist|WETH9|ZRXToken).json" }, "repository": { "type": "git", diff --git a/packages/contracts/src/artifacts/index.ts b/packages/contracts/src/artifacts/index.ts index c30972a91..97c1b6209 100644 --- a/packages/contracts/src/artifacts/index.ts +++ b/packages/contracts/src/artifacts/index.ts @@ -19,6 +19,7 @@ import * as InvalidERC721Receiver from '../../generated-artifacts/InvalidERC721R import * as IValidator from '../../generated-artifacts/IValidator.json'; import * as IWallet from '../../generated-artifacts/IWallet.json'; import * as MixinAuthorizable from '../../generated-artifacts/MixinAuthorizable.json'; +import * as MultiAssetProxy from '../../generated-artifacts/MultiAssetProxy.json'; import * as MultiSigWallet from '../../generated-artifacts/MultiSigWallet.json'; import * as MultiSigWalletWithTimeLock from '../../generated-artifacts/MultiSigWalletWithTimeLock.json'; import * as OrderValidator from '../../generated-artifacts/OrderValidator.json'; @@ -57,6 +58,7 @@ export const artifacts = { IWallet: IWallet as ContractArtifact, InvalidERC721Receiver: InvalidERC721Receiver as ContractArtifact, MixinAuthorizable: MixinAuthorizable as ContractArtifact, + MultiAssetProxy: MultiAssetProxy as ContractArtifact, MultiSigWallet: MultiSigWallet as ContractArtifact, MultiSigWalletWithTimeLock: MultiSigWalletWithTimeLock as ContractArtifact, OrderValidator: OrderValidator as ContractArtifact, diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index b8305993e..94ea408f5 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -12,7 +12,9 @@ import { DummyMultipleReturnERC20TokenContract } from '../../generated-wrappers/ import { DummyNoReturnERC20TokenContract } from '../../generated-wrappers/dummy_no_return_erc20_token'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; +import { IAssetDataContract } from '../../generated-wrappers/i_asset_data'; import { IAssetProxyContract } from '../../generated-wrappers/i_asset_proxy'; +import { MultiAssetProxyContract } from '../../generated-wrappers/multi_asset_proxy'; import { artifacts } from '../../src/artifacts'; import { expectTransactionFailedAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions'; import { chaiSetup } from '../utils/chai_setup'; @@ -30,26 +32,35 @@ const assetProxyInterface = new IAssetProxyContract( constants.NULL_ADDRESS, provider, ); +const assetDataInterface = new IAssetDataContract( + artifacts.IAssetData.compilerOutput.abi, + constants.NULL_ADDRESS, + provider, +); // tslint:disable:no-unnecessary-type-assertion -describe('Asset Transfer Proxies', () => { +describe.only('Asset Transfer Proxies', () => { let owner: string; let notAuthorized: string; - let exchangeAddress: string; - let makerAddress: string; - let takerAddress: string; + let authorized: string; + let fromAddress: string; + let toAddress: string; - let zrxToken: DummyERC20TokenContract; - let erc721Token: DummyERC721TokenContract; + let erc20TokenA: DummyERC20TokenContract; + let erc20TokenB: DummyERC20TokenContract; + let erc721TokenA: DummyERC721TokenContract; + let erc721TokenB: DummyERC721TokenContract; let erc721Receiver: DummyERC721ReceiverContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; let noReturnErc20Token: DummyNoReturnERC20TokenContract; let multipleReturnErc20Token: DummyMultipleReturnERC20TokenContract; + let multiAssetProxy: MultiAssetProxyContract; let erc20Wrapper: ERC20Wrapper; let erc721Wrapper: ERC721Wrapper; - let erc721MakerTokenId: BigNumber; + let erc721AFromTokenId: BigNumber; + let erc721BFromTokenId: BigNumber; before(async () => { await blockchainLifecycle.startAsync(); @@ -59,41 +70,73 @@ describe('Asset Transfer Proxies', () => { }); before(async () => { const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([owner, notAuthorized, exchangeAddress, makerAddress, takerAddress] = _.slice( - accounts, - 0, - 5, - )); + const usedAddresses = ([owner, notAuthorized, authorized, fromAddress, toAddress] = _.slice(accounts, 0, 5)); erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); - const numDummyErc20ToDeploy = 1; - [zrxToken] = await erc20Wrapper.deployDummyTokensAsync(numDummyErc20ToDeploy, constants.DUMMY_TOKEN_DECIMALS); + // Deploy AssetProxies erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); + erc721Proxy = await erc721Wrapper.deployProxyAsync(); + multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync( + artifacts.MultiAssetProxy, + provider, + txDefaults, + ); + + // Configure ERC20Proxy await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeAddress, { + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { from: owner, }), constants.AWAIT_TRANSACTION_MINED_MS, ); - [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); - erc721Proxy = await erc721Wrapper.deployProxyAsync(); - await erc721Wrapper.setBalancesAndAllowancesAsync(); - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - erc721MakerTokenId = erc721Balances[makerAddress][erc721Token.address][0]; + // Configure ERC721Proxy await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeAddress, { + await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(authorized, { from: owner, }), constants.AWAIT_TRANSACTION_MINED_MS, ); - erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync( - artifacts.DummyERC721Receiver, - provider, - txDefaults, + await web3Wrapper.awaitTransactionSuccessAsync( + await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + // Configure MultiAssetProxy + await web3Wrapper.awaitTransactionSuccessAsync( + await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(authorized, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc721Proxy.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + // Deploy and configure ERC20 tokens + const numDummyErc20ToDeploy = 2; + [erc20TokenA, erc20TokenB] = await erc20Wrapper.deployDummyTokensAsync( + numDummyErc20ToDeploy, + constants.DUMMY_TOKEN_DECIMALS, ); noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync( artifacts.DummyNoReturnERC20Token, @@ -104,30 +147,32 @@ describe('Asset Transfer Proxies', () => { constants.DUMMY_TOKEN_DECIMALS, constants.DUMMY_TOKEN_TOTAL_SUPPLY, ); + multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync( + artifacts.DummyMultipleReturnERC20Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + constants.DUMMY_TOKEN_DECIMALS, + constants.DUMMY_TOKEN_TOTAL_SUPPLY, + ); + + await erc20Wrapper.setBalancesAndAllowancesAsync(); await web3Wrapper.awaitTransactionSuccessAsync( - await noReturnErc20Token.setBalance.sendTransactionAsync(makerAddress, constants.INITIAL_ERC20_BALANCE), + await noReturnErc20Token.setBalance.sendTransactionAsync(fromAddress, constants.INITIAL_ERC20_BALANCE), constants.AWAIT_TRANSACTION_MINED_MS, ); await web3Wrapper.awaitTransactionSuccessAsync( await noReturnErc20Token.approve.sendTransactionAsync( erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, - { from: makerAddress }, + { from: fromAddress }, ), constants.AWAIT_TRANSACTION_MINED_MS, ); - multipleReturnErc20Token = await DummyMultipleReturnERC20TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyMultipleReturnERC20Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - constants.DUMMY_TOKEN_DECIMALS, - constants.DUMMY_TOKEN_TOTAL_SUPPLY, - ); await web3Wrapper.awaitTransactionSuccessAsync( await multipleReturnErc20Token.setBalance.sendTransactionAsync( - makerAddress, + fromAddress, constants.INITIAL_ERC20_BALANCE, ), constants.AWAIT_TRANSACTION_MINED_MS, @@ -136,10 +181,23 @@ describe('Asset Transfer Proxies', () => { await multipleReturnErc20Token.approve.sendTransactionAsync( erc20Proxy.address, constants.INITIAL_ERC20_ALLOWANCE, - { from: makerAddress }, + { from: fromAddress }, ), constants.AWAIT_TRANSACTION_MINED_MS, ); + + // Deploy and configure ERC721 tokens and receiver + [erc721TokenA, erc721TokenB] = await erc721Wrapper.deployDummyTokensAsync(); + erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync( + artifacts.DummyERC721Receiver, + provider, + txDefaults, + ); + + await erc721Wrapper.setBalancesAndAllowancesAsync(); + const erc721Balances = await erc721Wrapper.getBalancesAsync(); + erc721AFromTokenId = erc721Balances[fromAddress][erc721TokenA.address][0]; + erc721BFromTokenId = erc721Balances[fromAddress][erc721TokenB.address][0]; }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -147,7 +205,8 @@ describe('Asset Transfer Proxies', () => { afterEach(async () => { await blockchainLifecycle.revertAsync(); }); - describe('Transfer Proxy - ERC20', () => { + + describe('ERC20Proxy', () => { it('should revert if undefined function is called', async () => { const undefinedSelector = '0x01020304'; await expectTransactionFailedWithoutReasonAsync( @@ -159,141 +218,146 @@ describe('Asset Transfer Proxies', () => { }), ); }); + it('should have an id of 0xf47261b0', async () => { + const proxyId = await erc20Proxy.getProxyId.callAsync(); + const expectedProxyId = '0xf47261b0'; + expect(proxyId).to.equal(expectedProxyId); + }); describe('transferFrom', () => { it('should successfully transfer tokens', async () => { // Construct ERC20 asset data - const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); - // Perform a transfer from makerAddress to takerAddress + const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + // Perform a transfer from fromAddress to toAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(10); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await web3Wrapper.awaitTransactionSuccessAsync( await web3Wrapper.sendTransactionAsync({ to: erc20Proxy.address, data, - from: exchangeAddress, + from: authorized, }), constants.AWAIT_TRANSACTION_MINED_MS, ); // Verify transfer was successful const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(amount), + expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenA.address].minus(amount), ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].add(amount), + expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenA.address].add(amount), ); }); it('should successfully transfer tokens that do not return a value', async () => { // Construct ERC20 asset data const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address); - // Perform a transfer from makerAddress to takerAddress - const initialMakerBalance = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const initialTakerBalance = await noReturnErc20Token.balanceOf.callAsync(takerAddress); + // Perform a transfer from fromAddress to toAddress + const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress); + const initialToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress); const amount = new BigNumber(10); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await web3Wrapper.awaitTransactionSuccessAsync( await web3Wrapper.sendTransactionAsync({ to: erc20Proxy.address, data, - from: exchangeAddress, + from: authorized, }), constants.AWAIT_TRANSACTION_MINED_MS, ); // Verify transfer was successful - const newMakerBalance = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const newTakerBalance = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - expect(newMakerBalance).to.be.bignumber.equal(initialMakerBalance.minus(amount)); - expect(newTakerBalance).to.be.bignumber.equal(initialTakerBalance.plus(amount)); + const newFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress); + const newToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress); + expect(newFromBalance).to.be.bignumber.equal(initialFromBalance.minus(amount)); + expect(newToBalance).to.be.bignumber.equal(initialToBalance.plus(amount)); }); it('should successfully transfer tokens and ignore extra assetData', async () => { // Construct ERC20 asset data const extraData = '0102030405060708'; - const encodedAssetData = `${assetDataUtils.encodeERC20AssetData(zrxToken.address)}${extraData}`; - // Perform a transfer from makerAddress to takerAddress + const encodedAssetData = `${assetDataUtils.encodeERC20AssetData(erc20TokenA.address)}${extraData}`; + // Perform a transfer from fromAddress to toAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(10); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await web3Wrapper.awaitTransactionSuccessAsync( await web3Wrapper.sendTransactionAsync({ to: erc20Proxy.address, data, - from: exchangeAddress, + from: authorized, }), constants.AWAIT_TRANSACTION_MINED_MS, ); // Verify transfer was successful const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address].minus(amount), + expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenA.address].minus(amount), ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address].add(amount), + expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenA.address].add(amount), ); }); it('should do nothing if transferring 0 amount of a token', async () => { // Construct ERC20 asset data - const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); - // Perform a transfer from makerAddress to takerAddress + const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + // Perform a transfer from fromAddress to toAddress const erc20Balances = await erc20Wrapper.getBalancesAsync(); const amount = new BigNumber(0); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await web3Wrapper.awaitTransactionSuccessAsync( await web3Wrapper.sendTransactionAsync({ to: erc20Proxy.address, data, - from: exchangeAddress, + from: authorized, }), constants.AWAIT_TRANSACTION_MINED_MS, ); // Verify transfer was successful const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrxToken.address], + expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenA.address], ); - expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrxToken.address], + expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenA.address], ); }); - it('should throw if allowances are too low', async () => { + it('should revert if allowances are too low', async () => { // Construct ERC20 asset data - const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); + const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); // Create allowance less than transfer amount. Set allowance on proxy. const allowance = new BigNumber(0); const amount = new BigNumber(10); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await web3Wrapper.awaitTransactionSuccessAsync( - await zrxToken.approve.sendTransactionAsync(erc20Proxy.address, allowance, { - from: makerAddress, + await erc20TokenA.approve.sendTransactionAsync(erc20Proxy.address, allowance, { + from: fromAddress, }), constants.AWAIT_TRANSACTION_MINED_MS, ); @@ -303,7 +367,7 @@ describe('Asset Transfer Proxies', () => { web3Wrapper.sendTransactionAsync({ to: erc20Proxy.address, data, - from: exchangeAddress, + from: authorized, }), RevertReason.TransferFailed, ); @@ -311,7 +375,7 @@ describe('Asset Transfer Proxies', () => { expect(newBalances).to.deep.equal(erc20Balances); }); - it('should throw if allowances are too low and token does not return a value', async () => { + it('should revert if allowances are too low and token does not return a value', async () => { // Construct ERC20 asset data const encodedAssetData = assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address); // Create allowance less than transfer amount. Set allowance on proxy. @@ -319,42 +383,42 @@ describe('Asset Transfer Proxies', () => { const amount = new BigNumber(10); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await web3Wrapper.awaitTransactionSuccessAsync( await noReturnErc20Token.approve.sendTransactionAsync(erc20Proxy.address, allowance, { - from: makerAddress, + from: fromAddress, }), constants.AWAIT_TRANSACTION_MINED_MS, ); - const initialMakerBalance = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const initialTakerBalance = await noReturnErc20Token.balanceOf.callAsync(takerAddress); + const initialFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress); + const initialToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress); // Perform a transfer; expect this to fail. await expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc20Proxy.address, data, - from: exchangeAddress, + from: authorized, }), RevertReason.TransferFailed, ); - const newMakerBalance = await noReturnErc20Token.balanceOf.callAsync(makerAddress); - const newTakerBalance = await noReturnErc20Token.balanceOf.callAsync(takerAddress); - expect(newMakerBalance).to.be.bignumber.equal(initialMakerBalance); - expect(newTakerBalance).to.be.bignumber.equal(initialTakerBalance); + const newFromBalance = await noReturnErc20Token.balanceOf.callAsync(fromAddress); + const newToBalance = await noReturnErc20Token.balanceOf.callAsync(toAddress); + expect(newFromBalance).to.be.bignumber.equal(initialFromBalance); + expect(newToBalance).to.be.bignumber.equal(initialToBalance); }); - it('should throw if requesting address is not authorized', async () => { + it('should revert if caller is not authorized', async () => { // Construct ERC20 asset data - const encodedAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); - // Perform a transfer from makerAddress to takerAddress + const encodedAssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + // Perform a transfer from fromAddress to toAddress const amount = new BigNumber(10); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); const erc20Balances = await erc20Wrapper.getBalancesAsync(); @@ -370,42 +434,36 @@ describe('Asset Transfer Proxies', () => { expect(newBalances).to.deep.equal(erc20Balances); }); - it('should throw if token returns more than 32 bytes', async () => { + it('should revert if token returns more than 32 bytes', async () => { // Construct ERC20 asset data const encodedAssetData = assetDataUtils.encodeERC20AssetData(multipleReturnErc20Token.address); const amount = new BigNumber(10); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); - const initialMakerBalance = await multipleReturnErc20Token.balanceOf.callAsync(makerAddress); - const initialTakerBalance = await multipleReturnErc20Token.balanceOf.callAsync(takerAddress); + const initialFromBalance = await multipleReturnErc20Token.balanceOf.callAsync(fromAddress); + const initialToBalance = await multipleReturnErc20Token.balanceOf.callAsync(toAddress); // Perform a transfer; expect this to fail. await expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc20Proxy.address, data, - from: exchangeAddress, + from: authorized, }), RevertReason.TransferFailed, ); - const newMakerBalance = await multipleReturnErc20Token.balanceOf.callAsync(makerAddress); - const newTakerBalance = await multipleReturnErc20Token.balanceOf.callAsync(takerAddress); - expect(newMakerBalance).to.be.bignumber.equal(initialMakerBalance); - expect(newTakerBalance).to.be.bignumber.equal(initialTakerBalance); + const newFromBalance = await multipleReturnErc20Token.balanceOf.callAsync(fromAddress); + const newToBalance = await multipleReturnErc20Token.balanceOf.callAsync(toAddress); + expect(newFromBalance).to.be.bignumber.equal(initialFromBalance); + expect(newToBalance).to.be.bignumber.equal(initialToBalance); }); }); - - it('should have an id of 0xf47261b0', async () => { - const proxyId = await erc20Proxy.getProxyId.callAsync(); - const expectedProxyId = '0xf47261b0'; - expect(proxyId).to.equal(expectedProxyId); - }); }); - describe('Transfer Proxy - ERC721', () => { + describe('ERC721Proxy', () => { it('should revert if undefined function is called', async () => { const undefinedSelector = '0x01020304'; await expectTransactionFailedWithoutReasonAsync( @@ -417,76 +475,81 @@ describe('Asset Transfer Proxies', () => { }), ); }); + it('should have an id of 0x02571792', async () => { + const proxyId = await erc721Proxy.getProxyId.callAsync(); + const expectedProxyId = '0x02571792'; + expect(proxyId).to.equal(expectedProxyId); + }); describe('transferFrom', () => { it('should successfully transfer tokens', async () => { // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); + const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); // Verify pre-condition - const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(ownerMakerAsset).to.be.equal(makerAddress); - // Perform a transfer from makerAddress to takerAddress + const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset).to.be.equal(fromAddress); + // Perform a transfer from fromAddress to toAddress const amount = new BigNumber(1); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await web3Wrapper.awaitTransactionSuccessAsync( await web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, data, - from: exchangeAddress, + from: authorized, }), constants.AWAIT_TRANSACTION_MINED_MS, ); // Verify transfer was successful - const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress); + const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(newOwnerFromAsset).to.be.bignumber.equal(toAddress); }); it('should successfully transfer tokens and ignore extra assetData', async () => { // Construct ERC721 asset data const extraData = '0102030405060708'; const encodedAssetData = `${assetDataUtils.encodeERC721AssetData( - erc721Token.address, - erc721MakerTokenId, + erc721TokenA.address, + erc721AFromTokenId, )}${extraData}`; // Verify pre-condition - const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(ownerMakerAsset).to.be.equal(makerAddress); - // Perform a transfer from makerAddress to takerAddress + const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset).to.be.equal(fromAddress); + // Perform a transfer from fromAddress to toAddress const amount = new BigNumber(1); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await web3Wrapper.awaitTransactionSuccessAsync( await web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, data, - from: exchangeAddress, + from: authorized, }), constants.AWAIT_TRANSACTION_MINED_MS, ); // Verify transfer was successful - const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress); + const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(newOwnerFromAsset).to.be.bignumber.equal(toAddress); }); it('should not call onERC721Received when transferring to a smart contract', async () => { // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); + const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); // Verify pre-condition - const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(ownerMakerAsset).to.be.equal(makerAddress); - // Perform a transfer from makerAddress to takerAddress + const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset).to.be.equal(fromAddress); + // Perform a transfer from fromAddress to toAddress const amount = new BigNumber(1); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, + fromAddress, erc721Receiver.address, amount, ); @@ -495,79 +558,79 @@ describe('Asset Transfer Proxies', () => { await web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, data, - from: exchangeAddress, + from: authorized, gas: constants.MAX_TRANSFER_FROM_GAS, }), ); // Verify that no log was emitted by erc721 receiver expect(tx.logs.length).to.be.equal(1); // Verify transfer was successful - const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(newOwnerMakerAsset).to.be.bignumber.equal(erc721Receiver.address); + const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(newOwnerFromAsset).to.be.bignumber.equal(erc721Receiver.address); }); - it('should throw if transferring 0 amount of a token', async () => { + it('should revert if transferring 0 amount of a token', async () => { // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); + const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); // Verify pre-condition - const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(ownerMakerAsset).to.be.equal(makerAddress); - // Perform a transfer from makerAddress to takerAddress + const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset).to.be.equal(fromAddress); + // Perform a transfer from fromAddress to toAddress const amount = new BigNumber(0); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, data, - from: exchangeAddress, + from: authorized, }), RevertReason.InvalidAmount, ); - const newOwner = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(newOwner).to.be.equal(ownerMakerAsset); + const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(newOwner).to.be.equal(ownerFromAsset); }); - it('should throw if transferring > 1 amount of a token', async () => { + it('should revert if transferring > 1 amount of a token', async () => { // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); + const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); // Verify pre-condition - const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(ownerMakerAsset).to.be.equal(makerAddress); - // Perform a transfer from makerAddress to takerAddress + const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset).to.be.equal(fromAddress); + // Perform a transfer from fromAddress to toAddress const amount = new BigNumber(500); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, data, - from: exchangeAddress, + from: authorized, }), RevertReason.InvalidAmount, ); - const newOwner = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(newOwner).to.be.equal(ownerMakerAsset); + const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(newOwner).to.be.equal(ownerFromAsset); }); - it('should throw if allowances are too low', async () => { + it('should revert if allowances are too low', async () => { // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); + const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); // Verify pre-condition - const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(ownerMakerAsset).to.be.equal(makerAddress); - // Remove transfer approval for makerAddress. + const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset).to.be.equal(fromAddress); + // Remove transfer approval for fromAddress. await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Token.approve.sendTransactionAsync(constants.NULL_ADDRESS, erc721MakerTokenId, { - from: makerAddress, + await erc721TokenA.approve.sendTransactionAsync(constants.NULL_ADDRESS, erc721AFromTokenId, { + from: fromAddress, }), constants.AWAIT_TRANSACTION_MINED_MS, ); @@ -575,34 +638,34 @@ describe('Asset Transfer Proxies', () => { const amount = new BigNumber(1); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await expectTransactionFailedAsync( web3Wrapper.sendTransactionAsync({ to: erc721Proxy.address, data, - from: exchangeAddress, + from: authorized, }), RevertReason.TransferFailed, ); - const newOwner = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(newOwner).to.be.equal(ownerMakerAsset); + const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(newOwner).to.be.equal(ownerFromAsset); }); - it('should throw if requesting address is not authorized', async () => { + it('should revert if caller is not authorized', async () => { // Construct ERC721 asset data - const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId); + const encodedAssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); // Verify pre-condition - const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(ownerMakerAsset).to.be.equal(makerAddress); - // Perform a transfer from makerAddress to takerAddress + const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset).to.be.equal(fromAddress); + // Perform a transfer from fromAddress to toAddress const amount = new BigNumber(1); const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( encodedAssetData, - makerAddress, - takerAddress, + fromAddress, + toAddress, amount, ); await expectTransactionFailedAsync( @@ -613,16 +676,242 @@ describe('Asset Transfer Proxies', () => { }), RevertReason.SenderNotAuthorized, ); - const newOwner = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(newOwner).to.be.equal(ownerMakerAsset); + const newOwner = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(newOwner).to.be.equal(ownerFromAsset); }); }); - - it('should have an id of 0x02571792', async () => { - const proxyId = await erc721Proxy.getProxyId.callAsync(); - const expectedProxyId = '0x02571792'; + }); + describe.only('MultiAssetProxy', () => { + it('should revert if undefined function is called', async () => { + const undefinedSelector = '0x01020304'; + await expectTransactionFailedWithoutReasonAsync( + web3Wrapper.sendTransactionAsync({ + from: owner, + to: multiAssetProxy.address, + value: constants.ZERO_AMOUNT, + data: undefinedSelector, + }), + ); + }); + it('should have an id of 0x94cfcdd7', async () => { + const proxyId = await multiAssetProxy.getProxyId.callAsync(); + const expectedProxyId = '0x94cfcdd7'; expect(proxyId).to.equal(expectedProxyId); }); + describe('transferFrom', () => { + it('should transfer a single ERC20 token', async () => { + const inputAmount = new BigNumber(1); + const erc20Amount = new BigNumber(10); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const amounts = [erc20Amount]; + const nestedAssetData = [erc20AssetData]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // Verify transfer was successful + const newBalances = await erc20Wrapper.getBalancesAsync(); + const totalAmount = inputAmount.times(erc20Amount); + expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount), + ); + expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenA.address].add(totalAmount), + ); + }); + it('should successfully transfer multiple of the same ERC20 token', async () => { + const inputAmount = new BigNumber(1); + const erc20Amount1 = new BigNumber(10); + const erc20Amount2 = new BigNumber(20); + const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const amounts = [erc20Amount1, erc20Amount2]; + const nestedAssetData = [erc20AssetData1, erc20AssetData2]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // Verify transfer was successful + const newBalances = await erc20Wrapper.getBalancesAsync(); + const totalAmount = inputAmount.times(erc20Amount1).plus(inputAmount.times(erc20Amount2)); + expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount), + ); + expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenA.address].add(totalAmount), + ); + }); + it('should successfully transfer multiple different ERC20 tokens', async () => { + const inputAmount = new BigNumber(1); + const erc20Amount1 = new BigNumber(10); + const erc20Amount2 = new BigNumber(20); + const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address); + const amounts = [erc20Amount1, erc20Amount2]; + const nestedAssetData = [erc20AssetData1, erc20AssetData2]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // Verify transfer was successful + const newBalances = await erc20Wrapper.getBalancesAsync(); + const totalErc20AAmount = inputAmount.times(erc20Amount1); + const totalErc20BAmount = inputAmount.times(erc20Amount2); + expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount), + ); + expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenA.address].add(totalErc20AAmount), + ); + expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount), + ); + expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenB.address].add(totalErc20BAmount), + ); + }); + it('should transfer a single ERC721 token', async () => { + const inputAmount = new BigNumber(1); + const erc721Amount = new BigNumber(1); + const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); + const amounts = [erc721Amount]; + const nestedAssetData = [erc721AssetData]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset).to.be.equal(fromAddress); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(newOwnerFromAsset).to.be.equal(toAddress); + }); + it('should successfully transfer multiple of the same ERC721 token', async () => { + const erc721Balances = await erc721Wrapper.getBalancesAsync(); + const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1]; + const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); + const erc721AssetData2 = assetDataUtils.encodeERC721AssetData( + erc721TokenA.address, + erc721AFromTokenId2, + ); + const inputAmount = new BigNumber(1); + const erc721Amount = new BigNumber(1); + const amounts = [erc721Amount, erc721Amount]; + const nestedAssetData = [erc721AssetData1, erc721AssetData2]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const ownerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset1).to.be.equal(fromAddress); + const ownerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2); + expect(ownerFromAsset2).to.be.equal(fromAddress); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + gas: constants.MAX_TRANSFER_FROM_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newOwnerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + const newOwnerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2); + expect(newOwnerFromAsset1).to.be.equal(toAddress); + expect(newOwnerFromAsset2).to.be.equal(toAddress); + }); + it('should successfully transfer multiple different ERC721 tokens', async () => { + const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); + const erc721AssetData2 = assetDataUtils.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId); + const inputAmount = new BigNumber(1); + const erc721Amount = new BigNumber(1); + const amounts = [erc721Amount, erc721Amount]; + const nestedAssetData = [erc721AssetData1, erc721AssetData2]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const ownerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset1).to.be.equal(fromAddress); + const ownerFromAsset2 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId); + expect(ownerFromAsset2).to.be.equal(fromAddress); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + gas: constants.MAX_TRANSFER_FROM_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newOwnerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + const newOwnerFromAsset2 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId); + expect(newOwnerFromAsset1).to.be.equal(toAddress); + expect(newOwnerFromAsset2).to.be.equal(toAddress); + }); + it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => {}); + it('should successfully transfer tokens and ignore extra assetData', async () => {}); + it('should successfully transfer correct amounts when the `amount` > 1', async () => {}); + it('should revert if a single transfer fails', async () => {}); + it('should revert if an AssetProxy is not registered', async () => {}); + it('should revert if the length of `amounts` does not match the length of `nestedAssetData`', async () => {}); + it('should revert if amounts multiplication results in an overflow', async () => {}); + it('should revert if an element of `nestedAssetData` is < 4 bytes long', async () => {}); + it('should revert with the same reason as the called AssetProxy', async () => {}); + it('should revert if caller is not authorized', async () => {}); + }); }); }); // tslint:enable:no-unnecessary-type-assertion diff --git a/packages/contracts/tsconfig.json b/packages/contracts/tsconfig.json index 8b29365cc..e0f85079a 100644 --- a/packages/contracts/tsconfig.json +++ b/packages/contracts/tsconfig.json @@ -26,6 +26,7 @@ "./generated-artifacts/IWallet.json", "./generated-artifacts/InvalidERC721Receiver.json", "./generated-artifacts/MixinAuthorizable.json", + "./generated-artifacts/MultiAssetProxy.json", "./generated-artifacts/MultiSigWallet.json", "./generated-artifacts/MultiSigWalletWithTimeLock.json", "./generated-artifacts/OrderValidator.json", -- cgit v1.2.3 From b773d5e592cc0ee57c902c0e2692554f54003234 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 12 Nov 2018 11:22:43 -0800 Subject: feat: Add and revert reasons --- packages/types/CHANGELOG.json | 9 +++++++++ packages/types/src/index.ts | 2 ++ 2 files changed, 11 insertions(+) diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json index 0b32b60f0..53b24aff0 100644 --- a/packages/types/CHANGELOG.json +++ b/packages/types/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "1.4.0", + "changes": [ + { + "note": "Add `LengthMismatch` and `LengthGreaterThan3Required` revert reasons", + "pr": 1224 + } + ] + }, { "version": "1.3.0", "changes": [ diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 1a86f45e6..26d8f8e22 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -195,6 +195,7 @@ export enum RevertReason { FailedExecution = 'FAILED_EXECUTION', AssetProxyAlreadyExists = 'ASSET_PROXY_ALREADY_EXISTS', LengthGreaterThan0Required = 'LENGTH_GREATER_THAN_0_REQUIRED', + LengthGreaterThan3Required = 'LENGTH_GREATER_THAN_3_REQUIRED', LengthGreaterThan131Required = 'LENGTH_GREATER_THAN_131_REQUIRED', Length0Required = 'LENGTH_0_REQUIRED', Length65Required = 'LENGTH_65_REQUIRED', @@ -209,6 +210,7 @@ export enum RevertReason { MakerNotWhitelisted = 'MAKER_NOT_WHITELISTED', TakerNotWhitelisted = 'TAKER_NOT_WHITELISTED', AssetProxyDoesNotExist = 'ASSET_PROXY_DOES_NOT_EXIST', + LengthMismatch = 'LENGTH_MISMATCH', LibBytesGreaterThanZeroLengthRequired = 'GREATER_THAN_ZERO_LENGTH_REQUIRED', LibBytesGreaterOrEqualTo4LengthRequired = 'GREATER_OR_EQUAL_TO_4_LENGTH_REQUIRED', LibBytesGreaterOrEqualTo20LengthRequired = 'GREATER_OR_EQUAL_TO_20_LENGTH_REQUIRED', -- cgit v1.2.3 From 6f92f0a7b5cc9aa9ccd3a1729d6e26da54ebaddf Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 12 Nov 2018 11:23:34 -0800 Subject: Add more tests for MAP --- .../protocol/AssetProxy/MultiAssetProxy.sol | 2 +- packages/contracts/test/asset_proxy/proxies.ts | 355 ++++++++++++++++++++- 2 files changed, 342 insertions(+), 15 deletions(-) diff --git a/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol index 531ee2264..fc86ee942 100644 --- a/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol +++ b/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol @@ -286,4 +286,4 @@ contract MultiAssetProxy is { return PROXY_ID; } -} \ No newline at end of file +} diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index 94ea408f5..913c91320 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -39,7 +39,7 @@ const assetDataInterface = new IAssetDataContract( ); // tslint:disable:no-unnecessary-type-assertion -describe.only('Asset Transfer Proxies', () => { +describe('Asset Transfer Proxies', () => { let owner: string; let notAuthorized: string; let authorized: string; @@ -721,7 +721,6 @@ describe.only('Asset Transfer Proxies', () => { }), constants.AWAIT_TRANSACTION_MINED_MS, ); - // Verify transfer was successful const newBalances = await erc20Wrapper.getBalancesAsync(); const totalAmount = inputAmount.times(erc20Amount); expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( @@ -755,7 +754,6 @@ describe.only('Asset Transfer Proxies', () => { }), constants.AWAIT_TRANSACTION_MINED_MS, ); - // Verify transfer was successful const newBalances = await erc20Wrapper.getBalancesAsync(); const totalAmount = inputAmount.times(erc20Amount1).plus(inputAmount.times(erc20Amount2)); expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( @@ -789,7 +787,6 @@ describe.only('Asset Transfer Proxies', () => { }), constants.AWAIT_TRANSACTION_MINED_MS, ); - // Verify transfer was successful const newBalances = await erc20Wrapper.getBalancesAsync(); const totalErc20AAmount = inputAmount.times(erc20Amount1); const totalErc20BAmount = inputAmount.times(erc20Amount2); @@ -901,16 +898,346 @@ describe.only('Asset Transfer Proxies', () => { expect(newOwnerFromAsset1).to.be.equal(toAddress); expect(newOwnerFromAsset2).to.be.equal(toAddress); }); - it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => {}); - it('should successfully transfer tokens and ignore extra assetData', async () => {}); - it('should successfully transfer correct amounts when the `amount` > 1', async () => {}); - it('should revert if a single transfer fails', async () => {}); - it('should revert if an AssetProxy is not registered', async () => {}); - it('should revert if the length of `amounts` does not match the length of `nestedAssetData`', async () => {}); - it('should revert if amounts multiplication results in an overflow', async () => {}); - it('should revert if an element of `nestedAssetData` is < 4 bytes long', async () => {}); - it('should revert with the same reason as the called AssetProxy', async () => {}); - it('should revert if caller is not authorized', async () => {}); + it('should successfully transfer a combination of ERC20 and ERC721 tokens', async () => { + const inputAmount = new BigNumber(1); + const erc20Amount = new BigNumber(10); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const erc721Amount = new BigNumber(1); + const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); + const amounts = [erc20Amount, erc721Amount]; + const nestedAssetData = [erc20AssetData, erc721AssetData]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset).to.be.equal(fromAddress); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newBalances = await erc20Wrapper.getBalancesAsync(); + const totalAmount = inputAmount.times(erc20Amount); + expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount), + ); + expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenA.address].add(totalAmount), + ); + const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(newOwnerFromAsset).to.be.equal(toAddress); + }); + it('should successfully transfer tokens and ignore extra assetData', async () => { + const inputAmount = new BigNumber(1); + const erc20Amount = new BigNumber(10); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const erc721Amount = new BigNumber(1); + const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); + const amounts = [erc20Amount, erc721Amount]; + const nestedAssetData = [erc20AssetData, erc721AssetData]; + const extraData = '0102030405060708'; + const assetData = `${assetDataInterface.MultiAsset.getABIEncodedTransactionData( + amounts, + nestedAssetData, + )}${extraData}`; + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const ownerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset).to.be.equal(fromAddress); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newBalances = await erc20Wrapper.getBalancesAsync(); + const totalAmount = inputAmount.times(erc20Amount); + expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenA.address].minus(totalAmount), + ); + expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenA.address].add(totalAmount), + ); + const newOwnerFromAsset = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(newOwnerFromAsset).to.be.equal(toAddress); + }); + it('should successfully transfer correct amounts when the `amount` > 1', async () => { + const inputAmount = new BigNumber(100); + const erc20Amount1 = new BigNumber(10); + const erc20Amount2 = new BigNumber(20); + const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address); + const amounts = [erc20Amount1, erc20Amount2]; + const nestedAssetData = [erc20AssetData1, erc20AssetData2]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newBalances = await erc20Wrapper.getBalancesAsync(); + const totalErc20AAmount = inputAmount.times(erc20Amount1); + const totalErc20BAmount = inputAmount.times(erc20Amount2); + expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount), + ); + expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenA.address].add(totalErc20AAmount), + ); + expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount), + ); + expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenB.address].add(totalErc20BAmount), + ); + }); + it('should successfully transfer a large amount of tokens', async () => { + const inputAmount = new BigNumber(1); + const erc20Amount1 = new BigNumber(10); + const erc20Amount2 = new BigNumber(20); + const erc20AssetData1 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address); + const erc721Amount = new BigNumber(1); + const erc721Balances = await erc721Wrapper.getBalancesAsync(); + const erc721AFromTokenId2 = erc721Balances[fromAddress][erc721TokenA.address][1]; + const erc721BFromTokenId2 = erc721Balances[fromAddress][erc721TokenB.address][1]; + const erc721AssetData1 = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); + const erc721AssetData2 = assetDataUtils.encodeERC721AssetData( + erc721TokenA.address, + erc721AFromTokenId2, + ); + const erc721AssetData3 = assetDataUtils.encodeERC721AssetData(erc721TokenB.address, erc721BFromTokenId); + const erc721AssetData4 = assetDataUtils.encodeERC721AssetData( + erc721TokenB.address, + erc721BFromTokenId2, + ); + const amounts = [erc721Amount, erc20Amount1, erc721Amount, erc20Amount2, erc721Amount, erc721Amount]; + const nestedAssetData = [ + erc721AssetData1, + erc20AssetData1, + erc721AssetData2, + erc20AssetData2, + erc721AssetData3, + erc721AssetData4, + ]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const ownerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + expect(ownerFromAsset1).to.be.equal(fromAddress); + const ownerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2); + expect(ownerFromAsset2).to.be.equal(fromAddress); + const ownerFromAsset3 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId); + expect(ownerFromAsset3).to.be.equal(fromAddress); + const ownerFromAsset4 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId2); + expect(ownerFromAsset4).to.be.equal(fromAddress); + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + gas: constants.MAX_EXECUTE_TRANSACTION_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newOwnerFromAsset1 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId); + const newOwnerFromAsset2 = await erc721TokenA.ownerOf.callAsync(erc721AFromTokenId2); + const newOwnerFromAsset3 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId); + const newOwnerFromAsset4 = await erc721TokenB.ownerOf.callAsync(erc721BFromTokenId2); + expect(newOwnerFromAsset1).to.be.equal(toAddress); + expect(newOwnerFromAsset2).to.be.equal(toAddress); + expect(newOwnerFromAsset3).to.be.equal(toAddress); + expect(newOwnerFromAsset4).to.be.equal(toAddress); + const newBalances = await erc20Wrapper.getBalancesAsync(); + const totalErc20AAmount = inputAmount.times(erc20Amount1); + const totalErc20BAmount = inputAmount.times(erc20Amount2); + expect(newBalances[fromAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenA.address].minus(totalErc20AAmount), + ); + expect(newBalances[toAddress][erc20TokenA.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenA.address].add(totalErc20AAmount), + ); + expect(newBalances[fromAddress][erc20TokenB.address]).to.be.bignumber.equal( + erc20Balances[fromAddress][erc20TokenB.address].minus(totalErc20BAmount), + ); + expect(newBalances[toAddress][erc20TokenB.address]).to.be.bignumber.equal( + erc20Balances[toAddress][erc20TokenB.address].add(totalErc20BAmount), + ); + }); + it('should revert if a single transfer fails', async () => { + const inputAmount = new BigNumber(1); + const erc20Amount = new BigNumber(10); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + // 2 is an invalid erc721 amount + const erc721Amount = new BigNumber(2); + const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); + const amounts = [erc20Amount, erc721Amount]; + const nestedAssetData = [erc20AssetData, erc721AssetData]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + RevertReason.InvalidAmount, + ); + }); + it('should revert if an AssetProxy is not registered', async () => { + const inputAmount = new BigNumber(1); + const erc20Amount = new BigNumber(10); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const erc721Amount = new BigNumber(1); + const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); + const invalidProxyId = '0x12345678'; + const invalidErc721AssetData = `${invalidProxyId}${erc721AssetData.slice(10)}`; + const amounts = [erc20Amount, erc721Amount]; + const nestedAssetData = [erc20AssetData, invalidErc721AssetData]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + RevertReason.AssetProxyDoesNotExist, + ); + }); + it('should revert if the length of `amounts` does not match the length of `nestedAssetData`', async () => { + const inputAmount = new BigNumber(1); + const erc20Amount = new BigNumber(10); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); + const amounts = [erc20Amount]; + const nestedAssetData = [erc20AssetData, erc721AssetData]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + RevertReason.LengthMismatch, + ); + }); + it('should revert if amounts multiplication results in an overflow', async () => { + const inputAmount = new BigNumber(2).pow(128); + const erc20Amount = new BigNumber(2).pow(128); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const amounts = [erc20Amount]; + const nestedAssetData = [erc20AssetData]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + RevertReason.Uint256Overflow, + ); + }); + it('should revert if an element of `nestedAssetData` is < 4 bytes long', async () => { + const inputAmount = new BigNumber(1); + const erc20Amount = new BigNumber(10); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const erc721Amount = new BigNumber(1); + const erc721AssetData = '0x123456'; + const amounts = [erc20Amount, erc721Amount]; + const nestedAssetData = [erc20AssetData, erc721AssetData]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + RevertReason.LengthGreaterThan3Required, + ); + }); + it('should revert if caller is not authorized', async () => { + const inputAmount = new BigNumber(1); + const erc20Amount = new BigNumber(10); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const erc721Amount = new BigNumber(1); + const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId); + const amounts = [erc20Amount, erc721Amount]; + const nestedAssetData = [erc20AssetData, erc721AssetData]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: notAuthorized, + }), + RevertReason.SenderNotAuthorized, + ); + }); }); }); }); -- cgit v1.2.3 From 7b51eddc03762085050eba8dfcf3325360e85403 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 12 Nov 2018 11:28:34 -0800 Subject: Fix linting errors --- packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol | 5 ++++- packages/contracts/test/utils/erc721_wrapper.ts | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol index fc86ee942..6db42e44f 100644 --- a/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol +++ b/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol @@ -221,7 +221,10 @@ contract MultiAssetProxy is } // Load AssetProxy id - let assetProxyId := and(calldataload(nestedAssetDataElementContentsStart), 0xffffffff00000000000000000000000000000000000000000000000000000000) + let assetProxyId := and( + calldataload(nestedAssetDataElementContentsStart), + 0xffffffff00000000000000000000000000000000000000000000000000000000 + ) // To lookup a value in a mapping, we load from the storage location keccak256(k, p), // where k is the key left padded to 32 bytes and p is the storage slot diff --git a/packages/contracts/test/utils/erc721_wrapper.ts b/packages/contracts/test/utils/erc721_wrapper.ts index 9bd8fad01..e9da553d0 100644 --- a/packages/contracts/test/utils/erc721_wrapper.ts +++ b/packages/contracts/test/utils/erc721_wrapper.ts @@ -29,6 +29,7 @@ export class ERC721Wrapper { this._contractOwnerAddress = contractOwnerAddress; } public async deployDummyTokensAsync(): Promise { + // tslint:disable-next-line:no-unused-variable for (const i of _.times(constants.NUM_DUMMY_ERC721_TO_DEPLOY)) { this._dummyTokenContracts.push( await DummyERC721TokenContract.deployFrom0xArtifactAsync( @@ -61,6 +62,7 @@ export class ERC721Wrapper { this._initialTokenIdsByOwner = {}; for (const dummyTokenContract of this._dummyTokenContracts) { for (const tokenOwnerAddress of this._tokenOwnerAddresses) { + // tslint:disable-next-line:no-unused-variable for (const i of _.times(constants.NUM_ERC721_TOKENS_TO_MINT)) { const tokenId = generatePseudoRandomSalt(); await this.mintAsync(dummyTokenContract.address, tokenId, tokenOwnerAddress); -- cgit v1.2.3 From 743c43f768e4701847cc4299783a09878f2402ee Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 12 Nov 2018 17:17:43 -0800 Subject: Add Exchange tests with MultiAsset orders --- packages/contracts/test/asset_proxy/proxies.ts | 2 +- packages/contracts/test/exchange/core.ts | 383 +++++++++++++++++++++++-- 2 files changed, 362 insertions(+), 23 deletions(-) diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index 913c91320..face0b92b 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -681,7 +681,7 @@ describe('Asset Transfer Proxies', () => { }); }); }); - describe.only('MultiAssetProxy', () => { + describe('MultiAssetProxy', () => { it('should revert if undefined function is called', async () => { const undefinedSelector = '0x01020304'; await expectTransactionFailedWithoutReasonAsync( diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index fc8dc5346..9159b0d8f 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -14,6 +14,8 @@ import { DummyNoReturnERC20TokenContract } from '../../generated-wrappers/dummy_ import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated-wrappers/exchange'; +import { IAssetDataContract } from '../../generated-wrappers/i_asset_data'; +import { MultiAssetProxyContract } from '../../generated-wrappers/multi_asset_proxy'; import { ReentrantERC20TokenContract } from '../../generated-wrappers/reentrant_erc20_token'; import { TestStaticCallReceiverContract } from '../../generated-wrappers/test_static_call_receiver'; import { artifacts } from '../../src/artifacts'; @@ -31,6 +33,11 @@ import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper'; chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +const assetDataInterface = new IAssetDataContract( + artifacts.IAssetData.compilerOutput.abi, + constants.NULL_ADDRESS, + provider, +); // tslint:disable:no-unnecessary-type-assertion describe('Exchange core', () => { let makerAddress: string; @@ -47,6 +54,7 @@ describe('Exchange core', () => { let exchange: ExchangeContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; + let multiAssetProxy: MultiAssetProxyContract; let maliciousWallet: TestStaticCallReceiverContract; let maliciousValidator: TestStaticCallReceiverContract; @@ -76,59 +84,104 @@ describe('Exchange core', () => { erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); erc721Wrapper = new ERC721Wrapper(provider, usedAddresses, owner); + // Deploy AssetProxies, Exchange, tokens, and malicious contracts + erc20Proxy = await erc20Wrapper.deployProxyAsync(); + erc721Proxy = await erc721Wrapper.deployProxyAsync(); + multiAssetProxy = await MultiAssetProxyContract.deployFrom0xArtifactAsync( + artifacts.MultiAssetProxy, + provider, + txDefaults, + ); const numDummyErc20ToDeploy = 3; [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( numDummyErc20ToDeploy, constants.DUMMY_TOKEN_DECIMALS, ); - erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - [erc721Token] = await erc721Wrapper.deployDummyTokensAsync(); - erc721Proxy = await erc721Wrapper.deployProxyAsync(); - await erc721Wrapper.setBalancesAndAllowancesAsync(); - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address]; - erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address]; - exchange = await ExchangeContract.deployFrom0xArtifactAsync( artifacts.Exchange, provider, txDefaults, assetDataUtils.encodeERC20AssetData(zrxToken.address), ); - exchangeWrapper = new ExchangeWrapper(exchange, provider); - await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); - await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); + maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync( + artifacts.TestStaticCallReceiver, + provider, + txDefaults, + ); + reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync( + artifacts.ReentrantERC20Token, + provider, + txDefaults, + exchange.address, + ); + // Configure ERC20Proxy await web3Wrapper.awaitTransactionSuccessAsync( await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner, }), constants.AWAIT_TRANSACTION_MINED_MS, ); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + + // Configure ERC721Proxy await web3Wrapper.awaitTransactionSuccessAsync( await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner, }), constants.AWAIT_TRANSACTION_MINED_MS, ); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(multiAssetProxy.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); - maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync( - artifacts.TestStaticCallReceiver, - provider, - txDefaults, + // Configure MultiAssetProxy + await web3Wrapper.awaitTransactionSuccessAsync( + await multiAssetProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, ); - reentrantErc20Token = await ReentrantERC20TokenContract.deployFrom0xArtifactAsync( - artifacts.ReentrantERC20Token, - provider, - txDefaults, - exchange.address, + await web3Wrapper.awaitTransactionSuccessAsync( + await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await multiAssetProxy.registerAssetProxy.sendTransactionAsync(erc721Proxy.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, ); + // Configure Exchange + exchangeWrapper = new ExchangeWrapper(exchange, provider); + await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(multiAssetProxy.address, owner); + + // Configure ERC20 tokens + await erc20Wrapper.setBalancesAndAllowancesAsync(); + + // Configure ERC721 tokens + await erc721Wrapper.setBalancesAndAllowancesAsync(); + const erc721Balances = await erc721Wrapper.getBalancesAsync(); + erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address]; + erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address]; + + // Configure order defaults defaultMakerAssetAddress = erc20TokenA.address; defaultTakerAssetAddress = erc20TokenB.address; - const defaultOrderParams = { ...constants.STATIC_ORDER_PARAMS, exchangeAddress: exchange.address, @@ -707,6 +760,292 @@ describe('Exchange core', () => { }); }); + describe('Testing exchange of multiple assets', () => { + it('should allow multiple assets to be exchanged for a single asset', async () => { + const makerAmounts = [new BigNumber(10), new BigNumber(20)]; + const makerNestedAssetData = [ + assetDataUtils.encodeERC20AssetData(erc20TokenA.address), + assetDataUtils.encodeERC20AssetData(erc20TokenB.address), + ]; + const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( + makerAmounts, + makerNestedAssetData, + ); + const makerAssetAmount = new BigNumber(1); + const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); + const takerAssetAmount = new BigNumber(10); + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData, + takerAssetData, + makerAssetAmount, + takerAssetAmount, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }); + + const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); + const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); + const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + + await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); + + const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); + const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); + const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + + expect(finalMakerBalanceA).to.be.bignumber.equal( + initialMakerBalanceA.minus(makerAmounts[0].times(makerAssetAmount)), + ); + expect(finalMakerBalanceB).to.be.bignumber.equal( + initialMakerBalanceB.minus(makerAmounts[1].times(makerAssetAmount)), + ); + expect(finalMakerZrxBalance).to.be.bignumber.equal(initialMakerZrxBalance.plus(takerAssetAmount)); + expect(finalTakerBalanceA).to.be.bignumber.equal( + initialTakerBalanceA.plus(makerAmounts[0].times(makerAssetAmount)), + ); + expect(finalTakerBalanceB).to.be.bignumber.equal( + initialTakerBalanceB.plus(makerAmounts[1].times(makerAssetAmount)), + ); + expect(finalTakerZrxBalance).to.be.bignumber.equal(initialTakerZrxBalance.minus(takerAssetAmount)); + }); + it('should allow multiple assets to be exchanged for multiple assets', async () => { + const makerAmounts = [new BigNumber(10), new BigNumber(20)]; + const makerNestedAssetData = [ + assetDataUtils.encodeERC20AssetData(erc20TokenA.address), + assetDataUtils.encodeERC20AssetData(erc20TokenB.address), + ]; + const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( + makerAmounts, + makerNestedAssetData, + ); + const makerAssetAmount = new BigNumber(1); + const takerAmounts = [new BigNumber(10), new BigNumber(1)]; + const takerAssetId = erc721TakerAssetIds[0]; + const takerNestedAssetData = [ + assetDataUtils.encodeERC20AssetData(zrxToken.address), + assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId), + ]; + const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( + takerAmounts, + takerNestedAssetData, + ); + const takerAssetAmount = new BigNumber(1); + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData, + takerAssetData, + makerAssetAmount, + takerAssetAmount, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }); + + const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); + const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); + const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + const initialOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); + expect(initialOwnerTakerAsset).to.be.bignumber.equal(takerAddress); + + await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress); + + const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); + const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); + const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + const finalOwnerTakerAsset = await erc721Token.ownerOf.callAsync(takerAssetId); + + expect(finalMakerBalanceA).to.be.bignumber.equal( + initialMakerBalanceA.minus(makerAmounts[0].times(makerAssetAmount)), + ); + expect(finalMakerBalanceB).to.be.bignumber.equal( + initialMakerBalanceB.minus(makerAmounts[1].times(makerAssetAmount)), + ); + expect(finalMakerZrxBalance).to.be.bignumber.equal( + initialMakerZrxBalance.plus(takerAmounts[0].times(takerAssetAmount)), + ); + expect(finalTakerBalanceA).to.be.bignumber.equal( + initialTakerBalanceA.plus(makerAmounts[0].times(makerAssetAmount)), + ); + expect(finalTakerBalanceB).to.be.bignumber.equal( + initialTakerBalanceB.plus(makerAmounts[1].times(makerAssetAmount)), + ); + expect(finalTakerZrxBalance).to.be.bignumber.equal( + initialTakerZrxBalance.minus(takerAmounts[0].times(takerAssetAmount)), + ); + expect(finalOwnerTakerAsset).to.be.equal(makerAddress); + }); + it('should allow an order selling multiple assets to be partially filled', async () => { + const makerAmounts = [new BigNumber(10), new BigNumber(20)]; + const makerNestedAssetData = [ + assetDataUtils.encodeERC20AssetData(erc20TokenA.address), + assetDataUtils.encodeERC20AssetData(erc20TokenB.address), + ]; + const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( + makerAmounts, + makerNestedAssetData, + ); + const makerAssetAmount = new BigNumber(30); + const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); + const takerAssetAmount = new BigNumber(10); + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData, + takerAssetData, + makerAssetAmount, + takerAssetAmount, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }); + + const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); + const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); + const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + + const takerAssetFillAmount = takerAssetAmount.dividedToIntegerBy(2); + await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + takerAssetFillAmount, + }); + + const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); + const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); + const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + + expect(finalMakerBalanceA).to.be.bignumber.equal( + initialMakerBalanceA.minus( + makerAmounts[0].times( + makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ), + ); + expect(finalMakerBalanceB).to.be.bignumber.equal( + initialMakerBalanceB.minus( + makerAmounts[1].times( + makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ), + ); + expect(finalMakerZrxBalance).to.be.bignumber.equal( + initialMakerZrxBalance.plus( + takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ); + expect(finalTakerBalanceA).to.be.bignumber.equal( + initialTakerBalanceA.plus( + makerAmounts[0].times( + makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ), + ); + expect(finalTakerBalanceB).to.be.bignumber.equal( + initialTakerBalanceB.plus( + makerAmounts[1].times( + makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ), + ); + expect(finalTakerZrxBalance).to.be.bignumber.equal( + initialTakerZrxBalance.minus( + takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ); + }); + it('should allow an order buying multiple assets to be partially filled', async () => { + const takerAmounts = [new BigNumber(10), new BigNumber(20)]; + const takerNestedAssetData = [ + assetDataUtils.encodeERC20AssetData(erc20TokenA.address), + assetDataUtils.encodeERC20AssetData(erc20TokenB.address), + ]; + const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData( + takerAmounts, + takerNestedAssetData, + ); + const takerAssetAmount = new BigNumber(30); + const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); + const makerAssetAmount = new BigNumber(10); + signedOrder = await orderFactory.newSignedOrderAsync({ + makerAssetData, + takerAssetData, + makerAssetAmount, + takerAssetAmount, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }); + + const initialMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); + const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const initialTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); + const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + + const takerAssetFillAmount = takerAssetAmount.dividedToIntegerBy(2); + await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { + takerAssetFillAmount, + }); + + const finalMakerBalanceA = await erc20TokenA.balanceOf.callAsync(makerAddress); + const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress); + const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress); + const finalTakerBalanceA = await erc20TokenA.balanceOf.callAsync(takerAddress); + const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress); + const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress); + + expect(finalMakerBalanceA).to.be.bignumber.equal( + initialMakerBalanceA.plus( + takerAmounts[0].times( + takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ), + ); + expect(finalMakerBalanceB).to.be.bignumber.equal( + initialMakerBalanceB.plus( + takerAmounts[1].times( + takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ), + ); + expect(finalMakerZrxBalance).to.be.bignumber.equal( + initialMakerZrxBalance.minus( + makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ); + expect(finalTakerBalanceA).to.be.bignumber.equal( + initialTakerBalanceA.minus( + takerAmounts[0].times( + takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ), + ); + expect(finalTakerBalanceB).to.be.bignumber.equal( + initialTakerBalanceB.minus( + takerAmounts[1].times( + takerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ), + ); + expect(finalTakerZrxBalance).to.be.bignumber.equal( + initialTakerZrxBalance.plus( + makerAssetAmount.times(takerAssetFillAmount).dividedToIntegerBy(takerAssetAmount), + ), + ); + }); + }); + describe('getOrderInfo', () => { beforeEach(async () => { signedOrder = await orderFactory.newSignedOrderAsync(); -- cgit v1.2.3 From 0b9e9eb0e4fb58ffbda84f0e77c3b0c83902d669 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 26 Nov 2018 17:52:16 -0800 Subject: Don't load assetProxy if currentProxyid is equal to the last seen proxyid --- .../protocol/AssetProxy/MultiAssetProxy.sol | 26 ++++++++++++++-------- packages/contracts/test/asset_proxy/proxies.ts | 1 + 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol index 6db42e44f..42231e73b 100644 --- a/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol +++ b/packages/contracts/contracts/protocol/AssetProxy/MultiAssetProxy.sol @@ -18,13 +18,11 @@ pragma solidity 0.4.24; -import "../../utils/SafeMath/SafeMath.sol"; import "../Exchange/MixinAssetProxyDispatcher.sol"; import "./MixinAuthorizable.sol"; contract MultiAssetProxy is - SafeMath, MixinAssetProxyDispatcher, MixinAuthorizable { @@ -172,6 +170,10 @@ contract MultiAssetProxy is // Calculate number of bytes in `amounts` contents let amountsByteLen := mul(amountsLen, 32) + // Initialize `assetProxyId` and `assetProxy` to 0 + let assetProxyId := 0 + let assetProxy := 0 + // Loop through `amounts` and `nestedAssetData`, calling `transferFrom` for each respective element for {let i := 0} lt(i, amountsByteLen) {i := add(i, 32)} { @@ -221,16 +223,22 @@ contract MultiAssetProxy is } // Load AssetProxy id - let assetProxyId := and( + let currentAssetProxyId := and( calldataload(nestedAssetDataElementContentsStart), 0xffffffff00000000000000000000000000000000000000000000000000000000 ) - // To lookup a value in a mapping, we load from the storage location keccak256(k, p), - // where k is the key left padded to 32 bytes and p is the storage slot - mstore(132, assetProxyId) - mstore(164, assetProxies_slot) - let assetProxy := sload(keccak256(132, 64)) + // Only load `assetProxy` if `currentAssetProxyId` does not equal `assetProxyId` + // We do not need to check if `currentAssetProxyId` is 0 since `assetProxy` is also initialized to 0 + if iszero(eq(currentAssetProxyId, assetProxyId)) { + // Update `assetProxyId` + assetProxyId := currentAssetProxyId + // To lookup a value in a mapping, we load from the storage location keccak256(k, p), + // where k is the key left padded to 32 bytes and p is the storage slot + mstore(132, assetProxyId) + mstore(164, assetProxies_slot) + assetProxy := sload(keccak256(132, 64)) + } // Revert if AssetProxy with given id does not exist if iszero(assetProxy) { @@ -257,7 +265,7 @@ contract MultiAssetProxy is 0, // pointer to start of input add(164, nestedAssetDataElementLen), // length of input 0, // write output over memory that won't be reused - 0 // reserve 512 bytes for output + 0 // don't copy output to memory ) // Revert with reason given by AssetProxy if `transferFrom` call failed diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts index face0b92b..8fa1e602a 100644 --- a/packages/contracts/test/asset_proxy/proxies.ts +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -695,6 +695,7 @@ describe('Asset Transfer Proxies', () => { }); it('should have an id of 0x94cfcdd7', async () => { const proxyId = await multiAssetProxy.getProxyId.callAsync(); + // first 4 bytes of `keccak256('MultiAsset(uint256[],bytes[])')` const expectedProxyId = '0x94cfcdd7'; expect(proxyId).to.equal(expectedProxyId); }); -- cgit v1.2.3 From 708f4e9bb887431dec278546f2c01c124f8b61c2 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 26 Nov 2018 17:52:53 -0800 Subject: Update CHANGELOG --- packages/contracts/CHANGELOG.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/contracts/CHANGELOG.json b/packages/contracts/CHANGELOG.json index 00f94c83b..7dfa06990 100644 --- a/packages/contracts/CHANGELOG.json +++ b/packages/contracts/CHANGELOG.json @@ -1,4 +1,14 @@ [ + { + "name": "MultiAssetProxy", + "version": "1.0.0", + "changes": [ + { + "note": "Add MultiAssetProxy implementation", + "pr": 1224 + } + ] + }, { "name": "OrderValidator", "version": "1.0.1", -- cgit v1.2.3 From 9266df43a4b8963be9fa90c4e7bab5e91256b8fd Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Wed, 28 Nov 2018 20:08:34 +1100 Subject: chore: Add Ganache to NetworkId enum --- packages/contract-addresses/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/contract-addresses/src/index.ts b/packages/contract-addresses/src/index.ts index b36997f23..7989631e3 100644 --- a/packages/contract-addresses/src/index.ts +++ b/packages/contract-addresses/src/index.ts @@ -16,6 +16,7 @@ export enum NetworkId { Ropsten = 3, Rinkeby = 4, Kovan = 42, + Ganache = 50, } const networkToAddresses: { [networkId: number]: ContractAddresses } = { -- cgit v1.2.3 From c0b9214138a1f2832417ba2ad352569070827e70 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Wed, 28 Nov 2018 21:49:26 +1100 Subject: Updated CHANGELOGS --- packages/0x.js/CHANGELOG.json | 9 +++++++++ packages/0x.js/CHANGELOG.md | 4 ++++ packages/abi-gen-templates/CHANGELOG.json | 9 +++++++++ packages/abi-gen-templates/CHANGELOG.md | 14 ++++++++++++++ packages/abi-gen-wrappers/CHANGELOG.json | 3 ++- packages/abi-gen-wrappers/CHANGELOG.md | 4 ++++ packages/asset-buyer/CHANGELOG.json | 9 +++++++++ packages/asset-buyer/CHANGELOG.md | 4 ++++ packages/base-contract/CHANGELOG.json | 9 +++++++++ packages/base-contract/CHANGELOG.md | 4 ++++ packages/connect/CHANGELOG.json | 9 +++++++++ packages/connect/CHANGELOG.md | 4 ++++ packages/contract-addresses/CHANGELOG.json | 3 ++- packages/contract-addresses/CHANGELOG.md | 5 +++++ packages/contract-artifacts/CHANGELOG.json | 3 ++- packages/contract-artifacts/CHANGELOG.md | 4 ++++ packages/contract-wrappers/CHANGELOG.json | 9 +++++++++ packages/contract-wrappers/CHANGELOG.md | 4 ++++ packages/dev-utils/CHANGELOG.json | 9 +++++++++ packages/dev-utils/CHANGELOG.md | 4 ++++ packages/fill-scenarios/CHANGELOG.json | 9 +++++++++ packages/fill-scenarios/CHANGELOG.md | 4 ++++ packages/migrations/CHANGELOG.json | 3 ++- packages/migrations/CHANGELOG.md | 6 ++++++ packages/order-utils/CHANGELOG.json | 9 +++++++++ packages/order-utils/CHANGELOG.md | 4 ++++ packages/order-watcher/CHANGELOG.json | 9 +++++++++ packages/order-watcher/CHANGELOG.md | 4 ++++ packages/react-docs/CHANGELOG.json | 9 +++++++++ packages/react-docs/CHANGELOG.md | 4 ++++ packages/react-shared/CHANGELOG.json | 9 +++++++++ packages/react-shared/CHANGELOG.md | 4 ++++ packages/sol-compiler/CHANGELOG.json | 9 +++++++++ packages/sol-compiler/CHANGELOG.md | 4 ++++ packages/sol-cov/CHANGELOG.json | 9 +++++++++ packages/sol-cov/CHANGELOG.md | 4 ++++ packages/sol-doc/CHANGELOG.json | 9 +++++++++ packages/sol-doc/CHANGELOG.md | 4 ++++ packages/subproviders/CHANGELOG.json | 9 +++++++++ packages/subproviders/CHANGELOG.md | 4 ++++ packages/web3-wrapper/CHANGELOG.json | 3 ++- packages/web3-wrapper/CHANGELOG.md | 4 ++++ 42 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 packages/abi-gen-templates/CHANGELOG.md diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json index 9ff4183e7..4ee1e92be 100644 --- a/packages/0x.js/CHANGELOG.json +++ b/packages/0x.js/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "2.0.6", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "2.0.5", diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md index 1f40bca7a..463ff923d 100644 --- a/packages/0x.js/CHANGELOG.md +++ b/packages/0x.js/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.6 - _November 28, 2018_ + + * Dependencies updated + ## v2.0.5 - _November 21, 2018_ * Dependencies updated diff --git a/packages/abi-gen-templates/CHANGELOG.json b/packages/abi-gen-templates/CHANGELOG.json index adf615b3b..baf852ad5 100644 --- a/packages/abi-gen-templates/CHANGELOG.json +++ b/packages/abi-gen-templates/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "1.0.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "version": "1.0.0", "changes": [ diff --git a/packages/abi-gen-templates/CHANGELOG.md b/packages/abi-gen-templates/CHANGELOG.md new file mode 100644 index 000000000..1c3f21c6c --- /dev/null +++ b/packages/abi-gen-templates/CHANGELOG.md @@ -0,0 +1,14 @@ + + +CHANGELOG + +## v1.0.1 - _November 28, 2018_ + + * Dependencies updated + +## v1.0.0 - _Invalid date_ + + * Initial publish (#1305) diff --git a/packages/abi-gen-wrappers/CHANGELOG.json b/packages/abi-gen-wrappers/CHANGELOG.json index f74d98afa..6905a7537 100644 --- a/packages/abi-gen-wrappers/CHANGELOG.json +++ b/packages/abi-gen-wrappers/CHANGELOG.json @@ -6,7 +6,8 @@ "pr": 1309, "note": "Update Exchange artifact to receive ZRX asset data as a constructor argument" } - ] + ], + "timestamp": 1543401373 }, { "version": "1.1.0", diff --git a/packages/abi-gen-wrappers/CHANGELOG.md b/packages/abi-gen-wrappers/CHANGELOG.md index 7d359f07b..30a10d6bd 100644 --- a/packages/abi-gen-wrappers/CHANGELOG.md +++ b/packages/abi-gen-wrappers/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.0 - _November 28, 2018_ + + * Update Exchange artifact to receive ZRX asset data as a constructor argument (#1309) + ## v1.1.0 - _November 21, 2018_ * `deployFrom0xArtifactAsync` additionally accepts artifacts that conform to the `SimpleContractArtifact` interface (#1298) diff --git a/packages/asset-buyer/CHANGELOG.json b/packages/asset-buyer/CHANGELOG.json index b7a83ccfc..28d3270e8 100644 --- a/packages/asset-buyer/CHANGELOG.json +++ b/packages/asset-buyer/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "3.0.2", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "version": "3.0.1", "changes": [ diff --git a/packages/asset-buyer/CHANGELOG.md b/packages/asset-buyer/CHANGELOG.md index 20702a531..be3ef67d1 100644 --- a/packages/asset-buyer/CHANGELOG.md +++ b/packages/asset-buyer/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.2 - _November 28, 2018_ + + * Dependencies updated + ## v3.0.1 - _November 21, 2018_ * Dependencies updated (#1276) diff --git a/packages/base-contract/CHANGELOG.json b/packages/base-contract/CHANGELOG.json index 66633136c..e4dff5530 100644 --- a/packages/base-contract/CHANGELOG.json +++ b/packages/base-contract/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "3.0.8", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "3.0.7", diff --git a/packages/base-contract/CHANGELOG.md b/packages/base-contract/CHANGELOG.md index 35032fc9f..f61b6c6ce 100644 --- a/packages/base-contract/CHANGELOG.md +++ b/packages/base-contract/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.8 - _November 28, 2018_ + + * Dependencies updated + ## v3.0.7 - _November 21, 2018_ * Dependencies updated diff --git a/packages/connect/CHANGELOG.json b/packages/connect/CHANGELOG.json index db9d8c92a..3abb895a7 100644 --- a/packages/connect/CHANGELOG.json +++ b/packages/connect/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "3.0.8", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "3.0.7", diff --git a/packages/connect/CHANGELOG.md b/packages/connect/CHANGELOG.md index 5e4013322..1dfc2672d 100644 --- a/packages/connect/CHANGELOG.md +++ b/packages/connect/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.8 - _November 28, 2018_ + + * Dependencies updated + ## v3.0.7 - _November 21, 2018_ * Dependencies updated diff --git a/packages/contract-addresses/CHANGELOG.json b/packages/contract-addresses/CHANGELOG.json index a671247e2..21ffaf510 100644 --- a/packages/contract-addresses/CHANGELOG.json +++ b/packages/contract-addresses/CHANGELOG.json @@ -10,7 +10,8 @@ "note": "Added Ganache snapshot addresses for network 50", "pr": 1318 } - ] + ], + "timestamp": 1543401373 }, { "version": "1.2.0", diff --git a/packages/contract-addresses/CHANGELOG.md b/packages/contract-addresses/CHANGELOG.md index 9801831f7..c006c3b22 100644 --- a/packages/contract-addresses/CHANGELOG.md +++ b/packages/contract-addresses/CHANGELOG.md @@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.0.0 - _November 28, 2018_ + + * Redeployed Rinkeby with testnet Exchange artifact (#1318) + * Added Ganache snapshot addresses for network 50 (#1318) + ## v1.2.0 - _November 21, 2018_ * Rinkeby Deployment diff --git a/packages/contract-artifacts/CHANGELOG.json b/packages/contract-artifacts/CHANGELOG.json index b75ad5766..03c88e71a 100644 --- a/packages/contract-artifacts/CHANGELOG.json +++ b/packages/contract-artifacts/CHANGELOG.json @@ -6,7 +6,8 @@ "pr": 1309, "note": "Update Exchange artifact to receive ZRX asset data as a constructor argument" } - ] + ], + "timestamp": 1543401373 }, { "version": "1.1.0", diff --git a/packages/contract-artifacts/CHANGELOG.md b/packages/contract-artifacts/CHANGELOG.md index b3c399985..9e48058f5 100644 --- a/packages/contract-artifacts/CHANGELOG.md +++ b/packages/contract-artifacts/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.1.2 - _November 28, 2018_ + + * Update Exchange artifact to receive ZRX asset data as a constructor argument (#1309) + ## v1.1.0 - _November 9, 2018_ * Update Forwarder artifact (#1192) diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index 711ab49a1..006a0904d 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "4.1.1", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "version": "4.1.0", "changes": [ diff --git a/packages/contract-wrappers/CHANGELOG.md b/packages/contract-wrappers/CHANGELOG.md index 201c65a4c..ebdcc9638 100644 --- a/packages/contract-wrappers/CHANGELOG.md +++ b/packages/contract-wrappers/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v4.1.1 - _November 28, 2018_ + + * Dependencies updated + ## v4.1.0 - _November 21, 2018_ * Add a `nonce` field for `TxOpts` so that it's now possible to re-broadcast stuck transactions with a higher gas amount (#1292) diff --git a/packages/dev-utils/CHANGELOG.json b/packages/dev-utils/CHANGELOG.json index 4f47f0f45..417a3c65e 100644 --- a/packages/dev-utils/CHANGELOG.json +++ b/packages/dev-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "1.0.19", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "1.0.18", diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 3ab8192c7..1842c6824 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.19 - _November 28, 2018_ + + * Dependencies updated + ## v1.0.18 - _November 21, 2018_ * Dependencies updated diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json index f83a6612d..58ba49509 100644 --- a/packages/fill-scenarios/CHANGELOG.json +++ b/packages/fill-scenarios/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "1.0.14", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "1.0.13", diff --git a/packages/fill-scenarios/CHANGELOG.md b/packages/fill-scenarios/CHANGELOG.md index 3c39e1650..aa7df302e 100644 --- a/packages/fill-scenarios/CHANGELOG.md +++ b/packages/fill-scenarios/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.14 - _November 28, 2018_ + + * Dependencies updated + ## v1.0.13 - _November 21, 2018_ * Dependencies updated diff --git a/packages/migrations/CHANGELOG.json b/packages/migrations/CHANGELOG.json index 07031fc09..56705fc1a 100644 --- a/packages/migrations/CHANGELOG.json +++ b/packages/migrations/CHANGELOG.json @@ -14,7 +14,8 @@ "note": "Fund the Forwarder with ZRX for fees.", "pr": 1309 } - ] + ], + "timestamp": 1543401373 }, { "version": "2.1.0", diff --git a/packages/migrations/CHANGELOG.md b/packages/migrations/CHANGELOG.md index 986e224b0..3808b2d3d 100644 --- a/packages/migrations/CHANGELOG.md +++ b/packages/migrations/CHANGELOG.md @@ -5,6 +5,12 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.2.0 - _November 28, 2018_ + + * Add CLI `0x-migrate` for running the 0x migrations in a language-agnostic way (#1324) + * Deploy testnet Exchange arfitact. Previously mainnet Exchange artifact was deployed. (#1309) + * Fund the Forwarder with ZRX for fees. (#1309) + ## v2.1.0 - _November 21, 2018_ * Export all type declarations used by the public interface, as well as the `ContractAddresses` mapping (#1301) diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json index a4f5dc622..6c8fd6239 100644 --- a/packages/order-utils/CHANGELOG.json +++ b/packages/order-utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "3.0.4", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "3.0.3", diff --git a/packages/order-utils/CHANGELOG.md b/packages/order-utils/CHANGELOG.md index b863cbc03..5eae590b5 100644 --- a/packages/order-utils/CHANGELOG.md +++ b/packages/order-utils/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.0.4 - _November 28, 2018_ + + * Dependencies updated + ## v3.0.3 - _November 21, 2018_ * Dependencies updated diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json index ca2de9831..4e56dc400 100644 --- a/packages/order-watcher/CHANGELOG.json +++ b/packages/order-watcher/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "2.2.6", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "2.2.5", diff --git a/packages/order-watcher/CHANGELOG.md b/packages/order-watcher/CHANGELOG.md index 7ae47fdda..37b4a7438 100644 --- a/packages/order-watcher/CHANGELOG.md +++ b/packages/order-watcher/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.2.6 - _November 28, 2018_ + + * Dependencies updated + ## v2.2.5 - _November 21, 2018_ * Dependencies updated diff --git a/packages/react-docs/CHANGELOG.json b/packages/react-docs/CHANGELOG.json index cecc270eb..d456a3b53 100644 --- a/packages/react-docs/CHANGELOG.json +++ b/packages/react-docs/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "1.0.20", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "1.0.19", diff --git a/packages/react-docs/CHANGELOG.md b/packages/react-docs/CHANGELOG.md index f0a56191d..e48f43fb8 100644 --- a/packages/react-docs/CHANGELOG.md +++ b/packages/react-docs/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.20 - _November 28, 2018_ + + * Dependencies updated + ## v1.0.19 - _November 21, 2018_ * Dependencies updated diff --git a/packages/react-shared/CHANGELOG.json b/packages/react-shared/CHANGELOG.json index bcbf2d9f9..a376bae29 100644 --- a/packages/react-shared/CHANGELOG.json +++ b/packages/react-shared/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "1.0.23", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "1.0.22", diff --git a/packages/react-shared/CHANGELOG.md b/packages/react-shared/CHANGELOG.md index c6fb9e479..a983e0af2 100644 --- a/packages/react-shared/CHANGELOG.md +++ b/packages/react-shared/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.23 - _November 28, 2018_ + + * Dependencies updated + ## v1.0.22 - _November 21, 2018_ * Dependencies updated diff --git a/packages/sol-compiler/CHANGELOG.json b/packages/sol-compiler/CHANGELOG.json index 2ca983c59..e9274f64e 100644 --- a/packages/sol-compiler/CHANGELOG.json +++ b/packages/sol-compiler/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "1.1.14", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "1.1.13", diff --git a/packages/sol-compiler/CHANGELOG.md b/packages/sol-compiler/CHANGELOG.md index e535df64e..a1782bb3b 100644 --- a/packages/sol-compiler/CHANGELOG.md +++ b/packages/sol-compiler/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.1.14 - _November 28, 2018_ + + * Dependencies updated + ## v1.1.13 - _November 21, 2018_ * Dependencies updated diff --git a/packages/sol-cov/CHANGELOG.json b/packages/sol-cov/CHANGELOG.json index 3dd04be8c..bc8aa71e1 100644 --- a/packages/sol-cov/CHANGELOG.json +++ b/packages/sol-cov/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "2.1.14", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "2.1.13", diff --git a/packages/sol-cov/CHANGELOG.md b/packages/sol-cov/CHANGELOG.md index e56a1393e..25ba93026 100644 --- a/packages/sol-cov/CHANGELOG.md +++ b/packages/sol-cov/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.14 - _November 28, 2018_ + + * Dependencies updated + ## v2.1.13 - _November 21, 2018_ * Dependencies updated diff --git a/packages/sol-doc/CHANGELOG.json b/packages/sol-doc/CHANGELOG.json index c3dcc81f1..332aeb025 100644 --- a/packages/sol-doc/CHANGELOG.json +++ b/packages/sol-doc/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "1.0.9", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "1.0.8", diff --git a/packages/sol-doc/CHANGELOG.md b/packages/sol-doc/CHANGELOG.md index a7a7fa0fa..5a1df59c7 100644 --- a/packages/sol-doc/CHANGELOG.md +++ b/packages/sol-doc/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v1.0.9 - _November 28, 2018_ + + * Dependencies updated + ## v1.0.8 - _November 21, 2018_ * Dependencies updated diff --git a/packages/subproviders/CHANGELOG.json b/packages/subproviders/CHANGELOG.json index 62c495b49..6da170be3 100644 --- a/packages/subproviders/CHANGELOG.json +++ b/packages/subproviders/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543401373, + "version": "2.1.6", + "changes": [ + { + "note": "Dependencies updated" + } + ] + }, { "timestamp": 1542821676, "version": "2.1.5", diff --git a/packages/subproviders/CHANGELOG.md b/packages/subproviders/CHANGELOG.md index 0251b6d9a..01dd8d652 100644 --- a/packages/subproviders/CHANGELOG.md +++ b/packages/subproviders/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v2.1.6 - _November 28, 2018_ + + * Dependencies updated + ## v2.1.5 - _November 21, 2018_ * Dependencies updated diff --git a/packages/web3-wrapper/CHANGELOG.json b/packages/web3-wrapper/CHANGELOG.json index ad6902e33..9f5194e0d 100644 --- a/packages/web3-wrapper/CHANGELOG.json +++ b/packages/web3-wrapper/CHANGELOG.json @@ -6,7 +6,8 @@ "note": "Unmarshall mined transaction receipts", "pr": 1308 } - ] + ], + "timestamp": 1543401373 }, { "version": "3.1.5", diff --git a/packages/web3-wrapper/CHANGELOG.md b/packages/web3-wrapper/CHANGELOG.md index fa88eee2c..fffaf1d0a 100644 --- a/packages/web3-wrapper/CHANGELOG.md +++ b/packages/web3-wrapper/CHANGELOG.md @@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only. CHANGELOG +## v3.1.6 - _November 28, 2018_ + + * Unmarshall mined transaction receipts (#1308) + ## v3.1.5 - _November 21, 2018_ * Add unmarshalling of transaction receipts (#1291) -- cgit v1.2.3 From 4861e480597547527397ca4df52c29e5caa798c8 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Wed, 28 Nov 2018 21:49:55 +1100 Subject: Publish - 0x.js@2.0.6 - @0x/abi-gen-templates@1.0.1 - @0x/abi-gen-wrappers@2.0.0 - @0x/asset-buyer@3.0.2 - @0x/base-contract@3.0.8 - @0x/connect@3.0.8 - @0x/contract-addresses@2.0.0 - @0x/contract-artifacts@1.1.2 - @0x/contract-wrappers@4.1.1 - contracts@2.1.56 - @0x/dev-tools-pages@0.0.8 - @0x/dev-utils@1.0.19 - @0x/fill-scenarios@1.0.14 - @0x/instant@1.0.2 - @0x/metacoin@0.0.30 - @0x/migrations@2.2.0 - @0x/order-utils@3.0.4 - @0x/order-watcher@2.2.6 - @0x/react-docs@1.0.20 - @0x/react-shared@1.0.23 - @0x/sol-compiler@1.1.14 - @0x/sol-cov@2.1.14 - @0x/sol-doc@1.0.9 - @0x/subproviders@2.1.6 - @0x/testnet-faucets@1.0.58 - @0x/web3-wrapper@3.1.6 - @0x/website@0.0.61 --- packages/0x.js/package.json | 22 +++++++++++----------- packages/abi-gen-templates/package.json | 2 +- packages/abi-gen-wrappers/package.json | 8 ++++---- packages/asset-buyer/package.json | 12 ++++++------ packages/base-contract/package.json | 4 ++-- packages/connect/package.json | 4 ++-- packages/contract-addresses/package.json | 2 +- packages/contract-artifacts/package.json | 2 +- packages/contract-wrappers/package.json | 20 ++++++++++---------- packages/contracts/package.json | 16 ++++++++-------- packages/dev-tools-pages/package.json | 4 ++-- packages/dev-utils/package.json | 6 +++--- packages/fill-scenarios/package.json | 12 ++++++------ packages/instant/package.json | 10 +++++----- packages/metacoin/package.json | 16 ++++++++-------- packages/migrations/package.json | 20 ++++++++++---------- packages/order-utils/package.json | 12 ++++++------ packages/order-watcher/package.json | 22 +++++++++++----------- packages/react-docs/package.json | 6 +++--- packages/react-shared/package.json | 4 ++-- packages/sol-compiler/package.json | 6 +++--- packages/sol-cov/package.json | 10 +++++----- packages/sol-doc/package.json | 4 ++-- packages/subproviders/package.json | 4 ++-- packages/testnet-faucets/package.json | 8 ++++---- packages/web3-wrapper/package.json | 2 +- packages/website/package.json | 14 +++++++------- 27 files changed, 126 insertions(+), 126 deletions(-) diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 3850e9038..aa038c302 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -1,6 +1,6 @@ { "name": "0x.js", - "version": "2.0.5", + "version": "2.0.6", "engines": { "node": ">=6.12" }, @@ -42,10 +42,10 @@ }, "license": "Apache-2.0", "devDependencies": { - "@0x/abi-gen-wrappers": "^1.1.0", - "@0x/contract-addresses": "^1.2.0", - "@0x/dev-utils": "^1.0.18", - "@0x/migrations": "^2.1.0", + "@0x/abi-gen-wrappers": "^2.0.0", + "@0x/contract-addresses": "^2.0.0", + "@0x/dev-utils": "^1.0.19", + "@0x/migrations": "^2.2.0", "@0x/tslint-config": "^1.0.10", "@types/lodash": "4.14.104", "@types/mocha": "^2.2.42", @@ -73,15 +73,15 @@ }, "dependencies": { "@0x/assert": "^1.0.18", - "@0x/base-contract": "^3.0.7", - "@0x/contract-wrappers": "^4.1.0", - "@0x/order-utils": "^3.0.3", - "@0x/order-watcher": "^2.2.5", - "@0x/subproviders": "^2.1.5", + "@0x/base-contract": "^3.0.8", + "@0x/contract-wrappers": "^4.1.1", + "@0x/order-utils": "^3.0.4", + "@0x/order-watcher": "^2.2.6", + "@0x/subproviders": "^2.1.6", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "@types/web3-provider-engine": "^14.0.0", "ethereum-types": "^1.1.2", "ethers": "~4.0.4", diff --git a/packages/abi-gen-templates/package.json b/packages/abi-gen-templates/package.json index e06be6127..09872ab49 100644 --- a/packages/abi-gen-templates/package.json +++ b/packages/abi-gen-templates/package.json @@ -1,6 +1,6 @@ { "name": "@0x/abi-gen-templates", - "version": "1.0.0", + "version": "1.0.1", "engines": { "node": ">=6.12" }, diff --git a/packages/abi-gen-wrappers/package.json b/packages/abi-gen-wrappers/package.json index 1b7015d55..e4f103cf7 100644 --- a/packages/abi-gen-wrappers/package.json +++ b/packages/abi-gen-wrappers/package.json @@ -1,6 +1,6 @@ { "name": "@0x/abi-gen-wrappers", - "version": "1.1.0", + "version": "2.0.0", "engines": { "node": ">=6.12" }, @@ -31,18 +31,18 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/abi-gen-wrappers/README.md", "devDependencies": { "@0x/abi-gen": "^1.0.17", - "@0x/abi-gen-templates": "^1.0.0", + "@0x/abi-gen-templates": "^1.0.1", "@0x/tslint-config": "^1.0.10", "@0x/types": "^1.3.0", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "ethereum-types": "^1.1.2", "ethers": "~4.0.4", "lodash": "^4.17.5", "shx": "^0.2.2" }, "dependencies": { - "@0x/base-contract": "^3.0.7" + "@0x/base-contract": "^3.0.8" }, "publishConfig": { "access": "public" diff --git a/packages/asset-buyer/package.json b/packages/asset-buyer/package.json index 50b276704..780b2e3e2 100644 --- a/packages/asset-buyer/package.json +++ b/packages/asset-buyer/package.json @@ -1,6 +1,6 @@ { "name": "@0x/asset-buyer", - "version": "3.0.1", + "version": "3.0.2", "engines": { "node": ">=6.12" }, @@ -37,15 +37,15 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/asset-buyer/README.md", "dependencies": { "@0x/assert": "^1.0.18", - "@0x/connect": "^3.0.7", - "@0x/contract-wrappers": "^4.1.0", + "@0x/connect": "^3.0.8", + "@0x/contract-wrappers": "^4.1.1", "@0x/json-schemas": "^2.1.2", - "@0x/order-utils": "^3.0.3", - "@0x/subproviders": "^2.1.5", + "@0x/order-utils": "^3.0.4", + "@0x/subproviders": "^2.1.6", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "ethereum-types": "^1.1.2", "lodash": "^4.17.5" }, diff --git a/packages/base-contract/package.json b/packages/base-contract/package.json index 2ae42d66b..2a331b3cb 100644 --- a/packages/base-contract/package.json +++ b/packages/base-contract/package.json @@ -1,6 +1,6 @@ { "name": "@0x/base-contract", - "version": "3.0.7", + "version": "3.0.8", "engines": { "node": ">=6.12" }, @@ -42,7 +42,7 @@ "dependencies": { "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "ethereum-types": "^1.1.2", "ethers": "~4.0.4", "lodash": "^4.17.5" diff --git a/packages/connect/package.json b/packages/connect/package.json index d05f24463..2f3d30d84 100644 --- a/packages/connect/package.json +++ b/packages/connect/package.json @@ -1,6 +1,6 @@ { "name": "@0x/connect", - "version": "3.0.7", + "version": "3.0.8", "engines": { "node": ">=6.12" }, @@ -46,7 +46,7 @@ "dependencies": { "@0x/assert": "^1.0.18", "@0x/json-schemas": "^2.1.2", - "@0x/order-utils": "^3.0.3", + "@0x/order-utils": "^3.0.4", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", diff --git a/packages/contract-addresses/package.json b/packages/contract-addresses/package.json index 9f0db4d30..c75ae6efa 100644 --- a/packages/contract-addresses/package.json +++ b/packages/contract-addresses/package.json @@ -1,6 +1,6 @@ { "name": "@0x/contract-addresses", - "version": "1.2.0", + "version": "2.0.0", "engines": { "node": ">=6.12" }, diff --git a/packages/contract-artifacts/package.json b/packages/contract-artifacts/package.json index 9c25e3ac5..71d887545 100644 --- a/packages/contract-artifacts/package.json +++ b/packages/contract-artifacts/package.json @@ -1,6 +1,6 @@ { "name": "@0x/contract-artifacts", - "version": "1.1.0", + "version": "1.1.2", "engines": { "node": ">=6.12" }, diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index 999375ea5..e11d1a63e 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -1,6 +1,6 @@ { "name": "@0x/contract-wrappers", - "version": "4.1.0", + "version": "4.1.1", "description": "Smart TS wrappers for 0x smart contracts", "keywords": [ "0xproject", @@ -37,9 +37,9 @@ "node": ">=6.0.0" }, "devDependencies": { - "@0x/dev-utils": "^1.0.18", - "@0x/migrations": "^2.1.0", - "@0x/subproviders": "^2.1.5", + "@0x/dev-utils": "^1.0.19", + "@0x/migrations": "^2.2.0", + "@0x/subproviders": "^2.1.6", "@0x/tslint-config": "^1.0.10", "@types/lodash": "4.14.104", "@types/mocha": "^2.2.42", @@ -65,17 +65,17 @@ "web3-provider-engine": "14.0.6" }, "dependencies": { - "@0x/abi-gen-wrappers": "^1.1.0", + "@0x/abi-gen-wrappers": "^2.0.0", "@0x/assert": "^1.0.18", - "@0x/contract-addresses": "^1.2.0", - "@0x/contract-artifacts": "^1.1.0", - "@0x/fill-scenarios": "^1.0.13", + "@0x/contract-addresses": "^2.0.0", + "@0x/contract-artifacts": "^1.1.2", + "@0x/fill-scenarios": "^1.0.14", "@0x/json-schemas": "^2.1.2", - "@0x/order-utils": "^3.0.3", + "@0x/order-utils": "^3.0.4", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "ethereum-types": "^1.1.2", "ethereumjs-blockstream": "6.0.0", "ethereumjs-util": "^5.1.1", diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 25445c4f8..6f031fcee 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "contracts", - "version": "2.1.55", + "version": "2.1.56", "engines": { "node": ">=6.12" }, @@ -46,10 +46,10 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts/README.md", "devDependencies": { "@0x/abi-gen": "^1.0.17", - "@0x/dev-utils": "^1.0.18", - "@0x/sol-compiler": "^1.1.13", - "@0x/sol-cov": "^2.1.13", - "@0x/subproviders": "^2.1.5", + "@0x/dev-utils": "^1.0.19", + "@0x/sol-compiler": "^1.1.14", + "@0x/sol-cov": "^2.1.14", + "@0x/subproviders": "^2.1.6", "@0x/tslint-config": "^1.0.10", "@types/bn.js": "^4.11.0", "@types/ethereumjs-abi": "^0.6.0", @@ -71,12 +71,12 @@ "yargs": "^10.0.3" }, "dependencies": { - "@0x/base-contract": "^3.0.7", - "@0x/order-utils": "^3.0.3", + "@0x/base-contract": "^3.0.8", + "@0x/order-utils": "^3.0.4", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "@types/js-combinatorics": "^0.5.29", "bn.js": "^4.11.8", "ethereum-types": "^1.1.2", diff --git a/packages/dev-tools-pages/package.json b/packages/dev-tools-pages/package.json index eb320c103..4b13beb01 100644 --- a/packages/dev-tools-pages/package.json +++ b/packages/dev-tools-pages/package.json @@ -1,6 +1,6 @@ { "name": "@0x/dev-tools-pages", - "version": "0.0.7", + "version": "0.0.8", "engines": { "node": ">=6.12" }, @@ -16,7 +16,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@0x/react-shared": "^1.0.22", + "@0x/react-shared": "^1.0.23", "basscss": "^8.0.3", "bowser": "^1.9.3", "less": "^2.7.2", diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index e7cd62a81..a3b5c9090 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@0x/dev-utils", - "version": "1.0.18", + "version": "1.0.19", "engines": { "node": ">=6.12" }, @@ -41,11 +41,11 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/subproviders": "^2.1.5", + "@0x/subproviders": "^2.1.6", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "@types/web3-provider-engine": "^14.0.0", "chai": "^4.0.1", "ethereum-types": "^1.1.2", diff --git a/packages/fill-scenarios/package.json b/packages/fill-scenarios/package.json index e91ed8a4e..b29eb674c 100644 --- a/packages/fill-scenarios/package.json +++ b/packages/fill-scenarios/package.json @@ -1,6 +1,6 @@ { "name": "@0x/fill-scenarios", - "version": "1.0.13", + "version": "1.0.14", "description": "0x order fill scenario generator", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -28,14 +28,14 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/abi-gen-wrappers": "^1.1.0", - "@0x/base-contract": "^3.0.7", - "@0x/contract-artifacts": "^1.1.0", - "@0x/order-utils": "^3.0.3", + "@0x/abi-gen-wrappers": "^2.0.0", + "@0x/base-contract": "^3.0.8", + "@0x/contract-artifacts": "^1.1.2", + "@0x/order-utils": "^3.0.4", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "ethereum-types": "^1.1.2", "ethers": "~4.0.4", "lodash": "^4.17.5" diff --git a/packages/instant/package.json b/packages/instant/package.json index 4422dc83f..c75e2408d 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -1,6 +1,6 @@ { "name": "@0x/instant", - "version": "1.0.1", + "version": "1.0.2", "engines": { "node": ">=6.12" }, @@ -46,14 +46,14 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md", "dependencies": { "@0x/assert": "^1.0.18", - "@0x/asset-buyer": "^3.0.1", + "@0x/asset-buyer": "^3.0.2", "@0x/json-schemas": "^2.1.2", - "@0x/order-utils": "^3.0.3", - "@0x/subproviders": "^2.1.5", + "@0x/order-utils": "^3.0.4", + "@0x/subproviders": "^2.1.6", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "bowser": "^1.9.4", "copy-to-clipboard": "^3.0.8", "ethereum-types": "^1.1.2", diff --git a/packages/metacoin/package.json b/packages/metacoin/package.json index 332fbb466..7622fa5d4 100644 --- a/packages/metacoin/package.json +++ b/packages/metacoin/package.json @@ -1,6 +1,6 @@ { "name": "@0x/metacoin", - "version": "0.0.29", + "version": "0.0.30", "engines": { "node": ">=6.12" }, @@ -30,15 +30,15 @@ "license": "Apache-2.0", "dependencies": { "@0x/abi-gen": "^1.0.17", - "@0x/abi-gen-templates": "^1.0.0", - "@0x/base-contract": "^3.0.7", - "@0x/sol-cov": "^2.1.13", - "@0x/subproviders": "^2.1.5", + "@0x/abi-gen-templates": "^1.0.1", + "@0x/base-contract": "^3.0.8", + "@0x/sol-cov": "^2.1.14", + "@0x/subproviders": "^2.1.6", "@0x/tslint-config": "^1.0.10", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "@types/mocha": "^5.2.2", "copyfiles": "^2.0.0", "ethereum-types": "^1.1.2", @@ -47,8 +47,8 @@ "run-s": "^0.0.0" }, "devDependencies": { - "@0x/dev-utils": "^1.0.18", - "@0x/sol-compiler": "^1.1.13", + "@0x/dev-utils": "^1.0.19", + "@0x/sol-compiler": "^1.1.14", "chai": "^4.0.1", "chai-as-promised": "^7.1.0", "chai-bignumber": "^2.0.1", diff --git a/packages/migrations/package.json b/packages/migrations/package.json index b006a470a..f4dd1f9f9 100644 --- a/packages/migrations/package.json +++ b/packages/migrations/package.json @@ -1,6 +1,6 @@ { "name": "@0x/migrations", - "version": "2.1.0", + "version": "2.2.0", "engines": { "node": ">=6.12" }, @@ -26,7 +26,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@0x/dev-utils": "^1.0.18", + "@0x/dev-utils": "^1.0.19", "@0x/tslint-config": "^1.0.10", "@0x/types": "^1.3.0", "@types/yargs": "^10.0.0", @@ -39,16 +39,16 @@ "yargs": "^10.0.3" }, "dependencies": { - "@0x/abi-gen-wrappers": "^1.1.0", - "@0x/base-contract": "^3.0.7", - "@0x/contract-addresses": "^1.2.0", - "@0x/contract-artifacts": "^1.1.0", - "@0x/order-utils": "^3.0.3", - "@0x/sol-compiler": "^1.1.13", - "@0x/subproviders": "^2.1.5", + "@0x/abi-gen-wrappers": "^2.0.0", + "@0x/base-contract": "^3.0.8", + "@0x/contract-addresses": "^2.0.0", + "@0x/contract-artifacts": "^1.1.2", + "@0x/order-utils": "^3.0.4", + "@0x/sol-compiler": "^1.1.14", + "@0x/subproviders": "^2.1.6", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "@ledgerhq/hw-app-eth": "^4.3.0", "ethereum-types": "^1.1.2", "ethers": "~4.0.4", diff --git a/packages/order-utils/package.json b/packages/order-utils/package.json index e032d6e7d..50229dafb 100644 --- a/packages/order-utils/package.json +++ b/packages/order-utils/package.json @@ -1,6 +1,6 @@ { "name": "@0x/order-utils", - "version": "3.0.3", + "version": "3.0.4", "engines": { "node": ">=6.12" }, @@ -35,7 +35,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/order-utils/README.md", "devDependencies": { - "@0x/dev-utils": "^1.0.18", + "@0x/dev-utils": "^1.0.19", "@0x/tslint-config": "^1.0.10", "@types/bn.js": "^4.11.0", "@types/lodash": "4.14.104", @@ -53,15 +53,15 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/abi-gen-wrappers": "^1.1.0", + "@0x/abi-gen-wrappers": "^2.0.0", "@0x/assert": "^1.0.18", - "@0x/base-contract": "^3.0.7", - "@0x/contract-artifacts": "^1.1.0", + "@0x/base-contract": "^3.0.8", + "@0x/contract-artifacts": "^1.1.2", "@0x/json-schemas": "^2.1.2", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "@types/node": "*", "bn.js": "^4.11.8", "ethereum-types": "^1.1.2", diff --git a/packages/order-watcher/package.json b/packages/order-watcher/package.json index 4257bd2a8..9a51203f4 100644 --- a/packages/order-watcher/package.json +++ b/packages/order-watcher/package.json @@ -1,6 +1,6 @@ { "name": "@0x/order-watcher", - "version": "2.2.5", + "version": "2.2.6", "description": "An order watcher daemon that watches for order validity", "keywords": [ "0x", @@ -33,8 +33,8 @@ "node": ">=6.0.0" }, "devDependencies": { - "@0x/dev-utils": "^1.0.18", - "@0x/migrations": "^2.1.0", + "@0x/dev-utils": "^1.0.19", + "@0x/migrations": "^2.2.0", "@0x/tslint-config": "^1.0.10", "@types/bintrees": "^1.0.2", "@types/lodash": "4.14.104", @@ -57,19 +57,19 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/abi-gen-wrappers": "^1.1.0", + "@0x/abi-gen-wrappers": "^2.0.0", "@0x/assert": "^1.0.18", - "@0x/base-contract": "^3.0.7", - "@0x/contract-addresses": "^1.2.0", - "@0x/contract-artifacts": "^1.1.0", - "@0x/contract-wrappers": "^4.1.0", - "@0x/fill-scenarios": "^1.0.13", + "@0x/base-contract": "^3.0.8", + "@0x/contract-addresses": "^2.0.0", + "@0x/contract-artifacts": "^1.1.2", + "@0x/contract-wrappers": "^4.1.1", + "@0x/fill-scenarios": "^1.0.14", "@0x/json-schemas": "^2.1.2", - "@0x/order-utils": "^3.0.3", + "@0x/order-utils": "^3.0.4", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "bintrees": "^1.0.2", "ethereum-types": "^1.1.2", "ethereumjs-blockstream": "6.0.0", diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index 6e819ee72..968ac4e34 100644 --- a/packages/react-docs/package.json +++ b/packages/react-docs/package.json @@ -1,6 +1,6 @@ { "name": "@0x/react-docs", - "version": "1.0.19", + "version": "1.0.20", "engines": { "node": ">=6.12" }, @@ -24,7 +24,7 @@ "url": "https://github.com/0xProject/0x-monorepo.git" }, "devDependencies": { - "@0x/dev-utils": "^1.0.18", + "@0x/dev-utils": "^1.0.19", "@0x/tslint-config": "^1.0.10", "@types/compare-versions": "^3.0.0", "@types/styled-components": "^4.0.0", @@ -34,7 +34,7 @@ "typescript": "3.0.1" }, "dependencies": { - "@0x/react-shared": "^1.0.22", + "@0x/react-shared": "^1.0.23", "@0x/types": "^1.3.0", "@0x/utils": "^2.0.6", "@types/lodash": "4.14.104", diff --git a/packages/react-shared/package.json b/packages/react-shared/package.json index 34412e14e..b5816ad98 100644 --- a/packages/react-shared/package.json +++ b/packages/react-shared/package.json @@ -1,6 +1,6 @@ { "name": "@0x/react-shared", - "version": "1.0.22", + "version": "1.0.23", "engines": { "node": ">=6.12" }, @@ -25,7 +25,7 @@ "url": "https://github.com/0xProject/0x-monorepo.git" }, "devDependencies": { - "@0x/dev-utils": "^1.0.18", + "@0x/dev-utils": "^1.0.19", "@0x/tslint-config": "^1.0.10", "make-promises-safe": "^1.1.0", "shx": "^0.2.2", diff --git a/packages/sol-compiler/package.json b/packages/sol-compiler/package.json index bcc235866..d27c0ee31 100644 --- a/packages/sol-compiler/package.json +++ b/packages/sol-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sol-compiler", - "version": "1.1.13", + "version": "1.1.14", "engines": { "node": ">=6.12" }, @@ -42,7 +42,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/sol-compiler/README.md", "devDependencies": { - "@0x/dev-utils": "^1.0.18", + "@0x/dev-utils": "^1.0.19", "@0x/tslint-config": "^1.0.10", "@types/mkdirp": "^0.5.2", "@types/require-from-string": "^1.2.0", @@ -71,7 +71,7 @@ "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "@types/yargs": "^11.0.0", "chalk": "^2.3.0", "ethereum-types": "^1.1.2", diff --git a/packages/sol-cov/package.json b/packages/sol-cov/package.json index 9dcb3b854..73c11980f 100644 --- a/packages/sol-cov/package.json +++ b/packages/sol-cov/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sol-cov", - "version": "2.1.13", + "version": "2.1.14", "engines": { "node": ">=6.12" }, @@ -42,12 +42,12 @@ }, "homepage": "https://github.com/0xProject/0x.js/packages/sol-cov/README.md", "dependencies": { - "@0x/dev-utils": "^1.0.18", - "@0x/sol-compiler": "^1.1.13", - "@0x/subproviders": "^2.1.5", + "@0x/dev-utils": "^1.0.19", + "@0x/sol-compiler": "^1.1.14", + "@0x/subproviders": "^2.1.6", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "@types/solidity-parser-antlr": "^0.2.0", "ethereum-types": "^1.1.2", "ethereumjs-util": "^5.1.1", diff --git a/packages/sol-doc/package.json b/packages/sol-doc/package.json index 6bf76d47a..edf1707d6 100644 --- a/packages/sol-doc/package.json +++ b/packages/sol-doc/package.json @@ -1,6 +1,6 @@ { "name": "@0x/sol-doc", - "version": "1.0.8", + "version": "1.0.9", "description": "Solidity documentation generator", "main": "lib/src/index.js", "types": "lib/src/index.d.js", @@ -25,7 +25,7 @@ "author": "F. Eugene Aumson", "license": "Apache-2.0", "dependencies": { - "@0x/sol-compiler": "^1.1.13", + "@0x/sol-compiler": "^1.1.14", "@0x/types": "^1.3.0", "@0x/utils": "^2.0.6", "ethereum-types": "^1.1.2", diff --git a/packages/subproviders/package.json b/packages/subproviders/package.json index 3b367054b..86f3738af 100644 --- a/packages/subproviders/package.json +++ b/packages/subproviders/package.json @@ -1,6 +1,6 @@ { "name": "@0x/subproviders", - "version": "2.1.5", + "version": "2.1.6", "engines": { "node": ">=6.12" }, @@ -33,7 +33,7 @@ "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "@ledgerhq/hw-app-eth": "^4.3.0", "@ledgerhq/hw-transport-u2f": "4.24.0", "@types/eth-lightwallet": "^3.0.0", diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json index cdfd1b7ff..8c4b942a3 100644 --- a/packages/testnet-faucets/package.json +++ b/packages/testnet-faucets/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@0x/testnet-faucets", - "version": "1.0.57", + "version": "1.0.58", "engines": { "node": ">=6.12" }, @@ -18,11 +18,11 @@ "author": "Fabio Berger", "license": "Apache-2.0", "dependencies": { - "0x.js": "^2.0.5", - "@0x/subproviders": "^2.1.5", + "0x.js": "^2.0.6", + "@0x/subproviders": "^2.1.6", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "body-parser": "^1.17.1", "ethereum-types": "^1.1.2", "ethereumjs-tx": "^1.3.5", diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index 2eba35da7..218d85bfc 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -1,6 +1,6 @@ { "name": "@0x/web3-wrapper", - "version": "3.1.5", + "version": "3.1.6", "engines": { "node": ">=6.12" }, diff --git a/packages/website/package.json b/packages/website/package.json index 52ba206c6..dc10c7b1c 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -1,6 +1,6 @@ { "name": "@0x/website", - "version": "0.0.60", + "version": "0.0.61", "engines": { "node": ">=6.12" }, @@ -20,16 +20,16 @@ "author": "Fabio Berger", "license": "Apache-2.0", "dependencies": { - "@0x/contract-wrappers": "^4.1.0", + "@0x/contract-wrappers": "^4.1.1", "@0x/json-schemas": "^2.1.2", - "@0x/order-utils": "^3.0.3", - "@0x/react-docs": "^1.0.19", - "@0x/react-shared": "^1.0.22", - "@0x/subproviders": "^2.1.5", + "@0x/order-utils": "^3.0.4", + "@0x/react-docs": "^1.0.20", + "@0x/react-shared": "^1.0.23", + "@0x/subproviders": "^2.1.6", "@0x/types": "^1.3.0", "@0x/typescript-typings": "^3.0.4", "@0x/utils": "^2.0.6", - "@0x/web3-wrapper": "^3.1.5", + "@0x/web3-wrapper": "^3.1.6", "accounting": "^0.4.1", "basscss": "^8.0.3", "blockies": "^0.0.2", -- cgit v1.2.3 From 53f8e1b3b426424bf4459605b48e706eb7fb6d90 Mon Sep 17 00:00:00 2001 From: Fabio B Date: Wed, 28 Nov 2018 16:56:48 +0000 Subject: Add troubleshooting section to dev-utils section --- packages/dev-utils/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/dev-utils/README.md b/packages/dev-utils/README.md index 73b0b8816..b85159dd8 100644 --- a/packages/dev-utils/README.md +++ b/packages/dev-utils/README.md @@ -27,6 +27,21 @@ If your project is in [TypeScript](https://www.typescriptlang.org/), add the fol } ``` +## Troubleshooting + +If you are still seeing TS type errors complaining about missing DOM types such as `Response`: + +``` +error TS2304: Cannot find name 'Response'. +``` + +Then you need to explicitly add the `dom` lib to your compiler options in `tsconfig.json`. The `dom` library is included by default, but customizing the `lib` option can cause it to be dropped. + +``` +"compilerOptions": { + "lib": [..., "dom"], +``` + ## Contributing We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository. -- cgit v1.2.3 From ca894935f269a385f28e5d3a51720282ab403697 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 28 Nov 2018 10:07:11 -0800 Subject: Take out asset name and data as we can use the selected event properties --- packages/instant/src/util/analytics.ts | 10 +--------- packages/instant/src/util/buy_quote_updater.ts | 8 +------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts index dd6021453..b85c6cee2 100644 --- a/packages/instant/src/util/analytics.ts +++ b/packages/instant/src/util/analytics.ts @@ -185,17 +185,9 @@ export const analytics = { ...buyQuoteEventProperties(buyQuote), fetchedVia, }), - trackQuoteError: ( - errorMessage: string, - assetName: string, - assetData: string, - assetAmount: BigNumber, - fetchedVia: QuoteFetchedVia, - ) => { + trackQuoteError: (errorMessage: string, assetAmount: BigNumber, fetchedVia: QuoteFetchedVia) => { trackingEventFnWithPayload(EventNames.QUOTE_ERROR)({ errorMessage, - assetName, - assetData, assetAmount: assetAmount.toString(), fetchedVia, }); diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index 3c982ed1f..d6c4bd71b 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -37,13 +37,7 @@ export const buyQuoteUpdater = { } catch (error) { if (options.dispatchErrors) { dispatch(actions.setQuoteRequestStateFailure()); - analytics.trackQuoteError( - error.message ? error.message : 'other', - asset.metaData.name, - asset.assetData, - assetUnitAmount, - options.fetchedVia, - ); + analytics.trackQuoteError(error.message ? error.message : 'other', assetUnitAmount, options.fetchedVia); let errorMessage; if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); -- cgit v1.2.3 From 1b23c430fcb6e4e816b60f85343b5fe009ab4754 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 28 Nov 2018 10:25:04 -0800 Subject: fix(instant): Half opacity instead of darkening on hover for clickable text --- packages/instant/src/components/ui/text.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx index 8e573d7b9..e149e2d8e 100644 --- a/packages/instant/src/components/ui/text.tsx +++ b/packages/instant/src/components/ui/text.tsx @@ -31,7 +31,7 @@ export const Text: React.StatelessComponent = ({ href, onClick, ...re return ; }; -const darkenOnHoverAmount = 0.3; +const opacityOnHoverAmount = 0.5; export const StyledText = styled.div < TextProps > @@ -56,8 +56,7 @@ export const StyledText = ${props => (props.textAlign ? `text-align: ${props.textAlign}` : '')}; ${props => (props.width ? `width: ${props.width}` : '')}; &:hover { - ${props => - props.onClick ? `color: ${darken(darkenOnHoverAmount, props.theme[props.fontColor || 'white'])}` : ''}; + ${props => (props.onClick ? `opacity: ${opacityOnHoverAmount};` : '')}; } } `; -- cgit v1.2.3 From 06b2f12b10c6c2030b326b3e817d497415862299 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 28 Nov 2018 10:38:24 -0800 Subject: fix(instant): Fix uncontrolled input warning for token selector --- packages/instant/src/components/erc20_token_selector.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/instant/src/components/erc20_token_selector.tsx b/packages/instant/src/components/erc20_token_selector.tsx index 0a3d4427a..f7d5a4fe4 100644 --- a/packages/instant/src/components/erc20_token_selector.tsx +++ b/packages/instant/src/components/erc20_token_selector.tsx @@ -19,12 +19,12 @@ export interface ERC20TokenSelectorProps { } export interface ERC20TokenSelectorState { - searchQuery?: string; + searchQuery: string; } export class ERC20TokenSelector extends React.Component { public state: ERC20TokenSelectorState = { - searchQuery: undefined, + searchQuery: '', }; public render(): React.ReactNode { const { tokens, onTokenSelect } = this.props; @@ -62,10 +62,10 @@ export class ERC20TokenSelector extends React.Component }; private readonly _isTokenQueryMatch = (token: ERC20Asset): boolean => { const { searchQuery } = this.state; - if (_.isUndefined(searchQuery)) { + const searchQueryLowerCase = searchQuery.toLowerCase().trim(); + if (searchQueryLowerCase === '') { return true; } - const searchQueryLowerCase = searchQuery.toLowerCase(); const tokenName = token.metaData.name.toLowerCase(); const tokenSymbol = token.metaData.symbol.toLowerCase(); return _.startsWith(tokenSymbol, searchQueryLowerCase) || _.startsWith(tokenName, searchQueryLowerCase); -- cgit v1.2.3 From 3c000e70e3fe507b199871879dde54bcefe01e1e Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 28 Nov 2018 10:48:38 -0800 Subject: Linting --- packages/instant/src/components/ui/text.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/instant/src/components/ui/text.tsx b/packages/instant/src/components/ui/text.tsx index e149e2d8e..282477758 100644 --- a/packages/instant/src/components/ui/text.tsx +++ b/packages/instant/src/components/ui/text.tsx @@ -1,4 +1,3 @@ -import { darken } from 'polished'; import * as React from 'react'; import { ColorOption, styled } from '../../style/theme'; -- cgit v1.2.3 From 85d1dba1ef5ba4d34de46cb7eb54bfdc7acae380 Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 28 Nov 2018 13:35:38 -0800 Subject: fix: notify of the used font size for re-renders of scaling input --- packages/instant/src/components/scaling_input.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/instant/src/components/scaling_input.tsx b/packages/instant/src/components/scaling_input.tsx index 129162a74..791692257 100644 --- a/packages/instant/src/components/scaling_input.tsx +++ b/packages/instant/src/components/scaling_input.tsx @@ -98,6 +98,12 @@ export class ScalingInput extends React.Component Date: Tue, 27 Nov 2018 16:19:50 -0800 Subject: fix(instant): Progress bar background color should be 10% primary color --- packages/instant/src/components/timed_progress_bar.tsx | 17 +++++++++++++---- packages/instant/src/components/ui/container.tsx | 13 ++++++++++++- packages/instant/src/style/theme.ts | 12 ++++++++++-- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/packages/instant/src/components/timed_progress_bar.tsx b/packages/instant/src/components/timed_progress_bar.tsx index 8465b9cd0..fb3927088 100644 --- a/packages/instant/src/components/timed_progress_bar.tsx +++ b/packages/instant/src/components/timed_progress_bar.tsx @@ -1,8 +1,9 @@ import * as _ from 'lodash'; +import { transparentize } from 'polished'; import * as React from 'react'; import { PROGRESS_FINISH_ANIMATION_TIME_MS, PROGRESS_STALL_AT_WIDTH } from '../constants'; -import { ColorOption, css, keyframes, styled } from '../style/theme'; +import { ColorOption, css, keyframes, styled, ThemeConsumer } from '../style/theme'; import { Container } from './ui/container'; @@ -93,8 +94,16 @@ export interface ProgressBarProps extends ProgressProps {} export const ProgressBar: React.ComponentType> = React.forwardRef( (props, ref) => ( - - - + + {theme => ( + + + + )} + ), ); diff --git a/packages/instant/src/components/ui/container.tsx b/packages/instant/src/components/ui/container.tsx index 4dafe1386..a015ab5bc 100644 --- a/packages/instant/src/components/ui/container.tsx +++ b/packages/instant/src/components/ui/container.tsx @@ -27,6 +27,7 @@ export interface ContainerProps { borderBottom?: string; className?: string; backgroundColor?: ColorOption; + rawBackgroundColor?: string; hasBoxShadow?: boolean; zIndex?: number; whiteSpace?: string; @@ -38,6 +39,16 @@ export interface ContainerProps { flexGrow?: string | number; } +const getBackgroundColor = (theme: any, backgroundColor?: ColorOption, rawBackgroundColor?: string): string => { + if (backgroundColor) { + return theme[backgroundColor] as string; + } + if (rawBackgroundColor) { + return rawBackgroundColor; + } + return 'none'; +}; + export const Container = styled.div < ContainerProps > @@ -70,7 +81,7 @@ export const Container = ${props => props.width && stylesForMedia('width', props.width)} ${props => props.height && stylesForMedia('height', props.height)} ${props => props.borderRadius && stylesForMedia('border-radius', props.borderRadius)} - background-color: ${props => (props.backgroundColor ? props.theme[props.backgroundColor] : 'none')}; + background-color: ${props => getBackgroundColor(props.theme, props.backgroundColor, props.rawBackgroundColor)}; border-color: ${props => (props.borderColor ? props.theme[props.borderColor] : 'none')}; &:hover { ${props => diff --git a/packages/instant/src/style/theme.ts b/packages/instant/src/style/theme.ts index a0751286b..71e4b7052 100644 --- a/packages/instant/src/style/theme.ts +++ b/packages/instant/src/style/theme.ts @@ -1,6 +1,14 @@ import * as styledComponents from 'styled-components'; -const { default: styled, css, keyframes, withTheme, createGlobalStyle, ThemeProvider } = styledComponents; +const { + default: styled, + css, + keyframes, + withTheme, + createGlobalStyle, + ThemeConsumer, + ThemeProvider, +} = styledComponents; export type Theme = { [key in ColorOption]: string }; @@ -45,4 +53,4 @@ export const generateOverlayBlack = (opacity = 0.6) => { return `rgba(0, 0, 0, ${opacity})`; }; -export { styled, css, keyframes, withTheme, createGlobalStyle, ThemeProvider }; +export { styled, css, keyframes, withTheme, createGlobalStyle, ThemeConsumer, ThemeProvider }; -- cgit v1.2.3 From f4cc14f43862c639d2cf9f6e5359e4822f1bcacf Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 28 Nov 2018 14:11:06 -0800 Subject: QuoteFetchedVia -> QuoteFetchOrigin --- packages/instant/src/components/zero_ex_instant_provider.tsx | 4 ++-- .../instant/src/containers/selected_erc20_asset_amount_input.ts | 4 ++-- packages/instant/src/redux/async_data.ts | 4 ++-- packages/instant/src/types.ts | 2 +- packages/instant/src/util/analytics.ts | 6 +++--- packages/instant/src/util/buy_quote_updater.ts | 4 ++-- packages/instant/src/util/heartbeater_factory.ts | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 9f2c95e36..7841f5bf7 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -11,7 +11,7 @@ import { asyncData } from '../redux/async_data'; import { DEFAULT_STATE, DefaultState, State } from '../redux/reducer'; import { store, Store } from '../redux/store'; import { fonts } from '../style/fonts'; -import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource, QuoteFetchedVia } from '../types'; +import { AccountState, AffiliateInfo, AssetMetaData, Network, OrderSource, QuoteFetchOrigin } from '../types'; import { analytics, disableAnalytics } from '../util/analytics'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; @@ -117,7 +117,7 @@ export class ZeroExInstantProvider extends React.Component { const { buyOrderState, providerState, selectedAsset, selectedAssetUnitAmount, affiliateInfo } = state; const assetBuyer = providerState.assetBuyer; diff --git a/packages/instant/src/types.ts b/packages/instant/src/types.ts index a8139c185..ea2bcbc2f 100644 --- a/packages/instant/src/types.ts +++ b/packages/instant/src/types.ts @@ -21,7 +21,7 @@ export enum OrderProcessState { Failure = 'FAILURE', } -export enum QuoteFetchedVia { +export enum QuoteFetchOrigin { Manual = 'Manual', Heartbeat = 'Heartbeat', } diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts index b85c6cee2..c0ed8c638 100644 --- a/packages/instant/src/util/analytics.ts +++ b/packages/instant/src/util/analytics.ts @@ -2,7 +2,7 @@ import { BuyQuote } from '@0x/asset-buyer'; import { BigNumber } from '@0x/utils'; import * as _ from 'lodash'; -import { AffiliateInfo, Asset, Network, OrderSource, ProviderState, QuoteFetchedVia } from '../types'; +import { AffiliateInfo, Asset, Network, OrderSource, ProviderState, QuoteFetchOrigin } from '../types'; import { EventProperties, heapUtil } from './heap'; @@ -180,12 +180,12 @@ export const analytics = { trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_CHOSE)(payload), trackTokenSelectorSearched: (searchText: string) => trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_SEARCHED)({ searchText }), - trackQuoteFetched: (buyQuote: BuyQuote, fetchedVia: QuoteFetchedVia) => + trackQuoteFetched: (buyQuote: BuyQuote, fetchedVia: QuoteFetchOrigin) => trackingEventFnWithPayload(EventNames.QUOTE_FETCHED)({ ...buyQuoteEventProperties(buyQuote), fetchedVia, }), - trackQuoteError: (errorMessage: string, assetAmount: BigNumber, fetchedVia: QuoteFetchedVia) => { + trackQuoteError: (errorMessage: string, assetAmount: BigNumber, fetchedVia: QuoteFetchOrigin) => { trackingEventFnWithPayload(EventNames.QUOTE_ERROR)({ errorMessage, assetAmount: assetAmount.toString(), diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index d6c4bd71b..14af57660 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -6,7 +6,7 @@ import { Dispatch } from 'redux'; import { oc } from 'ts-optchain'; import { Action, actions } from '../redux/actions'; -import { AffiliateInfo, ERC20Asset, QuoteFetchedVia } from '../types'; +import { AffiliateInfo, ERC20Asset, QuoteFetchOrigin } from '../types'; import { analytics } from '../util/analytics'; import { assetUtils } from '../util/asset'; import { errorFlasher } from '../util/error_flasher'; @@ -20,7 +20,7 @@ export const buyQuoteUpdater = { options: { setPending: boolean; dispatchErrors: boolean; - fetchedVia: QuoteFetchedVia; + fetchedVia: QuoteFetchOrigin; affiliateInfo?: AffiliateInfo; }, ): Promise => { diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts index bf9e4291f..84aeb4dbc 100644 --- a/packages/instant/src/util/heartbeater_factory.ts +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -1,6 +1,6 @@ import { asyncData } from '../redux/async_data'; import { Store } from '../redux/store'; -import { QuoteFetchedVia } from '../types'; +import { QuoteFetchOrigin } from '../types'; import { Heartbeater } from './heartbeater'; @@ -20,7 +20,7 @@ export const generateBuyQuoteHeartbeater = (options: HeartbeatFactoryOptions): H return new Heartbeater(async () => { await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(store.getState(), store.dispatch, { updateSilently: true, - fetchedVia: QuoteFetchedVia.Heartbeat, + fetchedVia: QuoteFetchOrigin.Heartbeat, }); }, shouldPerformImmediatelyOnStart); }; -- cgit v1.2.3 From ec01893e9c987fcbd3fd7bcb4ec34498a6f516cc Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 28 Nov 2018 14:11:53 -0800 Subject: fetchedVia -> fetchOrigin --- packages/instant/src/components/zero_ex_instant_provider.tsx | 2 +- .../src/containers/selected_erc20_asset_amount_input.ts | 2 +- packages/instant/src/redux/async_data.ts | 4 ++-- packages/instant/src/util/analytics.ts | 8 ++++---- packages/instant/src/util/buy_quote_updater.ts | 10 +++++++--- packages/instant/src/util/heartbeater_factory.ts | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index 7841f5bf7..f7880fc12 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -117,7 +117,7 @@ export class ZeroExInstantProvider extends React.Component { const { buyOrderState, providerState, selectedAsset, selectedAssetUnitAmount, affiliateInfo } = state; const assetBuyer = providerState.assetBuyer; @@ -102,7 +102,7 @@ export const asyncData = { { setPending: !options.updateSilently, dispatchErrors: !options.updateSilently, - fetchedVia: options.fetchedVia, + fetchOrigin: options.fetchOrigin, affiliateInfo, }, ); diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts index c0ed8c638..99e8736d3 100644 --- a/packages/instant/src/util/analytics.ts +++ b/packages/instant/src/util/analytics.ts @@ -180,16 +180,16 @@ export const analytics = { trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_CHOSE)(payload), trackTokenSelectorSearched: (searchText: string) => trackingEventFnWithPayload(EventNames.TOKEN_SELECTOR_SEARCHED)({ searchText }), - trackQuoteFetched: (buyQuote: BuyQuote, fetchedVia: QuoteFetchOrigin) => + trackQuoteFetched: (buyQuote: BuyQuote, fetchOrigin: QuoteFetchOrigin) => trackingEventFnWithPayload(EventNames.QUOTE_FETCHED)({ ...buyQuoteEventProperties(buyQuote), - fetchedVia, + fetchOrigin, }), - trackQuoteError: (errorMessage: string, assetAmount: BigNumber, fetchedVia: QuoteFetchOrigin) => { + trackQuoteError: (errorMessage: string, assetAmount: BigNumber, fetchOrigin: QuoteFetchOrigin) => { trackingEventFnWithPayload(EventNames.QUOTE_ERROR)({ errorMessage, assetAmount: assetAmount.toString(), - fetchedVia, + fetchOrigin, }); }, }; diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index 14af57660..06474b69f 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -20,7 +20,7 @@ export const buyQuoteUpdater = { options: { setPending: boolean; dispatchErrors: boolean; - fetchedVia: QuoteFetchOrigin; + fetchOrigin: QuoteFetchOrigin; affiliateInfo?: AffiliateInfo; }, ): Promise => { @@ -37,7 +37,11 @@ export const buyQuoteUpdater = { } catch (error) { if (options.dispatchErrors) { dispatch(actions.setQuoteRequestStateFailure()); - analytics.trackQuoteError(error.message ? error.message : 'other', assetUnitAmount, options.fetchedVia); + analytics.trackQuoteError( + error.message ? error.message : 'other', + assetUnitAmount, + options.fetchOrigin, + ); let errorMessage; if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); @@ -65,6 +69,6 @@ export const buyQuoteUpdater = { errorFlasher.clearError(dispatch); // invalidate the last buy quote. dispatch(actions.updateLatestBuyQuote(newBuyQuote)); - analytics.trackQuoteFetched(newBuyQuote, options.fetchedVia); + analytics.trackQuoteFetched(newBuyQuote, options.fetchOrigin); }, }; diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts index 84aeb4dbc..5f7ef55e5 100644 --- a/packages/instant/src/util/heartbeater_factory.ts +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -20,7 +20,7 @@ export const generateBuyQuoteHeartbeater = (options: HeartbeatFactoryOptions): H return new Heartbeater(async () => { await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(store.getState(), store.dispatch, { updateSilently: true, - fetchedVia: QuoteFetchOrigin.Heartbeat, + fetchOrigin: QuoteFetchOrigin.Heartbeat, }); }, shouldPerformImmediatelyOnStart); }; -- cgit v1.2.3 From 208ee935c843cfff9f0559a4c4058af3908f6261 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 28 Nov 2018 14:17:26 -0800 Subject: Move fetch origin out of options --- packages/instant/src/components/zero_ex_instant_provider.tsx | 3 +-- .../src/containers/selected_erc20_asset_amount_input.ts | 3 +-- packages/instant/src/redux/async_data.ts | 5 +++-- packages/instant/src/util/buy_quote_updater.ts | 10 +++------- packages/instant/src/util/heartbeater_factory.ts | 12 ++++++++---- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/instant/src/components/zero_ex_instant_provider.tsx b/packages/instant/src/components/zero_ex_instant_provider.tsx index f7880fc12..a4a03bbf4 100644 --- a/packages/instant/src/components/zero_ex_instant_provider.tsx +++ b/packages/instant/src/components/zero_ex_instant_provider.tsx @@ -115,9 +115,8 @@ export class ZeroExInstantProvider extends React.Component { const { buyOrderState, providerState, selectedAsset, selectedAssetUnitAmount, affiliateInfo } = state; const assetBuyer = providerState.assetBuyer; @@ -99,10 +100,10 @@ export const asyncData = { dispatch, selectedAsset as ERC20Asset, selectedAssetUnitAmount, + fetchOrigin, { setPending: !options.updateSilently, dispatchErrors: !options.updateSilently, - fetchOrigin: options.fetchOrigin, affiliateInfo, }, ); diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index 06474b69f..5685a7d00 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -17,10 +17,10 @@ export const buyQuoteUpdater = { dispatch: Dispatch, asset: ERC20Asset, assetUnitAmount: BigNumber, + fetchOrigin: QuoteFetchOrigin, options: { setPending: boolean; dispatchErrors: boolean; - fetchOrigin: QuoteFetchOrigin; affiliateInfo?: AffiliateInfo; }, ): Promise => { @@ -37,11 +37,7 @@ export const buyQuoteUpdater = { } catch (error) { if (options.dispatchErrors) { dispatch(actions.setQuoteRequestStateFailure()); - analytics.trackQuoteError( - error.message ? error.message : 'other', - assetUnitAmount, - options.fetchOrigin, - ); + analytics.trackQuoteError(error.message ? error.message : 'other', assetUnitAmount, fetchOrigin); let errorMessage; if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); @@ -69,6 +65,6 @@ export const buyQuoteUpdater = { errorFlasher.clearError(dispatch); // invalidate the last buy quote. dispatch(actions.updateLatestBuyQuote(newBuyQuote)); - analytics.trackQuoteFetched(newBuyQuote, options.fetchOrigin); + analytics.trackQuoteFetched(newBuyQuote, fetchOrigin); }, }; diff --git a/packages/instant/src/util/heartbeater_factory.ts b/packages/instant/src/util/heartbeater_factory.ts index 5f7ef55e5..cf29bf3ea 100644 --- a/packages/instant/src/util/heartbeater_factory.ts +++ b/packages/instant/src/util/heartbeater_factory.ts @@ -18,9 +18,13 @@ export const generateAccountHeartbeater = (options: HeartbeatFactoryOptions): He export const generateBuyQuoteHeartbeater = (options: HeartbeatFactoryOptions): Heartbeater => { const { store, shouldPerformImmediatelyOnStart } = options; return new Heartbeater(async () => { - await asyncData.fetchCurrentBuyQuoteAndDispatchToStore(store.getState(), store.dispatch, { - updateSilently: true, - fetchOrigin: QuoteFetchOrigin.Heartbeat, - }); + await asyncData.fetchCurrentBuyQuoteAndDispatchToStore( + store.getState(), + store.dispatch, + QuoteFetchOrigin.Heartbeat, + { + updateSilently: true, + }, + ); }, shouldPerformImmediatelyOnStart); }; -- cgit v1.2.3 From b9c983b4d691e58894b8b47f8e5952fdfcc275f9 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Wed, 28 Nov 2018 14:24:02 -0800 Subject: Use base unit value --- packages/instant/src/util/buy_quote_updater.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/instant/src/util/buy_quote_updater.ts b/packages/instant/src/util/buy_quote_updater.ts index 5685a7d00..c1899f8c1 100644 --- a/packages/instant/src/util/buy_quote_updater.ts +++ b/packages/instant/src/util/buy_quote_updater.ts @@ -37,7 +37,7 @@ export const buyQuoteUpdater = { } catch (error) { if (options.dispatchErrors) { dispatch(actions.setQuoteRequestStateFailure()); - analytics.trackQuoteError(error.message ? error.message : 'other', assetUnitAmount, fetchOrigin); + analytics.trackQuoteError(error.message ? error.message : 'other', baseUnitValue, fetchOrigin); let errorMessage; if (error.message === AssetBuyerError.InsufficientAssetLiquidity) { const assetName = assetUtils.bestNameForAsset(asset, 'of this asset'); -- cgit v1.2.3 From 77d6594ecb78a85d51a8aef39744ae62e4085bbc Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 1 Nov 2018 15:45:38 +0100 Subject: Plugging away at encoder --- packages/order-utils/test/abi_encoder.ts | 483 +++++++++++++++++++++++++++++++ 1 file changed, 483 insertions(+) create mode 100644 packages/order-utils/test/abi_encoder.ts diff --git a/packages/order-utils/test/abi_encoder.ts b/packages/order-utils/test/abi_encoder.ts new file mode 100644 index 000000000..46449662a --- /dev/null +++ b/packages/order-utils/test/abi_encoder.ts @@ -0,0 +1,483 @@ +import * as chai from 'chai'; +import 'mocha'; +import ethUtil = require('ethereumjs-util'); + +var _ = require('lodash'); + +import { assert } from '@0x/order-utils/src/assert'; + +import { chaiSetup } from '@0x/order-utils/test/utils/chai_setup'; +import { web3Wrapper } from '@0x/order-utils/test/utils/web3_wrapper'; + +import { MethodAbi, DataItem } from 'ethereum-types'; +import { throwError } from 'ethers/errors'; + +import { BigNumber } from '@0x/utils'; +import { MethodNotFound } from 'json-rpc-error'; +import { power } from 'js-combinatorics'; + +const simpleAbi = { + name: 'SimpleAbi', + inputs: [ + { + components: [ + { + name: 'greg', + type: 'uint256', + }, + { + name: 'gregStr', + type: 'string', + }, + ], + }, + ], +} as MethodAbi; +const fillOrderAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'makerAddress', + type: 'address', + }, + { + name: 'takerAddress', + type: 'address', + }, + { + name: 'feeRecipientAddress', + type: 'address', + }, + { + name: 'senderAddress', + type: 'address', + }, + { + name: 'makerAssetAmount', + type: 'uint256', + }, + { + name: 'takerAssetAmount', + type: 'uint256', + }, + { + name: 'makerFee', + type: 'uint256', + }, + { + name: 'takerFee', + type: 'uint256', + }, + { + name: 'expirationTimeSeconds', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'makerAssetData', + type: 'bytes', + }, + { + name: 'takerAssetData', + type: 'bytes', + }, + ], + name: 'order', + type: 'tuple', + }, + { + name: 'takerAssetFillAmount', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'orderSignature', + type: 'bytes', + }, + { + name: 'takerSignature', + type: 'bytes', + }, + ], + name: 'fillOrder', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +chaiSetup.configure(); +const expect = chai.expect; + +namespace AbiEncoder { + class Memory {} + + class Word {} + + abstract class DataType { + private dataItem: DataItem; + private hexValue: string; + + constructor(dataItem: DataItem) { + this.dataItem = dataItem; + this.hexValue = '0x'; + } + + protected assignHexValue(hexValue: string) { + this.hexValue = hexValue; + } + + public getHexValue(): string { + return this.hexValue; + } + + public abstract assignValue(value: any): void; + + // abstract match(type: string): Bool; + } + + class Calldata {} + + abstract class StaticDataType extends DataType { + constructor(dataItem: DataItem) { + super(dataItem); + } + } + + abstract class DynamicDataType extends DataType { + constructor(dataItem: DataItem) { + super(dataItem); + } + } + + class Address extends StaticDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + const hexValue = ethUtil.bufferToHex(new Buffer(value)); + this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'address'; + } + } + + class Bool extends StaticDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'bool'; + } + } + + class Int extends StaticDataType { + static matcher = RegExp( + '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + static DEFAULT_WIDTH = new BigNumber(1); + width: BigNumber = Byte.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = Byte.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.width = new BigNumber(matches[1], 10); + } + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class UInt extends StaticDataType { + static matcher = RegExp( + '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + static DEFAULT_WIDTH: number = 1; + width: number = UInt.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = Byte.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.width = parseInt(matches[1]); + } + } + + public getMaxValue() { + return new BigNumber(2).toPower(this.width - 1); + } + + public assignValue(value: BigNumber) { + if (value > this.getMaxValue()) { + throw 1; + } + + const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), 32); + const encodedValue = ethUtil.bufferToHex(valueBuf); + + this.assignHexValue(encodedValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class Byte extends StaticDataType { + static matcher = RegExp( + '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', + ); + + static DEFAULT_WIDTH = new BigNumber(1); + width: BigNumber = Byte.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = Byte.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.width = new BigNumber(matches[1], 10); + } + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class Tuple extends DynamicDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'tuple'; + } + } + + class Bytes extends StaticDataType { + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = SolArray.UNDEFINED_LENGTH; + + constructor(dataItem: DataItem) { + super(dataItem); + expect(Bytes.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'bytes'; + } + } + + class SolArray extends DynamicDataType { + static matcher = RegExp('^.+\\[([0-9]d*)\\]$'); + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = SolArray.UNDEFINED_LENGTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = SolArray.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.length = new BigNumber(matches[1], 10); + } + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class SolString extends DynamicDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(SolString.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), 32); + const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); + const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + const encodedValue = ethUtil.bufferToHex(encodedValueBuf); + + this.assignHexValue(encodedValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'string'; + } + } + + /* TODO + class Fixed extends StaticDataType {} + + class UFixed extends StaticDataType {}*/ + + class Pointer extends StaticDataType { + destDataType: DynamicDataType; + static metaDataItem = { name: '[ptr]', type: '[ptr]' } as DataItem; + + constructor(destDataType: DynamicDataType) { + super(Pointer.metaDataItem); + this.destDataType = destDataType; + } + + public assignValue(destDataType: DynamicDataType) { + this.destDataType = destDataType; + } + } + + class DataTypeFactory { + public static mapDataItemToDataType(dataItem: DataItem): DataType { + if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); + if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); + if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); + if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); + if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); + if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); + if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); + if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); + if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); + if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); + //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); + //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); + + throw new Error(`Unrecognized data type: '${dataItem.type}'`); + } + + public static create(dataItem: DataItem): DataType { + const dataType = DataTypeFactory.mapDataItemToDataType(dataItem); + if (dataType instanceof StaticDataType) { + return dataType; + } else if (dataType instanceof DynamicDataType) { + const pointer = new Pointer(dataType); + return pointer; + } + + throw new Error(`Unrecognized instance type: '${dataType}'`); + } + } + + export class Method { + name: string; + params: DataType[]; + + constructor(abi: MethodAbi) { + // super(); + this.name = abi.name; + this.params = []; + + _.each(abi.inputs, function(this: Method, input: DataItem) { + this.params.push(DataTypeFactory.create(input)); + }); + } + + encode(args: any[]): string { + //const calldata = new Calldata(this.name, this.params.length); + let params = this.params; + _.each(params, function(args: any[], i: number, param: DataType) { + param.assignValue(args[i]); + console.log(param.getHexValue()); + //param.encodeToCalldata(calldata); + }); + + return ''; + + //return calldata.getRaw(); + } + + /* + encodeOptimized(args: any[]): string { + const calldata = new Memory(); + // Assign values + optimizableParams : StaticDataType = []; + _.each(this.params, function(args: any[], i: number, param: DataType) { + param.assignValue(args[i]); + if (param instanceof DynamicDataType) { + + } + }); + + // Find non-parameter leaves + + + return ''; + } */ + + /* + decode(rawCalldata: string): any[] { + const calldata = new Calldata(this.name, this.params.length); + calldata.assignRaw(rawCalldata); + let args: any[]; + let params = this.params; + _.each(params, function(args: any[], i: number, param: DataType) { + param.decodeFromCalldata(calldata); + args.push(param.getValue()); + }); + + return args; + }*/ + } +} + +describe.only('ABI Encoder', () => { + describe.only('Just a Greg, Eh', () => { + it.only('Yessir', async () => { + const method = new AbiEncoder.Method(simpleAbi); + method.encode([new BigNumber(5), 'five']); + expect(true).to.be.true(); + }); + }); +}); -- cgit v1.2.3 From 79126f3b4a83d9cf58e5a7935051820ae5e1a8e0 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 6 Nov 2018 11:13:56 -0800 Subject: renamed --- packages/order-utils/test/abi_encoder.ts | 483 -------------------------- packages/order-utils/test/abi_encoder_test.ts | 479 +++++++++++++++++++++++++ 2 files changed, 479 insertions(+), 483 deletions(-) delete mode 100644 packages/order-utils/test/abi_encoder.ts create mode 100644 packages/order-utils/test/abi_encoder_test.ts diff --git a/packages/order-utils/test/abi_encoder.ts b/packages/order-utils/test/abi_encoder.ts deleted file mode 100644 index 46449662a..000000000 --- a/packages/order-utils/test/abi_encoder.ts +++ /dev/null @@ -1,483 +0,0 @@ -import * as chai from 'chai'; -import 'mocha'; -import ethUtil = require('ethereumjs-util'); - -var _ = require('lodash'); - -import { assert } from '@0x/order-utils/src/assert'; - -import { chaiSetup } from '@0x/order-utils/test/utils/chai_setup'; -import { web3Wrapper } from '@0x/order-utils/test/utils/web3_wrapper'; - -import { MethodAbi, DataItem } from 'ethereum-types'; -import { throwError } from 'ethers/errors'; - -import { BigNumber } from '@0x/utils'; -import { MethodNotFound } from 'json-rpc-error'; -import { power } from 'js-combinatorics'; - -const simpleAbi = { - name: 'SimpleAbi', - inputs: [ - { - components: [ - { - name: 'greg', - type: 'uint256', - }, - { - name: 'gregStr', - type: 'string', - }, - ], - }, - ], -} as MethodAbi; -const fillOrderAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'makerAddress', - type: 'address', - }, - { - name: 'takerAddress', - type: 'address', - }, - { - name: 'feeRecipientAddress', - type: 'address', - }, - { - name: 'senderAddress', - type: 'address', - }, - { - name: 'makerAssetAmount', - type: 'uint256', - }, - { - name: 'takerAssetAmount', - type: 'uint256', - }, - { - name: 'makerFee', - type: 'uint256', - }, - { - name: 'takerFee', - type: 'uint256', - }, - { - name: 'expirationTimeSeconds', - type: 'uint256', - }, - { - name: 'salt', - type: 'uint256', - }, - { - name: 'makerAssetData', - type: 'bytes', - }, - { - name: 'takerAssetData', - type: 'bytes', - }, - ], - name: 'order', - type: 'tuple', - }, - { - name: 'takerAssetFillAmount', - type: 'uint256', - }, - { - name: 'salt', - type: 'uint256', - }, - { - name: 'orderSignature', - type: 'bytes', - }, - { - name: 'takerSignature', - type: 'bytes', - }, - ], - name: 'fillOrder', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -chaiSetup.configure(); -const expect = chai.expect; - -namespace AbiEncoder { - class Memory {} - - class Word {} - - abstract class DataType { - private dataItem: DataItem; - private hexValue: string; - - constructor(dataItem: DataItem) { - this.dataItem = dataItem; - this.hexValue = '0x'; - } - - protected assignHexValue(hexValue: string) { - this.hexValue = hexValue; - } - - public getHexValue(): string { - return this.hexValue; - } - - public abstract assignValue(value: any): void; - - // abstract match(type: string): Bool; - } - - class Calldata {} - - abstract class StaticDataType extends DataType { - constructor(dataItem: DataItem) { - super(dataItem); - } - } - - abstract class DynamicDataType extends DataType { - constructor(dataItem: DataItem) { - super(dataItem); - } - } - - class Address extends StaticDataType { - constructor(dataItem: DataItem) { - super(dataItem); - expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string) { - const hexValue = ethUtil.bufferToHex(new Buffer(value)); - this.assignHexValue(hexValue); - } - - public static matchGrammar(type: string): boolean { - return type === 'address'; - } - } - - class Bool extends StaticDataType { - constructor(dataItem: DataItem) { - super(dataItem); - expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); - } - - public static matchGrammar(type: string): boolean { - return type === 'bool'; - } - } - - class Int extends StaticDataType { - static matcher = RegExp( - '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - static DEFAULT_WIDTH = new BigNumber(1); - width: BigNumber = Byte.DEFAULT_WIDTH; - - constructor(dataItem: DataItem) { - super(dataItem); - const matches = Byte.matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 1) { - this.width = new BigNumber(matches[1], 10); - } - } - - public assignValue(value: string) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } - } - - class UInt extends StaticDataType { - static matcher = RegExp( - '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - static DEFAULT_WIDTH: number = 1; - width: number = UInt.DEFAULT_WIDTH; - - constructor(dataItem: DataItem) { - super(dataItem); - const matches = Byte.matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 1) { - this.width = parseInt(matches[1]); - } - } - - public getMaxValue() { - return new BigNumber(2).toPower(this.width - 1); - } - - public assignValue(value: BigNumber) { - if (value > this.getMaxValue()) { - throw 1; - } - - const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), 32); - const encodedValue = ethUtil.bufferToHex(valueBuf); - - this.assignHexValue(encodedValue); - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } - } - - class Byte extends StaticDataType { - static matcher = RegExp( - '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', - ); - - static DEFAULT_WIDTH = new BigNumber(1); - width: BigNumber = Byte.DEFAULT_WIDTH; - - constructor(dataItem: DataItem) { - super(dataItem); - const matches = Byte.matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 1) { - this.width = new BigNumber(matches[1], 10); - } - } - - public assignValue(value: string) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } - } - - class Tuple extends DynamicDataType { - constructor(dataItem: DataItem) { - super(dataItem); - expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); - } - - public static matchGrammar(type: string): boolean { - return type === 'tuple'; - } - } - - class Bytes extends StaticDataType { - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = SolArray.UNDEFINED_LENGTH; - - constructor(dataItem: DataItem) { - super(dataItem); - expect(Bytes.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); - } - - public static matchGrammar(type: string): boolean { - return type === 'bytes'; - } - } - - class SolArray extends DynamicDataType { - static matcher = RegExp('^.+\\[([0-9]d*)\\]$'); - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = SolArray.UNDEFINED_LENGTH; - - constructor(dataItem: DataItem) { - super(dataItem); - const matches = SolArray.matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 1) { - this.length = new BigNumber(matches[1], 10); - } - } - - public assignValue(value: string) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } - } - - class SolString extends DynamicDataType { - constructor(dataItem: DataItem) { - super(dataItem); - expect(SolString.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string) { - const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), 32); - const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); - const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); - const encodedValue = ethUtil.bufferToHex(encodedValueBuf); - - this.assignHexValue(encodedValue); - } - - public static matchGrammar(type: string): boolean { - return type === 'string'; - } - } - - /* TODO - class Fixed extends StaticDataType {} - - class UFixed extends StaticDataType {}*/ - - class Pointer extends StaticDataType { - destDataType: DynamicDataType; - static metaDataItem = { name: '[ptr]', type: '[ptr]' } as DataItem; - - constructor(destDataType: DynamicDataType) { - super(Pointer.metaDataItem); - this.destDataType = destDataType; - } - - public assignValue(destDataType: DynamicDataType) { - this.destDataType = destDataType; - } - } - - class DataTypeFactory { - public static mapDataItemToDataType(dataItem: DataItem): DataType { - if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); - if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); - if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); - if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); - if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); - if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); - if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); - if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); - if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); - if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); - //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); - //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); - - throw new Error(`Unrecognized data type: '${dataItem.type}'`); - } - - public static create(dataItem: DataItem): DataType { - const dataType = DataTypeFactory.mapDataItemToDataType(dataItem); - if (dataType instanceof StaticDataType) { - return dataType; - } else if (dataType instanceof DynamicDataType) { - const pointer = new Pointer(dataType); - return pointer; - } - - throw new Error(`Unrecognized instance type: '${dataType}'`); - } - } - - export class Method { - name: string; - params: DataType[]; - - constructor(abi: MethodAbi) { - // super(); - this.name = abi.name; - this.params = []; - - _.each(abi.inputs, function(this: Method, input: DataItem) { - this.params.push(DataTypeFactory.create(input)); - }); - } - - encode(args: any[]): string { - //const calldata = new Calldata(this.name, this.params.length); - let params = this.params; - _.each(params, function(args: any[], i: number, param: DataType) { - param.assignValue(args[i]); - console.log(param.getHexValue()); - //param.encodeToCalldata(calldata); - }); - - return ''; - - //return calldata.getRaw(); - } - - /* - encodeOptimized(args: any[]): string { - const calldata = new Memory(); - // Assign values - optimizableParams : StaticDataType = []; - _.each(this.params, function(args: any[], i: number, param: DataType) { - param.assignValue(args[i]); - if (param instanceof DynamicDataType) { - - } - }); - - // Find non-parameter leaves - - - return ''; - } */ - - /* - decode(rawCalldata: string): any[] { - const calldata = new Calldata(this.name, this.params.length); - calldata.assignRaw(rawCalldata); - let args: any[]; - let params = this.params; - _.each(params, function(args: any[], i: number, param: DataType) { - param.decodeFromCalldata(calldata); - args.push(param.getValue()); - }); - - return args; - }*/ - } -} - -describe.only('ABI Encoder', () => { - describe.only('Just a Greg, Eh', () => { - it.only('Yessir', async () => { - const method = new AbiEncoder.Method(simpleAbi); - method.encode([new BigNumber(5), 'five']); - expect(true).to.be.true(); - }); - }); -}); diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts new file mode 100644 index 000000000..87e1e7fba --- /dev/null +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -0,0 +1,479 @@ +import * as chai from 'chai'; +import 'mocha'; +import ethUtil = require('ethereumjs-util'); + +var _ = require('lodash'); + +// import { assert } from '@0x/order-utils/src/assert'; + +import { chaiSetup } from './utils/chai_setup'; + +import { MethodAbi, DataItem } from 'ethereum-types'; + +import { BigNumber } from '@0x/utils'; + +const simpleAbi = { + name: 'SimpleAbi', + inputs: [ + { + components: [ + { + name: 'greg', + type: 'uint256', + }, + { + name: 'gregStr', + type: 'string', + }, + ], + }, + ], +} as MethodAbi; +const fillOrderAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'makerAddress', + type: 'address', + }, + { + name: 'takerAddress', + type: 'address', + }, + { + name: 'feeRecipientAddress', + type: 'address', + }, + { + name: 'senderAddress', + type: 'address', + }, + { + name: 'makerAssetAmount', + type: 'uint256', + }, + { + name: 'takerAssetAmount', + type: 'uint256', + }, + { + name: 'makerFee', + type: 'uint256', + }, + { + name: 'takerFee', + type: 'uint256', + }, + { + name: 'expirationTimeSeconds', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'makerAssetData', + type: 'bytes', + }, + { + name: 'takerAssetData', + type: 'bytes', + }, + ], + name: 'order', + type: 'tuple', + }, + { + name: 'takerAssetFillAmount', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'orderSignature', + type: 'bytes', + }, + { + name: 'takerSignature', + type: 'bytes', + }, + ], + name: 'fillOrder', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +chaiSetup.configure(); +const expect = chai.expect; + +namespace AbiEncoder { + class Memory {} + + class Word {} + + abstract class DataType { + private dataItem: DataItem; + private hexValue: string; + + constructor(dataItem: DataItem) { + this.dataItem = dataItem; + this.hexValue = '0x'; + } + + protected assignHexValue(hexValue: string) { + this.hexValue = hexValue; + } + + public getHexValue(): string { + return this.hexValue; + } + + public abstract assignValue(value: any): void; + + // abstract match(type: string): Bool; + } + + class Calldata {} + + abstract class StaticDataType extends DataType { + constructor(dataItem: DataItem) { + super(dataItem); + } + } + + abstract class DynamicDataType extends DataType { + constructor(dataItem: DataItem) { + super(dataItem); + } + } + + class Address extends StaticDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + const hexValue = ethUtil.bufferToHex(new Buffer(value)); + this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'address'; + } + } + + class Bool extends StaticDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'bool'; + } + } + + class Int extends StaticDataType { + static matcher = RegExp( + '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + static DEFAULT_WIDTH = new BigNumber(1); + width: BigNumber = Byte.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = Byte.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.width = new BigNumber(matches[1], 10); + } + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class UInt extends StaticDataType { + static matcher = RegExp( + '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + static DEFAULT_WIDTH: number = 1; + width: number = UInt.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = Byte.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.width = parseInt(matches[1]); + } + } + + public getMaxValue() { + return new BigNumber(2).toPower(this.width - 1); + } + + public assignValue(value: BigNumber) { + if (value > this.getMaxValue()) { + throw 1; + } + + const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), 32); + const encodedValue = ethUtil.bufferToHex(valueBuf); + + this.assignHexValue(encodedValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class Byte extends StaticDataType { + static matcher = RegExp( + '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', + ); + + static DEFAULT_WIDTH = new BigNumber(1); + width: BigNumber = Byte.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = Byte.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.width = new BigNumber(matches[1], 10); + } + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class Tuple extends DynamicDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'tuple'; + } + } + + class Bytes extends StaticDataType { + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = SolArray.UNDEFINED_LENGTH; + + constructor(dataItem: DataItem) { + super(dataItem); + expect(Bytes.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'bytes'; + } + } + + class SolArray extends DynamicDataType { + static matcher = RegExp('^.+\\[([0-9]d*)\\]$'); + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = SolArray.UNDEFINED_LENGTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = SolArray.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 1) { + this.length = new BigNumber(matches[1], 10); + } + } + + public assignValue(value: string) { + //const hexValue = ethUtil.bufferToHex(new Buffer(value)); + //this.assignHexValue(hexValue); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + } + + class SolString extends DynamicDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(SolString.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), 32); + const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); + const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + const encodedValue = ethUtil.bufferToHex(encodedValueBuf); + + this.assignHexValue(encodedValue); + } + + public static matchGrammar(type: string): boolean { + return type === 'string'; + } + } + + /* TODO + class Fixed extends StaticDataType {} + + class UFixed extends StaticDataType {}*/ + + class Pointer extends StaticDataType { + destDataType: DynamicDataType; + static metaDataItem = { name: '[ptr]', type: '[ptr]' } as DataItem; + + constructor(destDataType: DynamicDataType) { + super(Pointer.metaDataItem); + this.destDataType = destDataType; + } + + public assignValue(destDataType: DynamicDataType) { + this.destDataType = destDataType; + } + } + + class DataTypeFactory { + public static mapDataItemToDataType(dataItem: DataItem): DataType { + if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); + if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); + if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); + if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); + if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); + if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); + if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); + if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); + if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); + if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); + //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); + //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); + + throw new Error(`Unrecognized data type: '${dataItem.type}'`); + } + + public static create(dataItem: DataItem): DataType { + const dataType = DataTypeFactory.mapDataItemToDataType(dataItem); + if (dataType instanceof StaticDataType) { + return dataType; + } else if (dataType instanceof DynamicDataType) { + const pointer = new Pointer(dataType); + return pointer; + } + + throw new Error(`Unrecognized instance type: '${dataType}'`); + } + } + + export class Method { + name: string; + params: DataType[]; + + constructor(abi: MethodAbi) { + // super(); + this.name = abi.name; + this.params = []; + + _.each(abi.inputs, function(this: Method, input: DataItem) { + this.params.push(DataTypeFactory.create(input)); + }); + } + + encode(args: any[]): string { + //const calldata = new Calldata(this.name, this.params.length); + let params = this.params; + _.each(params, function(args: any[], i: number, param: DataType) { + param.assignValue(args[i]); + console.log(param.getHexValue()); + //param.encodeToCalldata(calldata); + }); + + return ''; + + //return calldata.getRaw(); + } + + /* + encodeOptimized(args: any[]): string { + const calldata = new Memory(); + // Assign values + optimizableParams : StaticDataType = []; + _.each(this.params, function(args: any[], i: number, param: DataType) { + param.assignValue(args[i]); + if (param instanceof DynamicDataType) { + + } + }); + + // Find non-parameter leaves + + + return ''; + } */ + + /* + decode(rawCalldata: string): any[] { + const calldata = new Calldata(this.name, this.params.length); + calldata.assignRaw(rawCalldata); + let args: any[]; + let params = this.params; + _.each(params, function(args: any[], i: number, param: DataType) { + param.decodeFromCalldata(calldata); + args.push(param.getValue()); + }); + + return args; + }*/ + } +} + +describe.only('ABI Encoder', () => { + describe.only('Just a Greg, Eh', () => { + it.only('Yessir', async () => { + const method = new AbiEncoder.Method(simpleAbi); + method.encode([new BigNumber(5), 'five']); + expect(true).to.be.true(); + }); + }); +}); -- cgit v1.2.3 From a1cff862c979d013f3f56af55c324fe38b588473 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 6 Nov 2018 11:32:29 -0800 Subject: Fixed width parsing for UINT --- packages/order-utils/test/abi_encoder_test.ts | 43 ++++++++++++++++----------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 87e1e7fba..f6f7fd93e 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -13,22 +13,24 @@ import { MethodAbi, DataItem } from 'ethereum-types'; import { BigNumber } from '@0x/utils'; const simpleAbi = { - name: 'SimpleAbi', + constant: false, inputs: [ { - components: [ - { - name: 'greg', - type: 'uint256', - }, - { - name: 'gregStr', - type: 'string', - }, - ], + name: 'greg', + type: 'uint208', + }, + { + name: 'gregStr', + type: 'string', }, ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', } as MethodAbi; + const fillOrderAbi = { constant: false, inputs: [ @@ -198,7 +200,7 @@ namespace AbiEncoder { super(dataItem); const matches = Byte.matcher.exec(dataItem.type); expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 1) { + if (matches !== null && matches.length === 2) { this.width = new BigNumber(matches[1], 10); } } @@ -218,16 +220,20 @@ namespace AbiEncoder { '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); - static DEFAULT_WIDTH: number = 1; + static DEFAULT_WIDTH: number = 256; width: number = UInt.DEFAULT_WIDTH; constructor(dataItem: DataItem) { super(dataItem); - const matches = Byte.matcher.exec(dataItem.type); + const matches = UInt.matcher.exec(dataItem.type); expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 1) { + console.log(JSON.stringify(matches)); + if (matches !== null && matches.length === 2) { this.width = parseInt(matches[1]); + } else { + this.width = 256; } + console.log('Width = ' + this.width); } public getMaxValue() { @@ -262,7 +268,7 @@ namespace AbiEncoder { super(dataItem); const matches = Byte.matcher.exec(dataItem.type); expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 1) { + if (matches !== null && matches.length === 2) { this.width = new BigNumber(matches[1], 10); } } @@ -377,6 +383,8 @@ namespace AbiEncoder { class DataTypeFactory { public static mapDataItemToDataType(dataItem: DataItem): DataType { + console.log(`Type: ${dataItem.type}`); + if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); @@ -415,7 +423,8 @@ namespace AbiEncoder { this.name = abi.name; this.params = []; - _.each(abi.inputs, function(this: Method, input: DataItem) { + _.each(abi.inputs, (input: DataItem) => { + console.log('--input--\n', input, '--end input--'); this.params.push(DataTypeFactory.create(input)); }); } -- cgit v1.2.3 From 331cca37e2bce35a1351f75a85b97ab60dfad196 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 6 Nov 2018 13:17:15 -0800 Subject: works for assigning value to uint --- packages/order-utils/test/abi_encoder_test.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index f6f7fd93e..6b32d59aa 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -227,25 +227,29 @@ namespace AbiEncoder { super(dataItem); const matches = UInt.matcher.exec(dataItem.type); expect(matches).to.be.not.null(); - console.log(JSON.stringify(matches)); if (matches !== null && matches.length === 2) { this.width = parseInt(matches[1]); } else { this.width = 256; } - console.log('Width = ' + this.width); } - public getMaxValue() { + public getMaxValue(): BigNumber { return new BigNumber(2).toPower(this.width - 1); } public assignValue(value: BigNumber) { - if (value > this.getMaxValue()) { - throw 1; + console.log(JSON.stringify(value)); + console.log(JSON.stringify(this.getMaxValue())); + if (value.greaterThan(this.getMaxValue())) { + throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; + } else if (value.lessThan(0)) { + throw `tried to assign value of ${value} to an unsigned integer.`; } - const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), 32); + const hexBase = 16; + const evmWordWidth = 32; + const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); const encodedValue = ethUtil.bufferToHex(valueBuf); this.assignHexValue(encodedValue); @@ -424,7 +428,6 @@ namespace AbiEncoder { this.params = []; _.each(abi.inputs, (input: DataItem) => { - console.log('--input--\n', input, '--end input--'); this.params.push(DataTypeFactory.create(input)); }); } @@ -432,7 +435,9 @@ namespace AbiEncoder { encode(args: any[]): string { //const calldata = new Calldata(this.name, this.params.length); let params = this.params; - _.each(params, function(args: any[], i: number, param: DataType) { + _.each(params, (param: DataType, i: number) => { + console.log('param:\n', param, '\n--end--\n'); + console.log('arg:\n', args[i], '\n--end\n'); param.assignValue(args[i]); console.log(param.getHexValue()); //param.encodeToCalldata(calldata); -- cgit v1.2.3 From bce62056d953abc424a5171adbaff1641f8f6626 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 6 Nov 2018 13:28:59 -0800 Subject: cleaner name/type for pointer --- packages/order-utils/test/abi_encoder_test.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 6b32d59aa..4b8ed48cc 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -137,6 +137,10 @@ namespace AbiEncoder { return this.hexValue; } + public getDataItem(): DataItem { + return this.dataItem; + } + public abstract assignValue(value: any): void; // abstract match(type: string): Bool; @@ -373,15 +377,25 @@ namespace AbiEncoder { class Pointer extends StaticDataType { destDataType: DynamicDataType; - static metaDataItem = { name: '[ptr]', type: '[ptr]' } as DataItem; constructor(destDataType: DynamicDataType) { - super(Pointer.metaDataItem); + const destDataItem = destDataType.getDataItem(); + const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; + super(dataItem); this.destDataType = destDataType; } + /* public assignValue(destDataType: DynamicDataType) { this.destDataType = destDataType; + }*/ + + public assignValue(value: any) { + this.destDataType.assignValue(value); + } + + public getHexValue(): string { + return this.destDataType.getHexValue(); } } -- cgit v1.2.3 From d5dbd8cd686195e38c3c4a0857e3231e0a026287 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 6 Nov 2018 13:47:44 -0800 Subject: tests for String --- packages/order-utils/test/abi_encoder_test.ts | 59 +++++++++++++++++++-------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 4b8ed48cc..0366ddfcc 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -120,7 +120,7 @@ namespace AbiEncoder { class Word {} - abstract class DataType { + export abstract class DataType { private dataItem: DataItem; private hexValue: string; @@ -148,19 +148,19 @@ namespace AbiEncoder { class Calldata {} - abstract class StaticDataType extends DataType { + export abstract class StaticDataType extends DataType { constructor(dataItem: DataItem) { super(dataItem); } } - abstract class DynamicDataType extends DataType { + export abstract class DynamicDataType extends DataType { constructor(dataItem: DataItem) { super(dataItem); } } - class Address extends StaticDataType { + export class Address extends StaticDataType { constructor(dataItem: DataItem) { super(dataItem); expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); @@ -176,7 +176,7 @@ namespace AbiEncoder { } } - class Bool extends StaticDataType { + export class Bool extends StaticDataType { constructor(dataItem: DataItem) { super(dataItem); expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); @@ -192,7 +192,7 @@ namespace AbiEncoder { } } - class Int extends StaticDataType { + export class Int extends StaticDataType { static matcher = RegExp( '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); @@ -219,7 +219,7 @@ namespace AbiEncoder { } } - class UInt extends StaticDataType { + export class UInt extends StaticDataType { static matcher = RegExp( '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); @@ -264,7 +264,7 @@ namespace AbiEncoder { } } - class Byte extends StaticDataType { + export class Byte extends StaticDataType { static matcher = RegExp( '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', ); @@ -291,7 +291,7 @@ namespace AbiEncoder { } } - class Tuple extends DynamicDataType { + export class Tuple extends DynamicDataType { constructor(dataItem: DataItem) { super(dataItem); expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); @@ -307,7 +307,7 @@ namespace AbiEncoder { } } - class Bytes extends StaticDataType { + export class Bytes extends StaticDataType { static UNDEFINED_LENGTH = new BigNumber(-1); length: BigNumber = SolArray.UNDEFINED_LENGTH; @@ -326,7 +326,7 @@ namespace AbiEncoder { } } - class SolArray extends DynamicDataType { + export class SolArray extends DynamicDataType { static matcher = RegExp('^.+\\[([0-9]d*)\\]$'); static UNDEFINED_LENGTH = new BigNumber(-1); length: BigNumber = SolArray.UNDEFINED_LENGTH; @@ -350,14 +350,16 @@ namespace AbiEncoder { } } - class SolString extends DynamicDataType { + export class SolString extends DynamicDataType { constructor(dataItem: DataItem) { super(dataItem); expect(SolString.matchGrammar(dataItem.type)).to.be.true(); } public assignValue(value: string) { - const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), 32); + const wordsForValue = Math.ceil(value.length / 32); + const paddedBytesForValue = wordsForValue * 32; + const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); const encodedValue = ethUtil.bufferToHex(encodedValueBuf); @@ -375,7 +377,7 @@ namespace AbiEncoder { class UFixed extends StaticDataType {}*/ - class Pointer extends StaticDataType { + export class Pointer extends StaticDataType { destDataType: DynamicDataType; constructor(destDataType: DynamicDataType) { @@ -399,7 +401,7 @@ namespace AbiEncoder { } } - class DataTypeFactory { + export class DataTypeFactory { public static mapDataItemToDataType(dataItem: DataItem): DataType { console.log(`Type: ${dataItem.type}`); @@ -497,11 +499,34 @@ namespace AbiEncoder { } describe.only('ABI Encoder', () => { - describe.only('Just a Greg, Eh', () => { - it.only('Yessir', async () => { + describe('Just a Greg, Eh', () => { + it('Yessir', async () => { const method = new AbiEncoder.Method(simpleAbi); method.encode([new BigNumber(5), 'five']); expect(true).to.be.true(); }); }); + + describe('String', () => { + const testStringDataItem = { name: 'testString', type: 'string' }; + it('Less than 32 bytes', async () => { + const stringDataType = new AbiEncoder.SolString(testStringDataItem); + stringDataType.assignValue('five'); + const expectedAbiEncodedString = + '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + + console.log(stringDataType.getHexValue()); + console.log(expectedAbiEncodedString); + expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + }); + + it('Greater than 32 bytes', async () => { + const stringDataType = new AbiEncoder.SolString(testStringDataItem); + const testValue = 'a'.repeat(40); + stringDataType.assignValue(testValue); + const expectedAbiEncodedString = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + }); + }); }); -- cgit v1.2.3 From 96bcc7e33281a3a7831e92dad65de5303ac39523 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 6 Nov 2018 16:33:25 -0800 Subject: Going towards first calldata impl --- packages/order-utils/test/abi_encoder_test.ts | 265 ++++++++++++++++++++++++-- 1 file changed, 250 insertions(+), 15 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 0366ddfcc..7e12dfee6 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -11,13 +11,14 @@ import { chaiSetup } from './utils/chai_setup'; import { MethodAbi, DataItem } from 'ethereum-types'; import { BigNumber } from '@0x/utils'; +import { assert } from '@0x/order-utils/src/assert'; const simpleAbi = { constant: false, inputs: [ { name: 'greg', - type: 'uint208', + type: 'uint256', }, { name: 'gregStr', @@ -116,17 +117,138 @@ chaiSetup.configure(); const expect = chai.expect; namespace AbiEncoder { - class Memory {} + class Word { + private value: string; + + constructor(value?: string) { + if (value === undefined) { + this.value = ''; + } else { + this.value = value; + } + } + + public set(value: string) { + if (value.length !== 64) { + throw `Tried to create word that is not 32 bytes: ${value}`; + } - class Word {} + this.value = value; + } + + public get(): string { + return this.value; + } + + public getAsHex(): string { + return `0x${this.value}`; + } + } + + enum CalldataSection { + NONE, + PARAMS, + DATA, + } + + class Memblock { + private dataType: DataType; + private location: { calldataSection: CalldataSection; offset: BigNumber }; + + constructor(dataType: DataType) { + this.dataType = dataType; + this.location = { + calldataSection: CalldataSection.NONE, + offset: new BigNumber(0), + }; + } + + public getSize(): BigNumber { + return new BigNumber(ethUtil.toBuffer(this.dataType.getHexValue()).byteLength); + } + + public assignLocation(calldataSection: CalldataSection, offset: BigNumber) { + this.location.calldataSection = calldataSection; + this.location.offset = offset; + } + + public get(): string { + return ethUtil.stripHexPrefix(this.dataType.getHexValue()); + } + } + + interface BindList { + [key: string]: Memblock; + } + + class Calldata { + private selector: string; + private params: Memblock[]; + private data: Memblock[]; + private dataOffset: BigNumber; + private currentDataOffset: BigNumber; + private currentParamOffset: BigNumber; + private bindList: BindList; + + constructor(selector: string, nParams: number) { + this.selector = selector; + console.log(this.selector); + this.params = []; + this.data = []; + const evmWordSize = 32; + this.dataOffset = new BigNumber(nParams).times(evmWordSize); + this.currentDataOffset = this.dataOffset; + this.currentParamOffset = new BigNumber(0); + this.bindList = {}; + } + + public bind(dataType: DataType, section: CalldataSection = CalldataSection.DATA) { + if (dataType.getId() in this.bindList) { + throw `Rebind`; + } + const memblock = new Memblock(dataType); + switch (section) { + case CalldataSection.PARAMS: + this.params.push(memblock); + memblock.assignLocation(section, this.currentParamOffset); + this.currentParamOffset = this.currentParamOffset.plus(memblock.getSize()); + break; + + case CalldataSection.DATA: + this.data.push(memblock); + memblock.assignLocation(section, this.currentDataOffset); + this.currentDataOffset = this.currentDataOffset.plus(memblock.getSize()); + break; + + default: + throw `Unrecognized calldata section: ${section}`; + } + + this.bindList[dataType.getId()] = memblock; + } + + public getHexValue(): string { + let hexValue = `0x${this.selector}`; + _.each(this.params, (memblock: Memblock) => { + hexValue += memblock.get(); + }); + _.each(this.data, (memblock: Memblock) => { + hexValue += memblock.get(); + }); + + return hexValue; + } + } export abstract class DataType { private dataItem: DataItem; private hexValue: string; + private memblock: Memblock | undefined; constructor(dataItem: DataItem) { this.dataItem = dataItem; this.hexValue = '0x'; + this.memblock = undefined; } protected assignHexValue(hexValue: string) { @@ -141,13 +263,25 @@ namespace AbiEncoder { return this.dataItem; } + public rbind(memblock: Memblock) { + this.memblock = memblock; + } + + public bind(calldata: Calldata) { + if (this.memblock !== undefined) return; // already binded + } + + public getId(): string { + return this.dataItem.name; + } + public abstract assignValue(value: any): void; + public abstract getSignature(): string; + public abstract encodeToCalldata(calldata: Calldata): void; // abstract match(type: string): Bool; } - class Calldata {} - export abstract class StaticDataType extends DataType { constructor(dataItem: DataItem) { super(dataItem); @@ -163,7 +297,7 @@ namespace AbiEncoder { export class Address extends StaticDataType { constructor(dataItem: DataItem) { super(dataItem); - expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + expect(Address.matchGrammar(dataItem.type)).to.be.true(); } public assignValue(value: string) { @@ -171,6 +305,14 @@ namespace AbiEncoder { this.assignHexValue(hexValue); } + public getSignature(): string { + throw 1; + } + + public encodeToCalldata(calldata: Calldata): void { + throw 2; + } + public static matchGrammar(type: string): boolean { return type === 'address'; } @@ -187,6 +329,14 @@ namespace AbiEncoder { //this.assignHexValue(hexValue); } + public getSignature(): string { + throw 1; + } + + public encodeToCalldata(calldata: Calldata): void { + throw 2; + } + public static matchGrammar(type: string): boolean { return type === 'bool'; } @@ -209,11 +359,19 @@ namespace AbiEncoder { } } + public encodeToCalldata(calldata: Calldata): void { + throw 2; + } + public assignValue(value: string) { //const hexValue = ethUtil.bufferToHex(new Buffer(value)); //this.assignHexValue(hexValue); } + public getSignature(): string { + throw 1; + } + public static matchGrammar(type: string): boolean { return this.matcher.test(type); } @@ -259,6 +417,14 @@ namespace AbiEncoder { this.assignHexValue(encodedValue); } + public getSignature(): string { + return `uint${this.width}`; + } + + public encodeToCalldata(calldata: Calldata): void { + throw 2; + } + public static matchGrammar(type: string): boolean { return this.matcher.test(type); } @@ -286,6 +452,14 @@ namespace AbiEncoder { //this.assignHexValue(hexValue); } + public getSignature(): string { + throw 1; + } + + public encodeToCalldata(calldata: Calldata): void { + throw 2; + } + public static matchGrammar(type: string): boolean { return this.matcher.test(type); } @@ -302,6 +476,14 @@ namespace AbiEncoder { //this.assignHexValue(hexValue); } + public getSignature(): string { + throw 1; + } + + public encodeToCalldata(calldata: Calldata): void { + throw 2; + } + public static matchGrammar(type: string): boolean { return type === 'tuple'; } @@ -321,6 +503,14 @@ namespace AbiEncoder { //this.assignHexValue(hexValue); } + public getSignature(): string { + throw 1; + } + + public encodeToCalldata(calldata: Calldata): void { + throw 2; + } + public static matchGrammar(type: string): boolean { return type === 'bytes'; } @@ -345,9 +535,17 @@ namespace AbiEncoder { //this.assignHexValue(hexValue); } + public encodeToCalldata(calldata: Calldata): void { + throw 2; + } + public static matchGrammar(type: string): boolean { return this.matcher.test(type); } + + public getSignature(): string { + throw 1; + } } export class SolString extends DynamicDataType { @@ -367,6 +565,12 @@ namespace AbiEncoder { this.assignHexValue(encodedValue); } + public getSignature(): string { + return 'string'; + } + + public encodeToCalldata(calldata: Calldata): void {} + public static matchGrammar(type: string): boolean { return type === 'string'; } @@ -399,6 +603,14 @@ namespace AbiEncoder { public getHexValue(): string { return this.destDataType.getHexValue(); } + + public getSignature(): string { + return this.destDataType.getSignature(); + } + + public encodeToCalldata(calldata: Calldata): void { + throw 2; + } } export class DataTypeFactory { @@ -437,6 +649,8 @@ namespace AbiEncoder { export class Method { name: string; params: DataType[]; + signature: string; + selector: string; constructor(abi: MethodAbi) { // super(); @@ -446,20 +660,40 @@ namespace AbiEncoder { _.each(abi.inputs, (input: DataItem) => { this.params.push(DataTypeFactory.create(input)); }); + + // Compute signature + this.signature = `${this.name}(`; + _.each(this.params, (param: DataType, i: number) => { + this.signature += param.getSignature(); + if (i < this.params.length - 1) { + this.signature += ','; + } + }); + this.signature += ')'; + + // Compute selector + this.selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(this.signature).slice(0, 4))); + + console.log(`--SIGNATURE--\n${this.signature}\n---------\n`); + console.log(`--SELECTOR--\n${this.selector}\n---------\n`); } encode(args: any[]): string { - //const calldata = new Calldata(this.name, this.params.length); - let params = this.params; + const calldata = new Calldata(this.selector, this.params.length); + + // Write params section + const params = this.params; _.each(params, (param: DataType, i: number) => { - console.log('param:\n', param, '\n--end--\n'); - console.log('arg:\n', args[i], '\n--end\n'); + // Assign value to param param.assignValue(args[i]); - console.log(param.getHexValue()); - //param.encodeToCalldata(calldata); + // Binds top-level parameter to the params section of calldata + calldata.bind(param, CalldataSection.PARAMS); + // Binds parameter's children to the data section of calldata, + // while retaining internal pointers + param.bind(calldata); }); - return ''; + return calldata.getHexValue(); //return calldata.getRaw(); } @@ -499,10 +733,11 @@ namespace AbiEncoder { } describe.only('ABI Encoder', () => { - describe('Just a Greg, Eh', () => { + describe.only('Just a Greg, Eh', () => { it('Yessir', async () => { const method = new AbiEncoder.Method(simpleAbi); - method.encode([new BigNumber(5), 'five']); + const calldata = method.encode([new BigNumber(5), 'five']); + console.log(calldata); expect(true).to.be.true(); }); }); -- cgit v1.2.3 From 7196046e2f38aba4709f61d1a88eb562d6c0a38a Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 6 Nov 2018 17:12:13 -0800 Subject: Prints full ABI encoding for subset of types, with off by factor of 2 for offsets --- packages/order-utils/test/abi_encoder_test.ts | 69 +++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 7e12dfee6..e1d0e8a91 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -153,12 +153,13 @@ namespace AbiEncoder { class Memblock { private dataType: DataType; - private location: { calldataSection: CalldataSection; offset: BigNumber }; + private location: { calldataSection: CalldataSection; sectionOffset: BigNumber; offset: BigNumber }; constructor(dataType: DataType) { this.dataType = dataType; this.location = { calldataSection: CalldataSection.NONE, + sectionOffset: new BigNumber(0), offset: new BigNumber(0), }; } @@ -167,14 +168,27 @@ namespace AbiEncoder { return new BigNumber(ethUtil.toBuffer(this.dataType.getHexValue()).byteLength); } - public assignLocation(calldataSection: CalldataSection, offset: BigNumber) { + public assignLocation(calldataSection: CalldataSection, sectionOffset: BigNumber, offset: BigNumber) { this.location.calldataSection = calldataSection; + this.location.sectionOffset = sectionOffset; this.location.offset = offset; } public get(): string { return ethUtil.stripHexPrefix(this.dataType.getHexValue()); } + + public getOffset(): BigNumber { + return this.location.offset; + } + + public getAbsoluteOffset(): BigNumber { + return this.location.sectionOffset.plus(this.location.offset); + } + + public getSection(): CalldataSection { + return this.location.calldataSection; + } } interface BindList { @@ -204,19 +218,19 @@ namespace AbiEncoder { public bind(dataType: DataType, section: CalldataSection = CalldataSection.DATA) { if (dataType.getId() in this.bindList) { - throw `Rebind`; + throw `Rebind on ${dataType.getId()}`; } const memblock = new Memblock(dataType); switch (section) { case CalldataSection.PARAMS: this.params.push(memblock); - memblock.assignLocation(section, this.currentParamOffset); + memblock.assignLocation(section, new BigNumber(0), this.currentParamOffset); this.currentParamOffset = this.currentParamOffset.plus(memblock.getSize()); break; case CalldataSection.DATA: this.data.push(memblock); - memblock.assignLocation(section, this.currentDataOffset); + memblock.assignLocation(section, this.dataOffset, this.currentDataOffset); this.currentDataOffset = this.currentDataOffset.plus(memblock.getSize()); break; @@ -225,6 +239,7 @@ namespace AbiEncoder { } this.bindList[dataType.getId()] = memblock; + dataType.rbind(memblock); } public getHexValue(): string { @@ -243,12 +258,14 @@ namespace AbiEncoder { export abstract class DataType { private dataItem: DataItem; private hexValue: string; - private memblock: Memblock | undefined; + protected memblock: Memblock | undefined; + protected children: DataType[]; constructor(dataItem: DataItem) { this.dataItem = dataItem; this.hexValue = '0x'; this.memblock = undefined; + this.children = []; } protected assignHexValue(hexValue: string) { @@ -268,13 +285,28 @@ namespace AbiEncoder { } public bind(calldata: Calldata) { - if (this.memblock !== undefined) return; // already binded + if (this.memblock === undefined) { + calldata.bind(this); + } + _.each(this.children, (child: DataType) => { + child.bind(calldata); + }); } public getId(): string { return this.dataItem.name; } + public getOffset(): BigNumber { + if (this.memblock === undefined) return new BigNumber(0); + return this.memblock.getOffset(); + } + + public getAbsoluteOffset(): BigNumber { + if (this.memblock === undefined) return new BigNumber(0); + return this.memblock.getAbsoluteOffset(); + } + public abstract assignValue(value: any): void; public abstract getSignature(): string; public abstract encodeToCalldata(calldata: Calldata): void; @@ -401,8 +433,6 @@ namespace AbiEncoder { } public assignValue(value: BigNumber) { - console.log(JSON.stringify(value)); - console.log(JSON.stringify(this.getMaxValue())); if (value.greaterThan(this.getMaxValue())) { throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; } else if (value.lessThan(0)) { @@ -589,6 +619,7 @@ namespace AbiEncoder { const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; super(dataItem); this.destDataType = destDataType; + this.children.push(destDataType); } /* @@ -601,7 +632,23 @@ namespace AbiEncoder { } public getHexValue(): string { - return this.destDataType.getHexValue(); + let offset = new BigNumber(0); + if (this.memblock !== undefined) { + switch (this.memblock.getSection()) { + case CalldataSection.PARAMS: + offset = this.destDataType.getAbsoluteOffset(); + break; + case CalldataSection.DATA: + offset = this.destDataType.getOffset(); + break; + } + } + + const hexBase = 16; + const evmWordWidth = 32; + const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${offset.toString(hexBase)}`), evmWordWidth); + const encodedValue = ethUtil.bufferToHex(valueBuf); + return encodedValue; } public getSignature(): string { @@ -693,6 +740,8 @@ namespace AbiEncoder { param.bind(calldata); }); + console.log(calldata); + return calldata.getHexValue(); //return calldata.getRaw(); -- cgit v1.2.3 From b99252baaedd9bc37ef4e53d02e4085bed7e5142 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 6 Nov 2018 17:18:40 -0800 Subject: ABI encodes some basic types correctly --- packages/order-utils/test/abi_encoder_test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index e1d0e8a91..1a33e4f78 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -211,7 +211,7 @@ namespace AbiEncoder { this.data = []; const evmWordSize = 32; this.dataOffset = new BigNumber(nParams).times(evmWordSize); - this.currentDataOffset = this.dataOffset; + this.currentDataOffset = new BigNumber(0); this.currentParamOffset = new BigNumber(0); this.bindList = {}; } @@ -243,7 +243,7 @@ namespace AbiEncoder { } public getHexValue(): string { - let hexValue = `0x${this.selector}`; + let hexValue = `${this.selector}`; _.each(this.params, (memblock: Memblock) => { hexValue += memblock.get(); }); @@ -634,6 +634,8 @@ namespace AbiEncoder { public getHexValue(): string { let offset = new BigNumber(0); if (this.memblock !== undefined) { + console.log('Abs Offset = ', JSON.stringify(this.destDataType.getAbsoluteOffset())); + console.log('Local Offset = ', JSON.stringify(this.destDataType.getOffset())); switch (this.memblock.getSection()) { case CalldataSection.PARAMS: offset = this.destDataType.getAbsoluteOffset(); -- cgit v1.2.3 From fc0e7b1132f8956c470a49b0e964d9ca1812e591 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 10:20:19 -0800 Subject: cleaner bind --- packages/order-utils/test/abi_encoder_test.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 1a33e4f78..292e615f3 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -216,7 +216,7 @@ namespace AbiEncoder { this.bindList = {}; } - public bind(dataType: DataType, section: CalldataSection = CalldataSection.DATA) { + public bind(dataType: DataType, section: CalldataSection) { if (dataType.getId() in this.bindList) { throw `Rebind on ${dataType.getId()}`; } @@ -284,12 +284,12 @@ namespace AbiEncoder { this.memblock = memblock; } - public bind(calldata: Calldata) { + public bind(calldata: Calldata, section: CalldataSection) { if (this.memblock === undefined) { - calldata.bind(this); + calldata.bind(this, section); } _.each(this.children, (child: DataType) => { - child.bind(calldata); + child.bind(calldata, CalldataSection.DATA); }); } @@ -729,17 +729,10 @@ namespace AbiEncoder { encode(args: any[]): string { const calldata = new Calldata(this.selector, this.params.length); - - // Write params section const params = this.params; _.each(params, (param: DataType, i: number) => { - // Assign value to param param.assignValue(args[i]); - // Binds top-level parameter to the params section of calldata - calldata.bind(param, CalldataSection.PARAMS); - // Binds parameter's children to the data section of calldata, - // while retaining internal pointers - param.bind(calldata); + param.bind(calldata, CalldataSection.PARAMS); }); console.log(calldata); -- cgit v1.2.3 From dfa9e435af7477bddb147a8af3700143b27b5dc6 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 10:44:22 -0800 Subject: Implemented Address --- packages/order-utils/test/abi_encoder_test.ts | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 292e615f3..7fb091019 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -309,9 +309,6 @@ namespace AbiEncoder { public abstract assignValue(value: any): void; public abstract getSignature(): string; - public abstract encodeToCalldata(calldata: Calldata): void; - - // abstract match(type: string): Bool; } export abstract class StaticDataType extends DataType { @@ -333,16 +330,13 @@ namespace AbiEncoder { } public assignValue(value: string) { - const hexValue = ethUtil.bufferToHex(new Buffer(value)); + const evmWordWidth = 32; + const hexValue = ethUtil.bufferToHex(ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth)); this.assignHexValue(hexValue); } public getSignature(): string { - throw 1; - } - - public encodeToCalldata(calldata: Calldata): void { - throw 2; + return `address`; } public static matchGrammar(type: string): boolean { @@ -634,8 +628,6 @@ namespace AbiEncoder { public getHexValue(): string { let offset = new BigNumber(0); if (this.memblock !== undefined) { - console.log('Abs Offset = ', JSON.stringify(this.destDataType.getAbsoluteOffset())); - console.log('Local Offset = ', JSON.stringify(this.destDataType.getOffset())); switch (this.memblock.getSection()) { case CalldataSection.PARAMS: offset = this.destDataType.getAbsoluteOffset(); @@ -786,6 +778,19 @@ describe.only('ABI Encoder', () => { }); }); + describe.only('Address', () => { + const testAddressDataItem = { name: 'testAddress', type: 'address' }; + it('Valid Address', async () => { + const addressDataType = new AbiEncoder.Address(testAddressDataItem); + addressDataType.assignValue('0xe41d2489571d322189246dafa5ebde1f4699f498'); + const expectedAbiEncodedAddress = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; + + console.log(addressDataType.getHexValue()); + console.log(expectedAbiEncodedAddress); + expect(addressDataType.getHexValue()).to.be.equal(expectedAbiEncodedAddress); + }); + }); + describe('String', () => { const testStringDataItem = { name: 'testString', type: 'string' }; it('Less than 32 bytes', async () => { -- cgit v1.2.3 From ef5ba0375acbff2f985be08a6bd19b0499628e33 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 10:49:53 -0800 Subject: Implemented bool type --- packages/order-utils/test/abi_encoder_test.ts | 35 +++++++++++++++++++-------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 7fb091019..76a6da00e 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -347,20 +347,18 @@ namespace AbiEncoder { export class Bool extends StaticDataType { constructor(dataItem: DataItem) { super(dataItem); - expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + expect(Bool.matchGrammar(dataItem.type)).to.be.true(); } - public assignValue(value: string) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); + public assignValue(value: boolean) { + const evmWordWidth = 32; + const encodedValue = value === true ? '0x1' : '0x0'; + const hexValue = ethUtil.bufferToHex(ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth)); + this.assignHexValue(hexValue); } public getSignature(): string { - throw 1; - } - - public encodeToCalldata(calldata: Calldata): void { - throw 2; + return 'bool'; } public static matchGrammar(type: string): boolean { @@ -778,7 +776,7 @@ describe.only('ABI Encoder', () => { }); }); - describe.only('Address', () => { + describe('Address', () => { const testAddressDataItem = { name: 'testAddress', type: 'address' }; it('Valid Address', async () => { const addressDataType = new AbiEncoder.Address(testAddressDataItem); @@ -791,6 +789,23 @@ describe.only('ABI Encoder', () => { }); }); + describe.only('Bool', () => { + const testBoolDataItem = { name: 'testBool', type: 'bool' }; + it('True', async () => { + const boolDataType = new AbiEncoder.Bool(testBoolDataItem); + boolDataType.assignValue(true); + const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + }); + + it('False', async () => { + const boolDataType = new AbiEncoder.Bool(testBoolDataItem); + boolDataType.assignValue(false); + const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + }); + }); + describe('String', () => { const testStringDataItem = { name: 'testString', type: 'string' }; it('Less than 32 bytes', async () => { -- cgit v1.2.3 From f6cf3de1c4cabc0fcd6a4bb98948096c4924c2bd Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 11:31:49 -0800 Subject: Implemented int --- packages/order-utils/test/abi_encoder_test.ts | 105 +++++++++++++++++++++----- 1 file changed, 87 insertions(+), 18 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 76a6da00e..b557f605d 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -371,29 +371,71 @@ namespace AbiEncoder { '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); - static DEFAULT_WIDTH = new BigNumber(1); - width: BigNumber = Byte.DEFAULT_WIDTH; + static DEFAULT_WIDTH: number = 256; + width: number = Int.DEFAULT_WIDTH; constructor(dataItem: DataItem) { super(dataItem); - const matches = Byte.matcher.exec(dataItem.type); + const matches = Int.matcher.exec(dataItem.type); expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 2) { - this.width = new BigNumber(matches[1], 10); + if (matches !== null && matches.length === 2 && matches[1] !== undefined) { + this.width = parseInt(matches[1]); + } else { + this.width = 256; } } - public encodeToCalldata(calldata: Calldata): void { - throw 2; + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).sub(1); } - public assignValue(value: string) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); + public getMinValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).times(-1); + } + + public assignValue(value: BigNumber) { + if (value.greaterThan(this.getMaxValue())) { + throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; + } else if (value.lessThan(this.getMinValue())) { + throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; + } + + const hexBase = 16; + const evmWordWidth = 32; + let valueBuf: Buffer; + if (value.greaterThanOrEqualTo(0)) { + valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); + } else { + // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. + // Step 1/3: Convert value to positive binary string + const binBase = 2; + const valueBin = value.times(-1).toString(binBase); + + // Step 2/3: Invert binary value + const bitsInEvmWord = 256; + let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, binBase); + + // Step 3/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const negativeValue = invertedValue.plus(1); + + // Convert the negated value to a hex string + valueBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), + evmWordWidth, + ); + } + + const encodedValue = ethUtil.bufferToHex(valueBuf); + this.assignHexValue(encodedValue); } public getSignature(): string { - throw 1; + return `uint${this.width}`; } public static matchGrammar(type: string): boolean { @@ -413,7 +455,7 @@ namespace AbiEncoder { super(dataItem); const matches = UInt.matcher.exec(dataItem.type); expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 2) { + if (matches !== null && matches.length === 2 && matches[1] !== undefined) { this.width = parseInt(matches[1]); } else { this.width = 256; @@ -421,7 +463,7 @@ namespace AbiEncoder { } public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1); + return new BigNumber(2).toPower(this.width).sub(1); } public assignValue(value: BigNumber) { @@ -443,10 +485,6 @@ namespace AbiEncoder { return `uint${this.width}`; } - public encodeToCalldata(calldata: Calldata): void { - throw 2; - } - public static matchGrammar(type: string): boolean { return this.matcher.test(type); } @@ -789,7 +827,7 @@ describe.only('ABI Encoder', () => { }); }); - describe.only('Bool', () => { + describe('Bool', () => { const testBoolDataItem = { name: 'testBool', type: 'bool' }; it('True', async () => { const boolDataType = new AbiEncoder.Bool(testBoolDataItem); @@ -806,6 +844,37 @@ describe.only('ABI Encoder', () => { }); }); + describe.only('Integer', () => { + const testIntDataItem = { name: 'testInt', type: 'int' }; + it('Positive - Base case', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(1)); + const expectedAbiEncodedInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Positive', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(437829473)); + const expectedAbiEncodedInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Negative - Base case', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(-1)); + const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Negative', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(-437829473)); + const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + }); + describe('String', () => { const testStringDataItem = { name: 'testString', type: 'string' }; it('Less than 32 bytes', async () => { -- cgit v1.2.3 From e2b115a15c4745494043697130e7d1980cde05f4 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 11:37:07 -0800 Subject: refactored UInt/Int --- packages/order-utils/test/abi_encoder_test.ts | 75 ++++++++++++--------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index b557f605d..f9e28015b 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -366,17 +366,14 @@ namespace AbiEncoder { } } - export class Int extends StaticDataType { - static matcher = RegExp( - '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - static DEFAULT_WIDTH: number = 256; - width: number = Int.DEFAULT_WIDTH; + abstract class Number extends StaticDataType { + static MAX_WIDTH: number = 256; + static DEFAULT_WIDTH: number = Number.MAX_WIDTH; + width: number = Number.DEFAULT_WIDTH; - constructor(dataItem: DataItem) { + constructor(dataItem: DataItem, matcher: RegExp) { super(dataItem); - const matches = Int.matcher.exec(dataItem.type); + const matches = matcher.exec(dataItem.type); expect(matches).to.be.not.null(); if (matches !== null && matches.length === 2 && matches[1] !== undefined) { this.width = parseInt(matches[1]); @@ -385,14 +382,6 @@ namespace AbiEncoder { } } - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).times(-1); - } - public assignValue(value: BigNumber) { if (value.greaterThan(this.getMaxValue())) { throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; @@ -434,8 +423,29 @@ namespace AbiEncoder { this.assignHexValue(encodedValue); } + public abstract getMaxValue(): BigNumber; + public abstract getMinValue(): BigNumber; + } + + export class Int extends Number { + static matcher = RegExp( + '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + constructor(dataItem: DataItem) { + super(dataItem, Int.matcher); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).times(-1); + } + public getSignature(): string { - return `uint${this.width}`; + return `int${this.width}`; } public static matchGrammar(type: string): boolean { @@ -443,42 +453,21 @@ namespace AbiEncoder { } } - export class UInt extends StaticDataType { + export class UInt extends Number { static matcher = RegExp( '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); - static DEFAULT_WIDTH: number = 256; - width: number = UInt.DEFAULT_WIDTH; - constructor(dataItem: DataItem) { - super(dataItem); - const matches = UInt.matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 2 && matches[1] !== undefined) { - this.width = parseInt(matches[1]); - } else { - this.width = 256; - } + super(dataItem, UInt.matcher); } public getMaxValue(): BigNumber { return new BigNumber(2).toPower(this.width).sub(1); } - public assignValue(value: BigNumber) { - if (value.greaterThan(this.getMaxValue())) { - throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; - } else if (value.lessThan(0)) { - throw `tried to assign value of ${value} to an unsigned integer.`; - } - - const hexBase = 16; - const evmWordWidth = 32; - const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); - const encodedValue = ethUtil.bufferToHex(valueBuf); - - this.assignHexValue(encodedValue); + public getMinValue(): BigNumber { + return new BigNumber(0); } public getSignature(): string { -- cgit v1.2.3 From cef254fa8cacdcba37ec41dec574566b9249b84c Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 11:39:48 -0800 Subject: tests for unsigned integer --- packages/order-utils/test/abi_encoder_test.ts | 30 ++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index f9e28015b..fb149faa1 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -833,7 +833,7 @@ describe.only('ABI Encoder', () => { }); }); - describe.only('Integer', () => { + describe('Integer', () => { const testIntDataItem = { name: 'testInt', type: 'int' }; it('Positive - Base case', async () => { const intDataType = new AbiEncoder.Int(testIntDataItem); @@ -862,6 +862,34 @@ describe.only('ABI Encoder', () => { const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f'; expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); }); + + // TODO: Add bounds tests + tests for different widths + }); + + describe('Unsigned Integer', () => { + const testIntDataItem = { name: 'testUInt', type: 'uint' }; + it('Lower Bound', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(0)); + const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + it('Base Case', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(1)); + const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + it('Random value', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(437829473)); + const expectedAbiEncodedUInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + // TODO: Add bounds tests + tests for different widths }); describe('String', () => { -- cgit v1.2.3 From 91a08b9fdd6ec406c6a0e853ff314501347fac94 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 12:54:29 -0800 Subject: Static bytes tests --- packages/order-utils/test/abi_encoder_test.ts | 92 +++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 12 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index fb149faa1..6f723036b 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -484,29 +484,43 @@ namespace AbiEncoder { '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', ); - static DEFAULT_WIDTH = new BigNumber(1); - width: BigNumber = Byte.DEFAULT_WIDTH; + static DEFAULT_WIDTH = 1; + width: number = Byte.DEFAULT_WIDTH; constructor(dataItem: DataItem) { super(dataItem); const matches = Byte.matcher.exec(dataItem.type); expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 2) { - this.width = new BigNumber(matches[1], 10); + if (matches !== null && matches.length === 3 && matches[2] !== undefined) { + this.width = parseInt(matches[2]); + } else { + this.width = Byte.DEFAULT_WIDTH; } } - public assignValue(value: string) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); - } + public assignValue(value: string | Buffer) { + // Convert value into a buffer and do bounds checking + const valueBuf = ethUtil.toBuffer(value); + if (valueBuf.byteLength > this.width) { + throw new Error( + `Tried to assign ${value} (${ + valueBuf.byteLength + } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, + ); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } - public getSignature(): string { - throw 1; + // Store value as hex + const evmWordWidth = 32; + const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); + const hexValue = ethUtil.bufferToHex(paddedValue); + this.assignHexValue(hexValue); } - public encodeToCalldata(calldata: Calldata): void { - throw 2; + public getSignature(): string { + // Note that `byte` reduces to `bytes1` + return `bytes${this.width}`; } public static matchGrammar(type: string): boolean { @@ -892,6 +906,60 @@ describe.only('ABI Encoder', () => { // TODO: Add bounds tests + tests for different widths }); + describe.only('Static Bytes', () => { + it('Byte (padded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x05'); + const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it.skip('Byte (no padding)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + + // @TODO: This does not catch the Error + expect(byteDataType.assignValue('0x5')).to.throw(); + }); + + it('Bytes1', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes1' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x05'); + const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it('Bytes32 (padded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x0001020304050607080911121314151617181920212223242526272829303132'); + const expectedAbiEncodedByte = '0x0001020304050607080911121314151617181920212223242526272829303132'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it('Bytes32 (unpadded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x1a18bf61'); + const expectedAbiEncodedByte = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it.skip('Bytes32 - Too long', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + + // @TODO: This does not catch the Error + expect( + byteDataType.assignValue('0x000102030405060708091112131415161718192021222324252627282930313233'), + ).to.throw( + `Tried to assign 0x000102030405060708091112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32`, + ); + }); + }); + describe('String', () => { const testStringDataItem = { name: 'testString', type: 'string' }; it('Less than 32 bytes', async () => { -- cgit v1.2.3 From f35cf030308c0f18ad26669bfb3b269a5b96a39e Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 13:14:46 -0800 Subject: implemented bytes (dynamic) --- packages/order-utils/test/abi_encoder_test.ts | 121 ++++++++++++++++---------- 1 file changed, 77 insertions(+), 44 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 6f723036b..926f7bc81 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -528,37 +528,73 @@ namespace AbiEncoder { } } - export class Tuple extends DynamicDataType { + export class Bytes extends StaticDataType { + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = SolArray.UNDEFINED_LENGTH; + constructor(dataItem: DataItem) { super(dataItem); - expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + expect(Bytes.matchGrammar(dataItem.type)).to.be.true(); } - public assignValue(value: string) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); + public assignValue(value: string | Buffer) { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); + } + const valueBuf = ethUtil.toBuffer(value); + if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + const wordsForValue = Math.ceil(valueBuf.byteLength / 32); + const paddedBytesForValue = wordsForValue * 32; + const paddedValueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); + const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); + const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); + const encodedValue = ethUtil.bufferToHex(encodedValueBuf); + + this.assignHexValue(encodedValue); } public getSignature(): string { - throw 1; + return 'bytes'; } - public encodeToCalldata(calldata: Calldata): void { - throw 2; + public static matchGrammar(type: string): boolean { + return type === 'bytes'; + } + } + + export class SolString extends StaticDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(SolString.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + const wordsForValue = Math.ceil(value.length / 32); + const paddedBytesForValue = wordsForValue * 32; + const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); + const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); + const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + const encodedValue = ethUtil.bufferToHex(encodedValueBuf); + + this.assignHexValue(encodedValue); + } + + public getSignature(): string { + return 'string'; } public static matchGrammar(type: string): boolean { - return type === 'tuple'; + return type === 'string'; } } - export class Bytes extends StaticDataType { - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = SolArray.UNDEFINED_LENGTH; - + export class Tuple extends DynamicDataType { constructor(dataItem: DataItem) { super(dataItem); - expect(Bytes.matchGrammar(dataItem.type)).to.be.true(); + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); } public assignValue(value: string) { @@ -575,7 +611,7 @@ namespace AbiEncoder { } public static matchGrammar(type: string): boolean { - return type === 'bytes'; + return type === 'tuple'; } } @@ -611,34 +647,6 @@ namespace AbiEncoder { } } - export class SolString extends DynamicDataType { - constructor(dataItem: DataItem) { - super(dataItem); - expect(SolString.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string) { - const wordsForValue = Math.ceil(value.length / 32); - const paddedBytesForValue = wordsForValue * 32; - const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); - const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); - const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); - const encodedValue = ethUtil.bufferToHex(encodedValueBuf); - - this.assignHexValue(encodedValue); - } - - public getSignature(): string { - return 'string'; - } - - public encodeToCalldata(calldata: Calldata): void {} - - public static matchGrammar(type: string): boolean { - return type === 'string'; - } - } - /* TODO class Fixed extends StaticDataType {} @@ -906,7 +914,7 @@ describe.only('ABI Encoder', () => { // TODO: Add bounds tests + tests for different widths }); - describe.only('Static Bytes', () => { + describe('Static Bytes', () => { it('Byte (padded)', async () => { const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; const byteDataType = new AbiEncoder.Byte(testByteDataItem); @@ -960,6 +968,31 @@ describe.only('ABI Encoder', () => { }); }); + describe.only('Bytes (Dynamic)', () => { + const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; + it('Less than 32 bytes', async () => { + const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); + bytesDataType.assignValue('0x010203'); + const expectedAbiEncodedBytes = + '0x00000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000'; + + expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + }); + + it('Greater than 32 bytes', async () => { + const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); + const testValue = '0x' + '61'.repeat(40); + bytesDataType.assignValue(testValue); + const expectedAbiEncodedBytes = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + }); + + // @TODO: Add test for throw on half-byte + // @TODO: Test with no 0x prefix + // @TODO: Test with Buffer as input + }); + describe('String', () => { const testStringDataItem = { name: 'testString', type: 'string' }; it('Less than 32 bytes', async () => { -- cgit v1.2.3 From 5053c19599e96b9886e6b36173350162b92405a3 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 13:44:27 -0800 Subject: constructor for an Array + a test -- appears to work --- packages/order-utils/test/abi_encoder_test.ts | 87 ++++++++++++++++++--------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 926f7bc81..bed75b935 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -528,7 +528,7 @@ namespace AbiEncoder { } } - export class Bytes extends StaticDataType { + export class Bytes extends DynamicDataType { static UNDEFINED_LENGTH = new BigNumber(-1); length: BigNumber = SolArray.UNDEFINED_LENGTH; @@ -565,7 +565,7 @@ namespace AbiEncoder { } } - export class SolString extends StaticDataType { + export class SolString extends DynamicDataType { constructor(dataItem: DataItem) { super(dataItem); expect(SolString.matchGrammar(dataItem.type)).to.be.true(); @@ -591,42 +591,63 @@ namespace AbiEncoder { } } - export class Tuple extends DynamicDataType { + export class SolArray extends DynamicDataType { + static matcher = RegExp('^(.+)\\[([0-9]d*)\\]$'); + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = SolArray.UNDEFINED_LENGTH; + type: string = '[undefined]'; + constructor(dataItem: DataItem) { super(dataItem); - expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + const matches = SolArray.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches === null || matches.length !== 3) { + throw new Error(`Could not parse array: ${dataItem.type}`); + } else if (matches[1] === undefined) { + throw new Error(`Could not parse array type: ${dataItem.type}`); + } else if (matches[2] === undefined) { + // Parse out array type and length + this.type = matches[1]; + this.length = new BigNumber(SolArray.UNDEFINED_LENGTH); + return; + } + + // Parse out array type and length + this.type = matches[1]; + this.length = new BigNumber(matches[2], 10); + if (this.length.lessThan(0)) { + throw new Error(`Bad array length: ${JSON.stringify(this.length)}`); + } + + // Construct children + for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { + const childDataItem = { + type: this.type, + name: `${this.getDataItem().name}[${idx.toString(10)}]`, + } as DataItem; + const child = DataTypeFactory.create(childDataItem); + this.children.push(child); + } } - public assignValue(value: string) { + public assignValue(value: any[]) { //const hexValue = ethUtil.bufferToHex(new Buffer(value)); //this.assignHexValue(hexValue); } - public getSignature(): string { - throw 1; - } - - public encodeToCalldata(calldata: Calldata): void { - throw 2; + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); } - public static matchGrammar(type: string): boolean { - return type === 'tuple'; + public getSignature(): string { + return `${this.type}[${this.length}]`; } } - export class SolArray extends DynamicDataType { - static matcher = RegExp('^.+\\[([0-9]d*)\\]$'); - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = SolArray.UNDEFINED_LENGTH; - + export class Tuple extends DynamicDataType { constructor(dataItem: DataItem) { super(dataItem); - const matches = SolArray.matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 1) { - this.length = new BigNumber(matches[1], 10); - } + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); } public assignValue(value: string) { @@ -634,16 +655,16 @@ namespace AbiEncoder { //this.assignHexValue(hexValue); } + public getSignature(): string { + throw 1; + } + public encodeToCalldata(calldata: Calldata): void { throw 2; } public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } - - public getSignature(): string { - throw 1; + return type === 'tuple'; } } @@ -816,7 +837,15 @@ namespace AbiEncoder { } describe.only('ABI Encoder', () => { - describe.only('Just a Greg, Eh', () => { + describe.only('Array', () => { + it('sample', async () => { + const testDataItem = { name: 'testArray', type: 'string[2]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + }); + }); + + describe('Just a Greg, Eh', () => { it('Yessir', async () => { const method = new AbiEncoder.Method(simpleAbi); const calldata = method.encode([new BigNumber(5), 'five']); -- cgit v1.2.3 From 7de7fe782398eded47598046699fdc0915597e33 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 14:18:05 -0800 Subject: Array of basic types works --- packages/order-utils/test/abi_encoder_test.ts | 53 ++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index bed75b935..a455e6a00 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -612,14 +612,16 @@ namespace AbiEncoder { return; } - // Parse out array type and length + // Parse out array type/length and construct children this.type = matches[1]; this.length = new BigNumber(matches[2], 10); if (this.length.lessThan(0)) { throw new Error(`Bad array length: ${JSON.stringify(this.length)}`); } + this.constructChildren(); + } - // Construct children + private constructChildren() { for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { const childDataItem = { type: this.type, @@ -631,8 +633,43 @@ namespace AbiEncoder { } public assignValue(value: any[]) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); + // Sanity check length + const valueLength = new BigNumber(value.length); + if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { + throw new Error( + `Expected array of length ${JSON.stringify(this.length)}, but got array of length ${JSON.stringify( + valueLength, + )}`, + ); + } + + // Assign length if not already set + if (this.length === SolArray.UNDEFINED_LENGTH) { + this.length = valueLength; + this.constructChildren(); + } + + // Assign values to children + for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { + const idxNumber = idx.toNumber(); + this.children[idxNumber].assignValue(value[idxNumber]); + } + } + + public getHexValue(): string { + const lengthBufUnpadded = ethUtil.toBuffer(`0x${this.length.toString(16)}`); + const lengthBuf = ethUtil.setLengthLeft(lengthBufUnpadded, 32); + let valueBuf = lengthBuf; + for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { + const idxNumber = idx.toNumber(); + const childValueHex = this.children[idxNumber].getHexValue(); + const childValueBuf = ethUtil.toBuffer(childValueHex); + valueBuf = Buffer.concat([valueBuf, childValueBuf]); + } + + // Convert value buffer to hex + const valueHex = ethUtil.bufferToHex(valueBuf); + return valueHex; } public static matchGrammar(type: string): boolean { @@ -839,9 +876,15 @@ namespace AbiEncoder { describe.only('ABI Encoder', () => { describe.only('Array', () => { it('sample', async () => { - const testDataItem = { name: 'testArray', type: 'string[2]' }; + const testDataItem = { name: 'testArray', type: 'int[2]' }; const dataType = new AbiEncoder.SolArray(testDataItem); console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue([new BigNumber(5), new BigNumber(6)]); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); }); }); -- cgit v1.2.3 From 39fa26b2f3b4c34c7ff4aa87d6b99ff1af8a4c36 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 14:23:50 -0800 Subject: arrays with dynamic size --- packages/order-utils/test/abi_encoder_test.ts | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index a455e6a00..0d7f68780 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -592,7 +592,7 @@ namespace AbiEncoder { } export class SolArray extends DynamicDataType { - static matcher = RegExp('^(.+)\\[([0-9]d*)\\]$'); + static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); static UNDEFINED_LENGTH = new BigNumber(-1); length: BigNumber = SolArray.UNDEFINED_LENGTH; type: string = '[undefined]'; @@ -601,14 +601,19 @@ namespace AbiEncoder { super(dataItem); const matches = SolArray.matcher.exec(dataItem.type); expect(matches).to.be.not.null(); + console.log(JSON.stringify(matches)); if (matches === null || matches.length !== 3) { throw new Error(`Could not parse array: ${dataItem.type}`); } else if (matches[1] === undefined) { throw new Error(`Could not parse array type: ${dataItem.type}`); } else if (matches[2] === undefined) { - // Parse out array type and length + throw new Error(`Could not parse array length: ${dataItem.type}`); + } + + // Check if length is undefined + if (matches[2] === '') { this.type = matches[1]; - this.length = new BigNumber(SolArray.UNDEFINED_LENGTH); + this.length = SolArray.UNDEFINED_LENGTH; return; } @@ -886,6 +891,18 @@ describe.only('ABI Encoder', () => { console.log('*'.repeat(60)); console.log(hexValue); }); + + it.only('sample undefined size', async () => { + const testDataItem = { name: 'testArray', type: 'int[]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue([new BigNumber(5), new BigNumber(6)]); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + }); }); describe('Just a Greg, Eh', () => { -- cgit v1.2.3 From 9f35096fa98a17be0e6ed98dcc4c363a5c4e96f7 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 14:37:05 -0800 Subject: corrected selector for Array --- packages/order-utils/test/abi_encoder_test.ts | 58 ++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 0d7f68780..8e24c8299 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -32,6 +32,21 @@ const simpleAbi = { type: 'function', } as MethodAbi; +const stringAbi = { + constant: false, + inputs: [ + { + name: 'greg', + type: 'string[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + const fillOrderAbi = { constant: false, inputs: [ @@ -145,7 +160,7 @@ namespace AbiEncoder { } } - enum CalldataSection { + export enum CalldataSection { NONE, PARAMS, DATA, @@ -195,7 +210,7 @@ namespace AbiEncoder { [key: string]: Memblock; } - class Calldata { + export class Calldata { private selector: string; private params: Memblock[]; private data: Memblock[]; @@ -530,7 +545,7 @@ namespace AbiEncoder { export class Bytes extends DynamicDataType { static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = SolArray.UNDEFINED_LENGTH; + length: BigNumber = Bytes.UNDEFINED_LENGTH; constructor(dataItem: DataItem) { super(dataItem); @@ -682,6 +697,9 @@ namespace AbiEncoder { } public getSignature(): string { + if (this.length.equals(SolArray.UNDEFINED_LENGTH)) { + return `${this.type}[]`; + } return `${this.type}[${this.length}]`; } } @@ -879,7 +897,7 @@ namespace AbiEncoder { } describe.only('ABI Encoder', () => { - describe.only('Array', () => { + describe('Array', () => { it('sample', async () => { const testDataItem = { name: 'testArray', type: 'int[2]' }; const dataType = new AbiEncoder.SolArray(testDataItem); @@ -892,7 +910,7 @@ describe.only('ABI Encoder', () => { console.log(hexValue); }); - it.only('sample undefined size', async () => { + it('sample undefined size', async () => { const testDataItem = { name: 'testArray', type: 'int[]' }; const dataType = new AbiEncoder.SolArray(testDataItem); console.log(JSON.stringify(dataType, null, 4)); @@ -903,15 +921,43 @@ describe.only('ABI Encoder', () => { console.log('*'.repeat(60)); console.log(hexValue); }); + + it('sample dynamic types', async () => { + const testDataItem = { name: 'testArray', type: 'string[]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue(['five', 'six', 'seven']); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + const calldata = new AbiEncoder.Calldata('0x01020304', 1); + dataType.bind(calldata, AbiEncoder.CalldataSection.PARAMS); + console.log('*'.repeat(60)); + console.log(calldata.getHexValue()); + }); }); - describe('Just a Greg, Eh', () => { + describe.only('Just a Greg, Eh', () => { it('Yessir', async () => { const method = new AbiEncoder.Method(simpleAbi); const calldata = method.encode([new BigNumber(5), 'five']); console.log(calldata); expect(true).to.be.true(); }); + + it.only('Yessir', async () => { + const method = new AbiEncoder.Method(stringAbi); + const calldata = method.encode([['five', 'six', 'seven']]); + console.log(method.signature); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); }); describe('Address', () => { -- cgit v1.2.3 From 2b4febe337ec188ef0f105f817c124d9a583d669 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 15:22:09 -0800 Subject: switched from depth-first to breadth-first param binding --- packages/order-utils/test/abi_encoder_test.ts | 87 ++++++++++++++++++--------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 8e24c8299..cd721202e 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -240,12 +240,20 @@ namespace AbiEncoder { case CalldataSection.PARAMS: this.params.push(memblock); memblock.assignLocation(section, new BigNumber(0), this.currentParamOffset); + + console.log(`Binding ${dataType.getDataItem().name} to PARAMS at ${this.currentParamOffset}`); this.currentParamOffset = this.currentParamOffset.plus(memblock.getSize()); break; case CalldataSection.DATA: this.data.push(memblock); memblock.assignLocation(section, this.dataOffset, this.currentDataOffset); + + console.log( + `Binding ${dataType.getDataItem().name} to DATA at ${memblock + .getAbsoluteOffset() + .toString(16)}`, + ); this.currentDataOffset = this.currentDataOffset.plus(memblock.getSize()); break; @@ -300,12 +308,7 @@ namespace AbiEncoder { } public bind(calldata: Calldata, section: CalldataSection) { - if (this.memblock === undefined) { - calldata.bind(this, section); - } - _.each(this.children, (child: DataType) => { - child.bind(calldata, CalldataSection.DATA); - }); + calldata.bind(this, section); } public getId(): string { @@ -322,6 +325,10 @@ namespace AbiEncoder { return this.memblock.getAbsoluteOffset(); } + public getChildren(): DataType[] { + return this.children; + } + public abstract assignValue(value: any): void; public abstract getSignature(): string; } @@ -680,12 +687,12 @@ namespace AbiEncoder { const lengthBufUnpadded = ethUtil.toBuffer(`0x${this.length.toString(16)}`); const lengthBuf = ethUtil.setLengthLeft(lengthBufUnpadded, 32); let valueBuf = lengthBuf; - for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { + /*for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { const idxNumber = idx.toNumber(); const childValueHex = this.children[idxNumber].getHexValue(); const childValueBuf = ethUtil.toBuffer(childValueHex); valueBuf = Buffer.concat([valueBuf, childValueBuf]); - } + }*/ // Convert value buffer to hex const valueHex = ethUtil.bufferToHex(valueBuf); @@ -815,6 +822,16 @@ namespace AbiEncoder { } } + class Queue { + private store: T[] = []; + push(val: T) { + this.store.push(val); + } + pop(): T | undefined { + return this.store.shift(); + } + } + export class Method { name: string; params: DataType[]; @@ -850,11 +867,23 @@ namespace AbiEncoder { encode(args: any[]): string { const calldata = new Calldata(this.selector, this.params.length); const params = this.params; + const paramQueue = new Queue(); _.each(params, (param: DataType, i: number) => { param.assignValue(args[i]); param.bind(calldata, CalldataSection.PARAMS); + _.each(param.getChildren(), (child: DataType) => { + paramQueue.push(child); + }); }); + let param: DataType | undefined = undefined; + while ((param = paramQueue.pop()) !== undefined) { + param.bind(calldata, CalldataSection.DATA); + _.each(param.getChildren(), (child: DataType) => { + paramQueue.push(child); + }); + } + console.log(calldata); return calldata.getHexValue(); @@ -897,6 +926,27 @@ namespace AbiEncoder { } describe.only('ABI Encoder', () => { + describe.only('Just a Greg, Eh', () => { + it('Yessir', async () => { + const method = new AbiEncoder.Method(simpleAbi); + const calldata = method.encode([new BigNumber(5), 'five']); + console.log(calldata); + expect(true).to.be.true(); + }); + + it.only('Yessir', async () => { + const method = new AbiEncoder.Method(stringAbi); + const calldata = method.encode([['five', 'six', 'seven']]); + console.log(method.signature); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + }); + describe('Array', () => { it('sample', async () => { const testDataItem = { name: 'testArray', type: 'int[2]' }; @@ -939,27 +989,6 @@ describe.only('ABI Encoder', () => { }); }); - describe.only('Just a Greg, Eh', () => { - it('Yessir', async () => { - const method = new AbiEncoder.Method(simpleAbi); - const calldata = method.encode([new BigNumber(5), 'five']); - console.log(calldata); - expect(true).to.be.true(); - }); - - it.only('Yessir', async () => { - const method = new AbiEncoder.Method(stringAbi); - const calldata = method.encode([['five', 'six', 'seven']]); - console.log(method.signature); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - }); - }); - describe('Address', () => { const testAddressDataItem = { name: 'testAddress', type: 'address' }; it('Valid Address', async () => { -- cgit v1.2.3 From 02f37fa2d9788b81c9f22abb04d12078bce1719e Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 16:20:07 -0800 Subject: Works for encoding arrays --- packages/order-utils/test/abi_encoder_test.ts | 58 +++++++++++++++++---------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index cd721202e..23af071b7 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -325,6 +325,11 @@ namespace AbiEncoder { return this.memblock.getAbsoluteOffset(); } + public getSize(): BigNumber { + if (this.memblock === undefined) return new BigNumber(0); + return this.memblock.getSize(); + } + public getChildren(): DataType[] { return this.children; } @@ -654,7 +659,7 @@ namespace AbiEncoder { type: this.type, name: `${this.getDataItem().name}[${idx.toString(10)}]`, } as DataItem; - const child = DataTypeFactory.create(childDataItem); + const child = DataTypeFactory.create(childDataItem, this); this.children.push(child); } } @@ -742,12 +747,14 @@ namespace AbiEncoder { export class Pointer extends StaticDataType { destDataType: DynamicDataType; + parentDataType: DataType; - constructor(destDataType: DynamicDataType) { + constructor(destDataType: DynamicDataType, parentDataType: DataType) { const destDataItem = destDataType.getDataItem(); const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; super(dataItem); this.destDataType = destDataType; + this.parentDataType = parentDataType; this.children.push(destDataType); } @@ -761,18 +768,16 @@ namespace AbiEncoder { } public getHexValue(): string { - let offset = new BigNumber(0); - if (this.memblock !== undefined) { - switch (this.memblock.getSection()) { - case CalldataSection.PARAMS: - offset = this.destDataType.getAbsoluteOffset(); - break; - case CalldataSection.DATA: - offset = this.destDataType.getOffset(); - break; - } - } + console.log( + '*'.repeat(40), + this.destDataType.getAbsoluteOffset().toString(16), + '^'.repeat(150), + this.parentDataType.getAbsoluteOffset().toString(16), + ); + let offset = this.destDataType + .getAbsoluteOffset() + .minus(this.parentDataType.getAbsoluteOffset().plus(this.parentDataType.getSize())); const hexBase = 16; const evmWordWidth = 32; const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${offset.toString(hexBase)}`), evmWordWidth); @@ -809,12 +814,12 @@ namespace AbiEncoder { throw new Error(`Unrecognized data type: '${dataItem.type}'`); } - public static create(dataItem: DataItem): DataType { + public static create(dataItem: DataItem, parentDataType: DataType): DataType { const dataType = DataTypeFactory.mapDataItemToDataType(dataItem); if (dataType instanceof StaticDataType) { return dataType; } else if (dataType instanceof DynamicDataType) { - const pointer = new Pointer(dataType); + const pointer = new Pointer(dataType, parentDataType); return pointer; } @@ -832,19 +837,19 @@ namespace AbiEncoder { } } - export class Method { + export class Method extends DataType { name: string; params: DataType[]; - signature: string; + private signature: string; selector: string; constructor(abi: MethodAbi) { - // super(); + super({ type: 'method', name: abi.name }); this.name = abi.name; this.params = []; _.each(abi.inputs, (input: DataItem) => { - this.params.push(DataTypeFactory.create(input)); + this.params.push(DataTypeFactory.create(input, this)); }); // Compute signature @@ -864,7 +869,11 @@ namespace AbiEncoder { console.log(`--SELECTOR--\n${this.selector}\n---------\n`); } - encode(args: any[]): string { + public getSignature(): string { + return this.signature; + } + + public assignValue(args: any[]) { const calldata = new Calldata(this.selector, this.params.length); const params = this.params; const paramQueue = new Queue(); @@ -886,11 +895,16 @@ namespace AbiEncoder { console.log(calldata); - return calldata.getHexValue(); + this.assignHexValue(calldata.getHexValue()); //return calldata.getRaw(); } + public encode(args: any[]): string { + this.assignValue(args); + return this.getHexValue(); + } + /* encodeOptimized(args: any[]): string { const calldata = new Memory(); @@ -937,7 +951,7 @@ describe.only('ABI Encoder', () => { it.only('Yessir', async () => { const method = new AbiEncoder.Method(stringAbi); const calldata = method.encode([['five', 'six', 'seven']]); - console.log(method.signature); + console.log(method.getSignature()); console.log(method.selector); console.log(calldata); -- cgit v1.2.3 From af27dd6fe48d4216fad470b5e4ce7b39ef0b0888 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 17:03:09 -0800 Subject: tests for tuple --- packages/order-utils/test/abi_encoder_test.ts | 161 ++++++++++++++++++++++++-- 1 file changed, 153 insertions(+), 8 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 23af071b7..499dfe5ee 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -47,6 +47,31 @@ const stringAbi = { type: 'function', } as MethodAbi; +const tupleAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + ], + name: 'order', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + const fillOrderAbi = { constant: false, inputs: [ @@ -717,22 +742,90 @@ namespace AbiEncoder { } export class Tuple extends DynamicDataType { + private length: BigNumber; + private childMap: { [key: string]: number }; + constructor(dataItem: DataItem) { super(dataItem); expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + this.length = new BigNumber(0); + this.childMap = {}; + if (dataItem.components !== undefined) { + this.constructChildren(dataItem.components); + this.length = new BigNumber(dataItem.components.length); + } } - public assignValue(value: string) { - //const hexValue = ethUtil.bufferToHex(new Buffer(value)); - //this.assignHexValue(hexValue); + private constructChildren(dataItems: DataItem[]) { + _.each(dataItems, (dataItem: DataItem) => { + const childDataItem = { + type: dataItem.type, + name: `${this.getDataItem().name}.${dataItem.name}`, + } as DataItem; + const child = DataTypeFactory.create(childDataItem, this); + this.childMap[dataItem.name] = this.children.length; + this.children.push(child); + }); } - public getSignature(): string { - throw 1; + private assignValueFromArray(value: any[]) { + // Sanity check length + const valueLength = new BigNumber(value.length); + if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { + throw new Error( + `Expected array of length ${JSON.stringify(this.length)}, but got array of length ${JSON.stringify( + valueLength, + )}`, + ); + } + + // Assign values to children + for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { + const idxNumber = idx.toNumber(); + this.children[idxNumber].assignValue(value[idxNumber]); + } } - public encodeToCalldata(calldata: Calldata): void { - throw 2; + private assignValueFromObject(obj: object) { + let childMap = _.cloneDeep(this.childMap); + _.forOwn(obj, (value: any, key: string) => { + if (key in childMap === false) { + throw new Error(`Could not assign tuple to object: unrecognized key '${key}'`); + } + this.children[this.childMap[key]].assignValue(value); + delete childMap[key]; + }); + + if (Object.keys(childMap).length !== 0) { + throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); + } + } + + public assignValue(value: any[] | object) { + if (value instanceof Array) { + this.assignValueFromArray(value); + } else if (typeof value === 'object') { + this.assignValueFromObject(value); + } else { + throw new Error(`Unexpected type for ${value}`); + } + } + + public getHexValue(): string { + return '0x'; + } + + public getSignature(): string { + // Compute signature + let signature = `(`; + _.each(this.children, (child: DataType, i: number) => { + signature += child.getSignature(); + if (i < this.children.length - 1) { + signature += ','; + } + }); + signature += ')'; + return signature; } public static matchGrammar(type: string): boolean { @@ -948,7 +1041,7 @@ describe.only('ABI Encoder', () => { expect(true).to.be.true(); }); - it.only('Yessir', async () => { + it('Array ABI', async () => { const method = new AbiEncoder.Method(stringAbi); const calldata = method.encode([['five', 'six', 'seven']]); console.log(method.getSignature()); @@ -959,6 +1052,58 @@ describe.only('ABI Encoder', () => { '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); }); + + it('Object ABI (Array input)', async () => { + const method = new AbiEncoder.Method(tupleAbi); + const calldata = method.encode([[new BigNumber(5), 'five']]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it('Object ABI (Object input)', async () => { + const method = new AbiEncoder.Method(tupleAbi); + const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it.skip('Object ABI (Object input - Missing Key)', async () => { + const method = new AbiEncoder.Method(tupleAbi); + const calldata = method.encode([{ someUint: new BigNumber(5) }]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + + // @TODO: Figure out how to catch throw + expect(calldata).to.be.equal(expectedCalldata); + }); + + it.skip('Object ABI (Object input - Too Many Keys)', async () => { + const method = new AbiEncoder.Method(tupleAbi); + const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five', unwantedKey: 14 }]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + + // @TODO: Figure out how to catch throw + expect(calldata).to.be.equal(expectedCalldata); + }); }); describe('Array', () => { -- cgit v1.2.3 From 993c5f4b4a084a210fa158e643027e63fb9443f8 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 7 Nov 2018 20:38:55 -0800 Subject: Another passing simple Abi 2 --- packages/order-utils/test/abi_encoder_test.ts | 342 +++++++++++++++++++++++++- 1 file changed, 335 insertions(+), 7 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 499dfe5ee..95b6630ee 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -72,6 +72,165 @@ const tupleAbi = { type: 'function', } as MethodAbi; +const crazyAbi = { + constant: false, + inputs: [ + /*{ + name: 'someUInt256', + type: 'uint256', + }, + { + name: 'someInt256', + type: 'int256', + }, + { + name: 'someInt32', + type: 'int32', + },*/ + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someBytes32', + type: 'bytes32', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someString', + type: 'string', + }, + /*{ + name: 'someAddress', + type: 'address', + }, + { + name: 'someBool', + type: 'bool', + },*/ + + /* + + { + name: 'someStaticArray', + type: 'uint8[3]', + }, + { + name: 'someStaticArrayWithDynamicMembers', + type: 'string[2]', + }, + { + name: 'someDynamicArrayWithDynamicMembers', + type: 'bytes[]', + }, + { + name: 'some2DArray', + type: 'string[][]', + }, + { + name: 'someTuple', + type: 'tuple', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'someStr', + type: 'string', + }, + ], + }, + { + name: 'someTupleWithDynamicTypes', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + //{ + // name: 'someStrArray', + // type: 'string[]', + /// }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + } /*, + { + name: 'someArrayOfTuplesWithDynamicTypes', + type: 'tuple[]', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + { + name: 'someStrArray', + type: 'string[]', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + },*/ + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +const simpleAbi2 = { + constant: false, + inputs: [ + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someBytes32', + type: 'bytes32', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + const fillOrderAbi = { constant: false, inputs: [ @@ -734,10 +893,24 @@ namespace AbiEncoder { } public getSignature(): string { + let type = this.type; + if (this.type === 'tuple') { + let tupleDataItem = { + type: 'tuple', + name: 'N/A', + } as DataItem; + const tupleComponents = this.getDataItem().components; + if (tupleComponents !== undefined) { + tupleDataItem.components = tupleComponents; + } + const tuple = new Tuple(tupleDataItem); + type = tuple.getSignature(); + } + if (this.length.equals(SolArray.UNDEFINED_LENGTH)) { - return `${this.type}[]`; + return `${type}[]`; } - return `${this.type}[${this.length}]`; + return `${type}[${this.length}]`; } } @@ -753,6 +926,8 @@ namespace AbiEncoder { if (dataItem.components !== undefined) { this.constructChildren(dataItem.components); this.length = new BigNumber(dataItem.components.length); + } else { + throw new Error('Components undefined'); } } @@ -773,9 +948,9 @@ namespace AbiEncoder { const valueLength = new BigNumber(value.length); if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { throw new Error( - `Expected array of length ${JSON.stringify(this.length)}, but got array of length ${JSON.stringify( - valueLength, - )}`, + `Expected array of ${JSON.stringify( + this.length, + )} elements, but got array of length ${JSON.stringify(valueLength)}`, ); } @@ -971,7 +1146,12 @@ namespace AbiEncoder { const params = this.params; const paramQueue = new Queue(); _.each(params, (param: DataType, i: number) => { - param.assignValue(args[i]); + try { + param.assignValue(args[i]); + } catch (e) { + console.log('Failed to assign to ', param.getDataItem().name); + throw e; + } param.bind(calldata, CalldataSection.PARAMS); _.each(param.getChildren(), (child: DataType) => { paramQueue.push(child); @@ -1034,6 +1214,154 @@ namespace AbiEncoder { describe.only('ABI Encoder', () => { describe.only('Just a Greg, Eh', () => { + it.skip('Crazy ABI', async () => { + const method = new AbiEncoder.Method(crazyAbi); + console.log(method.getSignature()); + + /* + (uint256 b,int256 c,int32 d, + bytes1 e,bytes32 f, + bytes g,string h,address i,bool j,uint8[3] k,string[2] l,bytes[] m, string[][] n, O o,P p, P[]) + + struct P { + uint256 a; + string b; + string[] c; + bytes d; + address e; + }*/ + + const args = [ + /*new BigNumber(437829473), // b + new BigNumber(-437829473), // c + new BigNumber(-14), // d*/ + '0xaf', // e (bytes1) + '0x0001020304050607080911121314151617181920212223242526272829303132', // f (bytes32) + '0x616161616161616161616161616161616161616161616161616161616161616161616161616161611114f324567838475647382938475677448899338457668899002020202020', // g + 'My first name is Greg and my last name is Hysen, what do ya know!', // h + ]; + const a2 = [ + /*'0xe41d2489571d322189246dafa5ebde1f4699f498', // i + true, // j*/ + [new BigNumber(127), new BigNumber(14), new BigNumber(54)], // k + [ + 'the little piping piper piped a piping pipper papper', + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ], // l + [ + '0x38745637834987324827439287423897238947239847', + '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', + '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', + ], // m + [ + [ + 'some string', + 'some another string', + 'there are just too many stringsup in', + 'here', + 'yall ghonna make me lose my mind', + ], + [ + 'the little piping piper piped a piping pipper papper', + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ], + [], + ], // n + [ + new BigNumber(4037824789), + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ], // o + [ + new BigNumber('239048320948320948230', 10), + 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', + /*[ + [ + '23432423342', + 'skdjfhdsjkfdhsfkjsdhfjkdshfdsjkfhsdjkfhsdjkfhdsjkfhdsjfhsdfjdshjkfsdhf', + 'sdfsdfdfdffsdf', + ], + [], + [], + ['23ehsdjkfhsiufhwfuefhesfhauhesufheuifhsefushfsufehfeuif'], + ],*/ + '0xf74848484848484848484848484848484848483847576879809433994458585848932091', + '0xe41d2489571d322189246dafa5ebde1f4699f498', + ], // p + /*[ + [ + new BigNumber('23904848320948230', 10), + 'akdhjasshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', + [ + [ + '234324342', + 'skdjfhdsjkfdhsfkjsjkfhsdjkfhsdjkfhdsjkfhdsjfhsdfjdshjkfsdhf', + 'sdffdfdffsdf', + ], + [], + [], + ['23ehsdjkfhsiufhwfuefsufheuifhsefushfsufehfeuif'], + ], + '0xf7484848484848484848484848484876879809433994458585848932091', + '0xe41d2489571d322189246dafa6ebde1f4699f498', + ], + [ + new BigNumber('23904832094832030', 10), + 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdkdhsajkdhsadjk', + [ + [ + '2343342', + 'skdjfhdsjkfdhsfkjsdhfjkdshfdsjkfhsdjkfhsdjkfhdssjfhsdfjdshjkfsdhf', + 'sdfsdfdfdffsf', + ], + [], + [], + ['jkfhsiufhwfuefhesfhauhesufhefeuif'], + ], + '0xf7484848484848484848484848484848484848384757687980943091', + '0xe41d2489571d322189246dafa5ebde1f469af498', + ], + [], + [], + ],*/ + ]; + + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + + /*const expectedCalldata = + '0x89771c30af00000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000009e00000000000000000000000000000000000000000000000000000000000000ae00000000000000000000000000000000000000000000000000000000000000047616161616161616161616161616161616161616161616161616161616161616161616161616161611114f3245678384756473829384756774488993384576688990020202020200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414d79206669727374206e616d65206973204772656720616e64206d79206c617374206e616d6520697320487973656e2c207768617420646f207961206b6e6f772100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; + */ + + //expect(calldata).to.be.equal(expectedCalldata); + + /*const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata);*/ + }); + + it.only('Simple ABI 2', async () => { + const method = new AbiEncoder.Method(crazyAbi); + + const args = [ + '0xaf', // e (bytes1) + '0x0001020304050607080911121314151617181920212223242526272829303132', // f (bytes32) + '0x616161616161616161616161616161616161616161616161616161616161616161616161616161611114f324567838475647382938475677448899338457668899002020202020', // g + 'My first name is Greg and my last name is Hysen, what do ya know!', // h + ]; + + const calldata = method.encode(args); + const expectedCalldata = + '0x7ac2bd96af000000000000000000000000000000000000000000000000000000000000000001020304050607080911121314151617181920212223242526272829303132000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000047616161616161616161616161616161616161616161616161616161616161616161616161616161611114f3245678384756473829384756774488993384576688990020202020200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414d79206669727374206e616d65206973204772656720616e64206d79206c617374206e616d6520697320487973656e2c207768617420646f207961206b6e6f772100000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + it('Yessir', async () => { const method = new AbiEncoder.Method(simpleAbi); const calldata = method.encode([new BigNumber(5), 'five']); @@ -1291,7 +1619,7 @@ describe.only('ABI Encoder', () => { }); }); - describe.only('Bytes (Dynamic)', () => { + describe('Bytes (Dynamic)', () => { const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; it('Less than 32 bytes', async () => { const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); -- cgit v1.2.3 From 1600820deab0636d37a9a50025576a75ae8feeec Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 8 Nov 2018 06:37:25 -0800 Subject: static array abi tests --- packages/order-utils/test/abi_encoder_test.ts | 131 ++++++++++++++++++-------- 1 file changed, 90 insertions(+), 41 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 95b6630ee..b27dc4793 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -72,6 +72,21 @@ const tupleAbi = { type: 'function', } as MethodAbi; +const staticArrayAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[3]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + const crazyAbi = { constant: false, inputs: [ @@ -86,7 +101,7 @@ const crazyAbi = { { name: 'someInt32', type: 'int32', - },*/ + }, { name: 'someByte', type: 'byte', @@ -102,7 +117,7 @@ const crazyAbi = { { name: 'someString', type: 'string', - }, + },*/ /*{ name: 'someAddress', type: 'address', @@ -112,12 +127,10 @@ const crazyAbi = { type: 'bool', },*/ - /* - { name: 'someStaticArray', type: 'uint8[3]', - }, + } /* { name: 'someStaticArrayWithDynamicMembers', type: 'string[2]', @@ -195,7 +208,7 @@ const crazyAbi = { type: 'address', }, ], - },*/ + },*/, ], name: 'simpleFunction', outputs: [], @@ -520,6 +533,7 @@ namespace AbiEncoder { public abstract assignValue(value: any): void; public abstract getSignature(): string; + public abstract isStatic(): boolean; } export abstract class StaticDataType extends DataType { @@ -550,6 +564,10 @@ namespace AbiEncoder { return `address`; } + public isStatic(): boolean { + return true; + } + public static matchGrammar(type: string): boolean { return type === 'address'; } @@ -572,6 +590,10 @@ namespace AbiEncoder { return 'bool'; } + public isStatic(): boolean { + return true; + } + public static matchGrammar(type: string): boolean { return type === 'bool'; } @@ -634,6 +656,10 @@ namespace AbiEncoder { this.assignHexValue(encodedValue); } + public isStatic(): boolean { + return true; + } + public abstract getMaxValue(): BigNumber; public abstract getMinValue(): BigNumber; } @@ -715,7 +741,7 @@ namespace AbiEncoder { if (valueBuf.byteLength > this.width) { throw new Error( `Tried to assign ${value} (${ - valueBuf.byteLength + valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); } else if (value.length % 2 !== 0) { @@ -734,6 +760,10 @@ namespace AbiEncoder { return `bytes${this.width}`; } + public isStatic(): boolean { + return true; + } + public static matchGrammar(type: string): boolean { return this.matcher.test(type); } @@ -771,6 +801,10 @@ namespace AbiEncoder { return 'bytes'; } + public isStatic(): boolean { + return false; + } + public static matchGrammar(type: string): boolean { return type === 'bytes'; } @@ -797,6 +831,10 @@ namespace AbiEncoder { return 'string'; } + public isStatic(): boolean { + return false; + } + public static matchGrammar(type: string): boolean { return type === 'string'; } @@ -807,6 +845,7 @@ namespace AbiEncoder { static UNDEFINED_LENGTH = new BigNumber(-1); length: BigNumber = SolArray.UNDEFINED_LENGTH; type: string = '[undefined]'; + isLengthDefined: boolean; constructor(dataItem: DataItem) { super(dataItem); @@ -825,10 +864,12 @@ namespace AbiEncoder { if (matches[2] === '') { this.type = matches[1]; this.length = SolArray.UNDEFINED_LENGTH; + this.isLengthDefined = false; return; } // Parse out array type/length and construct children + this.isLengthDefined = true; this.type = matches[1]; this.length = new BigNumber(matches[2], 10); if (this.length.lessThan(0)) { @@ -873,6 +914,10 @@ namespace AbiEncoder { } public getHexValue(): string { + if (this.isLengthDefined) { + return '0x'; + } + const lengthBufUnpadded = ethUtil.toBuffer(`0x${this.length.toString(16)}`); const lengthBuf = ethUtil.setLengthLeft(lengthBufUnpadded, 32); let valueBuf = lengthBuf; @@ -888,6 +933,10 @@ namespace AbiEncoder { return valueHex; } + public isStatic(): boolean { + return this.isLengthDefined; + } + public static matchGrammar(type: string): boolean { return this.matcher.test(type); } @@ -1003,6 +1052,10 @@ namespace AbiEncoder { return signature; } + public isStatic(): boolean { + return false; // @TODO: True in every case or only when dynamic data? + } + public static matchGrammar(type: string): boolean { return type === 'tuple'; } @@ -1057,6 +1110,10 @@ namespace AbiEncoder { return this.destDataType.getSignature(); } + public isStatic(): boolean { + return true; + } + public encodeToCalldata(calldata: Calldata): void { throw 2; } @@ -1084,9 +1141,9 @@ namespace AbiEncoder { public static create(dataItem: DataItem, parentDataType: DataType): DataType { const dataType = DataTypeFactory.mapDataItemToDataType(dataItem); - if (dataType instanceof StaticDataType) { + if (dataType.isStatic()) { return dataType; - } else if (dataType instanceof DynamicDataType) { + } else { const pointer = new Pointer(dataType, parentDataType); return pointer; } @@ -1178,6 +1235,10 @@ namespace AbiEncoder { return this.getHexValue(); } + public isStatic(): boolean { + return true; + } + /* encodeOptimized(args: any[]): string { const calldata = new Memory(); @@ -1214,36 +1275,13 @@ namespace AbiEncoder { describe.only('ABI Encoder', () => { describe.only('Just a Greg, Eh', () => { - it.skip('Crazy ABI', async () => { - const method = new AbiEncoder.Method(crazyAbi); + it.only('Crazy ABI', async () => { + const method = new AbiEncoder.Method(staticArrayAbi); console.log(method.getSignature()); - /* - (uint256 b,int256 c,int32 d, - bytes1 e,bytes32 f, - bytes g,string h,address i,bool j,uint8[3] k,string[2] l,bytes[] m, string[][] n, O o,P p, P[]) + const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; - struct P { - uint256 a; - string b; - string[] c; - bytes d; - address e; - }*/ - - const args = [ - /*new BigNumber(437829473), // b - new BigNumber(-437829473), // c - new BigNumber(-14), // d*/ - '0xaf', // e (bytes1) - '0x0001020304050607080911121314151617181920212223242526272829303132', // f (bytes32) - '0x616161616161616161616161616161616161616161616161616161616161616161616161616161611114f324567838475647382938475677448899338457668899002020202020', // g - 'My first name is Greg and my last name is Hysen, what do ya know!', // h - ]; const a2 = [ - /*'0xe41d2489571d322189246dafa5ebde1f4699f498', // i - true, // j*/ - [new BigNumber(127), new BigNumber(14), new BigNumber(54)], // k [ 'the little piping piper piped a piping pipper papper', 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', @@ -1330,11 +1368,10 @@ describe.only('ABI Encoder', () => { console.log('*'.repeat(40)); console.log(JSON.stringify(args)); - /*const expectedCalldata = - '0x89771c30af00000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000009e00000000000000000000000000000000000000000000000000000000000000ae00000000000000000000000000000000000000000000000000000000000000047616161616161616161616161616161616161616161616161616161616161616161616161616161611114f3245678384756473829384756774488993384576688990020202020200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414d79206669727374206e616d65206973204772656720616e64206d79206c617374206e616d6520697320487973656e2c207768617420646f207961206b6e6f772100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; - */ + const expectedCalldata = + '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; - //expect(calldata).to.be.equal(expectedCalldata); + expect(calldata).to.be.equal(expectedCalldata); /*const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); console.log(method.getSignature()); @@ -1346,8 +1383,20 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata);*/ }); - it.only('Simple ABI 2', async () => { - const method = new AbiEncoder.Method(crazyAbi); + it.only('Static Array ABI', async () => { + const method = new AbiEncoder.Method(staticArrayAbi); + const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + const expectedCalldata = + '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it('Simple ABI 2', async () => { + const method = new AbiEncoder.Method(simpleAbi2); const args = [ '0xaf', // e (bytes1) -- cgit v1.2.3 From a13099bde3aa6a47516127ae7c7d9f78488a3911 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 8 Nov 2018 07:27:14 -0800 Subject: hack for static arrays --- packages/order-utils/test/abi_encoder_test.ts | 134 +++++++++++++++----------- 1 file changed, 78 insertions(+), 56 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index b27dc4793..b8f1d39ad 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -130,7 +130,7 @@ const crazyAbi = { { name: 'someStaticArray', type: 'uint8[3]', - } /* + }, { name: 'someStaticArrayWithDynamicMembers', type: 'string[2]', @@ -170,9 +170,9 @@ const crazyAbi = { type: 'string', }, //{ - // name: 'someStrArray', - // type: 'string[]', - /// }, + // name: 'someStrArray', + // type: 'string[]', + /// }, { name: 'someBytes', type: 'bytes', @@ -889,6 +889,15 @@ namespace AbiEncoder { } } + // @TODO: HACKY -- shouldn't really have children for a + public getChildren(): DataType[] { + if (this.isStatic()) { + return []; + } else { + return this.children; + } + } + public assignValue(value: any[]) { // Sanity check length const valueLength = new BigNumber(value.length); @@ -913,26 +922,40 @@ namespace AbiEncoder { } } - public getHexValue(): string { - if (this.isLengthDefined) { - return '0x'; - } - + private getHexValueDynamicArray(): string { const lengthBufUnpadded = ethUtil.toBuffer(`0x${this.length.toString(16)}`); const lengthBuf = ethUtil.setLengthLeft(lengthBufUnpadded, 32); let valueBuf = lengthBuf; - /*for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { + + const valueHex = ethUtil.bufferToHex(valueBuf); + return valueHex; + } + + private getHexValueStaticArray(): string { + let valueBuf = new Buffer(""); + + for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { const idxNumber = idx.toNumber(); const childValueHex = this.children[idxNumber].getHexValue(); const childValueBuf = ethUtil.toBuffer(childValueHex); valueBuf = Buffer.concat([valueBuf, childValueBuf]); - }*/ + + console.log(JSON.stringify(idx)); + } // Convert value buffer to hex const valueHex = ethUtil.bufferToHex(valueBuf); return valueHex; } + public getHexValue(): string { + if (this.isLengthDefined) { + return this.getHexValueStaticArray(); + } else { + return this.getHexValueDynamicArray(); + } + } + public isStatic(): boolean { return this.isLengthDefined; } @@ -1275,56 +1298,54 @@ namespace AbiEncoder { describe.only('ABI Encoder', () => { describe.only('Just a Greg, Eh', () => { - it.only('Crazy ABI', async () => { - const method = new AbiEncoder.Method(staticArrayAbi); + it('Crazy ABI', async () => { + const method = new AbiEncoder.Method(crazyAbi); console.log(method.getSignature()); - const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; - - const a2 = [ + const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)], + [ + 'the little piping piper piped a piping pipper papper', + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ], // l + [ + '0x38745637834987324827439287423897238947239847', + '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', + '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', + ], // m + [ + [ + 'some string', + 'some another string', + 'there are just too many stringsup in', + 'here', + 'yall ghonna make me lose my mind', + ], [ 'the little piping piper piped a piping pipper papper', 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ], // l - [ - '0x38745637834987324827439287423897238947239847', - '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', - '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', - ], // m - [ - [ - 'some string', - 'some another string', - 'there are just too many stringsup in', - 'here', - 'yall ghonna make me lose my mind', - ], + ], + [], + ], // n + [ + new BigNumber(4037824789), + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ], // o + [ + new BigNumber('239048320948320948230', 10), + 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', + /*[ [ - 'the little piping piper piped a piping pipper papper', - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + '23432423342', + 'skdjfhdsjkfdhsfkjsdhfjkdshfdsjkfhsdjkfhsdjkfhdsjkfhdsjfhsdfjdshjkfsdhf', + 'sdfsdfdfdffsdf', ], [], - ], // n - [ - new BigNumber(4037824789), - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ], // o - [ - new BigNumber('239048320948320948230', 10), - 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', - /*[ - [ - '23432423342', - 'skdjfhdsjkfdhsfkjsdhfjkdshfdsjkfhsdjkfhsdjkfhdsjkfhdsjfhsdfjdshjkfsdhf', - 'sdfsdfdfdffsdf', - ], - [], - [], - ['23ehsdjkfhsiufhwfuefhesfhauhesufheuifhsefushfsufehfeuif'], - ],*/ - '0xf74848484848484848484848484848484848483847576879809433994458585848932091', - '0xe41d2489571d322189246dafa5ebde1f4699f498', - ], // p + [], + ['23ehsdjkfhsiufhwfuefhesfhauhesufheuifhsefushfsufehfeuif'], + ],*/ + '0xf74848484848484848484848484848484848483847576879809433994458585848932091', + '0xe41d2489571d322189246dafa5ebde1f4699f498', + ], // p /*[ [ new BigNumber('23904848320948230', 10), @@ -1367,11 +1388,12 @@ describe.only('ABI Encoder', () => { console.log(calldata); console.log('*'.repeat(40)); console.log(JSON.stringify(args)); + console.log(method.getSignature()); + /* const expectedCalldata = - '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; - - expect(calldata).to.be.equal(expectedCalldata); + '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata);*/ /*const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); console.log(method.getSignature()); -- cgit v1.2.3 From 637ab1076a4071505ea3d4797767826070a65e16 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 8 Nov 2018 10:31:30 -0800 Subject: ABI Encoding for all combinations of arrays --- packages/order-utils/test/abi_encoder.ts | 1098 ++++++++++++++++++++ packages/order-utils/test/abi_encoder_test.ts | 1336 +------------------------ packages/order-utils/test/abi_samples.ts | 358 +++++++ 3 files changed, 1501 insertions(+), 1291 deletions(-) create mode 100644 packages/order-utils/test/abi_encoder.ts create mode 100644 packages/order-utils/test/abi_samples.ts diff --git a/packages/order-utils/test/abi_encoder.ts b/packages/order-utils/test/abi_encoder.ts new file mode 100644 index 000000000..55b63b701 --- /dev/null +++ b/packages/order-utils/test/abi_encoder.ts @@ -0,0 +1,1098 @@ + +import * as chai from 'chai'; +import 'mocha'; +import ethUtil = require('ethereumjs-util'); + +var _ = require('lodash'); + +import { chaiSetup } from './utils/chai_setup'; + +import { MethodAbi, DataItem } from 'ethereum-types'; + +import { BigNumber } from '@0x/utils'; +import { bigNumberify } from 'ethers/utils'; + +chaiSetup.configure(); +const expect = chai.expect; + + +class Word { + private value: string; + + constructor(value?: string) { + if (value === undefined) { + this.value = ''; + } else { + this.value = value; + } + } + + public set(value: string) { + if (value.length !== 64) { + throw `Tried to create word that is not 32 bytes: ${value}`; + } + + this.value = value; + } + + public get(): string { + return this.value; + } + + public getAsHex(): string { + return `0x${this.value}`; + } +} + +export enum CalldataSection { + NONE, + PARAMS, + DATA, +} + +class Memblock { + private dataType: DataType; + private location: { calldataSection: CalldataSection; sectionOffset: BigNumber; offset: BigNumber }; + + constructor(dataType: DataType) { + this.dataType = dataType; + this.location = { + calldataSection: CalldataSection.NONE, + sectionOffset: new BigNumber(0), + offset: new BigNumber(0), + }; + } + + public getSize(): BigNumber { + return new BigNumber(ethUtil.toBuffer(this.dataType.getHexValue()).byteLength); + } + + public assignLocation(calldataSection: CalldataSection, sectionOffset: BigNumber, offset: BigNumber) { + this.location.calldataSection = calldataSection; + this.location.sectionOffset = sectionOffset; + this.location.offset = offset; + } + + public get(): string { + return ethUtil.stripHexPrefix(this.dataType.getHexValue()); + } + + public getOffset(): BigNumber { + return this.location.offset; + } + + public getAbsoluteOffset(): BigNumber { + return this.location.sectionOffset.plus(this.location.offset); + } + + public getSection(): CalldataSection { + return this.location.calldataSection; + } +} + +interface BindList { + [key: string]: Memblock; +} + +export class Calldata { + private selector: string; + private params: Memblock[]; + private data: Memblock[]; + private dataOffset: BigNumber; + private currentDataOffset: BigNumber; + private currentParamOffset: BigNumber; + private bindList: BindList; + + constructor(selector: string, nParams: number) { + this.selector = selector; + this.params = []; + this.data = []; + const evmWordSize = 32; + this.dataOffset = new BigNumber(nParams).times(evmWordSize); + this.currentDataOffset = new BigNumber(0); + this.currentParamOffset = new BigNumber(0); + this.bindList = {}; + } + + public bind(dataType: DataType, section: CalldataSection) { + if (dataType.getId() in this.bindList) { + throw `Rebind on ${dataType.getId()}`; + } + const memblock = new Memblock(dataType); + switch (section) { + case CalldataSection.PARAMS: + this.params.push(memblock); + memblock.assignLocation(section, new BigNumber(0), this.currentParamOffset); + + console.log(`Binding ${dataType.getDataItem().name} to PARAMS at ${this.currentParamOffset}`); + this.currentParamOffset = this.currentParamOffset.plus(memblock.getSize()); + break; + + case CalldataSection.DATA: + this.data.push(memblock); + memblock.assignLocation(section, this.dataOffset, this.currentDataOffset); + + console.log( + `Binding ${dataType.getDataItem().name} to DATA at ${memblock + .getAbsoluteOffset() + .toString(16)}`, + ); + this.currentDataOffset = this.currentDataOffset.plus(memblock.getSize()); + break; + + default: + throw `Unrecognized calldata section: ${section}`; + } + + this.bindList[dataType.getId()] = memblock; + dataType.rbind(memblock); + } + + public getHexValue(): string { + let hexValue = `${this.selector}`; + _.each(this.params, (memblock: Memblock) => { + hexValue += memblock.get(); + }); + _.each(this.data, (memblock: Memblock) => { + hexValue += memblock.get(); + }); + + return hexValue; + } +} + +export abstract class DataType { + private dataItem: DataItem; + private hexValue: string; + protected memblock: Memblock | undefined; + protected children: DataType[]; + + constructor(dataItem: DataItem) { + this.dataItem = dataItem; + this.hexValue = '0x'; + this.memblock = undefined; + this.children = []; + } + + protected assignHexValue(hexValue: string) { + this.hexValue = hexValue; + } + + public getHexValue(): string { + return this.hexValue; + } + + public getDataItem(): DataItem { + return this.dataItem; + } + + public rbind(memblock: Memblock) { + this.memblock = memblock; + } + + public bind(calldata: Calldata, section: CalldataSection) { + calldata.bind(this, section); + _.each(this.getChildren(), (child: DataType) => { + child.bind(calldata, CalldataSection.DATA); + }); + } + + public getId(): string { + return this.dataItem.name; + } + + public getOffset(): BigNumber { + if (this.memblock === undefined) return new BigNumber(0); + return this.memblock.getOffset(); + } + + public getAbsoluteOffset(): BigNumber { + if (this.memblock === undefined) return new BigNumber(0); + return this.memblock.getAbsoluteOffset(); + } + /* + public getSize(): BigNumber { + if (this.memblock === undefined) return new BigNumber(0); + return this.memblock.getSize(); + } + */ + + public getChildren(): DataType[] { + return this.children; + } + + public getSize(): BigNumber { + return this.getHeaderSize().plus(this.getBodySize()); + } + + public abstract assignValue(value: any): void; + public abstract getSignature(): string; + public abstract isStatic(): boolean; + public abstract getHeaderSize(): BigNumber; + public abstract getBodySize(): BigNumber; +} + +export abstract class StaticDataType extends DataType { + constructor(dataItem: DataItem) { + super(dataItem); + } +} + +export abstract class DynamicDataType extends DataType { + constructor(dataItem: DataItem) { + super(dataItem); + } +} + +export class Address extends StaticDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(Address.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + const evmWordWidth = 32; + const hexValue = ethUtil.bufferToHex(ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth)); + this.assignHexValue(hexValue); + } + + public getSignature(): string { + return `address`; + } + + public isStatic(): boolean { + return true; + } + + public getHeaderSize(): BigNumber { + return new BigNumber(0); + } + + public getBodySize(): BigNumber { + return new BigNumber(32); + } + + public static matchGrammar(type: string): boolean { + return type === 'address'; + } +} + +export class Bool extends StaticDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(Bool.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: boolean) { + const evmWordWidth = 32; + const encodedValue = value === true ? '0x1' : '0x0'; + const hexValue = ethUtil.bufferToHex(ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth)); + this.assignHexValue(hexValue); + } + + public getSignature(): string { + return 'bool'; + } + + public isStatic(): boolean { + return true; + } + + public getHeaderSize(): BigNumber { + return new BigNumber(0); + } + + public getBodySize(): BigNumber { + return new BigNumber(32); + } + + public static matchGrammar(type: string): boolean { + return type === 'bool'; + } +} + +abstract class Number extends StaticDataType { + static MAX_WIDTH: number = 256; + static DEFAULT_WIDTH: number = Number.MAX_WIDTH; + width: number = Number.DEFAULT_WIDTH; + + constructor(dataItem: DataItem, matcher: RegExp) { + super(dataItem); + const matches = matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 2 && matches[1] !== undefined) { + this.width = parseInt(matches[1]); + } else { + this.width = 256; + } + } + + public assignValue(value: BigNumber) { + if (value.greaterThan(this.getMaxValue())) { + throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; + } else if (value.lessThan(this.getMinValue())) { + throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; + } + + const hexBase = 16; + const evmWordWidth = 32; + let valueBuf: Buffer; + if (value.greaterThanOrEqualTo(0)) { + valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); + } else { + // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. + // Step 1/3: Convert value to positive binary string + const binBase = 2; + const valueBin = value.times(-1).toString(binBase); + + // Step 2/3: Invert binary value + const bitsInEvmWord = 256; + let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, binBase); + + // Step 3/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const negativeValue = invertedValue.plus(1); + + // Convert the negated value to a hex string + valueBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), + evmWordWidth, + ); + } + + const encodedValue = ethUtil.bufferToHex(valueBuf); + this.assignHexValue(encodedValue); + } + + public isStatic(): boolean { + return true; + } + + public getHeaderSize(): BigNumber { + return new BigNumber(0); + } + + public getBodySize(): BigNumber { + return new BigNumber(32); + } + + public abstract getMaxValue(): BigNumber; + public abstract getMinValue(): BigNumber; +} + +export class Int extends Number { + static matcher = RegExp( + '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + constructor(dataItem: DataItem) { + super(dataItem, Int.matcher); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).times(-1); + } + + public getSignature(): string { + return `int${this.width}`; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class UInt extends Number { + static matcher = RegExp( + '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + constructor(dataItem: DataItem) { + super(dataItem, UInt.matcher); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(0); + } + + public getSignature(): string { + return `uint${this.width}`; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Byte extends StaticDataType { + static matcher = RegExp( + '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', + ); + + static DEFAULT_WIDTH = 1; + width: number = Byte.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = Byte.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + if (matches !== null && matches.length === 3 && matches[2] !== undefined) { + this.width = parseInt(matches[2]); + } else { + this.width = Byte.DEFAULT_WIDTH; + } + } + + public assignValue(value: string | Buffer) { + // Convert value into a buffer and do bounds checking + const valueBuf = ethUtil.toBuffer(value); + if (valueBuf.byteLength > this.width) { + throw new Error( + `Tried to assign ${value} (${ + valueBuf.byteLength + } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, + ); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + // Store value as hex + const evmWordWidth = 32; + const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); + const hexValue = ethUtil.bufferToHex(paddedValue); + this.assignHexValue(hexValue); + } + + public getSignature(): string { + // Note that `byte` reduces to `bytes1` + return `bytes${this.width}`; + } + + public isStatic(): boolean { + return true; + } + + public getHeaderSize(): BigNumber { + return new BigNumber(0); + } + + public getBodySize(): BigNumber { + return new BigNumber(32); + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Bytes extends DynamicDataType { + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = Bytes.UNDEFINED_LENGTH; + + constructor(dataItem: DataItem) { + super(dataItem); + expect(Bytes.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string | Buffer) { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); + } + const valueBuf = ethUtil.toBuffer(value); + if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + const wordsForValue = Math.ceil(valueBuf.byteLength / 32); + const paddedBytesForValue = wordsForValue * 32; + const paddedValueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); + const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); + const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); + const encodedValue = ethUtil.bufferToHex(encodedValueBuf); + + this.assignHexValue(encodedValue); + } + + public getSignature(): string { + return 'bytes'; + } + + public isStatic(): boolean { + return false; + } + + public getHeaderSize(): BigNumber { + return new BigNumber(0); + } + + public getBodySize(): BigNumber { + return new BigNumber(this.getHexValue().length); + } + + public static matchGrammar(type: string): boolean { + return type === 'bytes'; + } +} + +export class SolString extends DynamicDataType { + constructor(dataItem: DataItem) { + super(dataItem); + expect(SolString.matchGrammar(dataItem.type)).to.be.true(); + } + + public assignValue(value: string) { + const wordsForValue = Math.ceil(value.length / 32); + const paddedBytesForValue = wordsForValue * 32; + const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); + const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); + const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + const encodedValue = ethUtil.bufferToHex(encodedValueBuf); + + this.assignHexValue(encodedValue); + } + + public getSignature(): string { + return 'string'; + } + + public isStatic(): boolean { + return false; + } + + public getHeaderSize(): BigNumber { + return new BigNumber(0); + } + + public getBodySize(): BigNumber { + return new BigNumber(this.getHexValue().length); + } + + public static matchGrammar(type: string): boolean { + return type === 'string'; + } +} + +export class SolArray extends DynamicDataType { + static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = SolArray.UNDEFINED_LENGTH; + type: string = '[undefined]'; + isLengthDefined: boolean; + isStaticArray: boolean; // An array is dynamic if it's lenghth is undefined or if its children are dynamic. + private elements: DataType[]; + + /* + --- Layout 1: Fixed Length Array with Static Types --- + Elem1, Elem2, ..., ElemN + + --- Layout 2: Fixed Length Array with Dynamic Types --- + PtrToArray, ..., Elem1, Elem2, ..., ElemN + + --- Layout 3: Dynamic Length Array with Static Types --- + PtrToArray, ..., ArrayLength, Elem1, Elem2, ..., ElemN + + --- Layout 4: Dynamic Length Array with Dynamic Types --- + PtrToArray, ..., ArrayLength, PtrToElem1, PtrToElem2, ..., PtrToElemN, ..., Elem1, Elem2, ..., ElemN + */ + + constructor(dataItem: DataItem) { + super(dataItem); + const matches = SolArray.matcher.exec(dataItem.type); + expect(matches).to.be.not.null(); + console.log(JSON.stringify(matches)); + if (matches === null || matches.length !== 3) { + throw new Error(`Could not parse array: ${dataItem.type}`); + } else if (matches[1] === undefined) { + throw new Error(`Could not parse array type: ${dataItem.type}`); + } else if (matches[2] === undefined) { + throw new Error(`Could not parse array length: ${dataItem.type}`); + } + + this.elements = []; + + // Check if length is undefined + if (matches[2] === '') { + this.type = matches[1]; + this.length = SolArray.UNDEFINED_LENGTH; + this.isLengthDefined = false; + this.isStaticArray = false; + return; + } + + // Parse out array type/length and construct children + this.isLengthDefined = true; + this.type = matches[1]; + this.length = new BigNumber(matches[2], 10); + if (this.length.lessThan(1)) { + throw new Error(`Bad array length: ${JSON.stringify(this.length)}`); + } + this.constructChildren(); + + // Check if we're static or not + this.isStaticArray = !(this.elements[0] instanceof Pointer); //this.elements[0].isStatic(); + //throw new Error(`Am I static? ${this.isStaticArray}`); + } + + private constructChildren() { + for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { + const childDataItem = { + type: this.type, + name: `${this.getDataItem().name}[${idx.toString(10)}]`, + } as DataItem; + const child = DataTypeFactory.create(childDataItem, this); + this.elements.push(child); + if (child instanceof Pointer) { + const pointsTo = child.getChildren()[0]; + console.log(JSON.stringify(pointsTo)); + this.children.push(pointsTo); // DataType pointing to + } + } + } + + // @TODO: HACKY -- shouldn't really have children for a + /* + public getChildren(): DataType[] { + if (this.isStatic()) { + return []; + } else { + return this.children; + } + }*/ + + public assignValue(value: any[]) { + // Sanity check length + const valueLength = new BigNumber(value.length); + if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { + throw new Error( + `Expected array of length ${JSON.stringify(this.length)}, but got array of length ${JSON.stringify( + valueLength, + )}`, + ); + } + + // Assign length if not already set + if (this.length === SolArray.UNDEFINED_LENGTH) { + this.length = valueLength; + this.constructChildren(); + } + + // Assign values to children + for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { + const idxNumber = idx.toNumber(); + this.elements[idxNumber].assignValue(value[idxNumber]); + } + } + + public getHexValue(): string { + let valueBuf = new Buffer(""); + + if (this.isLengthDefined === false) { + // Must include the array length + const lengthBufUnpadded = ethUtil.toBuffer(`0x${this.length.toString(16)}`); + const lengthBuf = ethUtil.setLengthLeft(lengthBufUnpadded, 32); + valueBuf = lengthBuf; + } + + for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { + const idxNumber = idx.toNumber(); + const childValueHex = this.elements[idxNumber].getHexValue(); + const childValueBuf = ethUtil.toBuffer(childValueHex); + valueBuf = Buffer.concat([valueBuf, childValueBuf]); + } + + // Convert value buffer to hex + const valueHex = ethUtil.bufferToHex(valueBuf); + return valueHex; + } + + public isStatic(): boolean { + return this.isStaticArray; + } + + public getHeaderSize(): BigNumber { + let size = new BigNumber(0); + if (!this.isLengthDefined) { + size = new BigNumber(32); // stores length of bytes + } + return size; + } + + public getBodySize(): BigNumber { + const evmWordWidth = new BigNumber(32); + const body = this.length.times(evmWordWidth); + return body; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } + + public getSignature(): string { + let type = this.type; + if (this.type === 'tuple') { + let tupleDataItem = { + type: 'tuple', + name: 'N/A', + } as DataItem; + const tupleComponents = this.getDataItem().components; + if (tupleComponents !== undefined) { + tupleDataItem.components = tupleComponents; + } + const tuple = new Tuple(tupleDataItem); + type = tuple.getSignature(); + } + + if (this.length.equals(SolArray.UNDEFINED_LENGTH)) { + return `${type}[]`; + } + return `${type}[${this.length}]`; + } +} + +export class Tuple extends DynamicDataType { + private length: BigNumber; + private childMap: { [key: string]: number }; + + constructor(dataItem: DataItem) { + super(dataItem); + expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); + this.length = new BigNumber(0); + this.childMap = {}; + if (dataItem.components !== undefined) { + this.constructChildren(dataItem.components); + this.length = new BigNumber(dataItem.components.length); + } else { + throw new Error('Components undefined'); + } + } + + private constructChildren(dataItems: DataItem[]) { + _.each(dataItems, (dataItem: DataItem) => { + const childDataItem = { + type: dataItem.type, + name: `${this.getDataItem().name}.${dataItem.name}`, + } as DataItem; + const child = DataTypeFactory.create(childDataItem, this); + this.childMap[dataItem.name] = this.children.length; + this.children.push(child); + }); + } + + private assignValueFromArray(value: any[]) { + // Sanity check length + const valueLength = new BigNumber(value.length); + if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { + throw new Error( + `Expected array of ${JSON.stringify( + this.length, + )} elements, but got array of length ${JSON.stringify(valueLength)}`, + ); + } + + // Assign values to children + for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { + const idxNumber = idx.toNumber(); + this.children[idxNumber].assignValue(value[idxNumber]); + } + } + + private assignValueFromObject(obj: object) { + let childMap = _.cloneDeep(this.childMap); + _.forOwn(obj, (value: any, key: string) => { + if (key in childMap === false) { + throw new Error(`Could not assign tuple to object: unrecognized key '${key}'`); + } + this.children[this.childMap[key]].assignValue(value); + delete childMap[key]; + }); + + if (Object.keys(childMap).length !== 0) { + throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); + } + } + + public assignValue(value: any[] | object) { + if (value instanceof Array) { + this.assignValueFromArray(value); + } else if (typeof value === 'object') { + this.assignValueFromObject(value); + } else { + throw new Error(`Unexpected type for ${value}`); + } + } + + public getHexValue(): string { + return '0x'; + } + + public getHeaderSize(): BigNumber { + return new BigNumber(0); + } + + public getBodySize(): BigNumber { + const evmWordWidth = new BigNumber(32); + const size = this.length.times(evmWordWidth); + return size; + } + + public getSignature(): string { + // Compute signature + let signature = `(`; + _.each(this.children, (child: DataType, i: number) => { + signature += child.getSignature(); + if (i < this.children.length - 1) { + signature += ','; + } + }); + signature += ')'; + return signature; + } + + public isStatic(): boolean { + return false; // @TODO: True in every case or only when dynamic data? + } + + public static matchGrammar(type: string): boolean { + return type === 'tuple'; + } +} + +/* TODO +class Fixed extends StaticDataType {} + +class UFixed extends StaticDataType {}*/ + +export class Pointer extends StaticDataType { + destDataType: DynamicDataType; + parentDataType: DataType; + + constructor(destDataType: DynamicDataType, parentDataType: DataType) { + const destDataItem = destDataType.getDataItem(); + const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; + super(dataItem); + this.destDataType = destDataType; + this.parentDataType = parentDataType; + this.children.push(destDataType); + } + + /* + public assignValue(destDataType: DynamicDataType) { + this.destDataType = destDataType; + }*/ + + public assignValue(value: any) { + this.destDataType.assignValue(value); + } + + public getHexValue(): string { + console.log( + '*'.repeat(40), + this.destDataType.getAbsoluteOffset().toString(16), + '^'.repeat(150), + this.parentDataType.getAbsoluteOffset().toString(16), + ); + + let offset = this.destDataType + .getAbsoluteOffset() + .minus(this.parentDataType.getAbsoluteOffset()) + .minus(this.parentDataType.getHeaderSize()); + + console.log("OFFSET == ", JSON.stringify(offset), " or in hex -- 0x", offset.toString(16)); + + const hexBase = 16; + const evmWordWidth = 32; + const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${offset.toString(hexBase)}`), evmWordWidth); + const encodedValue = ethUtil.bufferToHex(valueBuf); + return encodedValue; + } + + public getSignature(): string { + return this.destDataType.getSignature(); + } + + public isStatic(): boolean { + return true; + } + + public getHeaderSize(): BigNumber { + return new BigNumber(0); + } + + public getBodySize(): BigNumber { + return new BigNumber(32); + } + +} + +export class DataTypeFactory { + public static mapDataItemToDataType(dataItem: DataItem): DataType { + console.log(`Type: ${dataItem.type}`); + + if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); + if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); + if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); + if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); + if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); + if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); + if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); + if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); + if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); + if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); + //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); + //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); + + throw new Error(`Unrecognized data type: '${dataItem.type}'`); + } + + public static create(dataItem: DataItem, parentDataType: DataType): DataType { + const dataType = DataTypeFactory.mapDataItemToDataType(dataItem); + if (dataType.isStatic()) { + return dataType; + } else { + const pointer = new Pointer(dataType, parentDataType); + return pointer; + } + + throw new Error(`Unrecognized instance type: '${dataType}'`); + } +} + +class Queue { + private store: T[] = []; + push(val: T) { + this.store.push(val); + } + pop(): T | undefined { + return this.store.shift(); + } +} + +export class Method extends DataType { + name: string; + params: DataType[]; + private signature: string; + selector: string; + + constructor(abi: MethodAbi) { + super({ type: 'method', name: abi.name }); + this.name = abi.name; + this.params = []; + + _.each(abi.inputs, (input: DataItem) => { + this.params.push(DataTypeFactory.create(input, this)); + }); + + // Compute signature + this.signature = `${this.name}(`; + _.each(this.params, (param: DataType, i: number) => { + this.signature += param.getSignature(); + if (i < this.params.length - 1) { + this.signature += ','; + } + }); + this.signature += ')'; + + // Compute selector + this.selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(this.signature).slice(0, 4))); + + console.log(`--SIGNATURE--\n${this.signature}\n---------\n`); + console.log(`--SELECTOR--\n${this.selector}\n---------\n`); + } + + public getSignature(): string { + return this.signature; + } + + public assignValue(args: any[]) { + _.each(this.params, (param: DataType, i: number) => { + // Assign value to parameter + try { + param.assignValue(args[i]); + } catch (e) { + console.log('Failed to assign to ', param.getDataItem().name); + throw e; + } + + if (param instanceof Pointer) { + this.children.push(param.getChildren()[0]); + } + }); + } + + public getHexValue(): string { + let value = ""; + _.each(this.params, (param: DataType) => { + value += param.getHexValue(); + }); + + return value; + } + + public encode(args: any[]): string { + this.assignValue(args); + const calldata = new Calldata(this.selector, this.params.length); + this.bind(calldata, CalldataSection.PARAMS); + + return calldata.getHexValue(); + } + + public isStatic(): boolean { + return true; + } + + public getHeaderSize(): BigNumber { + // Exclude selector + return new BigNumber(0); + } + + public getBodySize(): BigNumber { + const nParams = new BigNumber(this.params.length); + const evmWordWidth = new BigNumber(32); + const size = nParams.times(evmWordWidth); + return size; + } + + /* + encodeOptimized(args: any[]): string { + const calldata = new Memory(); + // Assign values + optimizableParams : StaticDataType = []; + _.each(this.params, function(args: any[], i: number, param: DataType) { + param.assignValue(args[i]); + if (param instanceof DynamicDataType) { + + } + }); + + // Find non-parameter leaves + + + return ''; + } */ + + /* + decode(rawCalldata: string): any[] { + const calldata = new Calldata(this.name, this.params.length); + calldata.assignRaw(rawCalldata); + let args: any[]; + let params = this.params; + _.each(params, function(args: any[], i: number, param: DataType) { + param.decodeFromCalldata(calldata); + args.push(param.getValue()); + }); + + return args; + }*/ +} \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index b8f1d39ad..315537226 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -12,1294 +12,18 @@ import { MethodAbi, DataItem } from 'ethereum-types'; import { BigNumber } from '@0x/utils'; import { assert } from '@0x/order-utils/src/assert'; - -const simpleAbi = { - constant: false, - inputs: [ - { - name: 'greg', - type: 'uint256', - }, - { - name: 'gregStr', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -const stringAbi = { - constant: false, - inputs: [ - { - name: 'greg', - type: 'string[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -const tupleAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - ], - name: 'order', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -const staticArrayAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'uint8[3]', - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -const crazyAbi = { - constant: false, - inputs: [ - /*{ - name: 'someUInt256', - type: 'uint256', - }, - { - name: 'someInt256', - type: 'int256', - }, - { - name: 'someInt32', - type: 'int32', - }, - { - name: 'someByte', - type: 'byte', - }, - { - name: 'someBytes32', - type: 'bytes32', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someString', - type: 'string', - },*/ - /*{ - name: 'someAddress', - type: 'address', - }, - { - name: 'someBool', - type: 'bool', - },*/ - - { - name: 'someStaticArray', - type: 'uint8[3]', - }, - { - name: 'someStaticArrayWithDynamicMembers', - type: 'string[2]', - }, - { - name: 'someDynamicArrayWithDynamicMembers', - type: 'bytes[]', - }, - { - name: 'some2DArray', - type: 'string[][]', - }, - { - name: 'someTuple', - type: 'tuple', - components: [ - { - name: 'someUint32', - type: 'uint32', - }, - { - name: 'someStr', - type: 'string', - }, - ], - }, - { - name: 'someTupleWithDynamicTypes', - type: 'tuple', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - //{ - // name: 'someStrArray', - // type: 'string[]', - /// }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - } /*, - { - name: 'someArrayOfTuplesWithDynamicTypes', - type: 'tuple[]', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - { - name: 'someStrArray', - type: 'string[]', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - },*/, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -const simpleAbi2 = { - constant: false, - inputs: [ - { - name: 'someByte', - type: 'byte', - }, - { - name: 'someBytes32', - type: 'bytes32', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -const fillOrderAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'makerAddress', - type: 'address', - }, - { - name: 'takerAddress', - type: 'address', - }, - { - name: 'feeRecipientAddress', - type: 'address', - }, - { - name: 'senderAddress', - type: 'address', - }, - { - name: 'makerAssetAmount', - type: 'uint256', - }, - { - name: 'takerAssetAmount', - type: 'uint256', - }, - { - name: 'makerFee', - type: 'uint256', - }, - { - name: 'takerFee', - type: 'uint256', - }, - { - name: 'expirationTimeSeconds', - type: 'uint256', - }, - { - name: 'salt', - type: 'uint256', - }, - { - name: 'makerAssetData', - type: 'bytes', - }, - { - name: 'takerAssetData', - type: 'bytes', - }, - ], - name: 'order', - type: 'tuple', - }, - { - name: 'takerAssetFillAmount', - type: 'uint256', - }, - { - name: 'salt', - type: 'uint256', - }, - { - name: 'orderSignature', - type: 'bytes', - }, - { - name: 'takerSignature', - type: 'bytes', - }, - ], - name: 'fillOrder', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; +import * as AbiEncoder from './abi_encoder'; +import * as AbiSamples from './abi_samples'; chaiSetup.configure(); const expect = chai.expect; -namespace AbiEncoder { - class Word { - private value: string; - - constructor(value?: string) { - if (value === undefined) { - this.value = ''; - } else { - this.value = value; - } - } - - public set(value: string) { - if (value.length !== 64) { - throw `Tried to create word that is not 32 bytes: ${value}`; - } - - this.value = value; - } - - public get(): string { - return this.value; - } - - public getAsHex(): string { - return `0x${this.value}`; - } - } - - export enum CalldataSection { - NONE, - PARAMS, - DATA, - } - - class Memblock { - private dataType: DataType; - private location: { calldataSection: CalldataSection; sectionOffset: BigNumber; offset: BigNumber }; - - constructor(dataType: DataType) { - this.dataType = dataType; - this.location = { - calldataSection: CalldataSection.NONE, - sectionOffset: new BigNumber(0), - offset: new BigNumber(0), - }; - } - - public getSize(): BigNumber { - return new BigNumber(ethUtil.toBuffer(this.dataType.getHexValue()).byteLength); - } - - public assignLocation(calldataSection: CalldataSection, sectionOffset: BigNumber, offset: BigNumber) { - this.location.calldataSection = calldataSection; - this.location.sectionOffset = sectionOffset; - this.location.offset = offset; - } - - public get(): string { - return ethUtil.stripHexPrefix(this.dataType.getHexValue()); - } - - public getOffset(): BigNumber { - return this.location.offset; - } - - public getAbsoluteOffset(): BigNumber { - return this.location.sectionOffset.plus(this.location.offset); - } - - public getSection(): CalldataSection { - return this.location.calldataSection; - } - } - - interface BindList { - [key: string]: Memblock; - } - - export class Calldata { - private selector: string; - private params: Memblock[]; - private data: Memblock[]; - private dataOffset: BigNumber; - private currentDataOffset: BigNumber; - private currentParamOffset: BigNumber; - private bindList: BindList; - - constructor(selector: string, nParams: number) { - this.selector = selector; - console.log(this.selector); - this.params = []; - this.data = []; - const evmWordSize = 32; - this.dataOffset = new BigNumber(nParams).times(evmWordSize); - this.currentDataOffset = new BigNumber(0); - this.currentParamOffset = new BigNumber(0); - this.bindList = {}; - } - - public bind(dataType: DataType, section: CalldataSection) { - if (dataType.getId() in this.bindList) { - throw `Rebind on ${dataType.getId()}`; - } - const memblock = new Memblock(dataType); - switch (section) { - case CalldataSection.PARAMS: - this.params.push(memblock); - memblock.assignLocation(section, new BigNumber(0), this.currentParamOffset); - - console.log(`Binding ${dataType.getDataItem().name} to PARAMS at ${this.currentParamOffset}`); - this.currentParamOffset = this.currentParamOffset.plus(memblock.getSize()); - break; - - case CalldataSection.DATA: - this.data.push(memblock); - memblock.assignLocation(section, this.dataOffset, this.currentDataOffset); - - console.log( - `Binding ${dataType.getDataItem().name} to DATA at ${memblock - .getAbsoluteOffset() - .toString(16)}`, - ); - this.currentDataOffset = this.currentDataOffset.plus(memblock.getSize()); - break; - - default: - throw `Unrecognized calldata section: ${section}`; - } - - this.bindList[dataType.getId()] = memblock; - dataType.rbind(memblock); - } - - public getHexValue(): string { - let hexValue = `${this.selector}`; - _.each(this.params, (memblock: Memblock) => { - hexValue += memblock.get(); - }); - _.each(this.data, (memblock: Memblock) => { - hexValue += memblock.get(); - }); - - return hexValue; - } - } - - export abstract class DataType { - private dataItem: DataItem; - private hexValue: string; - protected memblock: Memblock | undefined; - protected children: DataType[]; - - constructor(dataItem: DataItem) { - this.dataItem = dataItem; - this.hexValue = '0x'; - this.memblock = undefined; - this.children = []; - } - - protected assignHexValue(hexValue: string) { - this.hexValue = hexValue; - } - - public getHexValue(): string { - return this.hexValue; - } - - public getDataItem(): DataItem { - return this.dataItem; - } - - public rbind(memblock: Memblock) { - this.memblock = memblock; - } - - public bind(calldata: Calldata, section: CalldataSection) { - calldata.bind(this, section); - } - - public getId(): string { - return this.dataItem.name; - } - - public getOffset(): BigNumber { - if (this.memblock === undefined) return new BigNumber(0); - return this.memblock.getOffset(); - } - - public getAbsoluteOffset(): BigNumber { - if (this.memblock === undefined) return new BigNumber(0); - return this.memblock.getAbsoluteOffset(); - } - - public getSize(): BigNumber { - if (this.memblock === undefined) return new BigNumber(0); - return this.memblock.getSize(); - } - - public getChildren(): DataType[] { - return this.children; - } - - public abstract assignValue(value: any): void; - public abstract getSignature(): string; - public abstract isStatic(): boolean; - } - - export abstract class StaticDataType extends DataType { - constructor(dataItem: DataItem) { - super(dataItem); - } - } - - export abstract class DynamicDataType extends DataType { - constructor(dataItem: DataItem) { - super(dataItem); - } - } - - export class Address extends StaticDataType { - constructor(dataItem: DataItem) { - super(dataItem); - expect(Address.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string) { - const evmWordWidth = 32; - const hexValue = ethUtil.bufferToHex(ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth)); - this.assignHexValue(hexValue); - } - - public getSignature(): string { - return `address`; - } - - public isStatic(): boolean { - return true; - } - - public static matchGrammar(type: string): boolean { - return type === 'address'; - } - } - - export class Bool extends StaticDataType { - constructor(dataItem: DataItem) { - super(dataItem); - expect(Bool.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: boolean) { - const evmWordWidth = 32; - const encodedValue = value === true ? '0x1' : '0x0'; - const hexValue = ethUtil.bufferToHex(ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth)); - this.assignHexValue(hexValue); - } - - public getSignature(): string { - return 'bool'; - } - - public isStatic(): boolean { - return true; - } - - public static matchGrammar(type: string): boolean { - return type === 'bool'; - } - } - - abstract class Number extends StaticDataType { - static MAX_WIDTH: number = 256; - static DEFAULT_WIDTH: number = Number.MAX_WIDTH; - width: number = Number.DEFAULT_WIDTH; - - constructor(dataItem: DataItem, matcher: RegExp) { - super(dataItem); - const matches = matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 2 && matches[1] !== undefined) { - this.width = parseInt(matches[1]); - } else { - this.width = 256; - } - } - - public assignValue(value: BigNumber) { - if (value.greaterThan(this.getMaxValue())) { - throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; - } else if (value.lessThan(this.getMinValue())) { - throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; - } - - const hexBase = 16; - const evmWordWidth = 32; - let valueBuf: Buffer; - if (value.greaterThanOrEqualTo(0)) { - valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); - } else { - // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. - // Step 1/3: Convert value to positive binary string - const binBase = 2; - const valueBin = value.times(-1).toString(binBase); - - // Step 2/3: Invert binary value - const bitsInEvmWord = 256; - let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, binBase); - - // Step 3/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const negativeValue = invertedValue.plus(1); - - // Convert the negated value to a hex string - valueBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), - evmWordWidth, - ); - } - - const encodedValue = ethUtil.bufferToHex(valueBuf); - this.assignHexValue(encodedValue); - } - - public isStatic(): boolean { - return true; - } - - public abstract getMaxValue(): BigNumber; - public abstract getMinValue(): BigNumber; - } - - export class Int extends Number { - static matcher = RegExp( - '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - constructor(dataItem: DataItem) { - super(dataItem, Int.matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).times(-1); - } - - public getSignature(): string { - return `int${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } - } - - export class UInt extends Number { - static matcher = RegExp( - '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - constructor(dataItem: DataItem) { - super(dataItem, UInt.matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(0); - } - - public getSignature(): string { - return `uint${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } - } - - export class Byte extends StaticDataType { - static matcher = RegExp( - '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', - ); - - static DEFAULT_WIDTH = 1; - width: number = Byte.DEFAULT_WIDTH; - - constructor(dataItem: DataItem) { - super(dataItem); - const matches = Byte.matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 3 && matches[2] !== undefined) { - this.width = parseInt(matches[2]); - } else { - this.width = Byte.DEFAULT_WIDTH; - } - } - - public assignValue(value: string | Buffer) { - // Convert value into a buffer and do bounds checking - const valueBuf = ethUtil.toBuffer(value); - if (valueBuf.byteLength > this.width) { - throw new Error( - `Tried to assign ${value} (${ - valueBuf.byteLength - } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, - ); - } else if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - // Store value as hex - const evmWordWidth = 32; - const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); - const hexValue = ethUtil.bufferToHex(paddedValue); - this.assignHexValue(hexValue); - } - - public getSignature(): string { - // Note that `byte` reduces to `bytes1` - return `bytes${this.width}`; - } - - public isStatic(): boolean { - return true; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } - } - - export class Bytes extends DynamicDataType { - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = Bytes.UNDEFINED_LENGTH; - - constructor(dataItem: DataItem) { - super(dataItem); - expect(Bytes.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string | Buffer) { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); - } - const valueBuf = ethUtil.toBuffer(value); - if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - const wordsForValue = Math.ceil(valueBuf.byteLength / 32); - const paddedBytesForValue = wordsForValue * 32; - const paddedValueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); - const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); - const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); - const encodedValue = ethUtil.bufferToHex(encodedValueBuf); - - this.assignHexValue(encodedValue); - } - - public getSignature(): string { - return 'bytes'; - } - - public isStatic(): boolean { - return false; - } - - public static matchGrammar(type: string): boolean { - return type === 'bytes'; - } - } - - export class SolString extends DynamicDataType { - constructor(dataItem: DataItem) { - super(dataItem); - expect(SolString.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string) { - const wordsForValue = Math.ceil(value.length / 32); - const paddedBytesForValue = wordsForValue * 32; - const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); - const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); - const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); - const encodedValue = ethUtil.bufferToHex(encodedValueBuf); - - this.assignHexValue(encodedValue); - } - - public getSignature(): string { - return 'string'; - } - - public isStatic(): boolean { - return false; - } - - public static matchGrammar(type: string): boolean { - return type === 'string'; - } - } - - export class SolArray extends DynamicDataType { - static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = SolArray.UNDEFINED_LENGTH; - type: string = '[undefined]'; - isLengthDefined: boolean; - - constructor(dataItem: DataItem) { - super(dataItem); - const matches = SolArray.matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - console.log(JSON.stringify(matches)); - if (matches === null || matches.length !== 3) { - throw new Error(`Could not parse array: ${dataItem.type}`); - } else if (matches[1] === undefined) { - throw new Error(`Could not parse array type: ${dataItem.type}`); - } else if (matches[2] === undefined) { - throw new Error(`Could not parse array length: ${dataItem.type}`); - } - - // Check if length is undefined - if (matches[2] === '') { - this.type = matches[1]; - this.length = SolArray.UNDEFINED_LENGTH; - this.isLengthDefined = false; - return; - } - - // Parse out array type/length and construct children - this.isLengthDefined = true; - this.type = matches[1]; - this.length = new BigNumber(matches[2], 10); - if (this.length.lessThan(0)) { - throw new Error(`Bad array length: ${JSON.stringify(this.length)}`); - } - this.constructChildren(); - } - - private constructChildren() { - for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { - const childDataItem = { - type: this.type, - name: `${this.getDataItem().name}[${idx.toString(10)}]`, - } as DataItem; - const child = DataTypeFactory.create(childDataItem, this); - this.children.push(child); - } - } - - // @TODO: HACKY -- shouldn't really have children for a - public getChildren(): DataType[] { - if (this.isStatic()) { - return []; - } else { - return this.children; - } - } - - public assignValue(value: any[]) { - // Sanity check length - const valueLength = new BigNumber(value.length); - if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { - throw new Error( - `Expected array of length ${JSON.stringify(this.length)}, but got array of length ${JSON.stringify( - valueLength, - )}`, - ); - } - - // Assign length if not already set - if (this.length === SolArray.UNDEFINED_LENGTH) { - this.length = valueLength; - this.constructChildren(); - } - - // Assign values to children - for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { - const idxNumber = idx.toNumber(); - this.children[idxNumber].assignValue(value[idxNumber]); - } - } - - private getHexValueDynamicArray(): string { - const lengthBufUnpadded = ethUtil.toBuffer(`0x${this.length.toString(16)}`); - const lengthBuf = ethUtil.setLengthLeft(lengthBufUnpadded, 32); - let valueBuf = lengthBuf; - - const valueHex = ethUtil.bufferToHex(valueBuf); - return valueHex; - } - - private getHexValueStaticArray(): string { - let valueBuf = new Buffer(""); - - for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { - const idxNumber = idx.toNumber(); - const childValueHex = this.children[idxNumber].getHexValue(); - const childValueBuf = ethUtil.toBuffer(childValueHex); - valueBuf = Buffer.concat([valueBuf, childValueBuf]); - - console.log(JSON.stringify(idx)); - } - - // Convert value buffer to hex - const valueHex = ethUtil.bufferToHex(valueBuf); - return valueHex; - } - - public getHexValue(): string { - if (this.isLengthDefined) { - return this.getHexValueStaticArray(); - } else { - return this.getHexValueDynamicArray(); - } - } - - public isStatic(): boolean { - return this.isLengthDefined; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } - - public getSignature(): string { - let type = this.type; - if (this.type === 'tuple') { - let tupleDataItem = { - type: 'tuple', - name: 'N/A', - } as DataItem; - const tupleComponents = this.getDataItem().components; - if (tupleComponents !== undefined) { - tupleDataItem.components = tupleComponents; - } - const tuple = new Tuple(tupleDataItem); - type = tuple.getSignature(); - } - - if (this.length.equals(SolArray.UNDEFINED_LENGTH)) { - return `${type}[]`; - } - return `${type}[${this.length}]`; - } - } - - export class Tuple extends DynamicDataType { - private length: BigNumber; - private childMap: { [key: string]: number }; - - constructor(dataItem: DataItem) { - super(dataItem); - expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); - this.length = new BigNumber(0); - this.childMap = {}; - if (dataItem.components !== undefined) { - this.constructChildren(dataItem.components); - this.length = new BigNumber(dataItem.components.length); - } else { - throw new Error('Components undefined'); - } - } - - private constructChildren(dataItems: DataItem[]) { - _.each(dataItems, (dataItem: DataItem) => { - const childDataItem = { - type: dataItem.type, - name: `${this.getDataItem().name}.${dataItem.name}`, - } as DataItem; - const child = DataTypeFactory.create(childDataItem, this); - this.childMap[dataItem.name] = this.children.length; - this.children.push(child); - }); - } - - private assignValueFromArray(value: any[]) { - // Sanity check length - const valueLength = new BigNumber(value.length); - if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { - throw new Error( - `Expected array of ${JSON.stringify( - this.length, - )} elements, but got array of length ${JSON.stringify(valueLength)}`, - ); - } - - // Assign values to children - for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { - const idxNumber = idx.toNumber(); - this.children[idxNumber].assignValue(value[idxNumber]); - } - } - - private assignValueFromObject(obj: object) { - let childMap = _.cloneDeep(this.childMap); - _.forOwn(obj, (value: any, key: string) => { - if (key in childMap === false) { - throw new Error(`Could not assign tuple to object: unrecognized key '${key}'`); - } - this.children[this.childMap[key]].assignValue(value); - delete childMap[key]; - }); - - if (Object.keys(childMap).length !== 0) { - throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); - } - } - - public assignValue(value: any[] | object) { - if (value instanceof Array) { - this.assignValueFromArray(value); - } else if (typeof value === 'object') { - this.assignValueFromObject(value); - } else { - throw new Error(`Unexpected type for ${value}`); - } - } - - public getHexValue(): string { - return '0x'; - } - - public getSignature(): string { - // Compute signature - let signature = `(`; - _.each(this.children, (child: DataType, i: number) => { - signature += child.getSignature(); - if (i < this.children.length - 1) { - signature += ','; - } - }); - signature += ')'; - return signature; - } - - public isStatic(): boolean { - return false; // @TODO: True in every case or only when dynamic data? - } - - public static matchGrammar(type: string): boolean { - return type === 'tuple'; - } - } - - /* TODO - class Fixed extends StaticDataType {} - - class UFixed extends StaticDataType {}*/ - - export class Pointer extends StaticDataType { - destDataType: DynamicDataType; - parentDataType: DataType; - - constructor(destDataType: DynamicDataType, parentDataType: DataType) { - const destDataItem = destDataType.getDataItem(); - const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; - super(dataItem); - this.destDataType = destDataType; - this.parentDataType = parentDataType; - this.children.push(destDataType); - } - - /* - public assignValue(destDataType: DynamicDataType) { - this.destDataType = destDataType; - }*/ - - public assignValue(value: any) { - this.destDataType.assignValue(value); - } - - public getHexValue(): string { - console.log( - '*'.repeat(40), - this.destDataType.getAbsoluteOffset().toString(16), - '^'.repeat(150), - this.parentDataType.getAbsoluteOffset().toString(16), - ); - - let offset = this.destDataType - .getAbsoluteOffset() - .minus(this.parentDataType.getAbsoluteOffset().plus(this.parentDataType.getSize())); - const hexBase = 16; - const evmWordWidth = 32; - const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${offset.toString(hexBase)}`), evmWordWidth); - const encodedValue = ethUtil.bufferToHex(valueBuf); - return encodedValue; - } - - public getSignature(): string { - return this.destDataType.getSignature(); - } - - public isStatic(): boolean { - return true; - } - - public encodeToCalldata(calldata: Calldata): void { - throw 2; - } - } - - export class DataTypeFactory { - public static mapDataItemToDataType(dataItem: DataItem): DataType { - console.log(`Type: ${dataItem.type}`); - - if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); - if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); - if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); - if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); - if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); - if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); - if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); - if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); - if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); - if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); - //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); - //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); - - throw new Error(`Unrecognized data type: '${dataItem.type}'`); - } - - public static create(dataItem: DataItem, parentDataType: DataType): DataType { - const dataType = DataTypeFactory.mapDataItemToDataType(dataItem); - if (dataType.isStatic()) { - return dataType; - } else { - const pointer = new Pointer(dataType, parentDataType); - return pointer; - } - - throw new Error(`Unrecognized instance type: '${dataType}'`); - } - } - - class Queue { - private store: T[] = []; - push(val: T) { - this.store.push(val); - } - pop(): T | undefined { - return this.store.shift(); - } - } - - export class Method extends DataType { - name: string; - params: DataType[]; - private signature: string; - selector: string; - - constructor(abi: MethodAbi) { - super({ type: 'method', name: abi.name }); - this.name = abi.name; - this.params = []; - - _.each(abi.inputs, (input: DataItem) => { - this.params.push(DataTypeFactory.create(input, this)); - }); - - // Compute signature - this.signature = `${this.name}(`; - _.each(this.params, (param: DataType, i: number) => { - this.signature += param.getSignature(); - if (i < this.params.length - 1) { - this.signature += ','; - } - }); - this.signature += ')'; - - // Compute selector - this.selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(this.signature).slice(0, 4))); - - console.log(`--SIGNATURE--\n${this.signature}\n---------\n`); - console.log(`--SELECTOR--\n${this.selector}\n---------\n`); - } - - public getSignature(): string { - return this.signature; - } - - public assignValue(args: any[]) { - const calldata = new Calldata(this.selector, this.params.length); - const params = this.params; - const paramQueue = new Queue(); - _.each(params, (param: DataType, i: number) => { - try { - param.assignValue(args[i]); - } catch (e) { - console.log('Failed to assign to ', param.getDataItem().name); - throw e; - } - param.bind(calldata, CalldataSection.PARAMS); - _.each(param.getChildren(), (child: DataType) => { - paramQueue.push(child); - }); - }); - - let param: DataType | undefined = undefined; - while ((param = paramQueue.pop()) !== undefined) { - param.bind(calldata, CalldataSection.DATA); - _.each(param.getChildren(), (child: DataType) => { - paramQueue.push(child); - }); - } - - console.log(calldata); - - this.assignHexValue(calldata.getHexValue()); - - //return calldata.getRaw(); - } - - public encode(args: any[]): string { - this.assignValue(args); - return this.getHexValue(); - } - - public isStatic(): boolean { - return true; - } - - /* - encodeOptimized(args: any[]): string { - const calldata = new Memory(); - // Assign values - optimizableParams : StaticDataType = []; - _.each(this.params, function(args: any[], i: number, param: DataType) { - param.assignValue(args[i]); - if (param instanceof DynamicDataType) { - - } - }); - - // Find non-parameter leaves - - - return ''; - } */ - - /* - decode(rawCalldata: string): any[] { - const calldata = new Calldata(this.name, this.params.length); - calldata.assignRaw(rawCalldata); - let args: any[]; - let params = this.params; - _.each(params, function(args: any[], i: number, param: DataType) { - param.decodeFromCalldata(calldata); - args.push(param.getValue()); - }); - - return args; - }*/ - } -} - describe.only('ABI Encoder', () => { describe.only('Just a Greg, Eh', () => { + + it('Crazy ABI', async () => { - const method = new AbiEncoder.Method(crazyAbi); + const method = new AbiEncoder.Method(AbiSamples.crazyAbi); console.log(method.getSignature()); const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)], @@ -1405,20 +129,50 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata);*/ }); - it.only('Static Array ABI', async () => { - const method = new AbiEncoder.Method(staticArrayAbi); - const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; + it.only('Fixed Lenfgth Array / Dynamic Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); + const args = [["Brave", "New", "World"]]; const calldata = method.encode(args); console.log(calldata); console.log('*'.repeat(40)); console.log(JSON.stringify(args)); + const expectedCalldata = + '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it.only('Unfixed Length Array / Dynamic Members ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.dynamicArrayDynamicMembersAbi); + const args = [["Brave", "New", "World"]]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it.only('Unfixed Length Array / Static Members ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.dynamicArrayStaticMembersAbi); + const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; + const calldata = method.encode(args); + const expectedCalldata = '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + + it.only('Fixed Length Array / Static Members ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); + const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; + const calldata = method.encode(args); const expectedCalldata = '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; expect(calldata).to.be.equal(expectedCalldata); }); + it('Simple ABI 2', async () => { - const method = new AbiEncoder.Method(simpleAbi2); + const method = new AbiEncoder.Method(AbiSamples.simpleAbi2); const args = [ '0xaf', // e (bytes1) @@ -1434,14 +188,14 @@ describe.only('ABI Encoder', () => { }); it('Yessir', async () => { - const method = new AbiEncoder.Method(simpleAbi); + const method = new AbiEncoder.Method(AbiSamples.simpleAbi); const calldata = method.encode([new BigNumber(5), 'five']); console.log(calldata); expect(true).to.be.true(); }); it('Array ABI', async () => { - const method = new AbiEncoder.Method(stringAbi); + const method = new AbiEncoder.Method(AbiSamples.stringAbi); const calldata = method.encode([['five', 'six', 'seven']]); console.log(method.getSignature()); console.log(method.selector); @@ -1453,7 +207,7 @@ describe.only('ABI Encoder', () => { }); it('Object ABI (Array input)', async () => { - const method = new AbiEncoder.Method(tupleAbi); + const method = new AbiEncoder.Method(AbiSamples.tupleAbi); const calldata = method.encode([[new BigNumber(5), 'five']]); console.log(method.getSignature()); console.log(method.selector); @@ -1465,7 +219,7 @@ describe.only('ABI Encoder', () => { }); it('Object ABI (Object input)', async () => { - const method = new AbiEncoder.Method(tupleAbi); + const method = new AbiEncoder.Method(AbiSamples.tupleAbi); const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); console.log(method.getSignature()); console.log(method.selector); @@ -1477,7 +231,7 @@ describe.only('ABI Encoder', () => { }); it.skip('Object ABI (Object input - Missing Key)', async () => { - const method = new AbiEncoder.Method(tupleAbi); + const method = new AbiEncoder.Method(AbiSamples.tupleAbi); const calldata = method.encode([{ someUint: new BigNumber(5) }]); console.log(method.getSignature()); console.log(method.selector); @@ -1491,7 +245,7 @@ describe.only('ABI Encoder', () => { }); it.skip('Object ABI (Object input - Too Many Keys)', async () => { - const method = new AbiEncoder.Method(tupleAbi); + const method = new AbiEncoder.Method(AbiSamples.tupleAbi); const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five', unwantedKey: 14 }]); console.log(method.getSignature()); console.log(method.selector); diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts new file mode 100644 index 000000000..2e7111504 --- /dev/null +++ b/packages/order-utils/test/abi_samples.ts @@ -0,0 +1,358 @@ +import { MethodAbi } from 'ethereum-types'; + +export const simpleAbi = { + constant: false, + inputs: [ + { + name: 'greg', + type: 'uint256', + }, + { + name: 'gregStr', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const stringAbi = { + constant: false, + inputs: [ + { + name: 'greg', + type: 'string[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const tupleAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + ], + name: 'order', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const staticArrayAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[3]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const staticArrayDynamicMembersAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'string[3]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const dynamicArrayDynamicMembersAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'string[]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const dynamicArrayStaticMembersAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const crazyAbi = { + constant: false, + inputs: [ + /*{ + name: 'someUInt256', + type: 'uint256', + }, + { + name: 'someInt256', + type: 'int256', + }, + { + name: 'someInt32', + type: 'int32', + }, + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someBytes32', + type: 'bytes32', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someString', + type: 'string', + },*/ + /*{ + name: 'someAddress', + type: 'address', + }, + { + name: 'someBool', + type: 'bool', + },*/ + + { + name: 'someStaticArray', + type: 'uint8[3]', + }, + { + name: 'someStaticArrayWithDynamicMembers', + type: 'string[2]', + }, + { + name: 'someDynamicArrayWithDynamicMembers', + type: 'bytes[]', + }, + { + name: 'some2DArray', + type: 'string[][]', + }, + { + name: 'someTuple', + type: 'tuple', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'someStr', + type: 'string', + }, + ], + }, + { + name: 'someTupleWithDynamicTypes', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + //{ + // name: 'someStrArray', + // type: 'string[]', + /// }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + } /*, + { + name: 'someArrayOfTuplesWithDynamicTypes', + type: 'tuple[]', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + { + name: 'someStrArray', + type: 'string[]', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + },*/, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const simpleAbi2 = { + constant: false, + inputs: [ + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someBytes32', + type: 'bytes32', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const fillOrderAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'makerAddress', + type: 'address', + }, + { + name: 'takerAddress', + type: 'address', + }, + { + name: 'feeRecipientAddress', + type: 'address', + }, + { + name: 'senderAddress', + type: 'address', + }, + { + name: 'makerAssetAmount', + type: 'uint256', + }, + { + name: 'takerAssetAmount', + type: 'uint256', + }, + { + name: 'makerFee', + type: 'uint256', + }, + { + name: 'takerFee', + type: 'uint256', + }, + { + name: 'expirationTimeSeconds', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'makerAssetData', + type: 'bytes', + }, + { + name: 'takerAssetData', + type: 'bytes', + }, + ], + name: 'order', + type: 'tuple', + }, + { + name: 'takerAssetFillAmount', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'orderSignature', + type: 'bytes', + }, + { + name: 'takerSignature', + type: 'bytes', + }, + ], + name: 'fillOrder', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; -- cgit v1.2.3 From e95aa617b6eb0e4b6bace7fbcf66de2658e314aa Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 8 Nov 2018 10:49:19 -0800 Subject: All existing ABI tests passing --- packages/order-utils/test/abi_encoder.ts | 18 +++++++++++++----- packages/order-utils/test/abi_encoder_test.ts | 13 ++++++++----- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/order-utils/test/abi_encoder.ts b/packages/order-utils/test/abi_encoder.ts index 55b63b701..77cb89056 100644 --- a/packages/order-utils/test/abi_encoder.ts +++ b/packages/order-utils/test/abi_encoder.ts @@ -74,6 +74,7 @@ class Memblock { } public get(): string { + console.log(`Unstripped = '${this.dataType.getHexValue()}' and Stripped = '${ethUtil.stripHexPrefix(this.dataType.getHexValue())}'`); return ethUtil.stripHexPrefix(this.dataType.getHexValue()); } @@ -472,6 +473,7 @@ export class Byte extends StaticDataType { const evmWordWidth = 32; const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); const hexValue = ethUtil.bufferToHex(paddedValue); + this.assignHexValue(hexValue); } @@ -538,7 +540,9 @@ export class Bytes extends DynamicDataType { } public getBodySize(): BigNumber { - return new BigNumber(this.getHexValue().length); + const valueBuf = ethUtil.toBuffer(this.getHexValue()); + const size = new BigNumber(valueBuf.byteLength); + return size; } public static matchGrammar(type: string): boolean { @@ -576,7 +580,9 @@ export class SolString extends DynamicDataType { } public getBodySize(): BigNumber { - return new BigNumber(this.getHexValue().length); + const valueBuf = ethUtil.toBuffer(this.getHexValue()); + const size = new BigNumber(valueBuf.byteLength); + return size; } public static matchGrammar(type: string): boolean { @@ -1032,12 +1038,14 @@ export class Method extends DataType { } public getHexValue(): string { - let value = ""; + let paramBufs: Buffer[] = []; _.each(this.params, (param: DataType) => { - value += param.getHexValue(); + paramBufs.push(ethUtil.toBuffer(param.getHexValue())); }); - return value; + const value = Buffer.concat(paramBufs); + const hexValue = ethUtil.bufferToHex(value); + return hexValue; } public encode(args: any[]): string { diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 315537226..91a32bfbf 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -22,7 +22,7 @@ describe.only('ABI Encoder', () => { describe.only('Just a Greg, Eh', () => { - it('Crazy ABI', async () => { + it.skip('Crazy ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi); console.log(method.getSignature()); @@ -114,6 +114,9 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); console.log(method.getSignature()); + const expectedCalldata = '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + /* const expectedCalldata = '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; @@ -129,7 +132,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata);*/ }); - it.only('Fixed Lenfgth Array / Dynamic Members', async () => { + it('Fixed Lenfgth Array / Dynamic Members', async () => { const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); const args = [["Brave", "New", "World"]]; const calldata = method.encode(args); @@ -141,7 +144,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it.only('Unfixed Length Array / Dynamic Members ABI', async () => { + it('Unfixed Length Array / Dynamic Members ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.dynamicArrayDynamicMembersAbi); const args = [["Brave", "New", "World"]]; const calldata = method.encode(args); @@ -152,7 +155,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it.only('Unfixed Length Array / Static Members ABI', async () => { + it('Unfixed Length Array / Static Members ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.dynamicArrayStaticMembersAbi); const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; const calldata = method.encode(args); @@ -161,7 +164,7 @@ describe.only('ABI Encoder', () => { }); - it.only('Fixed Length Array / Static Members ABI', async () => { + it('Fixed Length Array / Static Members ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; const calldata = method.encode(args); -- cgit v1.2.3 From 3bc45395cc0e1c5c483e7319967b6308122123bf Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 8 Nov 2018 11:14:44 -0800 Subject: static vs dynamic tuple differentatiion --- packages/order-utils/test/abi_encoder.ts | 30 +++++++++++++++------ packages/order-utils/test/abi_encoder_test.ts | 34 ++++++++++++++++------- packages/order-utils/test/abi_samples.ts | 39 ++++++++++++++++++++++++--- 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/packages/order-utils/test/abi_encoder.ts b/packages/order-utils/test/abi_encoder.ts index 77cb89056..853aac627 100644 --- a/packages/order-utils/test/abi_encoder.ts +++ b/packages/order-utils/test/abi_encoder.ts @@ -770,12 +770,14 @@ export class SolArray extends DynamicDataType { export class Tuple extends DynamicDataType { private length: BigNumber; private childMap: { [key: string]: number }; + private members: DataType[]; constructor(dataItem: DataItem) { super(dataItem); expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); this.length = new BigNumber(0); this.childMap = {}; + this.members = []; if (dataItem.components !== undefined) { this.constructChildren(dataItem.components); this.length = new BigNumber(dataItem.components.length); @@ -792,7 +794,11 @@ export class Tuple extends DynamicDataType { } as DataItem; const child = DataTypeFactory.create(childDataItem, this); this.childMap[dataItem.name] = this.children.length; - this.children.push(child); + + if (child instanceof Pointer) { + this.children.push(child.getChildren()[0]); + } + this.members.push(child); }); } @@ -810,7 +816,7 @@ export class Tuple extends DynamicDataType { // Assign values to children for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { const idxNumber = idx.toNumber(); - this.children[idxNumber].assignValue(value[idxNumber]); + this.members[idxNumber].assignValue(value[idxNumber]); } } @@ -820,7 +826,7 @@ export class Tuple extends DynamicDataType { if (key in childMap === false) { throw new Error(`Could not assign tuple to object: unrecognized key '${key}'`); } - this.children[this.childMap[key]].assignValue(value); + this.members[this.childMap[key]].assignValue(value); delete childMap[key]; }); @@ -840,7 +846,14 @@ export class Tuple extends DynamicDataType { } public getHexValue(): string { - return '0x'; + let paramBufs: Buffer[] = []; + _.each(this.members, (member: DataType) => { + paramBufs.push(ethUtil.toBuffer(member.getHexValue())); + }); + + const value = Buffer.concat(paramBufs); + const hexValue = ethUtil.bufferToHex(value); + return hexValue; } public getHeaderSize(): BigNumber { @@ -856,9 +869,9 @@ export class Tuple extends DynamicDataType { public getSignature(): string { // Compute signature let signature = `(`; - _.each(this.children, (child: DataType, i: number) => { - signature += child.getSignature(); - if (i < this.children.length - 1) { + _.each(this.members, (member: DataType, i: number) => { + signature += member.getSignature(); + if (i < this.members.length - 1) { signature += ','; } }); @@ -867,7 +880,8 @@ export class Tuple extends DynamicDataType { } public isStatic(): boolean { - return false; // @TODO: True in every case or only when dynamic data? + const isStaticTuple = this.children.length === 0; + return isStaticTuple; // @TODO: True in every case or only when dynamic data? } public static matchGrammar(type: string): boolean { diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 91a32bfbf..070bfe7f6 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -36,7 +36,7 @@ describe.only('ABI Encoder', () => { '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', ], // m - [ + /*[ [ 'some string', 'some another string', @@ -49,7 +49,7 @@ describe.only('ABI Encoder', () => { 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', ], [], - ], // n + ],*/ // n [ new BigNumber(4037824789), 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', @@ -114,7 +114,9 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); console.log(method.getSignature()); - const expectedCalldata = '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; + const expectedCalldata = '0x76c14e63000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e728347239823742398472398472984724892749874897428472894723948749874984787432942374349234732984723984237489237489237489234723894728947894748937428947289473894274982374329874238947238947328947238943724982374982374289347239800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272834732984732489237492387423987423984728947298432789423749823748923748927439820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; + + //const expectedCalldata = '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); /* @@ -209,8 +211,21 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it('Object ABI (Array input)', async () => { - const method = new AbiEncoder.Method(AbiSamples.tupleAbi); + it.only('Static Tuple', async () => { + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi); + const calldata = method.encode([[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it('Dynamic Tuple (Array input)', async () => { + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); const calldata = method.encode([[new BigNumber(5), 'five']]); console.log(method.getSignature()); console.log(method.selector); @@ -221,8 +236,9 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it('Object ABI (Object input)', async () => { - const method = new AbiEncoder.Method(AbiSamples.tupleAbi); + it('Dynamic Tuple (Object input)', async () => { + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); console.log(method.getSignature()); console.log(method.selector); @@ -234,7 +250,7 @@ describe.only('ABI Encoder', () => { }); it.skip('Object ABI (Object input - Missing Key)', async () => { - const method = new AbiEncoder.Method(AbiSamples.tupleAbi); + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); const calldata = method.encode([{ someUint: new BigNumber(5) }]); console.log(method.getSignature()); console.log(method.selector); @@ -248,7 +264,7 @@ describe.only('ABI Encoder', () => { }); it.skip('Object ABI (Object input - Too Many Keys)', async () => { - const method = new AbiEncoder.Method(AbiSamples.tupleAbi); + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five', unwantedKey: 14 }]); console.log(method.getSignature()); console.log(method.selector); diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts index 2e7111504..fb5cfbeb7 100644 --- a/packages/order-utils/test/abi_samples.ts +++ b/packages/order-utils/test/abi_samples.ts @@ -34,7 +34,7 @@ export const stringAbi = { type: 'function', } as MethodAbi; -export const tupleAbi = { +export const dynamicTupleAbi = { constant: false, inputs: [ { @@ -59,6 +59,39 @@ export const tupleAbi = { type: 'function', } as MethodAbi; +export const staticTupleAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint1', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + { + name: 'someUint3', + type: 'uint256', + }, + { + name: 'someBool', + type: 'bool', + }, + ], + name: 'order', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + export const staticArrayAbi = { constant: false, inputs: [ @@ -171,10 +204,10 @@ export const crazyAbi = { name: 'someDynamicArrayWithDynamicMembers', type: 'bytes[]', }, - { + /* { name: 'some2DArray', type: 'string[][]', - }, + }, */ { name: 'someTuple', type: 'tuple', -- cgit v1.2.3 From 687e6ccdd37da7c35a3fafac43f0fdff03351c0c Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 8 Nov 2018 11:33:51 -0800 Subject: Got the crazy ABI working --- packages/order-utils/test/abi_encoder.ts | 44 ++++++++++++++++++++++----- packages/order-utils/test/abi_encoder_test.ts | 4 +-- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/packages/order-utils/test/abi_encoder.ts b/packages/order-utils/test/abi_encoder.ts index 853aac627..ae573b39f 100644 --- a/packages/order-utils/test/abi_encoder.ts +++ b/packages/order-utils/test/abi_encoder.ts @@ -89,6 +89,10 @@ class Memblock { public getSection(): CalldataSection { return this.location.calldataSection; } + + public getName(): string { + return this.dataType.getDataItem().name; + } } interface BindList { @@ -120,13 +124,18 @@ export class Calldata { throw `Rebind on ${dataType.getId()}`; } const memblock = new Memblock(dataType); + + this.params.push(memblock); + memblock.assignLocation(section, new BigNumber(0), this.currentParamOffset); + + console.log(`Binding ${dataType.getDataItem().name} to PARAMS at ${this.currentParamOffset}`); + this.currentParamOffset = this.currentParamOffset.plus(memblock.getSize()); + console.log("CURRENT PARAM OFFSET -------- 0x", this.currentParamOffset.toString(16)); + + /* switch (section) { case CalldataSection.PARAMS: - this.params.push(memblock); - memblock.assignLocation(section, new BigNumber(0), this.currentParamOffset); - - console.log(`Binding ${dataType.getDataItem().name} to PARAMS at ${this.currentParamOffset}`); - this.currentParamOffset = this.currentParamOffset.plus(memblock.getSize()); + ; break; case CalldataSection.DATA: @@ -143,7 +152,7 @@ export class Calldata { default: throw `Unrecognized calldata section: ${section}`; - } + }*/ this.bindList[dataType.getId()] = memblock; dataType.rbind(memblock); @@ -160,6 +169,26 @@ export class Calldata { return hexValue; } + + public printAnnotated() { + let hexValue = `${this.selector}`; + console.log(hexValue); + let offset = new BigNumber(0); + _.each(this.params, (memblock: Memblock) => { + const offsetStr = `0x${offset.toString(16)}`; + const hexValue = memblock.get(); + const annotation = memblock.getName(); + + console.log(`${offsetStr} ${hexValue} ${annotation}`); + }); + _.each(this.data, (memblock: Memblock) => { + const offsetStr = `0x${offset.toString(16)}`; + const hexValue = memblock.get(); + const annotation = memblock.getName(); + + console.log(`${offsetStr} ${hexValue} ${annotation}`); + }); + } } export abstract class DataType { @@ -793,7 +822,7 @@ export class Tuple extends DynamicDataType { name: `${this.getDataItem().name}.${dataItem.name}`, } as DataItem; const child = DataTypeFactory.create(childDataItem, this); - this.childMap[dataItem.name] = this.children.length; + this.childMap[dataItem.name] = this.members.length; if (child instanceof Pointer) { this.children.push(child.getChildren()[0]); @@ -1065,6 +1094,7 @@ export class Method extends DataType { public encode(args: any[]): string { this.assignValue(args); const calldata = new Calldata(this.selector, this.params.length); + calldata.printAnnotated(); this.bind(calldata, CalldataSection.PARAMS); return calldata.getHexValue(); diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 070bfe7f6..553fac43e 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -22,7 +22,7 @@ describe.only('ABI Encoder', () => { describe.only('Just a Greg, Eh', () => { - it.skip('Crazy ABI', async () => { + it('Crazy ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi); console.log(method.getSignature()); @@ -211,7 +211,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it.only('Static Tuple', async () => { + it('Static Tuple', async () => { // This is dynamic because it has dynamic members const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi); const calldata = method.encode([[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]); -- cgit v1.2.3 From e59669c94937ec694d9e41c1a163a8a6f770fd32 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 8 Nov 2018 12:47:30 -0800 Subject: multidimensional arrays with static type --- packages/order-utils/test/abi_encoder.ts | 1 - packages/order-utils/test/abi_encoder_test.ts | 40 ++++++++++++ packages/order-utils/test/abi_samples.ts | 87 +++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) diff --git a/packages/order-utils/test/abi_encoder.ts b/packages/order-utils/test/abi_encoder.ts index ae573b39f..f63dc804a 100644 --- a/packages/order-utils/test/abi_encoder.ts +++ b/packages/order-utils/test/abi_encoder.ts @@ -690,7 +690,6 @@ export class SolArray extends DynamicDataType { this.elements.push(child); if (child instanceof Pointer) { const pointsTo = child.getChildren()[0]; - console.log(JSON.stringify(pointsTo)); this.children.push(pointsTo); // DataType pointing to } } diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 553fac43e..115580624 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -134,6 +134,46 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata);*/ }); + it.only('Multidimensional Arrays / Static Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi); + + // Eight 3-dimensional arrays of uint8[2][2][2] + let value = 0; + const args = []; + for (let i = 0; i < 8; ++i) { + args.push( + [ + [ + [new BigNumber(++value), new BigNumber(++value)], + [new BigNumber(++value), new BigNumber(++value)], + ], + [ + [new BigNumber(++value), new BigNumber(++value)], + [new BigNumber(++value), new BigNumber(++value)], + ] + ] + ); + } + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; expect(calldata).to.be.equal(expectedCalldata); + }); + + it('Fixed Lenfgth Array / Dynamic Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); + const args = [["Brave", "New", "World"]]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + const expectedCalldata = + '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + it('Fixed Lenfgth Array / Dynamic Members', async () => { const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); const args = [["Brave", "New", "World"]]; diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts index fb5cfbeb7..cbbc5f8f3 100644 --- a/packages/order-utils/test/abi_samples.ts +++ b/packages/order-utils/test/abi_samples.ts @@ -34,6 +34,93 @@ export const stringAbi = { type: 'function', } as MethodAbi; +export const multiDimensionalArraysStaticTypeAbi = { + constant: false, + inputs: [ + { + name: 'a', + type: 'uint8[][][]', + }, + { + name: 'b', + type: 'uint8[][][2]', + }, + { + name: 'c', + type: 'uint8[][2][]', + }, + { + name: 'd', + type: 'uint8[2][][]', + }, + { + name: 'e', + type: 'uint8[][2][2]', + }, + { + name: 'f', + type: 'uint8[2][2][]', + }, + { + name: 'g', + type: 'uint8[2][][2]', + }, + { + name: 'h', + type: 'uint8[2][2][2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const multiDimensionalArraysDynamicTypeAbi = { + constant: false, + inputs: [ + { + name: 'a', + type: 'string[][][2]', + }, + { + name: 'a', + type: 'string[][1][]', + }, + { + name: 'a', + type: 'string[1][1][2]', + }, + { + name: 'a', + type: 'string[][][]', + }, + { + name: 'a', + type: 'uint[][][]', + }, + { + name: 'b', + type: 'uint8[][2][]', + }, + { + name: 'c', + type: 'uint8[1][2][]', + }, + + { + name: 'c', + type: 'uint8[1][2][2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + export const dynamicTupleAbi = { constant: false, inputs: [ -- cgit v1.2.3 From 180d1ca63a45ca96df2c9f811075411ca495f693 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 8 Nov 2018 13:09:03 -0800 Subject: multidimensional arrays with dynamic objects --- packages/order-utils/test/abi_encoder_test.ts | 30 +++++++++++++++++++++++++++ packages/order-utils/test/abi_samples.ts | 25 ++++------------------ 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 115580624..4ba9bb611 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -160,6 +160,36 @@ describe.only('ABI Encoder', () => { console.log(method.getSignature()); console.log(JSON.stringify(args)); const expectedCalldata = '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; expect(calldata).to.be.equal(expectedCalldata); + expect(calldata).to.be.equal(expectedCalldata); + }); + + it.only('Multidimensional Arrays / Dynamic Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysDynamicTypeAbi); + + // Eight 3-dimensional arrays of string[2][2][2] + let value = 0; + const args = []; + for (let i = 0; i < 4; ++i) { + args.push( + [ + [ + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + ], + [ + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + ] + ] + ); + } + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); }); it('Fixed Lenfgth Array / Dynamic Members', async () => { diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts index cbbc5f8f3..0882b389a 100644 --- a/packages/order-utils/test/abi_samples.ts +++ b/packages/order-utils/test/abi_samples.ts @@ -80,38 +80,21 @@ export const multiDimensionalArraysStaticTypeAbi = { export const multiDimensionalArraysDynamicTypeAbi = { constant: false, inputs: [ - { - name: 'a', - type: 'string[][][2]', - }, - { - name: 'a', - type: 'string[][1][]', - }, - { - name: 'a', - type: 'string[1][1][2]', - }, { name: 'a', type: 'string[][][]', }, - { - name: 'a', - type: 'uint[][][]', - }, { name: 'b', - type: 'uint8[][2][]', + type: 'string[][][2]', }, { name: 'c', - type: 'uint8[1][2][]', + type: 'string[][2][]', }, - { - name: 'c', - type: 'uint8[1][2][2]', + name: 'h', + type: 'string[2][2][2]', }, ], name: 'simpleFunction', -- cgit v1.2.3 From 8f61f6d0f9532e95fd32cbab8dd0344b42de2da7 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 8 Nov 2018 13:41:58 -0800 Subject: Arrays of tuples --- packages/order-utils/test/abi_encoder.ts | 7 ++ packages/order-utils/test/abi_encoder_test.ts | 76 ++++++++++++- packages/order-utils/test/abi_samples.ts | 150 ++++++++++++++++++++++++++ 3 files changed, 231 insertions(+), 2 deletions(-) diff --git a/packages/order-utils/test/abi_encoder.ts b/packages/order-utils/test/abi_encoder.ts index f63dc804a..83ecc2f9a 100644 --- a/packages/order-utils/test/abi_encoder.ts +++ b/packages/order-utils/test/abi_encoder.ts @@ -686,6 +686,10 @@ export class SolArray extends DynamicDataType { type: this.type, name: `${this.getDataItem().name}[${idx.toString(10)}]`, } as DataItem; + const components = this.getDataItem().components; + if (components !== undefined) { + childDataItem.components = components; + } const child = DataTypeFactory.create(childDataItem, this); this.elements.push(child); if (child instanceof Pointer) { @@ -706,6 +710,9 @@ export class SolArray extends DynamicDataType { }*/ public assignValue(value: any[]) { + console.log('GREG'.repeat(15), JSON.stringify(value)); + + // Sanity check length const valueLength = new BigNumber(value.length); if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 4ba9bb611..cea33112d 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -134,7 +134,79 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata);*/ }); - it.only('Multidimensional Arrays / Static Members', async () => { + it('Array of Static Tuples (Array has defined length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDefinedLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it('Array of Static Tuples (Array has dynamic length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDynamicLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it('Array of Dynamic Tuples (Array has defined length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithDefinedLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it('Array of Dynamic Tuples (Array has dynamic length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithUndefinedLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it('Multidimensional Arrays / Static Members', async () => { const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi); // Eight 3-dimensional arrays of uint8[2][2][2] @@ -163,7 +235,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it.only('Multidimensional Arrays / Dynamic Members', async () => { + it('Multidimensional Arrays / Dynamic Members', async () => { const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysDynamicTypeAbi); // Eight 3-dimensional arrays of string[2][2][2] diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts index 0882b389a..7939cbaeb 100644 --- a/packages/order-utils/test/abi_samples.ts +++ b/packages/order-utils/test/abi_samples.ts @@ -129,6 +129,156 @@ export const dynamicTupleAbi = { type: 'function', } as MethodAbi; +export const arrayOfStaticTuplesWithDefinedLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + ], + name: 'order', + type: 'tuple[8]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfStaticTuplesWithDynamicLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfDynamicTuplesWithDefinedLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[8]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfDynamicTuplesWithUndefinedLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfDynamicTuplesAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const multidimensionalArrayOfDynamicTuplesAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[][2][]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + export const staticTupleAbi = { constant: false, inputs: [ -- cgit v1.2.3 From 8b91727364c205cd2bd44abdabd62c1044dd13d4 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 8 Nov 2018 13:57:36 -0800 Subject: types with default widths --- packages/order-utils/test/abi_encoder.ts | 22 +++++++---------- packages/order-utils/test/abi_encoder_test.ts | 12 +++++++++ packages/order-utils/test/abi_samples.ts | 35 +++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/packages/order-utils/test/abi_encoder.ts b/packages/order-utils/test/abi_encoder.ts index 83ecc2f9a..bcd427dee 100644 --- a/packages/order-utils/test/abi_encoder.ts +++ b/packages/order-utils/test/abi_encoder.ts @@ -781,20 +781,16 @@ export class SolArray extends DynamicDataType { } public getSignature(): string { - let type = this.type; - if (this.type === 'tuple') { - let tupleDataItem = { - type: 'tuple', - name: 'N/A', - } as DataItem; - const tupleComponents = this.getDataItem().components; - if (tupleComponents !== undefined) { - tupleDataItem.components = tupleComponents; - } - const tuple = new Tuple(tupleDataItem); - type = tuple.getSignature(); + let dataItem = { + type: this.type, + name: 'N/A', + } as DataItem; + const components = this.getDataItem().components; + if (components !== undefined) { + dataItem.components = components; } - + const elementDataType = DataTypeFactory.mapDataItemToDataType(dataItem); + const type = elementDataType.getSignature(); if (this.length.equals(SolArray.UNDEFINED_LENGTH)) { return `${type}[]`; } diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index cea33112d..ad43e70a3 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -134,6 +134,18 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata);*/ }); + it.only('Types with default widths', async () => { + const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); + const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + it('Array of Static Tuples (Array has defined length)', async () => { const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDefinedLengthAbi); diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts index 7939cbaeb..921408724 100644 --- a/packages/order-utils/test/abi_samples.ts +++ b/packages/order-utils/test/abi_samples.ts @@ -34,6 +34,41 @@ export const stringAbi = { type: 'function', } as MethodAbi; +export const typesWithDefaultWidthsAbi = { + constant: false, + inputs: [ + { + name: 'someUint', + type: 'uint', + }, + { + name: 'someInt', + type: 'int', + }, + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someUint', + type: 'uint[]', + }, + { + name: 'someInt', + type: 'int[]', + }, + { + name: 'someByte', + type: 'byte[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + export const multiDimensionalArraysStaticTypeAbi = { constant: false, inputs: [ -- cgit v1.2.3 From 1ca23b35ab067ed59a5d19543ceec646272e77a3 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 8 Nov 2018 14:23:41 -0800 Subject: complex type tesst --- packages/order-utils/test/abi_encoder_test.ts | 134 +++++++++++--------------- packages/order-utils/test/abi_samples.ts | 40 +++++--- 2 files changed, 80 insertions(+), 94 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index ad43e70a3..c8a0642ea 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -19,24 +19,23 @@ chaiSetup.configure(); const expect = chai.expect; describe.only('ABI Encoder', () => { - describe.only('Just a Greg, Eh', () => { - + describe.only('ABI Tests at Method Level', () => { it('Crazy ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi); console.log(method.getSignature()); - const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)], - [ + const someStaticArray = [new BigNumber(127), new BigNumber(14), new BigNumber(54)]; + const someStaticArrayWithDynamicMembers = [ 'the little piping piper piped a piping pipper papper', 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ], // l - [ + ]; + const someDynamicArrayWithDynamicMembers = [ '0x38745637834987324827439287423897238947239847', '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', - ], // m - /*[ + ]; + const some2DArray = [ [ 'some string', 'some another string', @@ -49,64 +48,32 @@ describe.only('ABI Encoder', () => { 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', ], [], - ],*/ // n - [ - new BigNumber(4037824789), - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ], // o - [ - new BigNumber('239048320948320948230', 10), - 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', - /*[ - [ - '23432423342', - 'skdjfhdsjkfdhsfkjsdhfjkdshfdsjkfhsdjkfhsdjkfhdsjkfhdsjfhsdfjdshjkfsdhf', - 'sdfsdfdfdffsdf', - ], - [], - [], - ['23ehsdjkfhsiufhwfuefhesfhauhesufheuifhsefushfsufehfeuif'], - ],*/ - '0xf74848484848484848484848484848484848483847576879809433994458585848932091', - '0xe41d2489571d322189246dafa5ebde1f4699f498', - ], // p - /*[ - [ - new BigNumber('23904848320948230', 10), - 'akdhjasshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', - [ - [ - '234324342', - 'skdjfhdsjkfdhsfkjsjkfhsdjkfhsdjkfhdsjkfhdsjfhsdfjdshjkfsdhf', - 'sdffdfdffsdf', - ], - [], - [], - ['23ehsdjkfhsiufhwfuefsufheuifhsefushfsufehfeuif'], - ], - '0xf7484848484848484848484848484876879809433994458585848932091', - '0xe41d2489571d322189246dafa6ebde1f4699f498', - ], - [ - new BigNumber('23904832094832030', 10), - 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdkdhsajkdhsadjk', - [ - [ - '2343342', - 'skdjfhdsjkfdhsfkjsdhfjkdshfdsjkfhsdjkfhsdjkfhdssjfhsdfjdshjkfsdhf', - 'sdfsdfdfdffsf', - ], - [], - [], - ['jkfhsiufhwfuefhesfhauhesufhefeuif'], - ], - '0xf7484848484848484848484848484848484848384757687980943091', - '0xe41d2489571d322189246dafa5ebde1f469af498', - ], - [], - [], - ],*/ ]; + const someTuple = { + someUint32: new BigNumber(4037824789), + someStr: 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.' + }; + const someTupleWithDynamicTypes = { + someUint: new BigNumber(4024789), + someStr: 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', + someBytes: '0x29384723894723843743289742389472398473289472348927489274894738427428947389facdea', + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', + }; + const someTupleWithDynamicTypes2 = { + someUint: new BigNumber(9024789), + someStr: 'ksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjkakdhjasjkdhasjkldshdjahdkjsahdaj', + someBytes: '0x29384723894398473289472348927489272384374328974238947274894738427428947389facde1', + someAddress: '0x746dafa5ebde1f4699f4981d3221892e41d24895', + }; + const someTupleWithDynamicTypes3 = { + someUint: new BigNumber(1024789), + someStr: 'sdhsajkdhsajkdhadjkashdjakdhjasjkdhasjkldshdjahdkjsahdajkksadhajkdhsajkdhsadjk', + someBytes: '0x38947238437432829384729742389472398473289472348927489274894738427428947389facdef', + someAddress: '0x89571d322189e415ebde1f4699f498d24246dafa', + }; + const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3]; + + const args = [someStaticArray, someStaticArrayWithDynamicMembers, someDynamicArrayWithDynamicMembers, some2DArray, someTuple, someTupleWithDynamicTypes, someArrayOfTuplesWithDynamicTypes]; const calldata = method.encode(args); console.log(calldata); @@ -114,27 +81,36 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); console.log(method.getSignature()); - const expectedCalldata = '0x76c14e63000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e728347239823742398472398472984724892749874897428472894723948749874984787432942374349234732984723984237489237489237489234723894728947894748937428947289473894274982374329874238947238947328947238943724982374982374289347239800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000272834732984732489237492387423987423984728947298432789423749823748923748927439820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; - + const expectedCalldata = '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; //const expectedCalldata = '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + }); - /* - const expectedCalldata = - '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata);*/ - - /*const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); - console.log(method.getSignature()); - console.log(method.selector); + it('Crazy ABI #1', async () => { + const method = new AbiEncoder.Method(AbiSamples.crazyAbi1); + const args = [ + new BigNumber(256745454), + new BigNumber(-256745454), + new BigNumber(434244), + '0x43', + '0x0001020304050607080911121314151617181920212223242526272829303132', + '0x0001020304050607080911121314151617181920212223242526272829303132080911121314151617181920212223242526272829303132', + 'Little peter piper piped a piping pepper pot', + '0xe41d2489571d322189246dafa5ebde1f4699f498', + true + ]; + const calldata = method.encode(args); console.log(calldata); - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata);*/ + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); }); - it.only('Types with default widths', async () => { + + it('Types with default widths', async () => { const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; const calldata = method.encode(args); diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts index 921408724..82427986a 100644 --- a/packages/order-utils/test/abi_samples.ts +++ b/packages/order-utils/test/abi_samples.ts @@ -407,10 +407,10 @@ export const dynamicArrayStaticMembersAbi = { type: 'function', } as MethodAbi; -export const crazyAbi = { +export const crazyAbi1 = { constant: false, inputs: [ - /*{ + { name: 'someUInt256', type: 'uint256', }, @@ -437,16 +437,26 @@ export const crazyAbi = { { name: 'someString', type: 'string', - },*/ - /*{ + }, + { name: 'someAddress', type: 'address', }, { name: 'someBool', type: 'bool', - },*/ + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; +export const crazyAbi = { + constant: false, + inputs: [ { name: 'someStaticArray', type: 'uint8[3]', @@ -459,10 +469,10 @@ export const crazyAbi = { name: 'someDynamicArrayWithDynamicMembers', type: 'bytes[]', }, - /* { + { name: 'some2DArray', type: 'string[][]', - }, */ + }, { name: 'someTuple', type: 'tuple', @@ -489,10 +499,10 @@ export const crazyAbi = { name: 'someStr', type: 'string', }, - //{ - // name: 'someStrArray', - // type: 'string[]', - /// }, + /*{ + name: 'someStrArray', + type: 'string[]', + },*/ { name: 'someBytes', type: 'bytes', @@ -502,7 +512,7 @@ export const crazyAbi = { type: 'address', }, ], - } /*, + }, { name: 'someArrayOfTuplesWithDynamicTypes', type: 'tuple[]', @@ -515,10 +525,10 @@ export const crazyAbi = { name: 'someStr', type: 'string', }, - { + /*{ name: 'someStrArray', type: 'string[]', - }, + },*/ { name: 'someBytes', type: 'bytes', @@ -528,7 +538,7 @@ export const crazyAbi = { type: 'address', }, ], - },*/, + } ], name: 'simpleFunction', outputs: [], -- cgit v1.2.3 From 78498b7019e02a9b280cc1ffe9fafbf30bff24ec Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 16:21:31 -0800 Subject: Initial port -- wont compile yet --- packages/order-utils/test/abi/abi_encoder.ts | 1 + packages/order-utils/test/abi/calldata.ts | 177 ++++++++++ packages/order-utils/test/abi/config.ts | 4 + packages/order-utils/test/abi/data_type.ts | 278 +++++++++++++++ packages/order-utils/test/abi/evm_data_types.ts | 439 ++++++++++++++++++++++++ 5 files changed, 899 insertions(+) create mode 100644 packages/order-utils/test/abi/abi_encoder.ts create mode 100644 packages/order-utils/test/abi/calldata.ts create mode 100644 packages/order-utils/test/abi/config.ts create mode 100644 packages/order-utils/test/abi/data_type.ts create mode 100644 packages/order-utils/test/abi/evm_data_types.ts diff --git a/packages/order-utils/test/abi/abi_encoder.ts b/packages/order-utils/test/abi/abi_encoder.ts new file mode 100644 index 000000000..12755209a --- /dev/null +++ b/packages/order-utils/test/abi/abi_encoder.ts @@ -0,0 +1 @@ +export * from './calldata_block'; \ No newline at end of file diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts new file mode 100644 index 000000000..dc0fcfb2b --- /dev/null +++ b/packages/order-utils/test/abi/calldata.ts @@ -0,0 +1,177 @@ +import ethUtil = require('ethereumjs-util'); +var _ = require('lodash'); + +export abstract class CalldataBlock { + private name: string; + private signature: string; + private offsetInBytes: number; + private headerSizeInBytes: number; + private bodySizeInBytes: number; + private relocatable: boolean; + + constructor(name: string, signature: string, offsetInBytes: number, headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { + this.name = name; + this.signature = signature; + this.offsetInBytes = offsetInBytes; + this.headerSizeInBytes = headerSizeInBytes; + this.bodySizeInBytes = bodySizeInBytes; + this.relocatable = relocatable; + } + + public getName(): string { + return this.name; + } + + public getSignature(): string { + return this.signature; + } + + public isRelocatable(): boolean { + return this.relocatable; + } + + public getHeaderSizeInBytes(): number { + return this.headerSizeInBytes; + } + + public getBodySizeInBytes(): number { + return this.bodySizeInBytes; + } + + public getSizeInBytes(): number { + return this.headerSizeInBytes + this.bodySizeInBytes; + } + + public getOffsetInBytes(): number { + return this.offsetInBytes; + } + + public abstract toHexString(): string; +} + +export class PayloadCalldataBlock extends CalldataBlock { + private payload: Buffer; + + constructor(name: string, signature: string, offsetInBytes: number, relocatable: boolean, payload: Buffer) { + const headerSizeInBytes = 0; + const bodySizeInBytes = payload.byteLength; + super(name, signature, offsetInBytes, headerSizeInBytes, bodySizeInBytes, relocatable); + this.payload = payload; + } + + public toHexString(): string { + const payloadHex = ethUtil.bufferToHex(this.payload); + return payloadHex; + } +} + +export class DependentCalldataBlock extends CalldataBlock { + public static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + private parent: CalldataBlock; + private dependency: CalldataBlock; + + constructor(name: string, signature: string, offsetInBytes: number, relocatable: boolean, parent: CalldataBlock, dependency: CalldataBlock) { + const headerSizeInBytes = 0; + const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; + super(name, signature, offsetInBytes, headerSizeInBytes, bodySizeInBytes, relocatable); + this.parent = parent; + this.dependency = dependency; + } + + public toHexString(): string { + const dependencyOffset = this.dependency.getOffsetInBytes(); + const parentOffset = this.parent.getOffsetInBytes(); + const parentHeaderSize = this.parent.getHeaderSizeInBytes(); + const pointer = dependencyOffset - parentOffset + parentHeaderSize; + const pointerBuf = new Buffer(`0x${pointer.toString(16)}`); + const evmWordWidthInBytes = 32; + const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); + const pointerHex = ethUtil.bufferToHex(pointerBufPadded); + return pointerHex; + } +} + +export class MemberCalldataBlock extends CalldataBlock { + private static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + private header: Buffer | undefined; + private members: CalldataBlock[]; + + constructor(name: string, signature: string, offsetInBytes: number, relocatable: boolean, members: CalldataBlock[], header?: Buffer) { + const headerSizeInBytes = (header === undefined) ? 0 : header.byteLength; + let bodySizeInBytes = 0; + _.each(members, (member: Memblock) => { + bodySizeInBytes += member.getSizeInBytes(); + }); + + super(name, signature, offsetInBytes, headerSizeInBytes, bodySizeInBytes, relocatable); + this.members = members; + this.header = header; + } + + public toHexString(): string { + let valueBuffers: Buffer[] = []; + if (this.header !== undefined) valueBuffers.push(this.header); + _.each(this.members, (member: CalldataBlock) => { + const memberHexString = member.toHexString(); + const memberBuf = ethUtil.toBuffer(memberHexString); + valueBuffers.push(memberBuf); + }); + const combinedValueBufs = Buffer.concat(valueBuffers); + const combinedValuesAsHex = ethUtil.bufferToHex(combinedValueBufs); + return combinedValuesAsHex; + } +} + +export class Calldata { + private selector: string; + private sizeInBytes: number; + private blocks: CalldataBlock[]; + + constructor() { + this.selector = '0x'; + this.sizeInBytes = 0; + this.blocks = []; + } + + public toHexString(): string { + let calldataString = `${this.selector}`; + _.each(this.blocks, (block: CalldataBlock) => { + const blockAsHexString = block.toHexString(); + const blockAsHexWithoutPrefix = ethUtil.stripHexPrefix(blockAsHexString); + calldataString += blockAsHexWithoutPrefix; + }); + return calldataString; + } + + public getSelectorHex(): string { + return this.selector; + } + + public getSizeInBytes(): number { + return this.sizeInBytes; + } + + public toAnnotatedString(): string { + return ""; + } + + public pushBlock(block: CalldataBlock) { + this.blocks.push(block); + this.sizeInBytes += block.getSizeInBytes(); + } + + public setSelector(selector: string) { + // Ensure we have a 0x prefix + if (selector.startsWith('0x')) { + this.selector = selector; + } else { + this.selector = `$0x${selector}`; + } + + // The selector must be 10 characters: '0x' followed by 4 bytes (two hex chars per byte) + if (this.selector.length !== 10) { + throw new Error(`Invalid selector '${this.selector}'`); + } + this.sizeInBytes += 8; + } +} \ No newline at end of file diff --git a/packages/order-utils/test/abi/config.ts b/packages/order-utils/test/abi/config.ts new file mode 100644 index 000000000..015cee59a --- /dev/null +++ b/packages/order-utils/test/abi/config.ts @@ -0,0 +1,4 @@ +import { DataTypeFactory } from './data_type'; +import { EvmDataTypeFactoryImpl } from './evm_data_types'; + +DataTypeFactory.setImpl(new EvmDataTypeFactoryImpl()); \ No newline at end of file diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts new file mode 100644 index 000000000..f74621085 --- /dev/null +++ b/packages/order-utils/test/abi/data_type.ts @@ -0,0 +1,278 @@ +import { Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock } from "./calldata"; +import { MethodAbi, DataItem } from 'ethereum-types'; +import { BigNumber } from '@0x/utils'; +import ethUtil = require('ethereumjs-util'); +var _ = require('lodash'); + + + +export abstract class DataType { + private dataItem: DataItem; + + constructor(dataItem: DataItem) { + this.dataItem = dataItem; + } + + public getDataItem(): DataItem { + return this.dataItem; + } + + protected abstract createCalldataBlock(): CalldataBlock; + public abstract encode(value: any, calldata: Calldata): void; + public abstract getSignature(): string; + public abstract isStatic(): boolean; +} + +export abstract class PayloadDataType extends DataType { + protected hasConstantSize: boolean; + + public constructor(dataItem: DataItem, hasConstantSize: boolean) { + super(dataItem); + this.hasConstantSize = hasConstantSize; + } + + protected generateCalldataBlock(payload: Buffer, calldata: Calldata): void { + const name = this.getDataItem().name; + const signature = this.getSignature(); + const offsetInBytes = calldata.getSizeInBytes(); + const relocatable = false; + const block = new PayloadCalldataBlock(name, signature, offsetInBytes, relocatable, payload); + calldata.pushBlock(block); + } + + public isStatic(): boolean { + // If a payload has a constant size then it's static + return this.hasConstantSize; + } +} + +export abstract class DependentDataType extends DataType { + protected dependency: DataType; + protected parent: DataType; + + public constructor(dataItem: DataItem, dependency: DataType, parent: DataType) { + super(dataItem); + this.dependency = dependency; + this.parent = parent; + } + + protected generateCalldataBlock(offsetInBytes: number, calldata: Calldata): CalldataBlock { + const name = this.getDataItem().name; + const signature = this.getSignature(); + const relocatable = false; + const parentBlock = calldata.lookupBlockByName(this.parent.getDataItem().name); + const dependencyBlock = calldata.lookupBlockByName(this.parent.getDataItem().name); + const block = new DependentCalldataBlock(name, signature, offsetInBytes, relocatable, dependencyBlock, parentBlock); + calldata.pushBlock(block); + } + + public encode(value: any, calldata: Calldata = new Calldata()): void { + const offsetInBytes = calldata.reserveSpace(DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES); + this.dependency.encode(value, calldata); + this.generateCalldataBlock(offsetInBytes, calldata); + } + + public isStatic(): boolean { + return true; + } +} + +export interface MemberMap { + [key: string]: number; +} + +export abstract class MemberDataType extends DataType { + private memberMap: MemberMap; + private members: DataType[]; + private isArray: boolean; + protected arrayLength: number | undefined; + + + public constructor(dataItem: DataItem, isArray: boolean = false, arrayLength?: number) { + super(dataItem); + + this.memberMap = {}; + this.members = []; + this.isArray = isArray; + this.arrayLength = arrayLength; + if (isArray && arrayLength !== undefined) { + [this.members, this.memberMap] = MemberDataType.createMembersWithLength(arrayLength); + } else if (!isArray) { + [this.members, this.memberMap] = MemberDataType.createMembersWithKeys(dataItem); + } + } + + private static createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { + // Sanity check + if (dataItem.components === undefined) { + throw new Error(`Expected components`); + } + + let members: DataType[] = []; + let memberMap: MemberMap = {}; + _.each(dataItem.components, (dataItem: DataItem) => { + const childDataItem = { + type: dataItem.type, + name: `${dataItem.name}.${dataItem.name}`, + } as DataItem; + const child = DataTypeFactory.create(childDataItem, this); + members.push(child); + memberMap[dataItem.name] = members.length; + }); + + return [members, memberMap]; + } + + private static createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { + let members: DataType[] = []; + let memberMap: MemberMap = {}; + const range = _.range(length); + _.each(range, (idx: number) => { + const childDataItem = { + type: this.type, + name: `${dataItem.name}[${idx.toString(10)}]`, + } as DataItem; + const components = dataItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = DataTypeFactory.create(childDataItem, this); + members.push(child); + memberMap[idx.toString(10)] = members.length; + }); + + return [members, memberMap]; + } + + protected encodeFromArray(value: any[], calldata: Calldata) { + // Sanity check length + const valueLength = new BigNumber(value.length); + if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { + throw new Error( + `Expected array of ${JSON.stringify( + this.length, + )} elements, but got array of length ${JSON.stringify(valueLength)}`, + ); + } + + // Assign values to children + for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { + const idxNumber = idx.toNumber(); + this.members[idxNumber].assignValue(value[idxNumber]); + } + } + + protected encodeFromObject(obj: object, calldata: Calldata) { + let childMap = _.cloneDeep(this.memberMap); + _.forOwn(obj, (value: any, key: string) => { + if (key in childMap === false) { + throw new Error(`Could not assign tuple to object: unrecognized key '${key}'`); + } + this.members[this.childMap[key]].assignValue(value); + delete childMap[key]; + }); + + if (Object.keys(childMap).length !== 0) { + throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); + } + } + + public encode(value: any[] | object, calldata = new Calldata()) { + if (value instanceof Array) { + this.encodeFromArray(value, calldata); + } else if (typeof value === 'object') { + this.encodeFromObject(value, encodeFromObject); + } else { + throw new Error(`Unexpected type for ${value}`); + } + } + + protected generateCalldataBlock(offsetInBytes: number, calldata: Calldata): CalldataBlock { + const name = this.getDataItem().name; + const signature = this.getSignature(); + const relocatable = false; + const parentBlock = calldata.lookupBlockByName(this.parent.getDataItem().name); + const dependencyBlock = calldata.lookupBlockByName(this.parent.getDataItem().name); + const block = new DependentCalldataBlock(name, signature, offsetInBytes, relocatable, dependencyBlock, parentBlock); + calldata.pushBlock(block); + } + + protected computeSignatureOfMembers(): string { + // Compute signature of members + let signature = `(`; + _.each(this.members, (member: DataType, i: number) => { + signature += member.getSignature(); + if (i < this.members.length - 1) { + signature += ','; + } + }); + signature += ')'; + return signature; + } + + public isStatic(): boolean { + /* For Tuple: + const isStaticTuple = this.children.length === 0; + return isStaticTuple; // @TODO: True in every case or only when dynamic data? + + For Array: + if isLengthDefined = false then this is false + + Otherwise if the first element is a Pointer then false + */ + + if (this.isArray && this.arrayLength === undefined) { + return true; + } + + // Search for dependent members + const dependentMember = _.find(this.members, (member: DataType) => { + return (member instanceof DependentDataType); + }); + const isStatic = (dependentMember === undefined); // static if we couldn't find a dependent member + return isStatic; + } +} + +export interface DataTypeFactoryImpl { + create: (dataItem: DataItem, parentDataType: DataType) => DataType; + mapDataItemToDataType: (dataItem: DataItem) => DataType; +} + +export class DataTypeFactory { + private static instance: DataTypeFactory; + private provider: DataTypeFactoryImpl | undefined; + + private constructor() { } + + private static getInstance(): DataTypeFactory { + if (!DataTypeFactory.instance) { + DataTypeFactory.instance = new DataTypeFactory(); + } + return DataTypeFactory.instance; + } + + public static setImpl(provider: DataTypeFactoryImpl) { + const instance = DataTypeFactory.getInstance(); + if (instance.provider !== undefined) { + throw new Error(`Tried to set implementation more than once`); + } + DataTypeFactory.getInstance().provider = provider; + } + + public static create(dataItem: DataItem, parentDataType: DataType): DataType { + const instance = DataTypeFactory.getInstance(); + if (instance.provider === undefined) { + throw new Error(`Tried to create before implementation is set`); + } + return instance.provider.create(dataItem, parentDataType); + } + + public static mapDataItemToDataType(dataItem: DataItem): DataType { + const instance = DataTypeFactory.getInstance(); + if (instance.provider === undefined) { + throw new Error(`Tried to create before implementation is set`); + } + return instance.provider.mapDataItemToDataType(dataItem); + } +} \ No newline at end of file diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts new file mode 100644 index 000000000..4e42a992a --- /dev/null +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -0,0 +1,439 @@ +import { DataType, DataTypeFactory, DataTypeFactoryImpl, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; + +import { MethodAbi, DataItem } from 'ethereum-types'; + +import ethUtil = require('ethereumjs-util'); + +import { BigNumber } from '@0x/utils'; + +export interface DataTypeStaticInterface { + matchGrammar: (type: string) => boolean; + encodeValue: (value: any) => Buffer; +} + +export class Address extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + + constructor(dataItem: DataItem) { + super(dataItem, Address.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Address.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); + } + } + + public getSignature(): string { + return 'address'; + } + + public static matchGrammar(type: string): boolean { + return type === 'address'; + } + + public static encodeValue(value: boolean): Buffer { + const evmWordWidth = 32; + const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth); + return encodedValueBuf; + } +} + +export class Bool extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + + constructor(dataItem: DataItem) { + super(dataItem, Bool.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Bool.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); + } + } + + public getSignature(): string { + return 'bool'; + } + + public static matchGrammar(type: string): boolean { + return type === 'bool'; + } + + public static encodeValue(value: boolean): Buffer { + const evmWordWidth = 32; + const encodedValue = value === true ? '0x1' : '0x0'; + const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth); + return encodedValueBuf; + } +} + +abstract class Number extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + static MAX_WIDTH: number = 256; + static DEFAULT_WIDTH: number = Number.MAX_WIDTH; + width: number = Number.DEFAULT_WIDTH; + + constructor(dataItem: DataItem, matcher: RegExp) { + super(dataItem, Number.SIZE_KNOWN_AT_COMPILE_TIME); + const matches = matcher.exec(dataItem.type); + if (matches === null) { + throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); + } + if (matches !== null && matches.length === 2 && matches[1] !== undefined) { + this.width = parseInt(matches[1]); + } else { + this.width = 256; + } + } + + public static encodeValue(value: BigNumber): Buffer { + if (value.greaterThan(this.getMaxValue())) { + throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; + } else if (value.lessThan(this.getMinValue())) { + throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; + } + + const hexBase = 16; + const evmWordWidth = 32; + let valueBuf: Buffer; + if (value.greaterThanOrEqualTo(0)) { + valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); + } else { + // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. + // Step 1/3: Convert value to positive binary string + const binBase = 2; + const valueBin = value.times(-1).toString(binBase); + + // Step 2/3: Invert binary value + const bitsInEvmWord = 256; + let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, binBase); + + // Step 3/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const negativeValue = invertedValue.plus(1); + + // Convert the negated value to a hex string + valueBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), + evmWordWidth, + ); + } + + return valueBuf; + } + + public abstract getMaxValue(): BigNumber; + public abstract getMinValue(): BigNumber; +} + +export class Int extends Number { + static matcher = RegExp( + '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + constructor(dataItem: DataItem) { + super(dataItem, Int.matcher); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).times(-1); + } + + public getSignature(): string { + return `int${this.width}`; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class UInt extends Number { + static matcher = RegExp( + '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + constructor(dataItem: DataItem) { + super(dataItem, UInt.matcher); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(0); + } + + public getSignature(): string { + return `uint${this.width}`; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Byte extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + static matcher = RegExp( + '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', + ); + + static DEFAULT_WIDTH = 1; + width: number = Byte.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem, Byte.SIZE_KNOWN_AT_COMPILE_TIME); + const matches = Byte.matcher.exec(dataItem.type); + if (!Byte.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); + } + if (matches !== null && matches.length === 3 && matches[2] !== undefined) { + this.width = parseInt(matches[2]); + } else { + this.width = Byte.DEFAULT_WIDTH; + } + } + + public getSignature(): string { + // Note that `byte` reduces to `bytes1` + return `bytes${this.width}`; + } + + public static encodeValue(value: string | Buffer): Buffer { + // Convert value into a buffer and do bounds checking + const valueBuf = ethUtil.toBuffer(value); + if (valueBuf.byteLength > this.width) { + throw new Error( + `Tried to assign ${value} (${ + valueBuf.byteLength + } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, + ); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + // Store value as hex + const evmWordWidth = 32; + const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); + return paddedValue; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Bytes extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = Bytes.UNDEFINED_LENGTH; + + constructor(dataItem: DataItem) { + super(dataItem, Bytes.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Bytes.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Bytes with bad input: ${dataItem}`); + } + } + + public encodeValue(value: string | Buffer): Buffer { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); + } + const valueBuf = ethUtil.toBuffer(value); + if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + const wordsForValue = Math.ceil(valueBuf.byteLength / 32); + const paddedBytesForValue = wordsForValue * 32; + const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedBytesForValue); + const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); + const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); + return encodedValueBuf; + } + + public getSignature(): string { + return 'bytes'; + } + + public static matchGrammar(type: string): boolean { + return type === 'bytes'; + } +} + +export class SolString extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; + constructor(dataItem: DataItem) { + super(dataItem, SolString.SIZE_KNOWN_AT_COMPILE_TIME); + if (!SolString.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); + } + } + + public static encodeValue(value: string): Buffer { + const wordsForValue = Math.ceil(value.length / 32); + const paddedBytesForValue = wordsForValue * 32; + const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); + const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); + const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + return encodedValueBuf; + } + + public getSignature(): string { + return 'string'; + } + + public static matchGrammar(type: string): boolean { + return type === 'string'; + } +} + +export class Pointer extends DependentDataType { + + constructor(destDataType: DataType, parentDataType: DataType) { + const destDataItem = destDataType.getDataItem(); + const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; + super(dataItem, destDataType, parentDataType); + } + + public getSignature(): string { + return this.dependency.getSignature(); + } +} + +export class Tuple extends MemberDataType { + private tupleSignature: string; + + constructor(dataItem: DataItem) { + super(dataItem); + if (!Tuple.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); + } + this.tupleSignature = this.computeSignatureOfMembers(); + } + + public getSignature(): string { + return this.tupleSignature; + } + + public static matchGrammar(type: string): boolean { + return type === 'tuple'; + } +} + +export class SolArray extends MemberDataType { + static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); + private arraySignature: string; + private elementType: string; + + constructor(dataItem: DataItem) { + // Sanity check + const matches = SolArray.matcher.exec(dataItem.type); + if (matches === null || matches.length !== 3) { + throw new Error(`Could not parse array: ${dataItem.type}`); + } else if (matches[1] === undefined) { + throw new Error(`Could not parse array type: ${dataItem.type}`); + } else if (matches[2] === undefined) { + throw new Error(`Could not parse array length: ${dataItem.type}`); + } + + const isArray = true; + const arrayLength = (matches[2] === '') ? undefined : parseInt(matches[2], 10); + super(dataItem, isArray, arrayLength); + this.elementType = matches[1]; + this.arraySignature = this.computeSignature(); + } + + private computeSignature(): string { + let dataItem = { + type: this.elementType, + name: 'N/A', + } as DataItem; + const components = this.getDataItem().components; + if (components !== undefined) { + dataItem.components = components; + } + const elementDataType = DataTypeFactory.mapDataItemToDataType(dataItem); + const type = elementDataType.getSignature(); + if (this.arrayLength === undefined) { + return `${type}[]`; + } else { + return `${type}[${this.arrayLength}]`; + } + } + + public getSignature(): string { + return this.arraySignature; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Method extends MemberDataType { + private methodSignature: string; + private methodSelector: string; + + constructor(abi: MethodAbi) { + super({ type: 'method', name: abi.name }); + this.methodSignature = this.computeSignature(); + this.methodSelector = this.computeSelector(); + } + + private computeSignature(): string { + const memberSignature = this.computeSignatureOfMembers(); + const methodSignature = `${this.getDataItem().name}${memberSignature}`; + return methodSignature; + } + + private computeSelector(): string { + const signature = this.computeSignature(); + const selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(signature).slice(0, 4))); + return selector; + } + + public getSignature(): string { + return this.methodSignature; + } + + public getSelector(): string { + return this.methodSelector; + } +} + +export class EvmDataTypeFactoryImpl implements DataTypeFactoryImpl { + + public mapDataItemToDataType(dataItem: DataItem): DataType { + console.log(`Type: ${dataItem.type}`); + + if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); + if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); + if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); + if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); + if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); + if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); + if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); + if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); + if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); + //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); + //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); + + throw new Error(`Unrecognized data type: '${dataItem.type}'`); + } + + public create(dataItem: DataItem, parentDataType: DataType): DataType { + const dataType = this.mapDataItemToDataType(dataItem); + if (dataType.isStatic()) { + return dataType; + } + + const pointer = new Pointer(dataType, parentDataType); + return pointer; + } +} \ No newline at end of file -- cgit v1.2.3 From 2802aed79cac4e73e64e1f33e328bcd05a4daf8b Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 18:10:58 -0800 Subject: refactored implementation done but not tested --- packages/order-utils/test/abi/calldata.ts | 140 +++++++++++++++++------- packages/order-utils/test/abi/data_type.ts | 98 +++++++++-------- packages/order-utils/test/abi/evm_data_types.ts | 17 ++- 3 files changed, 167 insertions(+), 88 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index dc0fcfb2b..725bdce62 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -9,15 +9,23 @@ export abstract class CalldataBlock { private bodySizeInBytes: number; private relocatable: boolean; - constructor(name: string, signature: string, offsetInBytes: number, headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { + constructor(name: string, signature: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { this.name = name; this.signature = signature; - this.offsetInBytes = offsetInBytes; + this.offsetInBytes = 0; this.headerSizeInBytes = headerSizeInBytes; this.bodySizeInBytes = bodySizeInBytes; this.relocatable = relocatable; } + protected setHeaderSize(headerSizeInBytes: number) { + this.headerSizeInBytes = headerSizeInBytes; + } + + protected setBodySize(bodySizeInBytes: number) { + this.bodySizeInBytes = bodySizeInBytes; + } + public getName(): string { return this.name; } @@ -46,22 +54,25 @@ export abstract class CalldataBlock { return this.offsetInBytes; } - public abstract toHexString(): string; + public setOffset(offsetInBytes: number) { + this.offsetInBytes = offsetInBytes; + } + + public abstract toBuffer(): Buffer; } export class PayloadCalldataBlock extends CalldataBlock { private payload: Buffer; - constructor(name: string, signature: string, offsetInBytes: number, relocatable: boolean, payload: Buffer) { + constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { const headerSizeInBytes = 0; const bodySizeInBytes = payload.byteLength; - super(name, signature, offsetInBytes, headerSizeInBytes, bodySizeInBytes, relocatable); + super(name, signature, /*offsetInBytes,*/ headerSizeInBytes, bodySizeInBytes, relocatable); this.payload = payload; } - public toHexString(): string { - const payloadHex = ethUtil.bufferToHex(this.payload); - return payloadHex; + public toBuffer(): Buffer { + return this.payload; } } @@ -70,15 +81,15 @@ export class DependentCalldataBlock extends CalldataBlock { private parent: CalldataBlock; private dependency: CalldataBlock; - constructor(name: string, signature: string, offsetInBytes: number, relocatable: boolean, parent: CalldataBlock, dependency: CalldataBlock) { + constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, parent: CalldataBlock, dependency: CalldataBlock) { const headerSizeInBytes = 0; const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; - super(name, signature, offsetInBytes, headerSizeInBytes, bodySizeInBytes, relocatable); + super(name, signature, /*offsetInBytes,*/ headerSizeInBytes, bodySizeInBytes, relocatable); this.parent = parent; this.dependency = dependency; } - public toHexString(): string { + public toBuffer(): Buffer { const dependencyOffset = this.dependency.getOffsetInBytes(); const parentOffset = this.parent.getOffsetInBytes(); const parentHeaderSize = this.parent.getHeaderSizeInBytes(); @@ -86,8 +97,11 @@ export class DependentCalldataBlock extends CalldataBlock { const pointerBuf = new Buffer(`0x${pointer.toString(16)}`); const evmWordWidthInBytes = 32; const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); - const pointerHex = ethUtil.bufferToHex(pointerBufPadded); - return pointerHex; + return pointerBufPadded; + } + + public getDependency(): CalldataBlock { + return this.dependency; } } @@ -96,51 +110,97 @@ export class MemberCalldataBlock extends CalldataBlock { private header: Buffer | undefined; private members: CalldataBlock[]; - constructor(name: string, signature: string, offsetInBytes: number, relocatable: boolean, members: CalldataBlock[], header?: Buffer) { - const headerSizeInBytes = (header === undefined) ? 0 : header.byteLength; + constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean) { + super(name, signature, /*offsetInBytes,*/ 0, 0, relocatable); + this.members = []; + this.header = undefined; + } + + public setMembers(members: CalldataBlock[]) { let bodySizeInBytes = 0; - _.each(members, (member: Memblock) => { + _.each(members, (member: CalldataBlock) => { bodySizeInBytes += member.getSizeInBytes(); }); - - super(name, signature, offsetInBytes, headerSizeInBytes, bodySizeInBytes, relocatable); this.members = members; + this.setBodySize(bodySizeInBytes); + } + + public setHeader(header: Buffer) { + this.setHeaderSize(header.byteLength); this.header = header; } - public toHexString(): string { - let valueBuffers: Buffer[] = []; - if (this.header !== undefined) valueBuffers.push(this.header); - _.each(this.members, (member: CalldataBlock) => { - const memberHexString = member.toHexString(); - const memberBuf = ethUtil.toBuffer(memberHexString); - valueBuffers.push(memberBuf); - }); - const combinedValueBufs = Buffer.concat(valueBuffers); - const combinedValuesAsHex = ethUtil.bufferToHex(combinedValueBufs); - return combinedValuesAsHex; + public toBuffer(): Buffer { + if (this.header !== undefined) return this.header; + return new Buffer(''); + } + + public getMembers(): CalldataBlock[] { + return this.members; + } +} + +class Queue { + private store: T[] = []; + push(val: T) { + this.store.push(val); + } + pop(): T | undefined { + return this.store.shift(); } } export class Calldata { private selector: string; private sizeInBytes: number; - private blocks: CalldataBlock[]; + private root: CalldataBlock | undefined; constructor() { this.selector = '0x'; this.sizeInBytes = 0; - this.blocks = []; + this.root = undefined; } public toHexString(): string { - let calldataString = `${this.selector}`; - _.each(this.blocks, (block: CalldataBlock) => { - const blockAsHexString = block.toHexString(); - const blockAsHexWithoutPrefix = ethUtil.stripHexPrefix(blockAsHexString); - calldataString += blockAsHexWithoutPrefix; - }); - return calldataString; + let selectorBuffer = ethUtil.toBuffer(this.selector); + if (this.root === undefined) { + throw new Error('expected root'); + } + + const blockQueue = new Queue(); + blockQueue.push(this.root); + + // Assign locations in breadth-first manner + let block: CalldataBlock | undefined; + let offset = 0; + while ((block = blockQueue.pop()) !== undefined) { + block.setOffset(offset); + if (block instanceof DependentCalldataBlock) { + blockQueue.push(block.getDependency()); + } else if (block instanceof MemberCalldataBlock) { + _.each(block.getMembers(), (member: CalldataBlock) => { + blockQueue.push(member); + }); + } + } + + // Fetch values using same technique + const valueBufs: Buffer[] = [selectorBuffer]; + while ((block = blockQueue.pop()) !== undefined) { + valueBufs.push(block.toBuffer()); + + if (block instanceof DependentCalldataBlock) { + blockQueue.push(block.getDependency()); + } else if (block instanceof MemberCalldataBlock) { + _.each(block.getMembers(), (member: CalldataBlock) => { + blockQueue.push(member); + }); + } + } + + const combinedBuffers = Buffer.concat(valueBufs); + const hexValue = ethUtil.bufferToHex(combinedBuffers); + return hexValue; } public getSelectorHex(): string { @@ -155,8 +215,8 @@ export class Calldata { return ""; } - public pushBlock(block: CalldataBlock) { - this.blocks.push(block); + public setRoot(block: CalldataBlock) { + this.root = block; this.sizeInBytes += block.getSizeInBytes(); } diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index f74621085..1a4610f7c 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -1,4 +1,4 @@ -import { Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock } from "./calldata"; +import { Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata"; import { MethodAbi, DataItem } from 'ethereum-types'; import { BigNumber } from '@0x/utils'; import ethUtil = require('ethereumjs-util'); @@ -17,7 +17,7 @@ export abstract class DataType { return this.dataItem; } - protected abstract createCalldataBlock(): CalldataBlock; + public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; public abstract encode(value: any, calldata: Calldata): void; public abstract getSignature(): string; public abstract isStatic(): boolean; @@ -31,19 +31,27 @@ export abstract class PayloadDataType extends DataType { this.hasConstantSize = hasConstantSize; } - protected generateCalldataBlock(payload: Buffer, calldata: Calldata): void { + public generateCalldataBlocks(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { + const encodedValue = this.encodeValue(value); const name = this.getDataItem().name; const signature = this.getSignature(); - const offsetInBytes = calldata.getSizeInBytes(); + // const offsetInBytes = calldata.getSizeInBytes(); const relocatable = false; - const block = new PayloadCalldataBlock(name, signature, offsetInBytes, relocatable, payload); - calldata.pushBlock(block); + const block = new PayloadCalldataBlock(name, signature, /*offsetInBytes,*/ relocatable, encodedValue); + return block; + } + + public encode(value: any, calldata: Calldata): void { + const block = this.generateCalldataBlock(value); + calldata.setRoot(block); } public isStatic(): boolean { // If a payload has a constant size then it's static return this.hasConstantSize; } + + public abstract encodeValue(value: any): Buffer; } export abstract class DependentDataType extends DataType { @@ -56,20 +64,21 @@ export abstract class DependentDataType extends DataType { this.parent = parent; } - protected generateCalldataBlock(offsetInBytes: number, calldata: Calldata): CalldataBlock { + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { + if (parentBlock === undefined) { + throw new Error(`DependentDataType requires a parent block to generate its block`); + } + const dependencyBlock = this.dependency.generateCalldataBlock(value); const name = this.getDataItem().name; const signature = this.getSignature(); const relocatable = false; - const parentBlock = calldata.lookupBlockByName(this.parent.getDataItem().name); - const dependencyBlock = calldata.lookupBlockByName(this.parent.getDataItem().name); - const block = new DependentCalldataBlock(name, signature, offsetInBytes, relocatable, dependencyBlock, parentBlock); - calldata.pushBlock(block); + const block = new DependentCalldataBlock(name, signature, /*offsetInBytes,*/ relocatable, dependencyBlock, parentBlock); + return block; } public encode(value: any, calldata: Calldata = new Calldata()): void { - const offsetInBytes = calldata.reserveSpace(DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES); - this.dependency.encode(value, calldata); - this.generateCalldataBlock(offsetInBytes, calldata); + const block = this.generateCalldataBlock(value); + calldata.setRoot(block); } public isStatic(): boolean { @@ -96,7 +105,7 @@ export abstract class MemberDataType extends DataType { this.isArray = isArray; this.arrayLength = arrayLength; if (isArray && arrayLength !== undefined) { - [this.members, this.memberMap] = MemberDataType.createMembersWithLength(arrayLength); + [this.members, this.memberMap] = MemberDataType.createMembersWithLength(dataItem, arrayLength); } else if (!isArray) { [this.members, this.memberMap] = MemberDataType.createMembersWithKeys(dataItem); } @@ -129,7 +138,7 @@ export abstract class MemberDataType extends DataType { const range = _.range(length); _.each(range, (idx: number) => { const childDataItem = { - type: this.type, + type: dataItem.type, name: `${dataItem.name}[${idx.toString(10)}]`, } as DataItem; const components = dataItem.components; @@ -144,57 +153,60 @@ export abstract class MemberDataType extends DataType { return [members, memberMap]; } - protected encodeFromArray(value: any[], calldata: Calldata) { + protected generateCalldataBlockFromArray(value: any[]): MemberCalldataBlock { // Sanity check length - const valueLength = new BigNumber(value.length); - if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { + if (this.arrayLength !== undefined && value.length !== this.arrayLength) { throw new Error( `Expected array of ${JSON.stringify( - this.length, - )} elements, but got array of length ${JSON.stringify(valueLength)}`, + this.arrayLength, + )} elements, but got array of length ${JSON.stringify(value.length)}`, ); } - // Assign values to children - for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { - const idxNumber = idx.toNumber(); - this.members[idxNumber].assignValue(value[idxNumber]); + let members = this.members; + if (this.arrayLength === undefined) { + [members,] = MemberDataType.createMembersWithLength(this.getDataItem(), value.length); } + + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), false); + const memberBlocks: CalldataBlock[] = []; + _.each(members, (member: DataType) => { + const block = member.generateCalldataBlock(value, methodBlock); + memberBlocks.push(block); + }); + methodBlock.setMembers(memberBlocks); + return methodBlock; } - protected encodeFromObject(obj: object, calldata: Calldata) { + protected generateCalldataBlockFromObject(obj: object): MemberCalldataBlock { + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), false); + const memberBlocks: CalldataBlock[] = []; let childMap = _.cloneDeep(this.memberMap); _.forOwn(obj, (value: any, key: string) => { if (key in childMap === false) { throw new Error(`Could not assign tuple to object: unrecognized key '${key}'`); } - this.members[this.childMap[key]].assignValue(value); + const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock); + memberBlocks.push(block); delete childMap[key]; }); if (Object.keys(childMap).length !== 0) { throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); } + + methodBlock.setMembers(memberBlocks); + return methodBlock; } - public encode(value: any[] | object, calldata = new Calldata()) { - if (value instanceof Array) { - this.encodeFromArray(value, calldata); - } else if (typeof value === 'object') { - this.encodeFromObject(value, encodeFromObject); - } else { - throw new Error(`Unexpected type for ${value}`); - } + public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value) : this.generateCalldataBlockFromObject(value, calldata); + return block; } - protected generateCalldataBlock(offsetInBytes: number, calldata: Calldata): CalldataBlock { - const name = this.getDataItem().name; - const signature = this.getSignature(); - const relocatable = false; - const parentBlock = calldata.lookupBlockByName(this.parent.getDataItem().name); - const dependencyBlock = calldata.lookupBlockByName(this.parent.getDataItem().name); - const block = new DependentCalldataBlock(name, signature, offsetInBytes, relocatable, dependencyBlock, parentBlock); - calldata.pushBlock(block); + public encode(value: any, calldata: Calldata = new Calldata()): void { + const block = this.generateCalldataBlock(value); + calldata.setRoot(block); } protected computeSignatureOfMembers(): string { diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index 4e42a992a..bdbaf5b3c 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -4,6 +4,8 @@ import { MethodAbi, DataItem } from 'ethereum-types'; import ethUtil = require('ethereumjs-util'); +import { Calldata } from './calldata'; + import { BigNumber } from '@0x/utils'; export interface DataTypeStaticInterface { @@ -29,7 +31,7 @@ export class Address extends PayloadDataType { return type === 'address'; } - public static encodeValue(value: boolean): Buffer { + public encodeValue(value: boolean): Buffer { const evmWordWidth = 32; const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth); return encodedValueBuf; @@ -54,7 +56,7 @@ export class Bool extends PayloadDataType { return type === 'bool'; } - public static encodeValue(value: boolean): Buffer { + public encodeValue(value: boolean): Buffer { const evmWordWidth = 32; const encodedValue = value === true ? '0x1' : '0x0'; const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth); @@ -81,7 +83,7 @@ abstract class Number extends PayloadDataType { } } - public static encodeValue(value: BigNumber): Buffer { + public encodeValue(value: BigNumber): Buffer { if (value.greaterThan(this.getMaxValue())) { throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; } else if (value.lessThan(this.getMinValue())) { @@ -204,7 +206,7 @@ export class Byte extends PayloadDataType { return `bytes${this.width}`; } - public static encodeValue(value: string | Buffer): Buffer { + public encodeValue(value: string | Buffer): Buffer { // Convert value into a buffer and do bounds checking const valueBuf = ethUtil.toBuffer(value); if (valueBuf.byteLength > this.width) { @@ -275,7 +277,7 @@ export class SolString extends PayloadDataType { } } - public static encodeValue(value: string): Buffer { + public encodeValue(value: string): Buffer { const wordsForValue = Math.ceil(value.length / 32); const paddedBytesForValue = wordsForValue * 32; const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); @@ -398,6 +400,11 @@ export class Method extends MemberDataType { return selector; } + public encode(value: any[] | object, calldata = new Calldata()) { + calldata.setSelector(this.methodSelector); + super.encode(value, calldata); + } + public getSignature(): string { return this.methodSignature; } -- cgit v1.2.3 From 1f7fbcf52ccc037c4c4bb49e629eecc83e5309e5 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 18:14:31 -0800 Subject: Should build? TBD! --- packages/order-utils/test/abi/abi_encoder.ts | 5 ++++- packages/order-utils/test/abi/evm_data_types.ts | 3 ++- packages/order-utils/test/abi_encoder_test.ts | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/order-utils/test/abi/abi_encoder.ts b/packages/order-utils/test/abi/abi_encoder.ts index 12755209a..ddcfb1fd1 100644 --- a/packages/order-utils/test/abi/abi_encoder.ts +++ b/packages/order-utils/test/abi/abi_encoder.ts @@ -1 +1,4 @@ -export * from './calldata_block'; \ No newline at end of file +export * from './config'; +export * from './calldata'; +export * from './data_type'; +export * from './evm_data_types'; \ No newline at end of file diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index bdbaf5b3c..15fe4c9e3 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -400,9 +400,10 @@ export class Method extends MemberDataType { return selector; } - public encode(value: any[] | object, calldata = new Calldata()) { + public encode(value: any[] | object, calldata = new Calldata()): string { calldata.setSelector(this.methodSelector); super.encode(value, calldata); + return calldata.toHexString(); } public getSignature(): string { diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index c8a0642ea..f46a1812c 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -12,7 +12,10 @@ import { MethodAbi, DataItem } from 'ethereum-types'; import { BigNumber } from '@0x/utils'; import { assert } from '@0x/order-utils/src/assert'; -import * as AbiEncoder from './abi_encoder'; +//import * as AbiEncoder from './abi_encoder'; + +import * as AbiEncoder from './abi/abi_encoder'; + import * as AbiSamples from './abi_samples'; chaiSetup.configure(); -- cgit v1.2.3 From 0835cf0ea2bb3c2c18d2a5d44ec914e2945af1b0 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 18:18:52 -0800 Subject: Refactor Builds! --- packages/order-utils/test/abi/data_type.ts | 14 +- packages/order-utils/test/abi/evm_data_types.ts | 8 +- packages/order-utils/test/abi_encoder.ts | 1152 ----------------------- packages/order-utils/test/abi_encoder_test.ts | 466 ++++----- 4 files changed, 247 insertions(+), 1393 deletions(-) delete mode 100644 packages/order-utils/test/abi_encoder.ts diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 1a4610f7c..201eb89f9 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -31,7 +31,7 @@ export abstract class PayloadDataType extends DataType { this.hasConstantSize = hasConstantSize; } - public generateCalldataBlocks(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { const encodedValue = this.encodeValue(value); const name = this.getDataItem().name; const signature = this.getSignature(); @@ -105,13 +105,13 @@ export abstract class MemberDataType extends DataType { this.isArray = isArray; this.arrayLength = arrayLength; if (isArray && arrayLength !== undefined) { - [this.members, this.memberMap] = MemberDataType.createMembersWithLength(dataItem, arrayLength); + [this.members, this.memberMap] = this.createMembersWithLength(dataItem, arrayLength); } else if (!isArray) { - [this.members, this.memberMap] = MemberDataType.createMembersWithKeys(dataItem); + [this.members, this.memberMap] = this.createMembersWithKeys(dataItem); } } - private static createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { + private createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { // Sanity check if (dataItem.components === undefined) { throw new Error(`Expected components`); @@ -132,7 +132,7 @@ export abstract class MemberDataType extends DataType { return [members, memberMap]; } - private static createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { + private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { let members: DataType[] = []; let memberMap: MemberMap = {}; const range = _.range(length); @@ -165,7 +165,7 @@ export abstract class MemberDataType extends DataType { let members = this.members; if (this.arrayLength === undefined) { - [members,] = MemberDataType.createMembersWithLength(this.getDataItem(), value.length); + [members,] = this.createMembersWithLength(this.getDataItem(), value.length); } const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), false); @@ -200,7 +200,7 @@ export abstract class MemberDataType extends DataType { } public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value) : this.generateCalldataBlockFromObject(value, calldata); + const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value) : this.generateCalldataBlockFromObject(value); return block; } diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index 15fe4c9e3..4dffedb8d 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -8,6 +8,8 @@ import { Calldata } from './calldata'; import { BigNumber } from '@0x/utils'; +var _ = require('lodash'); + export interface DataTypeStaticInterface { matchGrammar: (type: string) => boolean; encodeValue: (value: any) => Buffer; @@ -382,10 +384,14 @@ export class Method extends MemberDataType { private methodSignature: string; private methodSelector: string; + // TMP + public selector: string; + constructor(abi: MethodAbi) { super({ type: 'method', name: abi.name }); this.methodSignature = this.computeSignature(); - this.methodSelector = this.computeSelector(); + this.selector = this.methodSelector = this.computeSelector(); + } private computeSignature(): string { diff --git a/packages/order-utils/test/abi_encoder.ts b/packages/order-utils/test/abi_encoder.ts deleted file mode 100644 index bcd427dee..000000000 --- a/packages/order-utils/test/abi_encoder.ts +++ /dev/null @@ -1,1152 +0,0 @@ - -import * as chai from 'chai'; -import 'mocha'; -import ethUtil = require('ethereumjs-util'); - -var _ = require('lodash'); - -import { chaiSetup } from './utils/chai_setup'; - -import { MethodAbi, DataItem } from 'ethereum-types'; - -import { BigNumber } from '@0x/utils'; -import { bigNumberify } from 'ethers/utils'; - -chaiSetup.configure(); -const expect = chai.expect; - - -class Word { - private value: string; - - constructor(value?: string) { - if (value === undefined) { - this.value = ''; - } else { - this.value = value; - } - } - - public set(value: string) { - if (value.length !== 64) { - throw `Tried to create word that is not 32 bytes: ${value}`; - } - - this.value = value; - } - - public get(): string { - return this.value; - } - - public getAsHex(): string { - return `0x${this.value}`; - } -} - -export enum CalldataSection { - NONE, - PARAMS, - DATA, -} - -class Memblock { - private dataType: DataType; - private location: { calldataSection: CalldataSection; sectionOffset: BigNumber; offset: BigNumber }; - - constructor(dataType: DataType) { - this.dataType = dataType; - this.location = { - calldataSection: CalldataSection.NONE, - sectionOffset: new BigNumber(0), - offset: new BigNumber(0), - }; - } - - public getSize(): BigNumber { - return new BigNumber(ethUtil.toBuffer(this.dataType.getHexValue()).byteLength); - } - - public assignLocation(calldataSection: CalldataSection, sectionOffset: BigNumber, offset: BigNumber) { - this.location.calldataSection = calldataSection; - this.location.sectionOffset = sectionOffset; - this.location.offset = offset; - } - - public get(): string { - console.log(`Unstripped = '${this.dataType.getHexValue()}' and Stripped = '${ethUtil.stripHexPrefix(this.dataType.getHexValue())}'`); - return ethUtil.stripHexPrefix(this.dataType.getHexValue()); - } - - public getOffset(): BigNumber { - return this.location.offset; - } - - public getAbsoluteOffset(): BigNumber { - return this.location.sectionOffset.plus(this.location.offset); - } - - public getSection(): CalldataSection { - return this.location.calldataSection; - } - - public getName(): string { - return this.dataType.getDataItem().name; - } -} - -interface BindList { - [key: string]: Memblock; -} - -export class Calldata { - private selector: string; - private params: Memblock[]; - private data: Memblock[]; - private dataOffset: BigNumber; - private currentDataOffset: BigNumber; - private currentParamOffset: BigNumber; - private bindList: BindList; - - constructor(selector: string, nParams: number) { - this.selector = selector; - this.params = []; - this.data = []; - const evmWordSize = 32; - this.dataOffset = new BigNumber(nParams).times(evmWordSize); - this.currentDataOffset = new BigNumber(0); - this.currentParamOffset = new BigNumber(0); - this.bindList = {}; - } - - public bind(dataType: DataType, section: CalldataSection) { - if (dataType.getId() in this.bindList) { - throw `Rebind on ${dataType.getId()}`; - } - const memblock = new Memblock(dataType); - - this.params.push(memblock); - memblock.assignLocation(section, new BigNumber(0), this.currentParamOffset); - - console.log(`Binding ${dataType.getDataItem().name} to PARAMS at ${this.currentParamOffset}`); - this.currentParamOffset = this.currentParamOffset.plus(memblock.getSize()); - console.log("CURRENT PARAM OFFSET -------- 0x", this.currentParamOffset.toString(16)); - - /* - switch (section) { - case CalldataSection.PARAMS: - ; - break; - - case CalldataSection.DATA: - this.data.push(memblock); - memblock.assignLocation(section, this.dataOffset, this.currentDataOffset); - - console.log( - `Binding ${dataType.getDataItem().name} to DATA at ${memblock - .getAbsoluteOffset() - .toString(16)}`, - ); - this.currentDataOffset = this.currentDataOffset.plus(memblock.getSize()); - break; - - default: - throw `Unrecognized calldata section: ${section}`; - }*/ - - this.bindList[dataType.getId()] = memblock; - dataType.rbind(memblock); - } - - public getHexValue(): string { - let hexValue = `${this.selector}`; - _.each(this.params, (memblock: Memblock) => { - hexValue += memblock.get(); - }); - _.each(this.data, (memblock: Memblock) => { - hexValue += memblock.get(); - }); - - return hexValue; - } - - public printAnnotated() { - let hexValue = `${this.selector}`; - console.log(hexValue); - let offset = new BigNumber(0); - _.each(this.params, (memblock: Memblock) => { - const offsetStr = `0x${offset.toString(16)}`; - const hexValue = memblock.get(); - const annotation = memblock.getName(); - - console.log(`${offsetStr} ${hexValue} ${annotation}`); - }); - _.each(this.data, (memblock: Memblock) => { - const offsetStr = `0x${offset.toString(16)}`; - const hexValue = memblock.get(); - const annotation = memblock.getName(); - - console.log(`${offsetStr} ${hexValue} ${annotation}`); - }); - } -} - -export abstract class DataType { - private dataItem: DataItem; - private hexValue: string; - protected memblock: Memblock | undefined; - protected children: DataType[]; - - constructor(dataItem: DataItem) { - this.dataItem = dataItem; - this.hexValue = '0x'; - this.memblock = undefined; - this.children = []; - } - - protected assignHexValue(hexValue: string) { - this.hexValue = hexValue; - } - - public getHexValue(): string { - return this.hexValue; - } - - public getDataItem(): DataItem { - return this.dataItem; - } - - public rbind(memblock: Memblock) { - this.memblock = memblock; - } - - public bind(calldata: Calldata, section: CalldataSection) { - calldata.bind(this, section); - _.each(this.getChildren(), (child: DataType) => { - child.bind(calldata, CalldataSection.DATA); - }); - } - - public getId(): string { - return this.dataItem.name; - } - - public getOffset(): BigNumber { - if (this.memblock === undefined) return new BigNumber(0); - return this.memblock.getOffset(); - } - - public getAbsoluteOffset(): BigNumber { - if (this.memblock === undefined) return new BigNumber(0); - return this.memblock.getAbsoluteOffset(); - } - /* - public getSize(): BigNumber { - if (this.memblock === undefined) return new BigNumber(0); - return this.memblock.getSize(); - } - */ - - public getChildren(): DataType[] { - return this.children; - } - - public getSize(): BigNumber { - return this.getHeaderSize().plus(this.getBodySize()); - } - - public abstract assignValue(value: any): void; - public abstract getSignature(): string; - public abstract isStatic(): boolean; - public abstract getHeaderSize(): BigNumber; - public abstract getBodySize(): BigNumber; -} - -export abstract class StaticDataType extends DataType { - constructor(dataItem: DataItem) { - super(dataItem); - } -} - -export abstract class DynamicDataType extends DataType { - constructor(dataItem: DataItem) { - super(dataItem); - } -} - -export class Address extends StaticDataType { - constructor(dataItem: DataItem) { - super(dataItem); - expect(Address.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string) { - const evmWordWidth = 32; - const hexValue = ethUtil.bufferToHex(ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth)); - this.assignHexValue(hexValue); - } - - public getSignature(): string { - return `address`; - } - - public isStatic(): boolean { - return true; - } - - public getHeaderSize(): BigNumber { - return new BigNumber(0); - } - - public getBodySize(): BigNumber { - return new BigNumber(32); - } - - public static matchGrammar(type: string): boolean { - return type === 'address'; - } -} - -export class Bool extends StaticDataType { - constructor(dataItem: DataItem) { - super(dataItem); - expect(Bool.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: boolean) { - const evmWordWidth = 32; - const encodedValue = value === true ? '0x1' : '0x0'; - const hexValue = ethUtil.bufferToHex(ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth)); - this.assignHexValue(hexValue); - } - - public getSignature(): string { - return 'bool'; - } - - public isStatic(): boolean { - return true; - } - - public getHeaderSize(): BigNumber { - return new BigNumber(0); - } - - public getBodySize(): BigNumber { - return new BigNumber(32); - } - - public static matchGrammar(type: string): boolean { - return type === 'bool'; - } -} - -abstract class Number extends StaticDataType { - static MAX_WIDTH: number = 256; - static DEFAULT_WIDTH: number = Number.MAX_WIDTH; - width: number = Number.DEFAULT_WIDTH; - - constructor(dataItem: DataItem, matcher: RegExp) { - super(dataItem); - const matches = matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 2 && matches[1] !== undefined) { - this.width = parseInt(matches[1]); - } else { - this.width = 256; - } - } - - public assignValue(value: BigNumber) { - if (value.greaterThan(this.getMaxValue())) { - throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; - } else if (value.lessThan(this.getMinValue())) { - throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; - } - - const hexBase = 16; - const evmWordWidth = 32; - let valueBuf: Buffer; - if (value.greaterThanOrEqualTo(0)) { - valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); - } else { - // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. - // Step 1/3: Convert value to positive binary string - const binBase = 2; - const valueBin = value.times(-1).toString(binBase); - - // Step 2/3: Invert binary value - const bitsInEvmWord = 256; - let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, binBase); - - // Step 3/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const negativeValue = invertedValue.plus(1); - - // Convert the negated value to a hex string - valueBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), - evmWordWidth, - ); - } - - const encodedValue = ethUtil.bufferToHex(valueBuf); - this.assignHexValue(encodedValue); - } - - public isStatic(): boolean { - return true; - } - - public getHeaderSize(): BigNumber { - return new BigNumber(0); - } - - public getBodySize(): BigNumber { - return new BigNumber(32); - } - - public abstract getMaxValue(): BigNumber; - public abstract getMinValue(): BigNumber; -} - -export class Int extends Number { - static matcher = RegExp( - '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - constructor(dataItem: DataItem) { - super(dataItem, Int.matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).times(-1); - } - - public getSignature(): string { - return `int${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class UInt extends Number { - static matcher = RegExp( - '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - constructor(dataItem: DataItem) { - super(dataItem, UInt.matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(0); - } - - public getSignature(): string { - return `uint${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class Byte extends StaticDataType { - static matcher = RegExp( - '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', - ); - - static DEFAULT_WIDTH = 1; - width: number = Byte.DEFAULT_WIDTH; - - constructor(dataItem: DataItem) { - super(dataItem); - const matches = Byte.matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - if (matches !== null && matches.length === 3 && matches[2] !== undefined) { - this.width = parseInt(matches[2]); - } else { - this.width = Byte.DEFAULT_WIDTH; - } - } - - public assignValue(value: string | Buffer) { - // Convert value into a buffer and do bounds checking - const valueBuf = ethUtil.toBuffer(value); - if (valueBuf.byteLength > this.width) { - throw new Error( - `Tried to assign ${value} (${ - valueBuf.byteLength - } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, - ); - } else if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - // Store value as hex - const evmWordWidth = 32; - const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); - const hexValue = ethUtil.bufferToHex(paddedValue); - - this.assignHexValue(hexValue); - } - - public getSignature(): string { - // Note that `byte` reduces to `bytes1` - return `bytes${this.width}`; - } - - public isStatic(): boolean { - return true; - } - - public getHeaderSize(): BigNumber { - return new BigNumber(0); - } - - public getBodySize(): BigNumber { - return new BigNumber(32); - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class Bytes extends DynamicDataType { - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = Bytes.UNDEFINED_LENGTH; - - constructor(dataItem: DataItem) { - super(dataItem); - expect(Bytes.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string | Buffer) { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); - } - const valueBuf = ethUtil.toBuffer(value); - if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - const wordsForValue = Math.ceil(valueBuf.byteLength / 32); - const paddedBytesForValue = wordsForValue * 32; - const paddedValueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); - const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); - const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); - const encodedValue = ethUtil.bufferToHex(encodedValueBuf); - - this.assignHexValue(encodedValue); - } - - public getSignature(): string { - return 'bytes'; - } - - public isStatic(): boolean { - return false; - } - - public getHeaderSize(): BigNumber { - return new BigNumber(0); - } - - public getBodySize(): BigNumber { - const valueBuf = ethUtil.toBuffer(this.getHexValue()); - const size = new BigNumber(valueBuf.byteLength); - return size; - } - - public static matchGrammar(type: string): boolean { - return type === 'bytes'; - } -} - -export class SolString extends DynamicDataType { - constructor(dataItem: DataItem) { - super(dataItem); - expect(SolString.matchGrammar(dataItem.type)).to.be.true(); - } - - public assignValue(value: string) { - const wordsForValue = Math.ceil(value.length / 32); - const paddedBytesForValue = wordsForValue * 32; - const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); - const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); - const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); - const encodedValue = ethUtil.bufferToHex(encodedValueBuf); - - this.assignHexValue(encodedValue); - } - - public getSignature(): string { - return 'string'; - } - - public isStatic(): boolean { - return false; - } - - public getHeaderSize(): BigNumber { - return new BigNumber(0); - } - - public getBodySize(): BigNumber { - const valueBuf = ethUtil.toBuffer(this.getHexValue()); - const size = new BigNumber(valueBuf.byteLength); - return size; - } - - public static matchGrammar(type: string): boolean { - return type === 'string'; - } -} - -export class SolArray extends DynamicDataType { - static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = SolArray.UNDEFINED_LENGTH; - type: string = '[undefined]'; - isLengthDefined: boolean; - isStaticArray: boolean; // An array is dynamic if it's lenghth is undefined or if its children are dynamic. - private elements: DataType[]; - - /* - --- Layout 1: Fixed Length Array with Static Types --- - Elem1, Elem2, ..., ElemN - - --- Layout 2: Fixed Length Array with Dynamic Types --- - PtrToArray, ..., Elem1, Elem2, ..., ElemN - - --- Layout 3: Dynamic Length Array with Static Types --- - PtrToArray, ..., ArrayLength, Elem1, Elem2, ..., ElemN - - --- Layout 4: Dynamic Length Array with Dynamic Types --- - PtrToArray, ..., ArrayLength, PtrToElem1, PtrToElem2, ..., PtrToElemN, ..., Elem1, Elem2, ..., ElemN - */ - - constructor(dataItem: DataItem) { - super(dataItem); - const matches = SolArray.matcher.exec(dataItem.type); - expect(matches).to.be.not.null(); - console.log(JSON.stringify(matches)); - if (matches === null || matches.length !== 3) { - throw new Error(`Could not parse array: ${dataItem.type}`); - } else if (matches[1] === undefined) { - throw new Error(`Could not parse array type: ${dataItem.type}`); - } else if (matches[2] === undefined) { - throw new Error(`Could not parse array length: ${dataItem.type}`); - } - - this.elements = []; - - // Check if length is undefined - if (matches[2] === '') { - this.type = matches[1]; - this.length = SolArray.UNDEFINED_LENGTH; - this.isLengthDefined = false; - this.isStaticArray = false; - return; - } - - // Parse out array type/length and construct children - this.isLengthDefined = true; - this.type = matches[1]; - this.length = new BigNumber(matches[2], 10); - if (this.length.lessThan(1)) { - throw new Error(`Bad array length: ${JSON.stringify(this.length)}`); - } - this.constructChildren(); - - // Check if we're static or not - this.isStaticArray = !(this.elements[0] instanceof Pointer); //this.elements[0].isStatic(); - //throw new Error(`Am I static? ${this.isStaticArray}`); - } - - private constructChildren() { - for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { - const childDataItem = { - type: this.type, - name: `${this.getDataItem().name}[${idx.toString(10)}]`, - } as DataItem; - const components = this.getDataItem().components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = DataTypeFactory.create(childDataItem, this); - this.elements.push(child); - if (child instanceof Pointer) { - const pointsTo = child.getChildren()[0]; - this.children.push(pointsTo); // DataType pointing to - } - } - } - - // @TODO: HACKY -- shouldn't really have children for a - /* - public getChildren(): DataType[] { - if (this.isStatic()) { - return []; - } else { - return this.children; - } - }*/ - - public assignValue(value: any[]) { - console.log('GREG'.repeat(15), JSON.stringify(value)); - - - // Sanity check length - const valueLength = new BigNumber(value.length); - if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { - throw new Error( - `Expected array of length ${JSON.stringify(this.length)}, but got array of length ${JSON.stringify( - valueLength, - )}`, - ); - } - - // Assign length if not already set - if (this.length === SolArray.UNDEFINED_LENGTH) { - this.length = valueLength; - this.constructChildren(); - } - - // Assign values to children - for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { - const idxNumber = idx.toNumber(); - this.elements[idxNumber].assignValue(value[idxNumber]); - } - } - - public getHexValue(): string { - let valueBuf = new Buffer(""); - - if (this.isLengthDefined === false) { - // Must include the array length - const lengthBufUnpadded = ethUtil.toBuffer(`0x${this.length.toString(16)}`); - const lengthBuf = ethUtil.setLengthLeft(lengthBufUnpadded, 32); - valueBuf = lengthBuf; - } - - for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { - const idxNumber = idx.toNumber(); - const childValueHex = this.elements[idxNumber].getHexValue(); - const childValueBuf = ethUtil.toBuffer(childValueHex); - valueBuf = Buffer.concat([valueBuf, childValueBuf]); - } - - // Convert value buffer to hex - const valueHex = ethUtil.bufferToHex(valueBuf); - return valueHex; - } - - public isStatic(): boolean { - return this.isStaticArray; - } - - public getHeaderSize(): BigNumber { - let size = new BigNumber(0); - if (!this.isLengthDefined) { - size = new BigNumber(32); // stores length of bytes - } - return size; - } - - public getBodySize(): BigNumber { - const evmWordWidth = new BigNumber(32); - const body = this.length.times(evmWordWidth); - return body; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } - - public getSignature(): string { - let dataItem = { - type: this.type, - name: 'N/A', - } as DataItem; - const components = this.getDataItem().components; - if (components !== undefined) { - dataItem.components = components; - } - const elementDataType = DataTypeFactory.mapDataItemToDataType(dataItem); - const type = elementDataType.getSignature(); - if (this.length.equals(SolArray.UNDEFINED_LENGTH)) { - return `${type}[]`; - } - return `${type}[${this.length}]`; - } -} - -export class Tuple extends DynamicDataType { - private length: BigNumber; - private childMap: { [key: string]: number }; - private members: DataType[]; - - constructor(dataItem: DataItem) { - super(dataItem); - expect(Tuple.matchGrammar(dataItem.type)).to.be.true(); - this.length = new BigNumber(0); - this.childMap = {}; - this.members = []; - if (dataItem.components !== undefined) { - this.constructChildren(dataItem.components); - this.length = new BigNumber(dataItem.components.length); - } else { - throw new Error('Components undefined'); - } - } - - private constructChildren(dataItems: DataItem[]) { - _.each(dataItems, (dataItem: DataItem) => { - const childDataItem = { - type: dataItem.type, - name: `${this.getDataItem().name}.${dataItem.name}`, - } as DataItem; - const child = DataTypeFactory.create(childDataItem, this); - this.childMap[dataItem.name] = this.members.length; - - if (child instanceof Pointer) { - this.children.push(child.getChildren()[0]); - } - this.members.push(child); - }); - } - - private assignValueFromArray(value: any[]) { - // Sanity check length - const valueLength = new BigNumber(value.length); - if (this.length !== SolArray.UNDEFINED_LENGTH && valueLength.equals(this.length) === false) { - throw new Error( - `Expected array of ${JSON.stringify( - this.length, - )} elements, but got array of length ${JSON.stringify(valueLength)}`, - ); - } - - // Assign values to children - for (let idx = new BigNumber(0); idx.lessThan(this.length); idx = idx.plus(1)) { - const idxNumber = idx.toNumber(); - this.members[idxNumber].assignValue(value[idxNumber]); - } - } - - private assignValueFromObject(obj: object) { - let childMap = _.cloneDeep(this.childMap); - _.forOwn(obj, (value: any, key: string) => { - if (key in childMap === false) { - throw new Error(`Could not assign tuple to object: unrecognized key '${key}'`); - } - this.members[this.childMap[key]].assignValue(value); - delete childMap[key]; - }); - - if (Object.keys(childMap).length !== 0) { - throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); - } - } - - public assignValue(value: any[] | object) { - if (value instanceof Array) { - this.assignValueFromArray(value); - } else if (typeof value === 'object') { - this.assignValueFromObject(value); - } else { - throw new Error(`Unexpected type for ${value}`); - } - } - - public getHexValue(): string { - let paramBufs: Buffer[] = []; - _.each(this.members, (member: DataType) => { - paramBufs.push(ethUtil.toBuffer(member.getHexValue())); - }); - - const value = Buffer.concat(paramBufs); - const hexValue = ethUtil.bufferToHex(value); - return hexValue; - } - - public getHeaderSize(): BigNumber { - return new BigNumber(0); - } - - public getBodySize(): BigNumber { - const evmWordWidth = new BigNumber(32); - const size = this.length.times(evmWordWidth); - return size; - } - - public getSignature(): string { - // Compute signature - let signature = `(`; - _.each(this.members, (member: DataType, i: number) => { - signature += member.getSignature(); - if (i < this.members.length - 1) { - signature += ','; - } - }); - signature += ')'; - return signature; - } - - public isStatic(): boolean { - const isStaticTuple = this.children.length === 0; - return isStaticTuple; // @TODO: True in every case or only when dynamic data? - } - - public static matchGrammar(type: string): boolean { - return type === 'tuple'; - } -} - -/* TODO -class Fixed extends StaticDataType {} - -class UFixed extends StaticDataType {}*/ - -export class Pointer extends StaticDataType { - destDataType: DynamicDataType; - parentDataType: DataType; - - constructor(destDataType: DynamicDataType, parentDataType: DataType) { - const destDataItem = destDataType.getDataItem(); - const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; - super(dataItem); - this.destDataType = destDataType; - this.parentDataType = parentDataType; - this.children.push(destDataType); - } - - /* - public assignValue(destDataType: DynamicDataType) { - this.destDataType = destDataType; - }*/ - - public assignValue(value: any) { - this.destDataType.assignValue(value); - } - - public getHexValue(): string { - console.log( - '*'.repeat(40), - this.destDataType.getAbsoluteOffset().toString(16), - '^'.repeat(150), - this.parentDataType.getAbsoluteOffset().toString(16), - ); - - let offset = this.destDataType - .getAbsoluteOffset() - .minus(this.parentDataType.getAbsoluteOffset()) - .minus(this.parentDataType.getHeaderSize()); - - console.log("OFFSET == ", JSON.stringify(offset), " or in hex -- 0x", offset.toString(16)); - - const hexBase = 16; - const evmWordWidth = 32; - const valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${offset.toString(hexBase)}`), evmWordWidth); - const encodedValue = ethUtil.bufferToHex(valueBuf); - return encodedValue; - } - - public getSignature(): string { - return this.destDataType.getSignature(); - } - - public isStatic(): boolean { - return true; - } - - public getHeaderSize(): BigNumber { - return new BigNumber(0); - } - - public getBodySize(): BigNumber { - return new BigNumber(32); - } - -} - -export class DataTypeFactory { - public static mapDataItemToDataType(dataItem: DataItem): DataType { - console.log(`Type: ${dataItem.type}`); - - if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); - if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); - if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); - if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); - if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); - if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); - if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); - if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); - if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); - if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); - //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); - //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); - - throw new Error(`Unrecognized data type: '${dataItem.type}'`); - } - - public static create(dataItem: DataItem, parentDataType: DataType): DataType { - const dataType = DataTypeFactory.mapDataItemToDataType(dataItem); - if (dataType.isStatic()) { - return dataType; - } else { - const pointer = new Pointer(dataType, parentDataType); - return pointer; - } - - throw new Error(`Unrecognized instance type: '${dataType}'`); - } -} - -class Queue { - private store: T[] = []; - push(val: T) { - this.store.push(val); - } - pop(): T | undefined { - return this.store.shift(); - } -} - -export class Method extends DataType { - name: string; - params: DataType[]; - private signature: string; - selector: string; - - constructor(abi: MethodAbi) { - super({ type: 'method', name: abi.name }); - this.name = abi.name; - this.params = []; - - _.each(abi.inputs, (input: DataItem) => { - this.params.push(DataTypeFactory.create(input, this)); - }); - - // Compute signature - this.signature = `${this.name}(`; - _.each(this.params, (param: DataType, i: number) => { - this.signature += param.getSignature(); - if (i < this.params.length - 1) { - this.signature += ','; - } - }); - this.signature += ')'; - - // Compute selector - this.selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(this.signature).slice(0, 4))); - - console.log(`--SIGNATURE--\n${this.signature}\n---------\n`); - console.log(`--SELECTOR--\n${this.selector}\n---------\n`); - } - - public getSignature(): string { - return this.signature; - } - - public assignValue(args: any[]) { - _.each(this.params, (param: DataType, i: number) => { - // Assign value to parameter - try { - param.assignValue(args[i]); - } catch (e) { - console.log('Failed to assign to ', param.getDataItem().name); - throw e; - } - - if (param instanceof Pointer) { - this.children.push(param.getChildren()[0]); - } - }); - } - - public getHexValue(): string { - let paramBufs: Buffer[] = []; - _.each(this.params, (param: DataType) => { - paramBufs.push(ethUtil.toBuffer(param.getHexValue())); - }); - - const value = Buffer.concat(paramBufs); - const hexValue = ethUtil.bufferToHex(value); - return hexValue; - } - - public encode(args: any[]): string { - this.assignValue(args); - const calldata = new Calldata(this.selector, this.params.length); - calldata.printAnnotated(); - this.bind(calldata, CalldataSection.PARAMS); - - return calldata.getHexValue(); - } - - public isStatic(): boolean { - return true; - } - - public getHeaderSize(): BigNumber { - // Exclude selector - return new BigNumber(0); - } - - public getBodySize(): BigNumber { - const nParams = new BigNumber(this.params.length); - const evmWordWidth = new BigNumber(32); - const size = nParams.times(evmWordWidth); - return size; - } - - /* - encodeOptimized(args: any[]): string { - const calldata = new Memory(); - // Assign values - optimizableParams : StaticDataType = []; - _.each(this.params, function(args: any[], i: number, param: DataType) { - param.assignValue(args[i]); - if (param instanceof DynamicDataType) { - - } - }); - - // Find non-parameter leaves - - - return ''; - } */ - - /* - decode(rawCalldata: string): any[] { - const calldata = new Calldata(this.name, this.params.length); - calldata.assignRaw(rawCalldata); - let args: any[]; - let params = this.params; - _.each(params, function(args: any[], i: number, param: DataType) { - param.decodeFromCalldata(calldata); - args.push(param.getValue()); - }); - - return args; - }*/ -} \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index f46a1812c..d75c9dbf1 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -410,237 +410,237 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); }); - - describe('Array', () => { - it('sample', async () => { - const testDataItem = { name: 'testArray', type: 'int[2]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue([new BigNumber(5), new BigNumber(6)]); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - }); - - it('sample undefined size', async () => { - const testDataItem = { name: 'testArray', type: 'int[]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue([new BigNumber(5), new BigNumber(6)]); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - }); - - it('sample dynamic types', async () => { - const testDataItem = { name: 'testArray', type: 'string[]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue(['five', 'six', 'seven']); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - const calldata = new AbiEncoder.Calldata('0x01020304', 1); - dataType.bind(calldata, AbiEncoder.CalldataSection.PARAMS); - console.log('*'.repeat(60)); - console.log(calldata.getHexValue()); - }); - }); - - describe('Address', () => { - const testAddressDataItem = { name: 'testAddress', type: 'address' }; - it('Valid Address', async () => { - const addressDataType = new AbiEncoder.Address(testAddressDataItem); - addressDataType.assignValue('0xe41d2489571d322189246dafa5ebde1f4699f498'); - const expectedAbiEncodedAddress = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; - - console.log(addressDataType.getHexValue()); - console.log(expectedAbiEncodedAddress); - expect(addressDataType.getHexValue()).to.be.equal(expectedAbiEncodedAddress); - }); - }); - - describe('Bool', () => { - const testBoolDataItem = { name: 'testBool', type: 'bool' }; - it('True', async () => { - const boolDataType = new AbiEncoder.Bool(testBoolDataItem); - boolDataType.assignValue(true); - const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); - }); - - it('False', async () => { - const boolDataType = new AbiEncoder.Bool(testBoolDataItem); - boolDataType.assignValue(false); - const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000000'; - expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); - }); - }); - - describe('Integer', () => { - const testIntDataItem = { name: 'testInt', type: 'int' }; - it('Positive - Base case', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(1)); - const expectedAbiEncodedInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Positive', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(437829473)); - const expectedAbiEncodedInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Negative - Base case', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(-1)); - const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Negative', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(-437829473)); - const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - // TODO: Add bounds tests + tests for different widths - }); - - describe('Unsigned Integer', () => { - const testIntDataItem = { name: 'testUInt', type: 'uint' }; - it('Lower Bound', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(0)); - const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000000'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - it('Base Case', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(1)); - const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - it('Random value', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(437829473)); - const expectedAbiEncodedUInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - // TODO: Add bounds tests + tests for different widths - }); - - describe('Static Bytes', () => { - it('Byte (padded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x05'); - const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it.skip('Byte (no padding)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - - // @TODO: This does not catch the Error - expect(byteDataType.assignValue('0x5')).to.throw(); - }); - - it('Bytes1', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes1' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x05'); - const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it('Bytes32 (padded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x0001020304050607080911121314151617181920212223242526272829303132'); - const expectedAbiEncodedByte = '0x0001020304050607080911121314151617181920212223242526272829303132'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it('Bytes32 (unpadded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x1a18bf61'); - const expectedAbiEncodedByte = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it.skip('Bytes32 - Too long', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - - // @TODO: This does not catch the Error - expect( - byteDataType.assignValue('0x000102030405060708091112131415161718192021222324252627282930313233'), - ).to.throw( - `Tried to assign 0x000102030405060708091112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32`, - ); - }); - }); - - describe('Bytes (Dynamic)', () => { - const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; - it('Less than 32 bytes', async () => { - const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); - bytesDataType.assignValue('0x010203'); - const expectedAbiEncodedBytes = - '0x00000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000'; - - expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); - }); - - it('Greater than 32 bytes', async () => { - const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); - const testValue = '0x' + '61'.repeat(40); - bytesDataType.assignValue(testValue); - const expectedAbiEncodedBytes = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); - }); - - // @TODO: Add test for throw on half-byte - // @TODO: Test with no 0x prefix - // @TODO: Test with Buffer as input - }); - - describe('String', () => { - const testStringDataItem = { name: 'testString', type: 'string' }; - it('Less than 32 bytes', async () => { - const stringDataType = new AbiEncoder.SolString(testStringDataItem); - stringDataType.assignValue('five'); - const expectedAbiEncodedString = - '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - - console.log(stringDataType.getHexValue()); - console.log(expectedAbiEncodedString); - expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); - }); - - it('Greater than 32 bytes', async () => { - const stringDataType = new AbiEncoder.SolString(testStringDataItem); - const testValue = 'a'.repeat(40); - stringDataType.assignValue(testValue); - const expectedAbiEncodedString = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); - }); - }); + /* + describe('Array', () => { + it('sample', async () => { + const testDataItem = { name: 'testArray', type: 'int[2]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue([new BigNumber(5), new BigNumber(6)]); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + }); + + it('sample undefined size', async () => { + const testDataItem = { name: 'testArray', type: 'int[]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue([new BigNumber(5), new BigNumber(6)]); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + }); + + it('sample dynamic types', async () => { + const testDataItem = { name: 'testArray', type: 'string[]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue(['five', 'six', 'seven']); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + const calldata = new AbiEncoder.Calldata('0x01020304', 1); + dataType.bind(calldata, AbiEncoder.CalldataSection.PARAMS); + console.log('*'.repeat(60)); + console.log(calldata.getHexValue()); + }); + }); + + describe('Address', () => { + const testAddressDataItem = { name: 'testAddress', type: 'address' }; + it('Valid Address', async () => { + const addressDataType = new AbiEncoder.Address(testAddressDataItem); + addressDataType.assignValue('0xe41d2489571d322189246dafa5ebde1f4699f498'); + const expectedAbiEncodedAddress = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; + + console.log(addressDataType.getHexValue()); + console.log(expectedAbiEncodedAddress); + expect(addressDataType.getHexValue()).to.be.equal(expectedAbiEncodedAddress); + }); + }); + + describe('Bool', () => { + const testBoolDataItem = { name: 'testBool', type: 'bool' }; + it('True', async () => { + const boolDataType = new AbiEncoder.Bool(testBoolDataItem); + boolDataType.assignValue(true); + const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + }); + + it('False', async () => { + const boolDataType = new AbiEncoder.Bool(testBoolDataItem); + boolDataType.assignValue(false); + const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + }); + }); + + describe('Integer', () => { + const testIntDataItem = { name: 'testInt', type: 'int' }; + it('Positive - Base case', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(1)); + const expectedAbiEncodedInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Positive', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(437829473)); + const expectedAbiEncodedInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Negative - Base case', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(-1)); + const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Negative', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(-437829473)); + const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + // TODO: Add bounds tests + tests for different widths + }); + + describe('Unsigned Integer', () => { + const testIntDataItem = { name: 'testUInt', type: 'uint' }; + it('Lower Bound', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(0)); + const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + it('Base Case', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(1)); + const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + it('Random value', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(437829473)); + const expectedAbiEncodedUInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + // TODO: Add bounds tests + tests for different widths + }); + + describe('Static Bytes', () => { + it('Byte (padded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x05'); + const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it.skip('Byte (no padding)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + + // @TODO: This does not catch the Error + expect(byteDataType.assignValue('0x5')).to.throw(); + }); + + it('Bytes1', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes1' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x05'); + const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it('Bytes32 (padded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x0001020304050607080911121314151617181920212223242526272829303132'); + const expectedAbiEncodedByte = '0x0001020304050607080911121314151617181920212223242526272829303132'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it('Bytes32 (unpadded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x1a18bf61'); + const expectedAbiEncodedByte = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it.skip('Bytes32 - Too long', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + + // @TODO: This does not catch the Error + expect( + byteDataType.assignValue('0x000102030405060708091112131415161718192021222324252627282930313233'), + ).to.throw( + `Tried to assign 0x000102030405060708091112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32`, + ); + }); + }); + + describe('Bytes (Dynamic)', () => { + const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; + it('Less than 32 bytes', async () => { + const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); + bytesDataType.assignValue('0x010203'); + const expectedAbiEncodedBytes = + '0x00000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000'; + + expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + }); + + it('Greater than 32 bytes', async () => { + const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); + const testValue = '0x' + '61'.repeat(40); + bytesDataType.assignValue(testValue); + const expectedAbiEncodedBytes = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + }); + + // @TODO: Add test for throw on half-byte + // @TODO: Test with no 0x prefix + // @TODO: Test with Buffer as input + }); + + describe('String', () => { + const testStringDataItem = { name: 'testString', type: 'string' }; + it('Less than 32 bytes', async () => { + const stringDataType = new AbiEncoder.SolString(testStringDataItem); + stringDataType.assignValue('five'); + const expectedAbiEncodedString = + '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + + console.log(stringDataType.getHexValue()); + console.log(expectedAbiEncodedString); + expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + }); + + it('Greater than 32 bytes', async () => { + const stringDataType = new AbiEncoder.SolString(testStringDataItem); + const testValue = 'a'.repeat(40); + stringDataType.assignValue(testValue); + const expectedAbiEncodedString = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + }); + });*/ }); -- cgit v1.2.3 From 41e01e98064b129f588d72ed25267f4865c58f5c Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 18:59:27 -0800 Subject: making progress on basic example --- packages/order-utils/test/abi/data_type.ts | 15 +++++++++------ packages/order-utils/test/abi/evm_data_types.ts | 7 ++++--- packages/order-utils/test/abi_encoder_test.ts | 10 +++++++--- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 201eb89f9..c1e352508 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -95,15 +95,17 @@ export abstract class MemberDataType extends DataType { private members: DataType[]; private isArray: boolean; protected arrayLength: number | undefined; + protected arrayElementType: string | undefined; - public constructor(dataItem: DataItem, isArray: boolean = false, arrayLength?: number) { + public constructor(dataItem: DataItem, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { super(dataItem); this.memberMap = {}; this.members = []; this.isArray = isArray; this.arrayLength = arrayLength; + this.arrayElementType = arrayElementType; if (isArray && arrayLength !== undefined) { [this.members, this.memberMap] = this.createMembersWithLength(dataItem, arrayLength); } else if (!isArray) { @@ -119,10 +121,10 @@ export abstract class MemberDataType extends DataType { let members: DataType[] = []; let memberMap: MemberMap = {}; - _.each(dataItem.components, (dataItem: DataItem) => { + _.each(dataItem.components, (memberItem: DataItem) => { const childDataItem = { - type: dataItem.type, - name: `${dataItem.name}.${dataItem.name}`, + type: memberItem.type, + name: `${dataItem.name}.${memberItem.name}`, } as DataItem; const child = DataTypeFactory.create(childDataItem, this); members.push(child); @@ -133,12 +135,13 @@ export abstract class MemberDataType extends DataType { } private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { + console.log(dataItem); let members: DataType[] = []; let memberMap: MemberMap = {}; const range = _.range(length); _.each(range, (idx: number) => { const childDataItem = { - type: dataItem.type, + type: this.arrayElementType, name: `${dataItem.name}[${idx.toString(10)}]`, } as DataItem; const components = dataItem.components; @@ -164,7 +167,7 @@ export abstract class MemberDataType extends DataType { } let members = this.members; - if (this.arrayLength === undefined) { + if (this.isArray && this.arrayLength === undefined) { [members,] = this.createMembersWithLength(this.getDataItem(), value.length); } diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index 4dffedb8d..1bd4f0d51 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -347,9 +347,10 @@ export class SolArray extends MemberDataType { } const isArray = true; + const arrayElementType = matches[1]; const arrayLength = (matches[2] === '') ? undefined : parseInt(matches[2], 10); - super(dataItem, isArray, arrayLength); - this.elementType = matches[1]; + super(dataItem, isArray, arrayLength, arrayElementType); + this.elementType = arrayElementType; this.arraySignature = this.computeSignature(); } @@ -388,7 +389,7 @@ export class Method extends MemberDataType { public selector: string; constructor(abi: MethodAbi) { - super({ type: 'method', name: abi.name }); + super({ type: 'method', name: abi.name, components: abi.inputs }); this.methodSignature = this.computeSignature(); this.selector = this.methodSelector = this.computeSelector(); diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index d75c9dbf1..6655af1ff 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -18,13 +18,15 @@ import * as AbiEncoder from './abi/abi_encoder'; import * as AbiSamples from './abi_samples'; +AbiEncoder.DataTypeFactory.setImpl(new AbiEncoder.EvmDataTypeFactoryImpl()); + chaiSetup.configure(); const expect = chai.expect; describe.only('ABI Encoder', () => { describe.only('ABI Tests at Method Level', () => { - it('Crazy ABI', async () => { + it.skip('Crazy ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi); console.log(method.getSignature()); @@ -89,8 +91,9 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it('Crazy ABI #1', async () => { + it.skip('Crazy ABI #1', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi1); + const args = [ new BigNumber(256745454), new BigNumber(-256745454), @@ -115,6 +118,7 @@ describe.only('ABI Encoder', () => { it('Types with default widths', async () => { const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); + console.log(method); const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; const calldata = method.encode(args); console.log(calldata); @@ -325,7 +329,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it('Yessir', async () => { + it.only('Yessir', async () => { const method = new AbiEncoder.Method(AbiSamples.simpleAbi); const calldata = method.encode([new BigNumber(5), 'five']); console.log(calldata); -- cgit v1.2.3 From 3027e6bc0db9d0722f0dcf09f84505ad8440e65b Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 20:18:23 -0800 Subject: resolved issue with value.greaterThan --- packages/order-utils/test/abi/data_type.ts | 5 +++-- packages/order-utils/test/abi/evm_data_types.ts | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index c1e352508..be7ae6154 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -32,6 +32,7 @@ export abstract class PayloadDataType extends DataType { } public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { + //console.log(); const encodedValue = this.encodeValue(value); const name = this.getDataItem().name; const signature = this.getSignature(); @@ -173,8 +174,8 @@ export abstract class MemberDataType extends DataType { const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), false); const memberBlocks: CalldataBlock[] = []; - _.each(members, (member: DataType) => { - const block = member.generateCalldataBlock(value, methodBlock); + _.each(members, (member: DataType, idx: number) => { + const block = member.generateCalldataBlock(value[idx], methodBlock); memberBlocks.push(block); }); methodBlock.setMembers(memberBlocks); diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index 1bd4f0d51..fcced646a 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -86,6 +86,7 @@ abstract class Number extends PayloadDataType { } public encodeValue(value: BigNumber): Buffer { + console.log(value); if (value.greaterThan(this.getMaxValue())) { throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; } else if (value.lessThan(this.getMinValue())) { -- cgit v1.2.3 From 58177060a49917d0de88e6a58559b13211127efe Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 20:22:48 -0800 Subject: works for simple case --- packages/order-utils/test/abi/calldata.ts | 3 ++- packages/order-utils/test/abi_encoder_test.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index 725bdce62..aaad332f8 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -81,7 +81,7 @@ export class DependentCalldataBlock extends CalldataBlock { private parent: CalldataBlock; private dependency: CalldataBlock; - constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, parent: CalldataBlock, dependency: CalldataBlock) { + constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { const headerSizeInBytes = 0; const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; super(name, signature, /*offsetInBytes,*/ headerSizeInBytes, bodySizeInBytes, relocatable); @@ -174,6 +174,7 @@ export class Calldata { let block: CalldataBlock | undefined; let offset = 0; while ((block = blockQueue.pop()) !== undefined) { + console.log(block.getName()); block.setOffset(offset); if (block instanceof DependentCalldataBlock) { blockQueue.push(block.getDependency()); diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 6655af1ff..0455169a3 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -116,7 +116,7 @@ describe.only('ABI Encoder', () => { }); - it('Types with default widths', async () => { + it.skip('Types with default widths', async () => { const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); console.log(method); const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; @@ -329,7 +329,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it.only('Yessir', async () => { + it('Yessir', async () => { const method = new AbiEncoder.Method(AbiSamples.simpleAbi); const calldata = method.encode([new BigNumber(5), 'five']); console.log(calldata); -- cgit v1.2.3 From addfe85f088fc1aa31638acdd73bae560afceb27 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 21:48:06 -0800 Subject: passing tests slowly but surely --- packages/order-utils/test/abi/calldata.ts | 16 +++++++++++++--- packages/order-utils/test/abi/data_type.ts | 7 ++++++- packages/order-utils/test/abi_encoder_test.ts | 5 +++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index aaad332f8..74f0c0924 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -91,10 +91,15 @@ export class DependentCalldataBlock extends CalldataBlock { public toBuffer(): Buffer { const dependencyOffset = this.dependency.getOffsetInBytes(); + console.log("Dependency Offset - ", dependencyOffset); const parentOffset = this.parent.getOffsetInBytes(); + console.log("Parent Offset - ", parentOffset); const parentHeaderSize = this.parent.getHeaderSizeInBytes(); - const pointer = dependencyOffset - parentOffset + parentHeaderSize; - const pointerBuf = new Buffer(`0x${pointer.toString(16)}`); + console.log("Parent Header size - ", parentHeaderSize); + const pointer: number = (dependencyOffset - parentOffset) + parentHeaderSize; + console.log("DAT PTR = ", pointer); + const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(16)}`); + console.log("Chye - ", pointerBuf); const evmWordWidthInBytes = 32; const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); return pointerBufPadded; @@ -122,7 +127,7 @@ export class MemberCalldataBlock extends CalldataBlock { bodySizeInBytes += member.getSizeInBytes(); }); this.members = members; - this.setBodySize(bodySizeInBytes); + this.setBodySize(0); } public setHeader(header: Buffer) { @@ -176,7 +181,9 @@ export class Calldata { while ((block = blockQueue.pop()) !== undefined) { console.log(block.getName()); block.setOffset(offset); + offset += block.getSizeInBytes(); if (block instanceof DependentCalldataBlock) { + console.log(block.getDependency()); blockQueue.push(block.getDependency()); } else if (block instanceof MemberCalldataBlock) { _.each(block.getMembers(), (member: CalldataBlock) => { @@ -185,8 +192,11 @@ export class Calldata { } } + console.log(this.root); + // Fetch values using same technique const valueBufs: Buffer[] = [selectorBuffer]; + blockQueue.push(this.root); while ((block = blockQueue.pop()) !== undefined) { valueBufs.push(block.toBuffer()); diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index be7ae6154..5311ffb81 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -167,12 +167,17 @@ export abstract class MemberDataType extends DataType { ); } + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), false); + let members = this.members; if (this.isArray && this.arrayLength === undefined) { [members,] = this.createMembersWithLength(this.getDataItem(), value.length); + + const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(16)}`), 32); + methodBlock.setHeader(lenBuf); } - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), false); + const memberBlocks: CalldataBlock[] = []; _.each(members, (member: DataType, idx: number) => { const block = member.generateCalldataBlock(value[idx], methodBlock); diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 0455169a3..e600542b8 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -116,7 +116,7 @@ describe.only('ABI Encoder', () => { }); - it.skip('Types with default widths', async () => { + it('Types with default widths', async () => { const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); console.log(method); const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; @@ -303,7 +303,7 @@ describe.only('ABI Encoder', () => { }); - it('Fixed Length Array / Static Members ABI', async () => { + it.only('Fixed Length Array / Static Members ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; const calldata = method.encode(args); @@ -338,6 +338,7 @@ describe.only('ABI Encoder', () => { it('Array ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.stringAbi); + console.log(method); const calldata = method.encode([['five', 'six', 'seven']]); console.log(method.getSignature()); console.log(method.selector); -- cgit v1.2.3 From 20f1526c7d0e1edb4a1de8e8a26811ae81b609dc Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 21:59:37 -0800 Subject: arrays working --- packages/order-utils/test/abi/calldata.ts | 2 +- packages/order-utils/test/abi/data_type.ts | 4 +++- packages/order-utils/test/abi_encoder_test.ts | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index 74f0c0924..c1630c34a 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -96,7 +96,7 @@ export class DependentCalldataBlock extends CalldataBlock { console.log("Parent Offset - ", parentOffset); const parentHeaderSize = this.parent.getHeaderSizeInBytes(); console.log("Parent Header size - ", parentHeaderSize); - const pointer: number = (dependencyOffset - parentOffset) + parentHeaderSize; + const pointer: number = dependencyOffset - (parentOffset + parentHeaderSize); console.log("DAT PTR = ", pointer); const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(16)}`); console.log("Chye - ", pointerBuf); diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 5311ffb81..72d270bf1 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -102,6 +102,8 @@ export abstract class MemberDataType extends DataType { public constructor(dataItem: DataItem, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { super(dataItem); + console.log('*'.repeat(40), arrayLength); + this.memberMap = {}; this.members = []; this.isArray = isArray; @@ -243,7 +245,7 @@ export abstract class MemberDataType extends DataType { */ if (this.isArray && this.arrayLength === undefined) { - return true; + return false; } // Search for dependent members diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index e600542b8..4c571434e 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -259,7 +259,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it('Fixed Lenfgth Array / Dynamic Members', async () => { + it.only('Fixed Lenfgth Array / Dynamic Members', async () => { const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); const args = [["Brave", "New", "World"]]; const calldata = method.encode(args); @@ -303,7 +303,7 @@ describe.only('ABI Encoder', () => { }); - it.only('Fixed Length Array / Static Members ABI', async () => { + it('Fixed Length Array / Static Members ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; const calldata = method.encode(args); -- cgit v1.2.3 From 59206c387e702492d22e975acf806ebe7de36bfd Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 22:24:24 -0800 Subject: Works for types with default widths --- packages/order-utils/test/abi/calldata.ts | 24 ++++++++++++++---------- packages/order-utils/test/abi/data_type.ts | 9 ++------- packages/order-utils/test/abi_encoder_test.ts | 6 +++--- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index c1630c34a..dc67a731e 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -91,15 +91,10 @@ export class DependentCalldataBlock extends CalldataBlock { public toBuffer(): Buffer { const dependencyOffset = this.dependency.getOffsetInBytes(); - console.log("Dependency Offset - ", dependencyOffset); const parentOffset = this.parent.getOffsetInBytes(); - console.log("Parent Offset - ", parentOffset); const parentHeaderSize = this.parent.getHeaderSizeInBytes(); - console.log("Parent Header size - ", parentHeaderSize); const pointer: number = dependencyOffset - (parentOffset + parentHeaderSize); - console.log("DAT PTR = ", pointer); const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(16)}`); - console.log("Chye - ", pointerBuf); const evmWordWidthInBytes = 32; const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); return pointerBufPadded; @@ -114,11 +109,13 @@ export class MemberCalldataBlock extends CalldataBlock { private static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; private header: Buffer | undefined; private members: CalldataBlock[]; + private contiguous: boolean; - constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean) { + constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, contiguous: boolean) { super(name, signature, /*offsetInBytes,*/ 0, 0, relocatable); this.members = []; this.header = undefined; + this.contiguous = contiguous; } public setMembers(members: CalldataBlock[]) { @@ -130,6 +127,10 @@ export class MemberCalldataBlock extends CalldataBlock { this.setBodySize(0); } + public isContiguous(): boolean { + return true; + } + public setHeader(header: Buffer) { this.setHeaderSize(header.byteLength); this.header = header; @@ -150,6 +151,9 @@ class Queue { push(val: T) { this.store.push(val); } + pushFront(val: T) { + this.store.unshift(val); + } pop(): T | undefined { return this.store.shift(); } @@ -186,8 +190,8 @@ export class Calldata { console.log(block.getDependency()); blockQueue.push(block.getDependency()); } else if (block instanceof MemberCalldataBlock) { - _.each(block.getMembers(), (member: CalldataBlock) => { - blockQueue.push(member); + _.eachRight(block.getMembers(), (member: CalldataBlock) => { + blockQueue.pushFront(member); }); } } @@ -203,8 +207,8 @@ export class Calldata { if (block instanceof DependentCalldataBlock) { blockQueue.push(block.getDependency()); } else if (block instanceof MemberCalldataBlock) { - _.each(block.getMembers(), (member: CalldataBlock) => { - blockQueue.push(member); + _.eachRight(block.getMembers(), (member: CalldataBlock) => { + blockQueue.pushFront(member); }); } } diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 72d270bf1..0783b9c56 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -32,7 +32,6 @@ export abstract class PayloadDataType extends DataType { } public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { - //console.log(); const encodedValue = this.encodeValue(value); const name = this.getDataItem().name; const signature = this.getSignature(); @@ -101,9 +100,6 @@ export abstract class MemberDataType extends DataType { public constructor(dataItem: DataItem, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { super(dataItem); - - console.log('*'.repeat(40), arrayLength); - this.memberMap = {}; this.members = []; this.isArray = isArray; @@ -138,7 +134,6 @@ export abstract class MemberDataType extends DataType { } private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { - console.log(dataItem); let members: DataType[] = []; let memberMap: MemberMap = {}; const range = _.range(length); @@ -169,7 +164,7 @@ export abstract class MemberDataType extends DataType { ); } - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), false); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), this.isStatic(), false); let members = this.members; if (this.isArray && this.arrayLength === undefined) { @@ -190,7 +185,7 @@ export abstract class MemberDataType extends DataType { } protected generateCalldataBlockFromObject(obj: object): MemberCalldataBlock { - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), false); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), this.isStatic(), false); const memberBlocks: CalldataBlock[] = []; let childMap = _.cloneDeep(this.memberMap); _.forOwn(obj, (value: any, key: string) => { diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 4c571434e..404f160fd 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -116,7 +116,7 @@ describe.only('ABI Encoder', () => { }); - it('Types with default widths', async () => { + it.only('Types with default widths', async () => { const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); console.log(method); const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; @@ -201,7 +201,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it('Multidimensional Arrays / Static Members', async () => { + it.only('Multidimensional Arrays / Static Members', async () => { const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi); // Eight 3-dimensional arrays of uint8[2][2][2] @@ -259,7 +259,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it.only('Fixed Lenfgth Array / Dynamic Members', async () => { + it('Fixed Lenfgth Array / Dynamic Members', async () => { const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); const args = [["Brave", "New", "World"]]; const calldata = method.encode(args); -- cgit v1.2.3 From bb7dd2373893366dcab0fe1c4cffe0c1e31af837 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 23:00:20 -0800 Subject: passing 18 tests --- packages/order-utils/test/abi/calldata.ts | 54 +++++++++++++++++++++++-- packages/order-utils/test/abi/data_type.ts | 13 ++++-- packages/order-utils/test/abi/evm_data_types.ts | 1 + packages/order-utils/test/abi_encoder_test.ts | 4 +- 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index dc67a731e..37dd30e57 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -1,4 +1,5 @@ import ethUtil = require('ethereumjs-util'); +import CommunicationChatBubbleOutline from 'material-ui/SvgIcon'; var _ = require('lodash'); export abstract class CalldataBlock { @@ -157,12 +158,18 @@ class Queue { pop(): T | undefined { return this.store.shift(); } + merge(q: Queue) { + this.store = this.store.concat(q.store); + } + mergeFront(q: Queue) { + this.store = q.store.concat(this.store); + } } export class Calldata { private selector: string; private sizeInBytes: number; - private root: CalldataBlock | undefined; + private root: MemberCalldataBlock | undefined; constructor() { this.selector = '0x'; @@ -170,12 +177,53 @@ export class Calldata { this.root = undefined; } + private createQueue(memberBlock: MemberCalldataBlock): Queue { + const blockQueue = new Queue(); + _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { + if (member instanceof MemberCalldataBlock) { + blockQueue.mergeFront(this.createQueue(member)); + } else { + blockQueue.pushFront(member); + } + }); + + // Children + _.each(memberBlock.getMembers(), (member: CalldataBlock) => { + if (member instanceof DependentCalldataBlock) { + const dependency = member.getDependency(); + if (dependency instanceof MemberCalldataBlock) { + blockQueue.merge(this.createQueue(dependency)); + } else { + blockQueue.push(dependency); + } + } + }); + + blockQueue.pushFront(memberBlock); + return blockQueue; + } + public toHexString(): string { let selectorBuffer = ethUtil.toBuffer(this.selector); if (this.root === undefined) { throw new Error('expected root'); } + const offsetQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; + let offset = 0; + while ((block = offsetQueue.pop()) !== undefined) { + block.setOffset(offset); + offset += block.getSizeInBytes(); + } + + const valueQueue = this.createQueue(this.root); + const valueBufs: Buffer[] = [selectorBuffer]; + while ((block = valueQueue.pop()) !== undefined) { + valueBufs.push(block.toBuffer()); + } + + /* const blockQueue = new Queue(); blockQueue.push(this.root); @@ -211,7 +259,7 @@ export class Calldata { blockQueue.pushFront(member); }); } - } + }*/ const combinedBuffers = Buffer.concat(valueBufs); const hexValue = ethUtil.bufferToHex(combinedBuffers); @@ -230,7 +278,7 @@ export class Calldata { return ""; } - public setRoot(block: CalldataBlock) { + public setRoot(block: MemberCalldataBlock) { this.root = block; this.sizeInBytes += block.getSizeInBytes(); } diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 0783b9c56..7f110c4fc 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -43,7 +43,7 @@ export abstract class PayloadDataType extends DataType { public encode(value: any, calldata: Calldata): void { const block = this.generateCalldataBlock(value); - calldata.setRoot(block); + // calldata.setRoot(block); } public isStatic(): boolean { @@ -78,7 +78,7 @@ export abstract class DependentDataType extends DataType { public encode(value: any, calldata: Calldata = new Calldata()): void { const block = this.generateCalldataBlock(value); - calldata.setRoot(block); + //calldata.setRoot(block); } public isStatic(): boolean { @@ -125,15 +125,20 @@ export abstract class MemberDataType extends DataType { type: memberItem.type, name: `${dataItem.name}.${memberItem.name}`, } as DataItem; + const components = memberItem.components; + if (components !== undefined) { + childDataItem.components = components; + } const child = DataTypeFactory.create(childDataItem, this); + memberMap[memberItem.name] = members.length; members.push(child); - memberMap[dataItem.name] = members.length; }); return [members, memberMap]; } private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { + console.log('!'.repeat(30), dataItem); let members: DataType[] = []; let memberMap: MemberMap = {}; const range = _.range(length); @@ -147,8 +152,8 @@ export abstract class MemberDataType extends DataType { childDataItem.components = components; } const child = DataTypeFactory.create(childDataItem, this); - members.push(child); memberMap[idx.toString(10)] = members.length; + members.push(child); }); return [members, memberMap]; diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index fcced646a..dfa541e80 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -315,6 +315,7 @@ export class Tuple extends MemberDataType { private tupleSignature: string; constructor(dataItem: DataItem) { + console.log(dataItem); super(dataItem); if (!Tuple.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 404f160fd..984507923 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -116,7 +116,7 @@ describe.only('ABI Encoder', () => { }); - it.only('Types with default widths', async () => { + it('Types with default widths', async () => { const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); console.log(method); const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; @@ -201,7 +201,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it.only('Multidimensional Arrays / Static Members', async () => { + it('Multidimensional Arrays / Static Members', async () => { const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi); // Eight 3-dimensional arrays of uint8[2][2][2] -- cgit v1.2.3 From 707a93e9f95e2adac992dc0e7903b7b1646c78c5 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 9 Nov 2018 23:00:58 -0800 Subject: Refactor passing all regression tests --- packages/order-utils/test/abi_encoder_test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 984507923..fa0fa23c1 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -26,7 +26,7 @@ const expect = chai.expect; describe.only('ABI Encoder', () => { describe.only('ABI Tests at Method Level', () => { - it.skip('Crazy ABI', async () => { + it('Crazy ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi); console.log(method.getSignature()); @@ -91,7 +91,7 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); - it.skip('Crazy ABI #1', async () => { + it('Crazy ABI #1', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi1); const args = [ -- cgit v1.2.3 From a9e3d489d60df1be4805f1c84f136eb127fef949 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 12 Nov 2018 12:49:03 -0800 Subject: Tests for nested tuples -- couldnt test because not supported by reference ABI encoder --- packages/order-utils/test/abi/data_type.ts | 2 +- packages/order-utils/test/abi_encoder_test.ts | 74 ++++++++++++++++++++++ packages/order-utils/test/abi_samples.ts | 89 +++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 1 deletion(-) diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 7f110c4fc..532407f52 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -195,7 +195,7 @@ export abstract class MemberDataType extends DataType { let childMap = _.cloneDeep(this.memberMap); _.forOwn(obj, (value: any, key: string) => { if (key in childMap === false) { - throw new Error(`Could not assign tuple to object: unrecognized key '${key}'`); + throw new Error(`Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`); } const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock); memberBlocks.push(block); diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index fa0fa23c1..0db5f4281 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -387,6 +387,80 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); + it.skip('Nested Tuples', async () => { + // Couldn't get nested tuples to work with Remix + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.nestedTuples); + const firstTuple = { + someUint32: new BigNumber(30472), + nestedTuple: { + someUint: new BigNumber('48384725243211555532'), + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' + } + }; + const secondTuple = { + someUint: new BigNumber(2984237422), + someStr: 'This string will exceed 256 bits, so it will spill into the next word of memory.', + nestedTuple: { + someUint32: new BigNumber(23), + secondNestedTuple: { + someUint: new BigNumber(234324), + someStr: 'Im also a short string -- oops I just got loooooooooooooooooonger!', + someBytes: '0x23847287fff3472984723498ff23487324987aaa237438911873429472ba', + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' + } + }, + someBytes: '0x2834y3947289423u489aaaff4783924739847489', + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699afaf', + }; + const thirdTuple = { + 'someUint': new BigNumber(37422), + 'someStr': 'This into the next word of memory. string will exceed 256 bits, so it will spill.', + 'nestedTuple': { + someUint32: new BigNumber(23999222), + 'secondNestedTuple': { + 'someUint': new BigNumber(324), + 'someStr': 'Im also a short st', + 'someBytes': '0x723498ff2348732498723847287fff3472984aaa237438911873429472ba', + 'someAddress': '0x46dafa5ebde1f4699f498e41d2489571d3221892' + } + }, + 'someBytes': '0x947289423u489aaaff472834y383924739847489', + 'someAddress': '0x46dafa5ebde1f46e41d2489571d322189299afaf', + }; + const fourthTuple = { + 'someUint': new BigNumber(222283488822), + 'someStr': 'exceed 256 bits, so it will spill into the. This string will next word of memory.', + 'nestedTuple': { + someUint32: new BigNumber(2300), + 'secondNestedTuple': { + 'someUint': new BigNumber(343224), + 'someStr': 'The alphabet backwards is arguably easier to say if thats the way you learned the first time.', + 'someBytes': '0x87324987aaa23743891187323847287fff3472984723498ff234429472ba', + 'someAddress': '0x71d322189246dafa5ebe41d24895de1f4699f498' + } + }, + 'someBytes': '0x2783924739847488343947289423u489aaaff490', + 'someAddress': '0xebde1d322189246dafa1f4699afafe41d2489575', + }; + const args = [ + [firstTuple], + [secondTuple, thirdTuple, fourthTuple] + ]; + + console.log('*'.repeat(250), method, '*'.repeat(250)); + + + const calldata = method.encode(args); + console.log(method.getSignature()); + console.log(method.selector); + console.log(JSON.stringify(args)); + + console.log(calldata); + const expectedCalldata = '0x'; + expect(calldata).to.be.equal(expectedCalldata); + }); + it.skip('Object ABI (Object input - Missing Key)', async () => { const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); const calldata = method.encode([{ someUint: new BigNumber(5) }]); diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts index 82427986a..3f7b1a927 100644 --- a/packages/order-utils/test/abi_samples.ts +++ b/packages/order-utils/test/abi_samples.ts @@ -547,6 +547,95 @@ export const crazyAbi = { type: 'function', } as MethodAbi; +export const nestedTuples = { + constant: false, + inputs: [ + { + name: 'firstTuple', + type: 'tuple[1]', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'nestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + ], + }, + { + name: 'secondTuple', + type: 'tuple[]', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + { + name: 'nestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'secondNestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + ], + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + export const simpleAbi2 = { constant: false, inputs: [ -- cgit v1.2.3 From 13d456fda1da19c8227acb2bf23106bcbdaf6f12 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 12 Nov 2018 13:06:45 -0800 Subject: removed old code --- packages/order-utils/test/abi/calldata.ts | 38 ------------------------------- 1 file changed, 38 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index 37dd30e57..94f6a5571 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -223,44 +223,6 @@ export class Calldata { valueBufs.push(block.toBuffer()); } - /* - const blockQueue = new Queue(); - blockQueue.push(this.root); - - // Assign locations in breadth-first manner - let block: CalldataBlock | undefined; - let offset = 0; - while ((block = blockQueue.pop()) !== undefined) { - console.log(block.getName()); - block.setOffset(offset); - offset += block.getSizeInBytes(); - if (block instanceof DependentCalldataBlock) { - console.log(block.getDependency()); - blockQueue.push(block.getDependency()); - } else if (block instanceof MemberCalldataBlock) { - _.eachRight(block.getMembers(), (member: CalldataBlock) => { - blockQueue.pushFront(member); - }); - } - } - - console.log(this.root); - - // Fetch values using same technique - const valueBufs: Buffer[] = [selectorBuffer]; - blockQueue.push(this.root); - while ((block = blockQueue.pop()) !== undefined) { - valueBufs.push(block.toBuffer()); - - if (block instanceof DependentCalldataBlock) { - blockQueue.push(block.getDependency()); - } else if (block instanceof MemberCalldataBlock) { - _.eachRight(block.getMembers(), (member: CalldataBlock) => { - blockQueue.pushFront(member); - }); - } - }*/ - const combinedBuffers = Buffer.concat(valueBufs); const hexValue = ethUtil.bufferToHex(combinedBuffers); return hexValue; -- cgit v1.2.3 From 9b1f56c9687d3fffb2f59fa98d619ead90c1b910 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 12 Nov 2018 17:10:32 -0800 Subject: Decoding works for some basic typs --- packages/order-utils/test/abi/calldata.ts | 102 ++++++++++++++++++++++- packages/order-utils/test/abi/data_type.ts | 43 +++++++++- packages/order-utils/test/abi/evm_data_types.ts | 103 +++++++++++++++++++++++- packages/order-utils/test/abi_encoder_test.ts | 40 ++++++--- 4 files changed, 274 insertions(+), 14 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index 94f6a5571..0445f68ec 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -164,6 +164,12 @@ class Queue { mergeFront(q: Queue) { this.store = q.store.concat(this.store); } + getStore(): T[] { + return this.store; + } + peek(): T | undefined { + return this.store.length >= 0 ? this.store[0] : undefined; + } } export class Calldata { @@ -203,7 +209,29 @@ export class Calldata { return blockQueue; } - public toHexString(): string { + /* + + // Basic optimize method that prunes duplicate branches of the tree + // Notes: + // 1. Pruning is at the calldata block level, so it is independent of type + // 2. + private optimize(blocks: CalldataBlock[]) { + // Build hash table of blocks + const blockLookupTable: { [key: string]: string } = {}; + _.each(blocks, (block: CalldataBlock) => { + if (blocks instanceof DependentCalldataBlock === false) { + + return; + } + + const leavesHash = block.hashLeaves(); + if (leavesHash in blockLookupTable) { + + } + }) + }*/ + + public toHexString(optimize: boolean = false): string { let selectorBuffer = ethUtil.toBuffer(this.selector); if (this.root === undefined) { throw new Error('expected root'); @@ -223,6 +251,8 @@ export class Calldata { valueBufs.push(block.toBuffer()); } + // if (optimize) this.optimize(valueQueue.getStore()); + const combinedBuffers = Buffer.concat(valueBufs); const hexValue = ethUtil.bufferToHex(combinedBuffers); return hexValue; @@ -259,4 +289,74 @@ export class Calldata { } this.sizeInBytes += 8; } +} + +export class RawCalldata { + private value: Buffer; + private offset: number; // tracks current offset into raw calldata; used for parsing + private selector: string; + private scopes: Queue; + + constructor(value: string | Buffer) { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Expected raw calldata to start with '0x'`); + } + const valueBuf = ethUtil.toBuffer(value); + this.selector = ethUtil.bufferToHex(valueBuf.slice(0, 4)); + this.value = valueBuf.slice(4); // disregard selector + this.offset = 0; + this.scopes = new Queue(); + this.scopes.push(0); + } + + public popBytes(lengthInBytes: number): Buffer { + const value = this.value.slice(this.offset, this.offset + lengthInBytes); + this.setOffset(this.offset + lengthInBytes); + return value; + } + + public popWord(): Buffer { + const wordInBytes = 32; + return this.popBytes(wordInBytes); + } + + public popWords(length: number): Buffer { + const wordInBytes = 32; + return this.popBytes(length * wordInBytes); + } + + public readBytes(from: number, to: number): Buffer { + const value = this.value.slice(from, to); + return value; + } + + public setOffset(offsetInBytes: number) { + this.offset = offsetInBytes; + console.log('0'.repeat(100), this.offset); + } + + public startScope() { + this.scopes.pushFront(this.offset); + } + + public endScope() { + this.scopes.pop(); + } + + public getOffset(): number { + return this.offset; + } + + public toAbsoluteOffset(relativeOffset: number) { + const scopeOffset = this.scopes.peek(); + if (scopeOffset === undefined) { + throw new Error(`Tried to access undefined scope.`); + } + const absoluteOffset = relativeOffset + scopeOffset; + return absoluteOffset; + } + + public getSelector(): string { + return this.selector; + } } \ No newline at end of file diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 532407f52..af170f7e6 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -1,4 +1,4 @@ -import { Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata"; +import { RawCalldata, Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata"; import { MethodAbi, DataItem } from 'ethereum-types'; import { BigNumber } from '@0x/utils'; import ethUtil = require('ethereumjs-util'); @@ -18,6 +18,7 @@ export abstract class DataType { } public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; + public abstract generateValue(calldata: RawCalldata): any; public abstract encode(value: any, calldata: Calldata): void; public abstract getSignature(): string; public abstract isStatic(): boolean; @@ -46,12 +47,18 @@ export abstract class PayloadDataType extends DataType { // calldata.setRoot(block); } + public generateValue(calldata: RawCalldata): any { + const value = this.decodeValue(calldata); + return value; + } + public isStatic(): boolean { // If a payload has a constant size then it's static return this.hasConstantSize; } public abstract encodeValue(value: any): Buffer; + public abstract decodeValue(calldata: RawCalldata): any; } export abstract class DependentDataType extends DataType { @@ -81,6 +88,17 @@ export abstract class DependentDataType extends DataType { //calldata.setRoot(block); } + public generateValue(calldata: RawCalldata): any { + const destinationOffsetBuf = calldata.popWord(); + const currentOffset = calldata.getOffset(); + const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), 16); + const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); + calldata.setOffset(destinationOffsetAbsolute); + const value = this.dependency.generateValue(calldata); + calldata.setOffset(currentOffset); + return value; + } + public isStatic(): boolean { return true; } @@ -179,7 +197,6 @@ export abstract class MemberDataType extends DataType { methodBlock.setHeader(lenBuf); } - const memberBlocks: CalldataBlock[] = []; _.each(members, (member: DataType, idx: number) => { const block = member.generateCalldataBlock(value[idx], methodBlock); @@ -220,6 +237,28 @@ export abstract class MemberDataType extends DataType { calldata.setRoot(block); } + public generateValue(calldata: RawCalldata): any[] { + let members = this.members; + if (this.isArray && this.arrayLength === undefined) { + const arrayLengthBuf = calldata.popWord(); + const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); + const hexBase = 16; + const arrayLength = new BigNumber(arrayLengthHex, hexBase); + + [members,] = this.createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); + } + + calldata.startScope(); + const decodedValue: any[] = []; + _.each(members, (member: DataType, idx: number) => { + let memberValue = member.generateValue(calldata); + decodedValue.push(memberValue); + }); + calldata.endScope(); + + return decodedValue; + } + protected computeSignatureOfMembers(): string { // Compute signature of members let signature = `(`; diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index dfa541e80..a05c29b28 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -4,7 +4,7 @@ import { MethodAbi, DataItem } from 'ethereum-types'; import ethUtil = require('ethereumjs-util'); -import { Calldata } from './calldata'; +import { Calldata, RawCalldata } from './calldata'; import { BigNumber } from '@0x/utils'; @@ -13,6 +13,7 @@ var _ = require('lodash'); export interface DataTypeStaticInterface { matchGrammar: (type: string) => boolean; encodeValue: (value: any) => Buffer; + // decodeValue: (value: Buffer) => [any, Buffer]; } export class Address extends PayloadDataType { @@ -38,6 +39,13 @@ export class Address extends PayloadDataType { const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth); return encodedValueBuf; } + + public decodeValue(calldata: RawCalldata): string { + const paddedValueBuf = calldata.popWord(); + const valueBuf = paddedValueBuf.slice(12); + const value = ethUtil.bufferToHex(valueBuf); + return value; + } } export class Bool extends PayloadDataType { @@ -64,6 +72,17 @@ export class Bool extends PayloadDataType { const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth); return encodedValueBuf; } + + public decodeValue(calldata: RawCalldata): boolean { + const valueBuf = calldata.popWord(); + const valueHex = ethUtil.bufferToHex(valueBuf); + const valueNumber = new BigNumber(valueHex, 16); + let value: boolean = (valueNumber.equals(0)) ? false : true; + if (!(valueNumber.equals(0) || valueNumber.equals(1))) { + throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); + } + return value; + } } abstract class Number extends PayloadDataType { @@ -126,6 +145,37 @@ abstract class Number extends PayloadDataType { return valueBuf; } + public decodeValue(calldata: RawCalldata): BigNumber { + const decodedValueBuf = calldata.popWord(); + const decodedValueHex = ethUtil.bufferToHex(decodedValueBuf); + let decodedValue = new BigNumber(decodedValueHex, 16); + if (this instanceof Int) { + // Check if we're negative + const binBase = 2; + const decodedValueBin = decodedValue.toString(binBase); + if (decodedValueBin[0] === '1') { + // Negative + // Step 1/3: Invert binary value + const bitsInEvmWord = 256; + let invertedValueBin = '1'.repeat(bitsInEvmWord - decodedValueBin.length); + _.each(decodedValueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, binBase); + + // Step 2/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const positiveDecodedValue = invertedValue.plus(binBase); + + // Step 3/3: Invert positive value + const negativeDecodedValue = positiveDecodedValue.times(-1); + decodedValue = negativeDecodedValue; + } + } + + return decodedValue; + } + public abstract getMaxValue(): BigNumber; public abstract getMinValue(): BigNumber; } @@ -228,6 +278,13 @@ export class Byte extends PayloadDataType { return paddedValue; } + public decodeValue(calldata: RawCalldata): string { + const paddedValueBuf = calldata.popWord(); + const valueBuf = paddedValueBuf.slice(0, this.width); + const value = ethUtil.bufferToHex(valueBuf); + return value; + } + public static matchGrammar(type: string): boolean { return this.matcher.test(type); } @@ -262,6 +319,17 @@ export class Bytes extends PayloadDataType { return encodedValueBuf; } + public decodeValue(calldata: RawCalldata): string { + const lengthBuf = calldata.popWord(); + const lengthHex = ethUtil.bufferToHex(lengthBuf); + const length = parseInt(lengthHex, 16); + const wordsForValue = Math.ceil(length / 32); + const paddedValueBuf = calldata.popWords(wordsForValue); + const valueBuf = paddedValueBuf.slice(0, length); + const decodedValue = ethUtil.bufferToHex(valueBuf); + return decodedValue; + } + public getSignature(): string { return 'bytes'; } @@ -289,6 +357,18 @@ export class SolString extends PayloadDataType { return encodedValueBuf; } + public decodeValue(calldata: RawCalldata): string { + const lengthBuf = calldata.popWord(); + const lengthHex = ethUtil.bufferToHex(lengthBuf); + const length = parseInt(lengthHex, 16); + const wordsForValue = Math.ceil(length / 32); + const paddedValueBuf = calldata.popWords(wordsForValue); + const valueBuf = paddedValueBuf.slice(0, length); + console.log('LENGTH UPINYA === ', length); + const value = valueBuf.toString('ascii'); + return value; + } + public getSignature(): string { return 'string'; } @@ -415,6 +495,27 @@ export class Method extends MemberDataType { return calldata.toHexString(); } + /* + protected decodeValue(value: Buffer): any[] { + const selectorBuf = value.slice(4); + const selectorHex = ethUtil.bufferToHex(selectorBuf); + if (this.selector !== selectorHex) { + throw new Error(`Tried to decode calldata with mismatched selector. Expected '${this.selector}', got '${selectorHex}'`); + } + const remainingValue = value.slice(9); + const decodedValue = super.decodeValue(remainingValue); + return decodedValue; + }*/ + + public decode(calldata: string): any[] { + const calldata_ = new RawCalldata(calldata); + if (this.selector !== calldata_.getSelector()) { + throw new Error(`Tried to decode calldata with mismatched selector. Expected '${this.selector}', got '${calldata_.getSelector()}'`); + } + const value = super.generateValue(calldata_); + return value; + } + public getSignature(): string { return this.methodSignature; } diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 0db5f4281..e8ebd7b98 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -310,6 +310,12 @@ describe.only('ABI Encoder', () => { const expectedCalldata = '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); @@ -327,38 +333,52 @@ describe.only('ABI Encoder', () => { const expectedCalldata = '0x7ac2bd96af000000000000000000000000000000000000000000000000000000000000000001020304050607080911121314151617181920212223242526272829303132000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000047616161616161616161616161616161616161616161616161616161616161616161616161616161611114f3245678384756473829384756774488993384576688990020202020200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414d79206669727374206e616d65206973204772656720616e64206d79206c617374206e616d6520697320487973656e2c207768617420646f207961206b6e6f772100000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); - }); - it('Yessir', async () => { - const method = new AbiEncoder.Method(AbiSamples.simpleAbi); - const calldata = method.encode([new BigNumber(5), 'five']); - console.log(calldata); - expect(true).to.be.true(); + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Array ABI', async () => { + it.only('Array ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.stringAbi); console.log(method); - const calldata = method.encode([['five', 'six', 'seven']]); + const args = [['five', 'six', 'seven']]; + const calldata = method.encode(args); console.log(method.getSignature()); console.log(method.selector); + /* console.log(calldata); const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); + expect(calldata).to.be.equal(expectedCalldata);*/ + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Static Tuple', async () => { // This is dynamic because it has dynamic members const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi); - const calldata = method.encode([[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]); + const args = [[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]; + const calldata = method.encode(args); console.log(method.getSignature()); console.log(method.selector); console.log(calldata); const expectedCalldata = '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Dynamic Tuple (Array input)', async () => { -- cgit v1.2.3 From c5d252ba4a79a0f16e053a071b0173a7bd666328 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 12 Nov 2018 17:14:25 -0800 Subject: decoding works for most cases --- packages/order-utils/test/abi_encoder_test.ts | 94 +++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 5 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index e8ebd7b98..631255e68 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -89,9 +89,15 @@ describe.only('ABI Encoder', () => { const expectedCalldata = '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; //const expectedCalldata = '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + /*const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson);*/ }); - it('Crazy ABI #1', async () => { + it.only('Crazy ABI #1', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi1); const args = [ @@ -113,6 +119,12 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); const expectedCalldata = '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); @@ -127,6 +139,12 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); const expectedCalldata = '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Array of Static Tuples (Array has defined length)', async () => { @@ -145,6 +163,12 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); const expectedCalldata = '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Array of Static Tuples (Array has dynamic length)', async () => { @@ -163,6 +187,12 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); const expectedCalldata = '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Array of Dynamic Tuples (Array has defined length)', async () => { @@ -181,6 +211,12 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); const expectedCalldata = '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Array of Dynamic Tuples (Array has dynamic length)', async () => { @@ -199,6 +235,12 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); const expectedCalldata = '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Multidimensional Arrays / Static Members', async () => { @@ -228,6 +270,12 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); const expectedCalldata = '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; expect(calldata).to.be.equal(expectedCalldata); expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Multidimensional Arrays / Dynamic Members', async () => { @@ -257,6 +305,12 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); const expectedCalldata = '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Fixed Lenfgth Array / Dynamic Members', async () => { @@ -269,6 +323,12 @@ describe.only('ABI Encoder', () => { const expectedCalldata = '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Fixed Lenfgth Array / Dynamic Members', async () => { @@ -281,6 +341,12 @@ describe.only('ABI Encoder', () => { const expectedCalldata = '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Unfixed Length Array / Dynamic Members ABI', async () => { @@ -292,6 +358,12 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(args)); const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Unfixed Length Array / Static Members ABI', async () => { @@ -300,6 +372,12 @@ describe.only('ABI Encoder', () => { const calldata = method.encode(args); const expectedCalldata = '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); @@ -341,7 +419,7 @@ describe.only('ABI Encoder', () => { expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it.only('Array ABI', async () => { + it('Array ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.stringAbi); console.log(method); const args = [['five', 'six', 'seven']]; @@ -349,11 +427,10 @@ describe.only('ABI Encoder', () => { console.log(method.getSignature()); console.log(method.selector); - /* console.log(calldata); const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata);*/ + expect(calldata).to.be.equal(expectedCalldata); // Test decoding const expectedDecodedValueJson = JSON.stringify(args); @@ -384,7 +461,8 @@ describe.only('ABI Encoder', () => { it('Dynamic Tuple (Array input)', async () => { // This is dynamic because it has dynamic members const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const calldata = method.encode([[new BigNumber(5), 'five']]); + const args = [[new BigNumber(5), 'five']]; + const calldata = method.encode(args); console.log(method.getSignature()); console.log(method.selector); @@ -392,6 +470,12 @@ describe.only('ABI Encoder', () => { const expectedCalldata = '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); it('Dynamic Tuple (Object input)', async () => { -- cgit v1.2.3 From 2a6b358717e3134aef3ff86cc5343b3b37edee7e Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 12 Nov 2018 17:28:46 -0800 Subject: Decoder works for uints with width less than 256 --- packages/order-utils/test/abi/evm_data_types.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index a05c29b28..abb4fba14 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -146,34 +146,34 @@ abstract class Number extends PayloadDataType { } public decodeValue(calldata: RawCalldata): BigNumber { - const decodedValueBuf = calldata.popWord(); - const decodedValueHex = ethUtil.bufferToHex(decodedValueBuf); - let decodedValue = new BigNumber(decodedValueHex, 16); + const paddedValueBuf = calldata.popWord(); + const paddedValueHex = ethUtil.bufferToHex(paddedValueBuf); + let value = new BigNumber(paddedValueHex, 16); if (this instanceof Int) { // Check if we're negative const binBase = 2; - const decodedValueBin = decodedValue.toString(binBase); - if (decodedValueBin[0] === '1') { + const paddedValueBin = value.toString(binBase); + const valueBin = paddedValueBin.slice(paddedValueBin.length - this.width); + if (valueBin[0].startsWith('1')) { // Negative // Step 1/3: Invert binary value - const bitsInEvmWord = 256; - let invertedValueBin = '1'.repeat(bitsInEvmWord - decodedValueBin.length); - _.each(decodedValueBin, (bit: string) => { + let invertedValueBin = ''; + _.each(valueBin, (bit: string) => { invertedValueBin += bit === '1' ? '0' : '1'; }); const invertedValue = new BigNumber(invertedValueBin, binBase); // Step 2/3: Add 1 to inverted value // The result is the two's-complement represent of the input value. - const positiveDecodedValue = invertedValue.plus(binBase); + const positiveValue = invertedValue.plus(1); // Step 3/3: Invert positive value - const negativeDecodedValue = positiveDecodedValue.times(-1); - decodedValue = negativeDecodedValue; + const negativeValue = positiveValue.times(-1); + value = negativeValue; } } - return decodedValue; + return value; } public abstract getMaxValue(): BigNumber; -- cgit v1.2.3 From 52474a0b8edb2f49f850069c1a79dcd9b5d77e9d Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 12 Nov 2018 17:49:52 -0800 Subject: decoding with objects as structs works --- packages/order-utils/test/abi/data_type.ts | 37 ++++++++++++++++--------- packages/order-utils/test/abi/evm_data_types.ts | 21 ++++---------- packages/order-utils/test/abi_encoder_test.ts | 21 ++++++++++---- 3 files changed, 45 insertions(+), 34 deletions(-) diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index af170f7e6..725d314f3 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -4,7 +4,9 @@ import { BigNumber } from '@0x/utils'; import ethUtil = require('ethereumjs-util'); var _ = require('lodash'); - +export interface GenerateValueRules { + structsAsObjects: boolean; +} export abstract class DataType { private dataItem: DataItem; @@ -18,7 +20,7 @@ export abstract class DataType { } public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; - public abstract generateValue(calldata: RawCalldata): any; + public abstract generateValue(calldata: RawCalldata, rules: GenerateValueRules): any; public abstract encode(value: any, calldata: Calldata): void; public abstract getSignature(): string; public abstract isStatic(): boolean; @@ -47,7 +49,7 @@ export abstract class PayloadDataType extends DataType { // calldata.setRoot(block); } - public generateValue(calldata: RawCalldata): any { + public generateValue(calldata: RawCalldata, rules: GenerateValueRules): any { const value = this.decodeValue(calldata); return value; } @@ -88,13 +90,13 @@ export abstract class DependentDataType extends DataType { //calldata.setRoot(block); } - public generateValue(calldata: RawCalldata): any { + public generateValue(calldata: RawCalldata, rules: GenerateValueRules): any { const destinationOffsetBuf = calldata.popWord(); const currentOffset = calldata.getOffset(); const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), 16); const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); calldata.setOffset(destinationOffsetAbsolute); - const value = this.dependency.generateValue(calldata); + const value = this.dependency.generateValue(calldata, rules); calldata.setOffset(currentOffset); return value; } @@ -237,7 +239,7 @@ export abstract class MemberDataType extends DataType { calldata.setRoot(block); } - public generateValue(calldata: RawCalldata): any[] { + public generateValue(calldata: RawCalldata, rules: GenerateValueRules): any[] | object { let members = this.members; if (this.isArray && this.arrayLength === undefined) { const arrayLengthBuf = calldata.popWord(); @@ -249,14 +251,23 @@ export abstract class MemberDataType extends DataType { } calldata.startScope(); - const decodedValue: any[] = []; - _.each(members, (member: DataType, idx: number) => { - let memberValue = member.generateValue(calldata); - decodedValue.push(memberValue); - }); + let value: any[] | object; + if (rules.structsAsObjects && !this.isArray) { + value = {}; + _.each(this.memberMap, (idx: number, key: string) => { + const member = this.members[idx]; + let memberValue = member.generateValue(calldata, rules); + (value as { [key: string]: any })[key] = memberValue; + }); + } else { + value = []; + _.each(members, (member: DataType, idx: number) => { + let memberValue = member.generateValue(calldata, rules); + (value as any[]).push(memberValue); + }); + } calldata.endScope(); - - return decodedValue; + return value; } protected computeSignatureOfMembers(): string { diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index abb4fba14..7b366a985 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -1,4 +1,4 @@ -import { DataType, DataTypeFactory, DataTypeFactoryImpl, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; +import { GenerateValueRules, DataType, DataTypeFactory, DataTypeFactoryImpl, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; import { MethodAbi, DataItem } from 'ethereum-types'; @@ -495,24 +495,15 @@ export class Method extends MemberDataType { return calldata.toHexString(); } - /* - protected decodeValue(value: Buffer): any[] { - const selectorBuf = value.slice(4); - const selectorHex = ethUtil.bufferToHex(selectorBuf); - if (this.selector !== selectorHex) { - throw new Error(`Tried to decode calldata with mismatched selector. Expected '${this.selector}', got '${selectorHex}'`); - } - const remainingValue = value.slice(9); - const decodedValue = super.decodeValue(remainingValue); - return decodedValue; - }*/ - - public decode(calldata: string): any[] { + public decode(calldata: string, decodeStructsAsObjects: boolean = false): any[] | object { const calldata_ = new RawCalldata(calldata); if (this.selector !== calldata_.getSelector()) { throw new Error(`Tried to decode calldata with mismatched selector. Expected '${this.selector}', got '${calldata_.getSelector()}'`); } - const value = super.generateValue(calldata_); + let rules: GenerateValueRules = { + structsAsObjects: decodeStructsAsObjects + }; + const value = super.generateValue(calldata_, rules); return value; } diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 631255e68..137850b35 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -26,7 +26,7 @@ const expect = chai.expect; describe.only('ABI Encoder', () => { describe.only('ABI Tests at Method Level', () => { - it('Crazy ABI', async () => { + it.only('Crazy ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi); console.log(method.getSignature()); @@ -78,7 +78,15 @@ describe.only('ABI Encoder', () => { }; const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3]; - const args = [someStaticArray, someStaticArrayWithDynamicMembers, someDynamicArrayWithDynamicMembers, some2DArray, someTuple, someTupleWithDynamicTypes, someArrayOfTuplesWithDynamicTypes]; + const args = { + someStaticArray: someStaticArray, + someStaticArrayWithDynamicMembers: someStaticArrayWithDynamicMembers, + someDynamicArrayWithDynamicMembers: someDynamicArrayWithDynamicMembers, + some2DArray: some2DArray, + someTuple: someTuple, + someTupleWithDynamicTypes: someTupleWithDynamicTypes, + someArrayOfTuplesWithDynamicTypes: someArrayOfTuplesWithDynamicTypes + }; const calldata = method.encode(args); console.log(calldata); @@ -91,13 +99,14 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); // Test decoding - /*const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata, true); const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson);*/ + console.log(`DECODED`, '*'.repeat(200), '\n', decodedValueJson); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it.only('Crazy ABI #1', async () => { + it('Crazy ABI #1', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi1); const args = [ -- cgit v1.2.3 From 7c733662e2669ca6682920f321c81e770605b3d5 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 13 Nov 2018 12:36:14 -0800 Subject: Annotated calldata (draft). --- packages/order-utils/test/abi/calldata.ts | 113 ++++++++++++++++++++---- packages/order-utils/test/abi/data_type.ts | 21 +++-- packages/order-utils/test/abi/evm_data_types.ts | 4 +- packages/order-utils/test/abi_encoder_test.ts | 7 +- 4 files changed, 118 insertions(+), 27 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index 0445f68ec..1173f90cc 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -9,10 +9,12 @@ export abstract class CalldataBlock { private headerSizeInBytes: number; private bodySizeInBytes: number; private relocatable: boolean; + private parentName: string; - constructor(name: string, signature: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { + constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { this.name = name; this.signature = signature; + this.parentName = parentName; this.offsetInBytes = 0; this.headerSizeInBytes = headerSizeInBytes; this.bodySizeInBytes = bodySizeInBytes; @@ -31,6 +33,10 @@ export abstract class CalldataBlock { return this.name; } + public getParentName(): string { + return this.parentName; + } + public getSignature(): string { return this.signature; } @@ -65,10 +71,10 @@ export abstract class CalldataBlock { export class PayloadCalldataBlock extends CalldataBlock { private payload: Buffer; - constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { + constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { const headerSizeInBytes = 0; const bodySizeInBytes = payload.byteLength; - super(name, signature, /*offsetInBytes,*/ headerSizeInBytes, bodySizeInBytes, relocatable); + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); this.payload = payload; } @@ -82,10 +88,10 @@ export class DependentCalldataBlock extends CalldataBlock { private parent: CalldataBlock; private dependency: CalldataBlock; - constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { + constructor(name: string, signature: string, parentName: string, relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { const headerSizeInBytes = 0; const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; - super(name, signature, /*offsetInBytes,*/ headerSizeInBytes, bodySizeInBytes, relocatable); + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); this.parent = parent; this.dependency = dependency; } @@ -112,8 +118,8 @@ export class MemberCalldataBlock extends CalldataBlock { private members: CalldataBlock[]; private contiguous: boolean; - constructor(name: string, signature: string, /*offsetInBytes: number,*/ relocatable: boolean, contiguous: boolean) { - super(name, signature, /*offsetInBytes,*/ 0, 0, relocatable); + constructor(name: string, signature: string, parentName: string, relocatable: boolean, contiguous: boolean) { + super(name, signature, parentName, 0, 0, relocatable); this.members = []; this.header = undefined; this.contiguous = contiguous; @@ -231,33 +237,110 @@ export class Calldata { }) }*/ - public toHexString(optimize: boolean = false): string { - let selectorBuffer = ethUtil.toBuffer(this.selector); + private generateAnnotatedHexString(): string { + let hexValue = `${this.selector}`; if (this.root === undefined) { throw new Error('expected root'); } - const offsetQueue = this.createQueue(this.root); + const valueQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; let offset = 0; - while ((block = offsetQueue.pop()) !== undefined) { - block.setOffset(offset); - offset += block.getSizeInBytes(); + const functionBlock = valueQueue.peek(); + let functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); + while ((block = valueQueue.pop()) !== undefined) { + // Set f + + // Process each block 1 word at a time + const size = block.getSizeInBytes(); + const name = block.getName(); + const parentName = block.getParentName(); + console.log('*'.repeat(50), parentName, ' vs ', name); + + //const ancestrialNamesOffset = name.startsWith('ptr<') ? 4 : 0; + //const parentOffset = name.lastIndexOf(parentName); + const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, '');//.replace(`${parentName}[`, '['); + const signature = block.getSignature(); + + // Current offset + let offsetStr = ''; + + // If this block is empty then it's a newline + let value = ''; + let nameStr = ''; + let line = ''; + if (size === 0) { + offsetStr = ' '.repeat(10); + value = ' '.repeat(74); + nameStr = `### ${prettyName.padEnd(80)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + offsetStr = `0x${offset.toString(16)}`.padEnd(10, ' '); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(0, 32))).padEnd(74); + if (block instanceof MemberCalldataBlock) { + nameStr = `### ${prettyName.padEnd(80)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + nameStr = ` ${prettyName.padEnd(80)}`; + line = `${offsetStr}${value}${nameStr}`; + } + } + + for (let j = 32; j < size; j += 32) { + offsetStr = `0x${(offset + j).toString(16)}`.padEnd(10, ' '); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(j, j + 32))).padEnd(74); + nameStr = ' '.repeat(40); + + line = `${line}\n${offsetStr}${value}${nameStr}`; + } + + // Append to hex value + hexValue = `${hexValue}\n${line}`; + offset += size; + } + + return hexValue; + } + + private generateCondensedHexString(): string { + let selectorBuffer = ethUtil.toBuffer(this.selector); + if (this.root === undefined) { + throw new Error('expected root'); } const valueQueue = this.createQueue(this.root); const valueBufs: Buffer[] = [selectorBuffer]; + let block: CalldataBlock | undefined; while ((block = valueQueue.pop()) !== undefined) { valueBufs.push(block.toBuffer()); } - // if (optimize) this.optimize(valueQueue.getStore()); - const combinedBuffers = Buffer.concat(valueBufs); const hexValue = ethUtil.bufferToHex(combinedBuffers); return hexValue; } + public toHexString(optimize: boolean = false, annotate: boolean = false): string { + if (this.root === undefined) { + throw new Error('expected root'); + } + + const offsetQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; + let offset = 0; + while ((block = offsetQueue.pop()) !== undefined) { + block.setOffset(offset); + offset += block.getSizeInBytes(); + } + + + // if (optimize) this.optimize(valueQueue.getStore()); + + const hexValue = annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); + return hexValue; + } + public getSelectorHex(): string { return this.selector; } diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 725d314f3..25f792c7a 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -38,9 +38,9 @@ export abstract class PayloadDataType extends DataType { const encodedValue = this.encodeValue(value); const name = this.getDataItem().name; const signature = this.getSignature(); - // const offsetInBytes = calldata.getSizeInBytes(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); const relocatable = false; - const block = new PayloadCalldataBlock(name, signature, /*offsetInBytes,*/ relocatable, encodedValue); + const block = new PayloadCalldataBlock(name, signature, parentName, /*offsetInBytes,*/ relocatable, encodedValue); return block; } @@ -77,11 +77,12 @@ export abstract class DependentDataType extends DataType { if (parentBlock === undefined) { throw new Error(`DependentDataType requires a parent block to generate its block`); } - const dependencyBlock = this.dependency.generateCalldataBlock(value); + const dependencyBlock = this.dependency.generateCalldataBlock(value, parentBlock); const name = this.getDataItem().name; const signature = this.getSignature(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); const relocatable = false; - const block = new DependentCalldataBlock(name, signature, /*offsetInBytes,*/ relocatable, dependencyBlock, parentBlock); + const block = new DependentCalldataBlock(name, signature, parentName, relocatable, dependencyBlock, parentBlock); return block; } @@ -179,7 +180,7 @@ export abstract class MemberDataType extends DataType { return [members, memberMap]; } - protected generateCalldataBlockFromArray(value: any[]): MemberCalldataBlock { + protected generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { // Sanity check length if (this.arrayLength !== undefined && value.length !== this.arrayLength) { throw new Error( @@ -189,7 +190,8 @@ export abstract class MemberDataType extends DataType { ); } - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), this.isStatic(), false); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); let members = this.members; if (this.isArray && this.arrayLength === undefined) { @@ -208,8 +210,9 @@ export abstract class MemberDataType extends DataType { return methodBlock; } - protected generateCalldataBlockFromObject(obj: object): MemberCalldataBlock { - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), this.isStatic(), false); + protected generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); const memberBlocks: CalldataBlock[] = []; let childMap = _.cloneDeep(this.memberMap); _.forOwn(obj, (value: any, key: string) => { @@ -230,7 +233,7 @@ export abstract class MemberDataType extends DataType { } public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value) : this.generateCalldataBlockFromObject(value); + const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value, parentBlock) : this.generateCalldataBlockFromObject(value, parentBlock); return block; } diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index 7b366a985..ea401247b 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -489,10 +489,10 @@ export class Method extends MemberDataType { return selector; } - public encode(value: any[] | object, calldata = new Calldata()): string { + public encode(value: any[] | object, calldata = new Calldata(), annotate: boolean = false): string { calldata.setSelector(this.methodSelector); super.encode(value, calldata); - return calldata.toHexString(); + return calldata.toHexString(false, annotate); } public decode(calldata: string, decodeStructsAsObjects: boolean = false): any[] | object { diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 137850b35..8b836f77f 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -17,6 +17,7 @@ import { assert } from '@0x/order-utils/src/assert'; import * as AbiEncoder from './abi/abi_encoder'; import * as AbiSamples from './abi_samples'; +import { Calldata } from './abi/calldata'; AbiEncoder.DataTypeFactory.setImpl(new AbiEncoder.EvmDataTypeFactoryImpl()); @@ -88,8 +89,12 @@ describe.only('ABI Encoder', () => { someArrayOfTuplesWithDynamicTypes: someArrayOfTuplesWithDynamicTypes }; - const calldata = method.encode(args); + const calldata = method.encode(args, new Calldata(), true); console.log(calldata); + + + throw new Error(`done`); + console.log('*'.repeat(40)); console.log(JSON.stringify(args)); console.log(method.getSignature()); -- cgit v1.2.3 From 457cb1dc843511d6e987b0ffce0f985d4e97c968 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 13 Nov 2018 15:55:07 -0800 Subject: optimizer works for basic case --- packages/order-utils/test/abi/calldata.ts | 98 +++++++++++++++++++++++-- packages/order-utils/test/abi/evm_data_types.ts | 4 +- packages/order-utils/test/abi_encoder_test.ts | 16 +++- 3 files changed, 109 insertions(+), 9 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index 1173f90cc..ab42b7d73 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -29,6 +29,10 @@ export abstract class CalldataBlock { this.bodySizeInBytes = bodySizeInBytes; } + protected setName(name: string) { + this.name = name; + } + public getName(): string { return this.name; } @@ -65,7 +69,14 @@ export abstract class CalldataBlock { this.offsetInBytes = offsetInBytes; } + public computeHash(): Buffer { + const rawData = this.getRawData(); + const hash = ethUtil.sha3(rawData); + return hash; + } + public abstract toBuffer(): Buffer; + public abstract getRawData(): Buffer; } export class PayloadCalldataBlock extends CalldataBlock { @@ -81,12 +92,19 @@ export class PayloadCalldataBlock extends CalldataBlock { public toBuffer(): Buffer { return this.payload; } + + public getRawData(): Buffer { + return this.payload; + } } export class DependentCalldataBlock extends CalldataBlock { public static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + public static RAW_DATA_START = new Buffer('<'); + public static RAW_DATA_END = new Buffer('>'); private parent: CalldataBlock; private dependency: CalldataBlock; + private aliasFor: CalldataBlock | undefined; constructor(name: string, signature: string, parentName: string, relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { const headerSizeInBytes = 0; @@ -94,13 +112,14 @@ export class DependentCalldataBlock extends CalldataBlock { super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); this.parent = parent; this.dependency = dependency; + this.aliasFor = undefined; } public toBuffer(): Buffer { - const dependencyOffset = this.dependency.getOffsetInBytes(); + const destinationOffset = (this.aliasFor !== undefined) ? this.aliasFor.getOffsetInBytes() : this.dependency.getOffsetInBytes(); const parentOffset = this.parent.getOffsetInBytes(); const parentHeaderSize = this.parent.getHeaderSizeInBytes(); - const pointer: number = dependencyOffset - (parentOffset + parentHeaderSize); + const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(16)}`); const evmWordWidthInBytes = 32; const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); @@ -110,6 +129,25 @@ export class DependentCalldataBlock extends CalldataBlock { public getDependency(): CalldataBlock { return this.dependency; } + + public setAlias(block: CalldataBlock) { + this.aliasFor = block; + this.setName(`${this.getName()} (alias for ${block.getName()})`); + } + + public getAlias(): CalldataBlock | undefined { + return this.aliasFor; + } + + public getRawData(): Buffer { + const dependencyRawData = this.dependency.getRawData(); + const rawDataComponents: Buffer[] = []; + rawDataComponents.push(DependentCalldataBlock.RAW_DATA_START); + rawDataComponents.push(dependencyRawData); + rawDataComponents.push(DependentCalldataBlock.RAW_DATA_END); + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } } export class MemberCalldataBlock extends CalldataBlock { @@ -125,6 +163,20 @@ export class MemberCalldataBlock extends CalldataBlock { this.contiguous = contiguous; } + public getRawData(): Buffer { + const rawDataComponents: Buffer[] = []; + if (this.header !== undefined) { + rawDataComponents.push(this.header); + } + _.each(this.members, (member: CalldataBlock) => { + const memberBuffer = member.getRawData(); + rawDataComponents.push(memberBuffer); + }); + + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } + public setMembers(members: CalldataBlock[]) { let bodySizeInBytes = 0; _.each(members, (member: CalldataBlock) => { @@ -201,8 +253,8 @@ export class Calldata { // Children _.each(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof DependentCalldataBlock) { - const dependency = member.getDependency(); + if (member instanceof DependentCalldataBlock && member.getAlias() === undefined) { + let dependency = member.getDependency(); if (dependency instanceof MemberCalldataBlock) { blockQueue.merge(this.createQueue(dependency)); } else { @@ -321,6 +373,41 @@ export class Calldata { return hexValue; } + public optimize() { + if (this.root === undefined) { + throw new Error('expected root'); + } + + const subtreesByHash: { [key: string]: DependentCalldataBlock[] } = {}; + + // 1. Create a queue of subtrees by hash + // Note that they are ordered the same as + const subtreeQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; + while ((block = subtreeQueue.pop()) !== undefined) { + console.log(block.getName()); + + if (block instanceof DependentCalldataBlock === false) continue; + const blockHashBuf = block.computeHash(); + const blockHashHex = ethUtil.bufferToHex(blockHashBuf); + if (blockHashHex in subtreesByHash === false) { + subtreesByHash[blockHashHex] = [block as DependentCalldataBlock]; + } else { + subtreesByHash[blockHashHex].push(block as DependentCalldataBlock); + } + } + + // Iterate through trees that have the same hash and + _.each(subtreesByHash, (subtrees: DependentCalldataBlock[], hash: string) => { + if (subtrees.length === 1) return; // No optimization + // Optimize + const lastSubtree = subtrees[subtrees.length - 1]; + for (let i = 0; i < subtrees.length - 1; ++i) { + subtrees[i].setAlias(lastSubtree); + } + }); + } + public toHexString(optimize: boolean = false, annotate: boolean = false): string { if (this.root === undefined) { throw new Error('expected root'); @@ -334,8 +421,7 @@ export class Calldata { offset += block.getSizeInBytes(); } - - // if (optimize) this.optimize(valueQueue.getStore()); + if (optimize) this.optimize(); const hexValue = annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); return hexValue; diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index ea401247b..a24046664 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -489,10 +489,10 @@ export class Method extends MemberDataType { return selector; } - public encode(value: any[] | object, calldata = new Calldata(), annotate: boolean = false): string { + public encode(value: any[] | object, calldata = new Calldata(), annotate: boolean = false, optimize: boolean = false): string { calldata.setSelector(this.methodSelector); super.encode(value, calldata); - return calldata.toHexString(false, annotate); + return calldata.toHexString(optimize, annotate); } public decode(calldata: string, decodeStructsAsObjects: boolean = false): any[] | object { diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 8b836f77f..5ef4203b7 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -27,7 +27,21 @@ const expect = chai.expect; describe.only('ABI Encoder', () => { describe.only('ABI Tests at Method Level', () => { - it.only('Crazy ABI', async () => { + it.only('Optimizer', async () => { + const method = new AbiEncoder.Method(AbiSamples.stringAbi); + const strings = [ + "Test String", + "Test String 2", + "Test String", + "Test String 2", + ]; + const args = [strings]; + + const optimizedCalldata = method.encode(args, new Calldata(), true, true); + console.log(optimizedCalldata); + }); + + it('Crazy ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi); console.log(method.getSignature()); -- cgit v1.2.3 From 27cb966991c9408025106624f36927548b9e873c Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 13 Nov 2018 16:17:24 -0800 Subject: Alias now points to dependency, not pointer --- packages/order-utils/test/abi/calldata.ts | 6 +++--- packages/order-utils/test/abi_encoder_test.ts | 25 ++++++++++++++++++++++++- packages/order-utils/test/abi_samples.ts | 19 +++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index ab42b7d73..04bea9628 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -403,7 +403,7 @@ export class Calldata { // Optimize const lastSubtree = subtrees[subtrees.length - 1]; for (let i = 0; i < subtrees.length - 1; ++i) { - subtrees[i].setAlias(lastSubtree); + subtrees[i].setAlias(lastSubtree.getDependency()); } }); } @@ -413,6 +413,8 @@ export class Calldata { throw new Error('expected root'); } + if (optimize) this.optimize(); + const offsetQueue = this.createQueue(this.root); let block: CalldataBlock | undefined; let offset = 0; @@ -421,8 +423,6 @@ export class Calldata { offset += block.getSizeInBytes(); } - if (optimize) this.optimize(); - const hexValue = annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); return hexValue; } diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 5ef4203b7..661fb62a6 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -27,7 +27,7 @@ const expect = chai.expect; describe.only('ABI Encoder', () => { describe.only('ABI Tests at Method Level', () => { - it.only('Optimizer', async () => { + it('Optimizer #1', async () => { const method = new AbiEncoder.Method(AbiSamples.stringAbi); const strings = [ "Test String", @@ -41,6 +41,29 @@ describe.only('ABI Encoder', () => { console.log(optimizedCalldata); }); + it.only('Optimizer #2', async () => { + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi2); + const stringArray = [ + "Test String", + "Test String", + "Test String", + "Test String", + ]; + const string = 'Test String'; + const args = [stringArray, string]; + + + const TEST = method.encode(args, new Calldata(), true, true); + console.log(TEST); + + const optimizedCalldata = method.encode(args, new Calldata(), false, true); + + console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); + const decodedArgs = method.decode(optimizedCalldata); + console.log(JSON.stringify(decodedArgs)); + //expect(decodedArgs).to.be.equal(args); + }); + it('Crazy ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi); console.log(method.getSignature()); diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts index 3f7b1a927..fa20c38f0 100644 --- a/packages/order-utils/test/abi_samples.ts +++ b/packages/order-utils/test/abi_samples.ts @@ -34,6 +34,25 @@ export const stringAbi = { type: 'function', } as MethodAbi; +export const optimizerAbi2 = { + constant: false, + inputs: [ + { + name: 'stringArray', + type: 'string[]', + }, + { + name: 'string', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + export const typesWithDefaultWidthsAbi = { constant: false, inputs: [ -- cgit v1.2.3 From b71577cc52d7f933ee7e5dc50e5b61457f04e3f4 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 13 Nov 2018 17:06:41 -0800 Subject: More robust implementation for optimizer --- packages/order-utils/test/abi/calldata.ts | 41 +++++++++++++++++-------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index 04bea9628..754d8f823 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -216,6 +216,11 @@ class Queue { pop(): T | undefined { return this.store.shift(); } + popBack(): T | undefined { + if (this.store.length === 0) return undefined; + const backElement = this.store.splice(-1, 1)[0]; + return backElement; + } merge(q: Queue) { this.store = this.store.concat(q.store); } @@ -378,34 +383,34 @@ export class Calldata { throw new Error('expected root'); } - const subtreesByHash: { [key: string]: DependentCalldataBlock[] } = {}; + const blocksByHash: { [key: string]: CalldataBlock } = {}; // 1. Create a queue of subtrees by hash // Note that they are ordered the same as const subtreeQueue = this.createQueue(this.root); let block: CalldataBlock | undefined; - while ((block = subtreeQueue.pop()) !== undefined) { + console.log('*'.repeat(100), ' OPTIMIZING ', '*'.repeat(100)); + while ((block = subtreeQueue.popBack()) !== undefined) { console.log(block.getName()); + if (block instanceof DependentCalldataBlock) { + const blockHashBuf = block.getDependency().computeHash(); + const blockHash = ethUtil.bufferToHex(blockHashBuf); + if (blockHash in blocksByHash) { + const blockWithSameHash = blocksByHash[blockHash]; + if (blockWithSameHash !== block.getDependency()) { + block.setAlias(blockWithSameHash); + } + } + continue; + } - if (block instanceof DependentCalldataBlock === false) continue; const blockHashBuf = block.computeHash(); - const blockHashHex = ethUtil.bufferToHex(blockHashBuf); - if (blockHashHex in subtreesByHash === false) { - subtreesByHash[blockHashHex] = [block as DependentCalldataBlock]; - } else { - subtreesByHash[blockHashHex].push(block as DependentCalldataBlock); + const blockHash = ethUtil.bufferToHex(blockHashBuf); + if (blockHash in blocksByHash === false) { + blocksByHash[blockHash] = block; } } - - // Iterate through trees that have the same hash and - _.each(subtreesByHash, (subtrees: DependentCalldataBlock[], hash: string) => { - if (subtrees.length === 1) return; // No optimization - // Optimize - const lastSubtree = subtrees[subtrees.length - 1]; - for (let i = 0; i < subtrees.length - 1; ++i) { - subtrees[i].setAlias(lastSubtree.getDependency()); - } - }); + console.log('*'.repeat(100), ' FINISHED OPTIMIZING ', '*'.repeat(100)); } public toHexString(optimize: boolean = false, annotate: boolean = false): string { -- cgit v1.2.3 From 93e967c3b356f0081254946bbf8d6875fc791a09 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 13 Nov 2018 17:40:14 -0800 Subject: Some tests --- packages/order-utils/test/abi_encoder_test.ts | 57 ++++++++++++++++++++++++++- packages/order-utils/test/abi_samples.ts | 50 +++++++++++++++++++++++ 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 661fb62a6..78e28015d 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -41,7 +41,7 @@ describe.only('ABI Encoder', () => { console.log(optimizedCalldata); }); - it.only('Optimizer #2', async () => { + it('Optimizer #2', async () => { const method = new AbiEncoder.Method(AbiSamples.optimizerAbi2); const stringArray = [ "Test String", @@ -60,8 +60,61 @@ describe.only('ABI Encoder', () => { console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); console.log(JSON.stringify(decodedArgs)); - //expect(decodedArgs).to.be.equal(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + + + it('Optimizer #3 (tuple should point to array)', async () => { + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi3); + const uint8Array = [ + new BigNumber(100), + new BigNumber(150), + new BigNumber(200), + new BigNumber(225), + ]; + const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; + const args = [uint8Array, uintTupleArray]; + + + const TEST = method.encode(args, new Calldata(), true, true); + console.log('*'.repeat(50), ' ENCODED DATA ', TEST); + + const optimizedCalldata = method.encode(args, new Calldata(), false, true); + + console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + console.log(JSON.stringify(decodedArgs)); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + + it.only('Optimizer #4 (Expect no optimization)', async () => { + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi4); + const uint8Array = [ + new BigNumber(100), + new BigNumber(150), + new BigNumber(200), + new BigNumber(225), + ]; + const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; + const args = [uint8Array, uintTupleArray]; + + + const TEST = method.encode(args, new Calldata(), true, true); + console.log('*'.repeat(50), ' ENCODED DATA ', TEST); + + const optimizedCalldata = method.encode(args, new Calldata(), false, true); + + console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + console.log(JSON.stringify(decodedArgs)); + expect(decodedArgsJson).to.be.equal(argsJson); }); it('Crazy ABI', async () => { diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts index fa20c38f0..5e8268f1a 100644 --- a/packages/order-utils/test/abi_samples.ts +++ b/packages/order-utils/test/abi_samples.ts @@ -53,6 +53,56 @@ export const optimizerAbi2 = { type: 'function', } as MethodAbi; +export const optimizerAbi3 = { + constant: false, + inputs: [ + { + name: 'uint8Array', + type: 'uint8[]', + }, + { + components: [ + { + name: 'uint', + type: 'uint', + }, + ], + name: 'uintTuple', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const optimizerAbi4 = { + constant: false, + inputs: [ + { + name: 'uint8Array', + type: 'uint8[4]', + }, + { + components: [ + { + name: 'uint', + type: 'uint', + }, + ], + name: 'uintTuple', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + export const typesWithDefaultWidthsAbi = { constant: false, inputs: [ -- cgit v1.2.3 From 2d2255e9af42f6441ee620c4aa74bd1981dfe03a Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 14 Nov 2018 13:51:08 -0800 Subject: Cleaner interface for encoding/decoding. Moved encode/decode parameters into a struct. --- packages/order-utils/test/abi/calldata.ts | 43 ++++++++-------------- packages/order-utils/test/abi/data_type.ts | 49 +++++++++++++------------ packages/order-utils/test/abi/evm_data_types.ts | 25 ++++++------- packages/order-utils/test/abi_encoder_test.ts | 25 ++++++------- 4 files changed, 63 insertions(+), 79 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index 754d8f823..7eb4e0026 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -2,6 +2,15 @@ import ethUtil = require('ethereumjs-util'); import CommunicationChatBubbleOutline from 'material-ui/SvgIcon'; var _ = require('lodash'); +export interface DecodingRules { + structsAsObjects: boolean; +} + +export interface EncodingRules { + optimize?: boolean; + annotate?: boolean; +} + export abstract class CalldataBlock { private name: string; private signature: string; @@ -237,11 +246,13 @@ class Queue { export class Calldata { private selector: string; + private rules: EncodingRules; private sizeInBytes: number; private root: MemberCalldataBlock | undefined; - constructor() { - this.selector = '0x'; + constructor(rules: EncodingRules) { + this.selector = ''; + this.rules = rules; this.sizeInBytes = 0; this.root = undefined; } @@ -272,28 +283,6 @@ export class Calldata { return blockQueue; } - /* - - // Basic optimize method that prunes duplicate branches of the tree - // Notes: - // 1. Pruning is at the calldata block level, so it is independent of type - // 2. - private optimize(blocks: CalldataBlock[]) { - // Build hash table of blocks - const blockLookupTable: { [key: string]: string } = {}; - _.each(blocks, (block: CalldataBlock) => { - if (blocks instanceof DependentCalldataBlock === false) { - - return; - } - - const leavesHash = block.hashLeaves(); - if (leavesHash in blockLookupTable) { - - } - }) - }*/ - private generateAnnotatedHexString(): string { let hexValue = `${this.selector}`; if (this.root === undefined) { @@ -413,12 +402,12 @@ export class Calldata { console.log('*'.repeat(100), ' FINISHED OPTIMIZING ', '*'.repeat(100)); } - public toHexString(optimize: boolean = false, annotate: boolean = false): string { + public toHexString(): string { if (this.root === undefined) { throw new Error('expected root'); } - if (optimize) this.optimize(); + if (this.rules.optimize) this.optimize(); const offsetQueue = this.createQueue(this.root); let block: CalldataBlock | undefined; @@ -428,7 +417,7 @@ export class Calldata { offset += block.getSizeInBytes(); } - const hexValue = annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); + const hexValue = this.rules.annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); return hexValue; } diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 25f792c7a..6ad0b2736 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -1,14 +1,14 @@ import { RawCalldata, Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata"; import { MethodAbi, DataItem } from 'ethereum-types'; +import { DecodingRules, EncodingRules } from './calldata'; import { BigNumber } from '@0x/utils'; import ethUtil = require('ethereumjs-util'); var _ = require('lodash'); -export interface GenerateValueRules { - structsAsObjects: boolean; -} - export abstract class DataType { + private static DEFAULT_ENCODING_RULES = { optimize: false, annotate: false } as EncodingRules; + private static DEFAULT_DECODING_RULES = { structsAsObjects: false } as DecodingRules; + private dataItem: DataItem; constructor(dataItem: DataItem) { @@ -19,9 +19,25 @@ export abstract class DataType { return this.dataItem; } + public encode(value: any, rules?: EncodingRules, selector?: string): string { + const rules_ = rules ? rules : DataType.DEFAULT_ENCODING_RULES; + const calldata = new Calldata(rules_); + if (selector) calldata.setSelector(selector); + const block = this.generateCalldataBlock(value); + calldata.setRoot(block as MemberCalldataBlock); // @TODO CHANGE + const calldataHex = calldata.toHexString(); + return calldataHex; + } + + public decode(calldata: string, rules?: DecodingRules): any { + const rawCalldata = new RawCalldata(calldata); + const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; + const value = this.generateValue(rawCalldata, rules_); + return value; + } + public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; - public abstract generateValue(calldata: RawCalldata, rules: GenerateValueRules): any; - public abstract encode(value: any, calldata: Calldata): void; + public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any; public abstract getSignature(): string; public abstract isStatic(): boolean; } @@ -44,12 +60,7 @@ export abstract class PayloadDataType extends DataType { return block; } - public encode(value: any, calldata: Calldata): void { - const block = this.generateCalldataBlock(value); - // calldata.setRoot(block); - } - - public generateValue(calldata: RawCalldata, rules: GenerateValueRules): any { + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { const value = this.decodeValue(calldata); return value; } @@ -86,12 +97,7 @@ export abstract class DependentDataType extends DataType { return block; } - public encode(value: any, calldata: Calldata = new Calldata()): void { - const block = this.generateCalldataBlock(value); - //calldata.setRoot(block); - } - - public generateValue(calldata: RawCalldata, rules: GenerateValueRules): any { + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { const destinationOffsetBuf = calldata.popWord(); const currentOffset = calldata.getOffset(); const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), 16); @@ -237,12 +243,7 @@ export abstract class MemberDataType extends DataType { return block; } - public encode(value: any, calldata: Calldata = new Calldata()): void { - const block = this.generateCalldataBlock(value); - calldata.setRoot(block); - } - - public generateValue(calldata: RawCalldata, rules: GenerateValueRules): any[] | object { + public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { let members = this.members; if (this.isArray && this.arrayLength === undefined) { const arrayLengthBuf = calldata.popWord(); diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index a24046664..9504b1a10 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -1,4 +1,6 @@ -import { GenerateValueRules, DataType, DataTypeFactory, DataTypeFactoryImpl, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; +import { DataType, DataTypeFactory, DataTypeFactoryImpl, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; + +import { DecodingRules, EncodingRules } from './calldata'; import { MethodAbi, DataItem } from 'ethereum-types'; @@ -13,7 +15,7 @@ var _ = require('lodash'); export interface DataTypeStaticInterface { matchGrammar: (type: string) => boolean; encodeValue: (value: any) => Buffer; - // decodeValue: (value: Buffer) => [any, Buffer]; + decodeValue: (rawCalldata: RawCalldata) => any; } export class Address extends PayloadDataType { @@ -489,21 +491,16 @@ export class Method extends MemberDataType { return selector; } - public encode(value: any[] | object, calldata = new Calldata(), annotate: boolean = false, optimize: boolean = false): string { - calldata.setSelector(this.methodSelector); - super.encode(value, calldata); - return calldata.toHexString(optimize, annotate); + public encode(value: any, rules?: EncodingRules): string { + const calldata = super.encode(value, rules, this.selector); + return calldata; } - public decode(calldata: string, decodeStructsAsObjects: boolean = false): any[] | object { - const calldata_ = new RawCalldata(calldata); - if (this.selector !== calldata_.getSelector()) { - throw new Error(`Tried to decode calldata with mismatched selector. Expected '${this.selector}', got '${calldata_.getSelector()}'`); + public decode(calldata: string, rules?: DecodingRules): any[] | object { + if (!calldata.startsWith(this.selector)) { + throw new Error(`Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`); } - let rules: GenerateValueRules = { - structsAsObjects: decodeStructsAsObjects - }; - const value = super.generateValue(calldata_, rules); + const value = super.decode(calldata, rules); return value; } diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 78e28015d..52b6bc067 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -27,7 +27,7 @@ const expect = chai.expect; describe.only('ABI Encoder', () => { describe.only('ABI Tests at Method Level', () => { - it('Optimizer #1', async () => { + it('Should reuse duplicated strings in string array', async () => { const method = new AbiEncoder.Method(AbiSamples.stringAbi); const strings = [ "Test String", @@ -37,7 +37,7 @@ describe.only('ABI Encoder', () => { ]; const args = [strings]; - const optimizedCalldata = method.encode(args, new Calldata(), true, true); + const optimizedCalldata = method.encode(args, { optimize: true, annotate: true }); console.log(optimizedCalldata); }); @@ -53,10 +53,10 @@ describe.only('ABI Encoder', () => { const args = [stringArray, string]; - const TEST = method.encode(args, new Calldata(), true, true); + const TEST = method.encode(args, { optimize: true, annotate: true }); console.log(TEST); - const optimizedCalldata = method.encode(args, new Calldata(), false, true); + const optimizedCalldata = method.encode(args, { optimize: true }); console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); const decodedArgs = method.decode(optimizedCalldata); @@ -79,10 +79,10 @@ describe.only('ABI Encoder', () => { const args = [uint8Array, uintTupleArray]; - const TEST = method.encode(args, new Calldata(), true, true); + const TEST = method.encode(args, { optimize: true, annotate: true }); console.log('*'.repeat(50), ' ENCODED DATA ', TEST); - const optimizedCalldata = method.encode(args, new Calldata(), false, true); + const optimizedCalldata = method.encode(args, { optimize: true }); console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); const decodedArgs = method.decode(optimizedCalldata); @@ -92,7 +92,7 @@ describe.only('ABI Encoder', () => { expect(decodedArgsJson).to.be.equal(argsJson); }); - it.only('Optimizer #4 (Expect no optimization)', async () => { + it('Optimizer #4 (Expect no optimization)', async () => { const method = new AbiEncoder.Method(AbiSamples.optimizerAbi4); const uint8Array = [ new BigNumber(100), @@ -104,10 +104,10 @@ describe.only('ABI Encoder', () => { const args = [uint8Array, uintTupleArray]; - const TEST = method.encode(args, new Calldata(), true, true); + const TEST = method.encode(args, { optimize: true, annotate: true }); console.log('*'.repeat(50), ' ENCODED DATA ', TEST); - const optimizedCalldata = method.encode(args, new Calldata(), false, true); + const optimizedCalldata = method.encode(args, { optimize: true }); console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); const decodedArgs = method.decode(optimizedCalldata); @@ -179,12 +179,9 @@ describe.only('ABI Encoder', () => { someArrayOfTuplesWithDynamicTypes: someArrayOfTuplesWithDynamicTypes }; - const calldata = method.encode(args, new Calldata(), true); + const calldata = method.encode(args); console.log(calldata); - - throw new Error(`done`); - console.log('*'.repeat(40)); console.log(JSON.stringify(args)); console.log(method.getSignature()); @@ -195,7 +192,7 @@ describe.only('ABI Encoder', () => { // Test decoding const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata, true); + const decodedValue = method.decode(calldata, { structsAsObjects: true }); const decodedValueJson = JSON.stringify(decodedValue); console.log(`DECODED`, '*'.repeat(200), '\n', decodedValueJson); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); -- cgit v1.2.3 From 063871e549483ba36e5ee2317249eb6dccda24a4 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 14 Nov 2018 13:53:15 -0800 Subject: removed old log messages --- packages/order-utils/test/abi/calldata.ts | 5 ----- packages/order-utils/test/abi/data_type.ts | 1 - packages/order-utils/test/abi/evm_data_types.ts | 5 ----- 3 files changed, 11 deletions(-) diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts index 7eb4e0026..32278e5c5 100644 --- a/packages/order-utils/test/abi/calldata.ts +++ b/packages/order-utils/test/abi/calldata.ts @@ -302,7 +302,6 @@ export class Calldata { const size = block.getSizeInBytes(); const name = block.getName(); const parentName = block.getParentName(); - console.log('*'.repeat(50), parentName, ' vs ', name); //const ancestrialNamesOffset = name.startsWith('ptr<') ? 4 : 0; //const parentOffset = name.lastIndexOf(parentName); @@ -378,9 +377,7 @@ export class Calldata { // Note that they are ordered the same as const subtreeQueue = this.createQueue(this.root); let block: CalldataBlock | undefined; - console.log('*'.repeat(100), ' OPTIMIZING ', '*'.repeat(100)); while ((block = subtreeQueue.popBack()) !== undefined) { - console.log(block.getName()); if (block instanceof DependentCalldataBlock) { const blockHashBuf = block.getDependency().computeHash(); const blockHash = ethUtil.bufferToHex(blockHashBuf); @@ -399,7 +396,6 @@ export class Calldata { blocksByHash[blockHash] = block; } } - console.log('*'.repeat(100), ' FINISHED OPTIMIZING ', '*'.repeat(100)); } public toHexString(): string { @@ -495,7 +491,6 @@ export class RawCalldata { public setOffset(offsetInBytes: number) { this.offset = offsetInBytes; - console.log('0'.repeat(100), this.offset); } public startScope() { diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 6ad0b2736..7884cf3cf 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -165,7 +165,6 @@ export abstract class MemberDataType extends DataType { } private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { - console.log('!'.repeat(30), dataItem); let members: DataType[] = []; let memberMap: MemberMap = {}; const range = _.range(length); diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index 9504b1a10..aff4bc991 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -107,7 +107,6 @@ abstract class Number extends PayloadDataType { } public encodeValue(value: BigNumber): Buffer { - console.log(value); if (value.greaterThan(this.getMaxValue())) { throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; } else if (value.lessThan(this.getMinValue())) { @@ -366,7 +365,6 @@ export class SolString extends PayloadDataType { const wordsForValue = Math.ceil(length / 32); const paddedValueBuf = calldata.popWords(wordsForValue); const valueBuf = paddedValueBuf.slice(0, length); - console.log('LENGTH UPINYA === ', length); const value = valueBuf.toString('ascii'); return value; } @@ -397,7 +395,6 @@ export class Tuple extends MemberDataType { private tupleSignature: string; constructor(dataItem: DataItem) { - console.log(dataItem); super(dataItem); if (!Tuple.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); @@ -516,8 +513,6 @@ export class Method extends MemberDataType { export class EvmDataTypeFactoryImpl implements DataTypeFactoryImpl { public mapDataItemToDataType(dataItem: DataItem): DataType { - console.log(`Type: ${dataItem.type}`); - if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); -- cgit v1.2.3 From 3ae434c31e720cb1caf65d776b75524dab388a2f Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 14 Nov 2018 14:27:33 -0800 Subject: Adding optimizer tests --- packages/order-utils/test/abi_encoder_test.ts | 36 +++++++++++++++++++-------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 52b6bc067..8ae23a222 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -25,6 +25,10 @@ chaiSetup.configure(); const expect = chai.expect; describe.only('ABI Encoder', () => { + describe.only('Optimizer', () => { + + }); + describe.only('ABI Tests at Method Level', () => { it('Should reuse duplicated strings in string array', async () => { @@ -37,33 +41,45 @@ describe.only('ABI Encoder', () => { ]; const args = [strings]; - const optimizedCalldata = method.encode(args, { optimize: true, annotate: true }); - console.log(optimizedCalldata); + // Verify optimized calldata is expected + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + + // Verify args decode properly + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); }); - it('Optimizer #2', async () => { + it.only('Should point array elements to a duplicated value from another parameter', async () => { const method = new AbiEncoder.Method(AbiSamples.optimizerAbi2); const stringArray = [ "Test String", "Test String", "Test String", - "Test String", + "Test String 2", ]; const string = 'Test String'; const args = [stringArray, string]; - - const TEST = method.encode(args, { optimize: true, annotate: true }); - console.log(TEST); - + // Verify optimized calldata is expected const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); + // Verify args decode properly const decodedArgs = method.decode(optimizedCalldata); const decodedArgsJson = JSON.stringify(decodedArgs); const argsJson = JSON.stringify(args); - console.log(JSON.stringify(decodedArgs)); expect(decodedArgsJson).to.be.equal(argsJson); + + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); }); -- cgit v1.2.3 From 1f22ce60610617b8176be0f49588067b809907cb Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 14 Nov 2018 14:46:12 -0800 Subject: Removed config.ts --- packages/order-utils/test/abi/abi_encoder.ts | 1 - packages/order-utils/test/abi/config.ts | 4 -- packages/order-utils/test/abi/data_type.ts | 72 +++++++------------------ packages/order-utils/test/abi/evm_data_types.ts | 36 ++++++++----- packages/order-utils/test/abi_encoder_test.ts | 4 +- 5 files changed, 44 insertions(+), 73 deletions(-) delete mode 100644 packages/order-utils/test/abi/config.ts diff --git a/packages/order-utils/test/abi/abi_encoder.ts b/packages/order-utils/test/abi/abi_encoder.ts index ddcfb1fd1..4f4906550 100644 --- a/packages/order-utils/test/abi/abi_encoder.ts +++ b/packages/order-utils/test/abi/abi_encoder.ts @@ -1,4 +1,3 @@ -export * from './config'; export * from './calldata'; export * from './data_type'; export * from './evm_data_types'; \ No newline at end of file diff --git a/packages/order-utils/test/abi/config.ts b/packages/order-utils/test/abi/config.ts deleted file mode 100644 index 015cee59a..000000000 --- a/packages/order-utils/test/abi/config.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { DataTypeFactory } from './data_type'; -import { EvmDataTypeFactoryImpl } from './evm_data_types'; - -DataTypeFactory.setImpl(new EvmDataTypeFactoryImpl()); \ No newline at end of file diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts index 7884cf3cf..4c4537a69 100644 --- a/packages/order-utils/test/abi/data_type.ts +++ b/packages/order-utils/test/abi/data_type.ts @@ -5,20 +5,31 @@ import { BigNumber } from '@0x/utils'; import ethUtil = require('ethereumjs-util'); var _ = require('lodash'); +export interface DataTypeFactory { + create: (dataItem: DataItem, parentDataType: DataType) => DataType; + mapDataItemToDataType: (dataItem: DataItem) => DataType; +} + export abstract class DataType { private static DEFAULT_ENCODING_RULES = { optimize: false, annotate: false } as EncodingRules; private static DEFAULT_DECODING_RULES = { structsAsObjects: false } as DecodingRules; private dataItem: DataItem; + private factory: DataTypeFactory; - constructor(dataItem: DataItem) { + constructor(dataItem: DataItem, factory: DataTypeFactory) { this.dataItem = dataItem; + this.factory = factory; } public getDataItem(): DataItem { return this.dataItem; } + public getFactory(): DataTypeFactory { + return this.factory; + } + public encode(value: any, rules?: EncodingRules, selector?: string): string { const rules_ = rules ? rules : DataType.DEFAULT_ENCODING_RULES; const calldata = new Calldata(rules_); @@ -45,8 +56,8 @@ export abstract class DataType { export abstract class PayloadDataType extends DataType { protected hasConstantSize: boolean; - public constructor(dataItem: DataItem, hasConstantSize: boolean) { - super(dataItem); + public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { + super(dataItem, factory); this.hasConstantSize = hasConstantSize; } @@ -78,8 +89,8 @@ export abstract class DependentDataType extends DataType { protected dependency: DataType; protected parent: DataType; - public constructor(dataItem: DataItem, dependency: DataType, parent: DataType) { - super(dataItem); + public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { + super(dataItem, factory); this.dependency = dependency; this.parent = parent; } @@ -125,8 +136,8 @@ export abstract class MemberDataType extends DataType { protected arrayElementType: string | undefined; - public constructor(dataItem: DataItem, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { - super(dataItem); + public constructor(dataItem: DataItem, factory: DataTypeFactory, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { + super(dataItem, factory); this.memberMap = {}; this.members = []; this.isArray = isArray; @@ -156,7 +167,7 @@ export abstract class MemberDataType extends DataType { if (components !== undefined) { childDataItem.components = components; } - const child = DataTypeFactory.create(childDataItem, this); + const child = this.getFactory().create(childDataItem, this); memberMap[memberItem.name] = members.length; members.push(child); }); @@ -177,7 +188,7 @@ export abstract class MemberDataType extends DataType { if (components !== undefined) { childDataItem.components = components; } - const child = DataTypeFactory.create(childDataItem, this); + const child = this.getFactory().create(childDataItem, this); memberMap[idx.toString(10)] = members.length; members.push(child); }); @@ -309,46 +320,3 @@ export abstract class MemberDataType extends DataType { return isStatic; } } - -export interface DataTypeFactoryImpl { - create: (dataItem: DataItem, parentDataType: DataType) => DataType; - mapDataItemToDataType: (dataItem: DataItem) => DataType; -} - -export class DataTypeFactory { - private static instance: DataTypeFactory; - private provider: DataTypeFactoryImpl | undefined; - - private constructor() { } - - private static getInstance(): DataTypeFactory { - if (!DataTypeFactory.instance) { - DataTypeFactory.instance = new DataTypeFactory(); - } - return DataTypeFactory.instance; - } - - public static setImpl(provider: DataTypeFactoryImpl) { - const instance = DataTypeFactory.getInstance(); - if (instance.provider !== undefined) { - throw new Error(`Tried to set implementation more than once`); - } - DataTypeFactory.getInstance().provider = provider; - } - - public static create(dataItem: DataItem, parentDataType: DataType): DataType { - const instance = DataTypeFactory.getInstance(); - if (instance.provider === undefined) { - throw new Error(`Tried to create before implementation is set`); - } - return instance.provider.create(dataItem, parentDataType); - } - - public static mapDataItemToDataType(dataItem: DataItem): DataType { - const instance = DataTypeFactory.getInstance(); - if (instance.provider === undefined) { - throw new Error(`Tried to create before implementation is set`); - } - return instance.provider.mapDataItemToDataType(dataItem); - } -} \ No newline at end of file diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts index aff4bc991..bb7c1d81d 100644 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ b/packages/order-utils/test/abi/evm_data_types.ts @@ -1,4 +1,4 @@ -import { DataType, DataTypeFactory, DataTypeFactoryImpl, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; +import { DataType, DataTypeFactory, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; import { DecodingRules, EncodingRules } from './calldata'; @@ -22,7 +22,7 @@ export class Address extends PayloadDataType { private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; constructor(dataItem: DataItem) { - super(dataItem, Address.SIZE_KNOWN_AT_COMPILE_TIME); + super(dataItem, EvmDataTypeFactory.getInstance(), Address.SIZE_KNOWN_AT_COMPILE_TIME); if (!Address.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); } @@ -54,7 +54,7 @@ export class Bool extends PayloadDataType { private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; constructor(dataItem: DataItem) { - super(dataItem, Bool.SIZE_KNOWN_AT_COMPILE_TIME); + super(dataItem, EvmDataTypeFactory.getInstance(), Bool.SIZE_KNOWN_AT_COMPILE_TIME); if (!Bool.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); } @@ -94,7 +94,7 @@ abstract class Number extends PayloadDataType { width: number = Number.DEFAULT_WIDTH; constructor(dataItem: DataItem, matcher: RegExp) { - super(dataItem, Number.SIZE_KNOWN_AT_COMPILE_TIME); + super(dataItem, EvmDataTypeFactory.getInstance(), Number.SIZE_KNOWN_AT_COMPILE_TIME); const matches = matcher.exec(dataItem.type); if (matches === null) { throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); @@ -243,7 +243,7 @@ export class Byte extends PayloadDataType { width: number = Byte.DEFAULT_WIDTH; constructor(dataItem: DataItem) { - super(dataItem, Byte.SIZE_KNOWN_AT_COMPILE_TIME); + super(dataItem, EvmDataTypeFactory.getInstance(), Byte.SIZE_KNOWN_AT_COMPILE_TIME); const matches = Byte.matcher.exec(dataItem.type); if (!Byte.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); @@ -297,7 +297,7 @@ export class Bytes extends PayloadDataType { length: BigNumber = Bytes.UNDEFINED_LENGTH; constructor(dataItem: DataItem) { - super(dataItem, Bytes.SIZE_KNOWN_AT_COMPILE_TIME); + super(dataItem, EvmDataTypeFactory.getInstance(), Bytes.SIZE_KNOWN_AT_COMPILE_TIME); if (!Bytes.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Bytes with bad input: ${dataItem}`); } @@ -343,7 +343,7 @@ export class Bytes extends PayloadDataType { export class SolString extends PayloadDataType { private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; constructor(dataItem: DataItem) { - super(dataItem, SolString.SIZE_KNOWN_AT_COMPILE_TIME); + super(dataItem, EvmDataTypeFactory.getInstance(), SolString.SIZE_KNOWN_AT_COMPILE_TIME); if (!SolString.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); } @@ -383,7 +383,7 @@ export class Pointer extends DependentDataType { constructor(destDataType: DataType, parentDataType: DataType) { const destDataItem = destDataType.getDataItem(); const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; - super(dataItem, destDataType, parentDataType); + super(dataItem, EvmDataTypeFactory.getInstance(), destDataType, parentDataType); } public getSignature(): string { @@ -395,7 +395,7 @@ export class Tuple extends MemberDataType { private tupleSignature: string; constructor(dataItem: DataItem) { - super(dataItem); + super(dataItem, EvmDataTypeFactory.getInstance()); if (!Tuple.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); } @@ -430,7 +430,7 @@ export class SolArray extends MemberDataType { const isArray = true; const arrayElementType = matches[1]; const arrayLength = (matches[2] === '') ? undefined : parseInt(matches[2], 10); - super(dataItem, isArray, arrayLength, arrayElementType); + super(dataItem, EvmDataTypeFactory.getInstance(), isArray, arrayLength, arrayElementType); this.elementType = arrayElementType; this.arraySignature = this.computeSignature(); } @@ -444,7 +444,7 @@ export class SolArray extends MemberDataType { if (components !== undefined) { dataItem.components = components; } - const elementDataType = DataTypeFactory.mapDataItemToDataType(dataItem); + const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); const type = elementDataType.getSignature(); if (this.arrayLength === undefined) { return `${type}[]`; @@ -470,7 +470,7 @@ export class Method extends MemberDataType { public selector: string; constructor(abi: MethodAbi) { - super({ type: 'method', name: abi.name, components: abi.inputs }); + super({ type: 'method', name: abi.name, components: abi.inputs }, EvmDataTypeFactory.getInstance()); this.methodSignature = this.computeSignature(); this.selector = this.methodSelector = this.computeSelector(); @@ -510,7 +510,17 @@ export class Method extends MemberDataType { } } -export class EvmDataTypeFactoryImpl implements DataTypeFactoryImpl { +export class EvmDataTypeFactory implements DataTypeFactory { + private static instance: DataTypeFactory; + + private constructor() { } + + public static getInstance(): DataTypeFactory { + if (!EvmDataTypeFactory.instance) { + EvmDataTypeFactory.instance = new EvmDataTypeFactory(); + } + return EvmDataTypeFactory.instance; + } public mapDataItemToDataType(dataItem: DataItem): DataType { if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 8ae23a222..295311acc 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -19,8 +19,6 @@ import * as AbiEncoder from './abi/abi_encoder'; import * as AbiSamples from './abi_samples'; import { Calldata } from './abi/calldata'; -AbiEncoder.DataTypeFactory.setImpl(new AbiEncoder.EvmDataTypeFactoryImpl()); - chaiSetup.configure(); const expect = chai.expect; @@ -56,7 +54,7 @@ describe.only('ABI Encoder', () => { console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); }); - it.only('Should point array elements to a duplicated value from another parameter', async () => { + it('Should point array elements to a duplicated value from another parameter', async () => { const method = new AbiEncoder.Method(AbiSamples.optimizerAbi2); const stringArray = [ "Test String", -- cgit v1.2.3 From 71c050375b23336e7bc2700b595032eefe0ca4c1 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 14 Nov 2018 15:15:01 -0800 Subject: Restructured to use index.ts for easier imports by client --- packages/order-utils/test/abi/abi_encoder.ts | 3 - packages/order-utils/test/abi/calldata.ts | 520 ------------------- packages/order-utils/test/abi/data_type.ts | 322 ------------ packages/order-utils/test/abi/evm_data_types.ts | 550 --------------------- packages/order-utils/test/abi_encoder/calldata.ts | 520 +++++++++++++++++++ packages/order-utils/test/abi_encoder/data_type.ts | 322 ++++++++++++ .../order-utils/test/abi_encoder/evm_data_types.ts | 550 +++++++++++++++++++++ packages/order-utils/test/abi_encoder/index.ts | 2 + packages/order-utils/test/abi_encoder_test.ts | 5 +- 9 files changed, 1396 insertions(+), 1398 deletions(-) delete mode 100644 packages/order-utils/test/abi/abi_encoder.ts delete mode 100644 packages/order-utils/test/abi/calldata.ts delete mode 100644 packages/order-utils/test/abi/data_type.ts delete mode 100644 packages/order-utils/test/abi/evm_data_types.ts create mode 100644 packages/order-utils/test/abi_encoder/calldata.ts create mode 100644 packages/order-utils/test/abi_encoder/data_type.ts create mode 100644 packages/order-utils/test/abi_encoder/evm_data_types.ts create mode 100644 packages/order-utils/test/abi_encoder/index.ts diff --git a/packages/order-utils/test/abi/abi_encoder.ts b/packages/order-utils/test/abi/abi_encoder.ts deleted file mode 100644 index 4f4906550..000000000 --- a/packages/order-utils/test/abi/abi_encoder.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './calldata'; -export * from './data_type'; -export * from './evm_data_types'; \ No newline at end of file diff --git a/packages/order-utils/test/abi/calldata.ts b/packages/order-utils/test/abi/calldata.ts deleted file mode 100644 index 32278e5c5..000000000 --- a/packages/order-utils/test/abi/calldata.ts +++ /dev/null @@ -1,520 +0,0 @@ -import ethUtil = require('ethereumjs-util'); -import CommunicationChatBubbleOutline from 'material-ui/SvgIcon'; -var _ = require('lodash'); - -export interface DecodingRules { - structsAsObjects: boolean; -} - -export interface EncodingRules { - optimize?: boolean; - annotate?: boolean; -} - -export abstract class CalldataBlock { - private name: string; - private signature: string; - private offsetInBytes: number; - private headerSizeInBytes: number; - private bodySizeInBytes: number; - private relocatable: boolean; - private parentName: string; - - constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { - this.name = name; - this.signature = signature; - this.parentName = parentName; - this.offsetInBytes = 0; - this.headerSizeInBytes = headerSizeInBytes; - this.bodySizeInBytes = bodySizeInBytes; - this.relocatable = relocatable; - } - - protected setHeaderSize(headerSizeInBytes: number) { - this.headerSizeInBytes = headerSizeInBytes; - } - - protected setBodySize(bodySizeInBytes: number) { - this.bodySizeInBytes = bodySizeInBytes; - } - - protected setName(name: string) { - this.name = name; - } - - public getName(): string { - return this.name; - } - - public getParentName(): string { - return this.parentName; - } - - public getSignature(): string { - return this.signature; - } - - public isRelocatable(): boolean { - return this.relocatable; - } - - public getHeaderSizeInBytes(): number { - return this.headerSizeInBytes; - } - - public getBodySizeInBytes(): number { - return this.bodySizeInBytes; - } - - public getSizeInBytes(): number { - return this.headerSizeInBytes + this.bodySizeInBytes; - } - - public getOffsetInBytes(): number { - return this.offsetInBytes; - } - - public setOffset(offsetInBytes: number) { - this.offsetInBytes = offsetInBytes; - } - - public computeHash(): Buffer { - const rawData = this.getRawData(); - const hash = ethUtil.sha3(rawData); - return hash; - } - - public abstract toBuffer(): Buffer; - public abstract getRawData(): Buffer; -} - -export class PayloadCalldataBlock extends CalldataBlock { - private payload: Buffer; - - constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { - const headerSizeInBytes = 0; - const bodySizeInBytes = payload.byteLength; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); - this.payload = payload; - } - - public toBuffer(): Buffer { - return this.payload; - } - - public getRawData(): Buffer { - return this.payload; - } -} - -export class DependentCalldataBlock extends CalldataBlock { - public static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; - public static RAW_DATA_START = new Buffer('<'); - public static RAW_DATA_END = new Buffer('>'); - private parent: CalldataBlock; - private dependency: CalldataBlock; - private aliasFor: CalldataBlock | undefined; - - constructor(name: string, signature: string, parentName: string, relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { - const headerSizeInBytes = 0; - const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); - this.parent = parent; - this.dependency = dependency; - this.aliasFor = undefined; - } - - public toBuffer(): Buffer { - const destinationOffset = (this.aliasFor !== undefined) ? this.aliasFor.getOffsetInBytes() : this.dependency.getOffsetInBytes(); - const parentOffset = this.parent.getOffsetInBytes(); - const parentHeaderSize = this.parent.getHeaderSizeInBytes(); - const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); - const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(16)}`); - const evmWordWidthInBytes = 32; - const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); - return pointerBufPadded; - } - - public getDependency(): CalldataBlock { - return this.dependency; - } - - public setAlias(block: CalldataBlock) { - this.aliasFor = block; - this.setName(`${this.getName()} (alias for ${block.getName()})`); - } - - public getAlias(): CalldataBlock | undefined { - return this.aliasFor; - } - - public getRawData(): Buffer { - const dependencyRawData = this.dependency.getRawData(); - const rawDataComponents: Buffer[] = []; - rawDataComponents.push(DependentCalldataBlock.RAW_DATA_START); - rawDataComponents.push(dependencyRawData); - rawDataComponents.push(DependentCalldataBlock.RAW_DATA_END); - const rawData = Buffer.concat(rawDataComponents); - return rawData; - } -} - -export class MemberCalldataBlock extends CalldataBlock { - private static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; - private header: Buffer | undefined; - private members: CalldataBlock[]; - private contiguous: boolean; - - constructor(name: string, signature: string, parentName: string, relocatable: boolean, contiguous: boolean) { - super(name, signature, parentName, 0, 0, relocatable); - this.members = []; - this.header = undefined; - this.contiguous = contiguous; - } - - public getRawData(): Buffer { - const rawDataComponents: Buffer[] = []; - if (this.header !== undefined) { - rawDataComponents.push(this.header); - } - _.each(this.members, (member: CalldataBlock) => { - const memberBuffer = member.getRawData(); - rawDataComponents.push(memberBuffer); - }); - - const rawData = Buffer.concat(rawDataComponents); - return rawData; - } - - public setMembers(members: CalldataBlock[]) { - let bodySizeInBytes = 0; - _.each(members, (member: CalldataBlock) => { - bodySizeInBytes += member.getSizeInBytes(); - }); - this.members = members; - this.setBodySize(0); - } - - public isContiguous(): boolean { - return true; - } - - public setHeader(header: Buffer) { - this.setHeaderSize(header.byteLength); - this.header = header; - } - - public toBuffer(): Buffer { - if (this.header !== undefined) return this.header; - return new Buffer(''); - } - - public getMembers(): CalldataBlock[] { - return this.members; - } -} - -class Queue { - private store: T[] = []; - push(val: T) { - this.store.push(val); - } - pushFront(val: T) { - this.store.unshift(val); - } - pop(): T | undefined { - return this.store.shift(); - } - popBack(): T | undefined { - if (this.store.length === 0) return undefined; - const backElement = this.store.splice(-1, 1)[0]; - return backElement; - } - merge(q: Queue) { - this.store = this.store.concat(q.store); - } - mergeFront(q: Queue) { - this.store = q.store.concat(this.store); - } - getStore(): T[] { - return this.store; - } - peek(): T | undefined { - return this.store.length >= 0 ? this.store[0] : undefined; - } -} - -export class Calldata { - private selector: string; - private rules: EncodingRules; - private sizeInBytes: number; - private root: MemberCalldataBlock | undefined; - - constructor(rules: EncodingRules) { - this.selector = ''; - this.rules = rules; - this.sizeInBytes = 0; - this.root = undefined; - } - - private createQueue(memberBlock: MemberCalldataBlock): Queue { - const blockQueue = new Queue(); - _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof MemberCalldataBlock) { - blockQueue.mergeFront(this.createQueue(member)); - } else { - blockQueue.pushFront(member); - } - }); - - // Children - _.each(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof DependentCalldataBlock && member.getAlias() === undefined) { - let dependency = member.getDependency(); - if (dependency instanceof MemberCalldataBlock) { - blockQueue.merge(this.createQueue(dependency)); - } else { - blockQueue.push(dependency); - } - } - }); - - blockQueue.pushFront(memberBlock); - return blockQueue; - } - - private generateAnnotatedHexString(): string { - let hexValue = `${this.selector}`; - if (this.root === undefined) { - throw new Error('expected root'); - } - - const valueQueue = this.createQueue(this.root); - - let block: CalldataBlock | undefined; - let offset = 0; - const functionBlock = valueQueue.peek(); - let functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); - while ((block = valueQueue.pop()) !== undefined) { - // Set f - - // Process each block 1 word at a time - const size = block.getSizeInBytes(); - const name = block.getName(); - const parentName = block.getParentName(); - - //const ancestrialNamesOffset = name.startsWith('ptr<') ? 4 : 0; - //const parentOffset = name.lastIndexOf(parentName); - const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, '');//.replace(`${parentName}[`, '['); - const signature = block.getSignature(); - - // Current offset - let offsetStr = ''; - - // If this block is empty then it's a newline - let value = ''; - let nameStr = ''; - let line = ''; - if (size === 0) { - offsetStr = ' '.repeat(10); - value = ' '.repeat(74); - nameStr = `### ${prettyName.padEnd(80)}`; - line = `\n${offsetStr}${value}${nameStr}`; - } else { - offsetStr = `0x${offset.toString(16)}`.padEnd(10, ' '); - value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(0, 32))).padEnd(74); - if (block instanceof MemberCalldataBlock) { - nameStr = `### ${prettyName.padEnd(80)}`; - line = `\n${offsetStr}${value}${nameStr}`; - } else { - nameStr = ` ${prettyName.padEnd(80)}`; - line = `${offsetStr}${value}${nameStr}`; - } - } - - for (let j = 32; j < size; j += 32) { - offsetStr = `0x${(offset + j).toString(16)}`.padEnd(10, ' '); - value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(j, j + 32))).padEnd(74); - nameStr = ' '.repeat(40); - - line = `${line}\n${offsetStr}${value}${nameStr}`; - } - - // Append to hex value - hexValue = `${hexValue}\n${line}`; - offset += size; - } - - return hexValue; - } - - private generateCondensedHexString(): string { - let selectorBuffer = ethUtil.toBuffer(this.selector); - if (this.root === undefined) { - throw new Error('expected root'); - } - - const valueQueue = this.createQueue(this.root); - const valueBufs: Buffer[] = [selectorBuffer]; - let block: CalldataBlock | undefined; - while ((block = valueQueue.pop()) !== undefined) { - valueBufs.push(block.toBuffer()); - } - - const combinedBuffers = Buffer.concat(valueBufs); - const hexValue = ethUtil.bufferToHex(combinedBuffers); - return hexValue; - } - - public optimize() { - if (this.root === undefined) { - throw new Error('expected root'); - } - - const blocksByHash: { [key: string]: CalldataBlock } = {}; - - // 1. Create a queue of subtrees by hash - // Note that they are ordered the same as - const subtreeQueue = this.createQueue(this.root); - let block: CalldataBlock | undefined; - while ((block = subtreeQueue.popBack()) !== undefined) { - if (block instanceof DependentCalldataBlock) { - const blockHashBuf = block.getDependency().computeHash(); - const blockHash = ethUtil.bufferToHex(blockHashBuf); - if (blockHash in blocksByHash) { - const blockWithSameHash = blocksByHash[blockHash]; - if (blockWithSameHash !== block.getDependency()) { - block.setAlias(blockWithSameHash); - } - } - continue; - } - - const blockHashBuf = block.computeHash(); - const blockHash = ethUtil.bufferToHex(blockHashBuf); - if (blockHash in blocksByHash === false) { - blocksByHash[blockHash] = block; - } - } - } - - public toHexString(): string { - if (this.root === undefined) { - throw new Error('expected root'); - } - - if (this.rules.optimize) this.optimize(); - - const offsetQueue = this.createQueue(this.root); - let block: CalldataBlock | undefined; - let offset = 0; - while ((block = offsetQueue.pop()) !== undefined) { - block.setOffset(offset); - offset += block.getSizeInBytes(); - } - - const hexValue = this.rules.annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); - return hexValue; - } - - public getSelectorHex(): string { - return this.selector; - } - - public getSizeInBytes(): number { - return this.sizeInBytes; - } - - public toAnnotatedString(): string { - return ""; - } - - public setRoot(block: MemberCalldataBlock) { - this.root = block; - this.sizeInBytes += block.getSizeInBytes(); - } - - public setSelector(selector: string) { - // Ensure we have a 0x prefix - if (selector.startsWith('0x')) { - this.selector = selector; - } else { - this.selector = `$0x${selector}`; - } - - // The selector must be 10 characters: '0x' followed by 4 bytes (two hex chars per byte) - if (this.selector.length !== 10) { - throw new Error(`Invalid selector '${this.selector}'`); - } - this.sizeInBytes += 8; - } -} - -export class RawCalldata { - private value: Buffer; - private offset: number; // tracks current offset into raw calldata; used for parsing - private selector: string; - private scopes: Queue; - - constructor(value: string | Buffer) { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Expected raw calldata to start with '0x'`); - } - const valueBuf = ethUtil.toBuffer(value); - this.selector = ethUtil.bufferToHex(valueBuf.slice(0, 4)); - this.value = valueBuf.slice(4); // disregard selector - this.offset = 0; - this.scopes = new Queue(); - this.scopes.push(0); - } - - public popBytes(lengthInBytes: number): Buffer { - const value = this.value.slice(this.offset, this.offset + lengthInBytes); - this.setOffset(this.offset + lengthInBytes); - return value; - } - - public popWord(): Buffer { - const wordInBytes = 32; - return this.popBytes(wordInBytes); - } - - public popWords(length: number): Buffer { - const wordInBytes = 32; - return this.popBytes(length * wordInBytes); - } - - public readBytes(from: number, to: number): Buffer { - const value = this.value.slice(from, to); - return value; - } - - public setOffset(offsetInBytes: number) { - this.offset = offsetInBytes; - } - - public startScope() { - this.scopes.pushFront(this.offset); - } - - public endScope() { - this.scopes.pop(); - } - - public getOffset(): number { - return this.offset; - } - - public toAbsoluteOffset(relativeOffset: number) { - const scopeOffset = this.scopes.peek(); - if (scopeOffset === undefined) { - throw new Error(`Tried to access undefined scope.`); - } - const absoluteOffset = relativeOffset + scopeOffset; - return absoluteOffset; - } - - public getSelector(): string { - return this.selector; - } -} \ No newline at end of file diff --git a/packages/order-utils/test/abi/data_type.ts b/packages/order-utils/test/abi/data_type.ts deleted file mode 100644 index 4c4537a69..000000000 --- a/packages/order-utils/test/abi/data_type.ts +++ /dev/null @@ -1,322 +0,0 @@ -import { RawCalldata, Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata"; -import { MethodAbi, DataItem } from 'ethereum-types'; -import { DecodingRules, EncodingRules } from './calldata'; -import { BigNumber } from '@0x/utils'; -import ethUtil = require('ethereumjs-util'); -var _ = require('lodash'); - -export interface DataTypeFactory { - create: (dataItem: DataItem, parentDataType: DataType) => DataType; - mapDataItemToDataType: (dataItem: DataItem) => DataType; -} - -export abstract class DataType { - private static DEFAULT_ENCODING_RULES = { optimize: false, annotate: false } as EncodingRules; - private static DEFAULT_DECODING_RULES = { structsAsObjects: false } as DecodingRules; - - private dataItem: DataItem; - private factory: DataTypeFactory; - - constructor(dataItem: DataItem, factory: DataTypeFactory) { - this.dataItem = dataItem; - this.factory = factory; - } - - public getDataItem(): DataItem { - return this.dataItem; - } - - public getFactory(): DataTypeFactory { - return this.factory; - } - - public encode(value: any, rules?: EncodingRules, selector?: string): string { - const rules_ = rules ? rules : DataType.DEFAULT_ENCODING_RULES; - const calldata = new Calldata(rules_); - if (selector) calldata.setSelector(selector); - const block = this.generateCalldataBlock(value); - calldata.setRoot(block as MemberCalldataBlock); // @TODO CHANGE - const calldataHex = calldata.toHexString(); - return calldataHex; - } - - public decode(calldata: string, rules?: DecodingRules): any { - const rawCalldata = new RawCalldata(calldata); - const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; - const value = this.generateValue(rawCalldata, rules_); - return value; - } - - public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; - public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any; - public abstract getSignature(): string; - public abstract isStatic(): boolean; -} - -export abstract class PayloadDataType extends DataType { - protected hasConstantSize: boolean; - - public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { - super(dataItem, factory); - this.hasConstantSize = hasConstantSize; - } - - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { - const encodedValue = this.encodeValue(value); - const name = this.getDataItem().name; - const signature = this.getSignature(); - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const relocatable = false; - const block = new PayloadCalldataBlock(name, signature, parentName, /*offsetInBytes,*/ relocatable, encodedValue); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any { - const value = this.decodeValue(calldata); - return value; - } - - public isStatic(): boolean { - // If a payload has a constant size then it's static - return this.hasConstantSize; - } - - public abstract encodeValue(value: any): Buffer; - public abstract decodeValue(calldata: RawCalldata): any; -} - -export abstract class DependentDataType extends DataType { - protected dependency: DataType; - protected parent: DataType; - - public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { - super(dataItem, factory); - this.dependency = dependency; - this.parent = parent; - } - - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { - if (parentBlock === undefined) { - throw new Error(`DependentDataType requires a parent block to generate its block`); - } - const dependencyBlock = this.dependency.generateCalldataBlock(value, parentBlock); - const name = this.getDataItem().name; - const signature = this.getSignature(); - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const relocatable = false; - const block = new DependentCalldataBlock(name, signature, parentName, relocatable, dependencyBlock, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any { - const destinationOffsetBuf = calldata.popWord(); - const currentOffset = calldata.getOffset(); - const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), 16); - const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); - calldata.setOffset(destinationOffsetAbsolute); - const value = this.dependency.generateValue(calldata, rules); - calldata.setOffset(currentOffset); - return value; - } - - public isStatic(): boolean { - return true; - } -} - -export interface MemberMap { - [key: string]: number; -} - -export abstract class MemberDataType extends DataType { - private memberMap: MemberMap; - private members: DataType[]; - private isArray: boolean; - protected arrayLength: number | undefined; - protected arrayElementType: string | undefined; - - - public constructor(dataItem: DataItem, factory: DataTypeFactory, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { - super(dataItem, factory); - this.memberMap = {}; - this.members = []; - this.isArray = isArray; - this.arrayLength = arrayLength; - this.arrayElementType = arrayElementType; - if (isArray && arrayLength !== undefined) { - [this.members, this.memberMap] = this.createMembersWithLength(dataItem, arrayLength); - } else if (!isArray) { - [this.members, this.memberMap] = this.createMembersWithKeys(dataItem); - } - } - - private createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { - // Sanity check - if (dataItem.components === undefined) { - throw new Error(`Expected components`); - } - - let members: DataType[] = []; - let memberMap: MemberMap = {}; - _.each(dataItem.components, (memberItem: DataItem) => { - const childDataItem = { - type: memberItem.type, - name: `${dataItem.name}.${memberItem.name}`, - } as DataItem; - const components = memberItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[memberItem.name] = members.length; - members.push(child); - }); - - return [members, memberMap]; - } - - private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { - let members: DataType[] = []; - let memberMap: MemberMap = {}; - const range = _.range(length); - _.each(range, (idx: number) => { - const childDataItem = { - type: this.arrayElementType, - name: `${dataItem.name}[${idx.toString(10)}]`, - } as DataItem; - const components = dataItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[idx.toString(10)] = members.length; - members.push(child); - }); - - return [members, memberMap]; - } - - protected generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { - // Sanity check length - if (this.arrayLength !== undefined && value.length !== this.arrayLength) { - throw new Error( - `Expected array of ${JSON.stringify( - this.arrayLength, - )} elements, but got array of length ${JSON.stringify(value.length)}`, - ); - } - - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); - - let members = this.members; - if (this.isArray && this.arrayLength === undefined) { - [members,] = this.createMembersWithLength(this.getDataItem(), value.length); - - const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(16)}`), 32); - methodBlock.setHeader(lenBuf); - } - - const memberBlocks: CalldataBlock[] = []; - _.each(members, (member: DataType, idx: number) => { - const block = member.generateCalldataBlock(value[idx], methodBlock); - memberBlocks.push(block); - }); - methodBlock.setMembers(memberBlocks); - return methodBlock; - } - - protected generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); - const memberBlocks: CalldataBlock[] = []; - let childMap = _.cloneDeep(this.memberMap); - _.forOwn(obj, (value: any, key: string) => { - if (key in childMap === false) { - throw new Error(`Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`); - } - const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock); - memberBlocks.push(block); - delete childMap[key]; - }); - - if (Object.keys(childMap).length !== 0) { - throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); - } - - methodBlock.setMembers(memberBlocks); - return methodBlock; - } - - public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value, parentBlock) : this.generateCalldataBlockFromObject(value, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { - let members = this.members; - if (this.isArray && this.arrayLength === undefined) { - const arrayLengthBuf = calldata.popWord(); - const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); - const hexBase = 16; - const arrayLength = new BigNumber(arrayLengthHex, hexBase); - - [members,] = this.createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); - } - - calldata.startScope(); - let value: any[] | object; - if (rules.structsAsObjects && !this.isArray) { - value = {}; - _.each(this.memberMap, (idx: number, key: string) => { - const member = this.members[idx]; - let memberValue = member.generateValue(calldata, rules); - (value as { [key: string]: any })[key] = memberValue; - }); - } else { - value = []; - _.each(members, (member: DataType, idx: number) => { - let memberValue = member.generateValue(calldata, rules); - (value as any[]).push(memberValue); - }); - } - calldata.endScope(); - return value; - } - - protected computeSignatureOfMembers(): string { - // Compute signature of members - let signature = `(`; - _.each(this.members, (member: DataType, i: number) => { - signature += member.getSignature(); - if (i < this.members.length - 1) { - signature += ','; - } - }); - signature += ')'; - return signature; - } - - public isStatic(): boolean { - /* For Tuple: - const isStaticTuple = this.children.length === 0; - return isStaticTuple; // @TODO: True in every case or only when dynamic data? - - For Array: - if isLengthDefined = false then this is false - - Otherwise if the first element is a Pointer then false - */ - - if (this.isArray && this.arrayLength === undefined) { - return false; - } - - // Search for dependent members - const dependentMember = _.find(this.members, (member: DataType) => { - return (member instanceof DependentDataType); - }); - const isStatic = (dependentMember === undefined); // static if we couldn't find a dependent member - return isStatic; - } -} diff --git a/packages/order-utils/test/abi/evm_data_types.ts b/packages/order-utils/test/abi/evm_data_types.ts deleted file mode 100644 index bb7c1d81d..000000000 --- a/packages/order-utils/test/abi/evm_data_types.ts +++ /dev/null @@ -1,550 +0,0 @@ -import { DataType, DataTypeFactory, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; - -import { DecodingRules, EncodingRules } from './calldata'; - -import { MethodAbi, DataItem } from 'ethereum-types'; - -import ethUtil = require('ethereumjs-util'); - -import { Calldata, RawCalldata } from './calldata'; - -import { BigNumber } from '@0x/utils'; - -var _ = require('lodash'); - -export interface DataTypeStaticInterface { - matchGrammar: (type: string) => boolean; - encodeValue: (value: any) => Buffer; - decodeValue: (rawCalldata: RawCalldata) => any; -} - -export class Address extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Address.SIZE_KNOWN_AT_COMPILE_TIME); - if (!Address.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); - } - } - - public getSignature(): string { - return 'address'; - } - - public static matchGrammar(type: string): boolean { - return type === 'address'; - } - - public encodeValue(value: boolean): Buffer { - const evmWordWidth = 32; - const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(12); - const value = ethUtil.bufferToHex(valueBuf); - return value; - } -} - -export class Bool extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Bool.SIZE_KNOWN_AT_COMPILE_TIME); - if (!Bool.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); - } - } - - public getSignature(): string { - return 'bool'; - } - - public static matchGrammar(type: string): boolean { - return type === 'bool'; - } - - public encodeValue(value: boolean): Buffer { - const evmWordWidth = 32; - const encodedValue = value === true ? '0x1' : '0x0'; - const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): boolean { - const valueBuf = calldata.popWord(); - const valueHex = ethUtil.bufferToHex(valueBuf); - const valueNumber = new BigNumber(valueHex, 16); - let value: boolean = (valueNumber.equals(0)) ? false : true; - if (!(valueNumber.equals(0) || valueNumber.equals(1))) { - throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); - } - return value; - } -} - -abstract class Number extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - static MAX_WIDTH: number = 256; - static DEFAULT_WIDTH: number = Number.MAX_WIDTH; - width: number = Number.DEFAULT_WIDTH; - - constructor(dataItem: DataItem, matcher: RegExp) { - super(dataItem, EvmDataTypeFactory.getInstance(), Number.SIZE_KNOWN_AT_COMPILE_TIME); - const matches = matcher.exec(dataItem.type); - if (matches === null) { - throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); - } - if (matches !== null && matches.length === 2 && matches[1] !== undefined) { - this.width = parseInt(matches[1]); - } else { - this.width = 256; - } - } - - public encodeValue(value: BigNumber): Buffer { - if (value.greaterThan(this.getMaxValue())) { - throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; - } else if (value.lessThan(this.getMinValue())) { - throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; - } - - const hexBase = 16; - const evmWordWidth = 32; - let valueBuf: Buffer; - if (value.greaterThanOrEqualTo(0)) { - valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); - } else { - // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. - // Step 1/3: Convert value to positive binary string - const binBase = 2; - const valueBin = value.times(-1).toString(binBase); - - // Step 2/3: Invert binary value - const bitsInEvmWord = 256; - let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, binBase); - - // Step 3/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const negativeValue = invertedValue.plus(1); - - // Convert the negated value to a hex string - valueBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), - evmWordWidth, - ); - } - - return valueBuf; - } - - public decodeValue(calldata: RawCalldata): BigNumber { - const paddedValueBuf = calldata.popWord(); - const paddedValueHex = ethUtil.bufferToHex(paddedValueBuf); - let value = new BigNumber(paddedValueHex, 16); - if (this instanceof Int) { - // Check if we're negative - const binBase = 2; - const paddedValueBin = value.toString(binBase); - const valueBin = paddedValueBin.slice(paddedValueBin.length - this.width); - if (valueBin[0].startsWith('1')) { - // Negative - // Step 1/3: Invert binary value - let invertedValueBin = ''; - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, binBase); - - // Step 2/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const positiveValue = invertedValue.plus(1); - - // Step 3/3: Invert positive value - const negativeValue = positiveValue.times(-1); - value = negativeValue; - } - } - - return value; - } - - public abstract getMaxValue(): BigNumber; - public abstract getMinValue(): BigNumber; -} - -export class Int extends Number { - static matcher = RegExp( - '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - constructor(dataItem: DataItem) { - super(dataItem, Int.matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).times(-1); - } - - public getSignature(): string { - return `int${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class UInt extends Number { - static matcher = RegExp( - '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - constructor(dataItem: DataItem) { - super(dataItem, UInt.matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(0); - } - - public getSignature(): string { - return `uint${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class Byte extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - static matcher = RegExp( - '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', - ); - - static DEFAULT_WIDTH = 1; - width: number = Byte.DEFAULT_WIDTH; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Byte.SIZE_KNOWN_AT_COMPILE_TIME); - const matches = Byte.matcher.exec(dataItem.type); - if (!Byte.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); - } - if (matches !== null && matches.length === 3 && matches[2] !== undefined) { - this.width = parseInt(matches[2]); - } else { - this.width = Byte.DEFAULT_WIDTH; - } - } - - public getSignature(): string { - // Note that `byte` reduces to `bytes1` - return `bytes${this.width}`; - } - - public encodeValue(value: string | Buffer): Buffer { - // Convert value into a buffer and do bounds checking - const valueBuf = ethUtil.toBuffer(value); - if (valueBuf.byteLength > this.width) { - throw new Error( - `Tried to assign ${value} (${ - valueBuf.byteLength - } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, - ); - } else if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - // Store value as hex - const evmWordWidth = 32; - const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); - return paddedValue; - } - - public decodeValue(calldata: RawCalldata): string { - const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(0, this.width); - const value = ethUtil.bufferToHex(valueBuf); - return value; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class Bytes extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = Bytes.UNDEFINED_LENGTH; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Bytes.SIZE_KNOWN_AT_COMPILE_TIME); - if (!Bytes.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Bytes with bad input: ${dataItem}`); - } - } - - public encodeValue(value: string | Buffer): Buffer { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); - } - const valueBuf = ethUtil.toBuffer(value); - if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - const wordsForValue = Math.ceil(valueBuf.byteLength / 32); - const paddedBytesForValue = wordsForValue * 32; - const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedBytesForValue); - const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); - const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const lengthBuf = calldata.popWord(); - const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, 16); - const wordsForValue = Math.ceil(length / 32); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); - const decodedValue = ethUtil.bufferToHex(valueBuf); - return decodedValue; - } - - public getSignature(): string { - return 'bytes'; - } - - public static matchGrammar(type: string): boolean { - return type === 'bytes'; - } -} - -export class SolString extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), SolString.SIZE_KNOWN_AT_COMPILE_TIME); - if (!SolString.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); - } - } - - public encodeValue(value: string): Buffer { - const wordsForValue = Math.ceil(value.length / 32); - const paddedBytesForValue = wordsForValue * 32; - const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); - const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); - const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const lengthBuf = calldata.popWord(); - const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, 16); - const wordsForValue = Math.ceil(length / 32); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); - const value = valueBuf.toString('ascii'); - return value; - } - - public getSignature(): string { - return 'string'; - } - - public static matchGrammar(type: string): boolean { - return type === 'string'; - } -} - -export class Pointer extends DependentDataType { - - constructor(destDataType: DataType, parentDataType: DataType) { - const destDataItem = destDataType.getDataItem(); - const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; - super(dataItem, EvmDataTypeFactory.getInstance(), destDataType, parentDataType); - } - - public getSignature(): string { - return this.dependency.getSignature(); - } -} - -export class Tuple extends MemberDataType { - private tupleSignature: string; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance()); - if (!Tuple.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); - } - this.tupleSignature = this.computeSignatureOfMembers(); - } - - public getSignature(): string { - return this.tupleSignature; - } - - public static matchGrammar(type: string): boolean { - return type === 'tuple'; - } -} - -export class SolArray extends MemberDataType { - static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); - private arraySignature: string; - private elementType: string; - - constructor(dataItem: DataItem) { - // Sanity check - const matches = SolArray.matcher.exec(dataItem.type); - if (matches === null || matches.length !== 3) { - throw new Error(`Could not parse array: ${dataItem.type}`); - } else if (matches[1] === undefined) { - throw new Error(`Could not parse array type: ${dataItem.type}`); - } else if (matches[2] === undefined) { - throw new Error(`Could not parse array length: ${dataItem.type}`); - } - - const isArray = true; - const arrayElementType = matches[1]; - const arrayLength = (matches[2] === '') ? undefined : parseInt(matches[2], 10); - super(dataItem, EvmDataTypeFactory.getInstance(), isArray, arrayLength, arrayElementType); - this.elementType = arrayElementType; - this.arraySignature = this.computeSignature(); - } - - private computeSignature(): string { - let dataItem = { - type: this.elementType, - name: 'N/A', - } as DataItem; - const components = this.getDataItem().components; - if (components !== undefined) { - dataItem.components = components; - } - const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); - const type = elementDataType.getSignature(); - if (this.arrayLength === undefined) { - return `${type}[]`; - } else { - return `${type}[${this.arrayLength}]`; - } - } - - public getSignature(): string { - return this.arraySignature; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class Method extends MemberDataType { - private methodSignature: string; - private methodSelector: string; - - // TMP - public selector: string; - - constructor(abi: MethodAbi) { - super({ type: 'method', name: abi.name, components: abi.inputs }, EvmDataTypeFactory.getInstance()); - this.methodSignature = this.computeSignature(); - this.selector = this.methodSelector = this.computeSelector(); - - } - - private computeSignature(): string { - const memberSignature = this.computeSignatureOfMembers(); - const methodSignature = `${this.getDataItem().name}${memberSignature}`; - return methodSignature; - } - - private computeSelector(): string { - const signature = this.computeSignature(); - const selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(signature).slice(0, 4))); - return selector; - } - - public encode(value: any, rules?: EncodingRules): string { - const calldata = super.encode(value, rules, this.selector); - return calldata; - } - - public decode(calldata: string, rules?: DecodingRules): any[] | object { - if (!calldata.startsWith(this.selector)) { - throw new Error(`Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`); - } - const value = super.decode(calldata, rules); - return value; - } - - public getSignature(): string { - return this.methodSignature; - } - - public getSelector(): string { - return this.methodSelector; - } -} - -export class EvmDataTypeFactory implements DataTypeFactory { - private static instance: DataTypeFactory; - - private constructor() { } - - public static getInstance(): DataTypeFactory { - if (!EvmDataTypeFactory.instance) { - EvmDataTypeFactory.instance = new EvmDataTypeFactory(); - } - return EvmDataTypeFactory.instance; - } - - public mapDataItemToDataType(dataItem: DataItem): DataType { - if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); - if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); - if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); - if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); - if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); - if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); - if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); - if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); - if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); - //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); - //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); - - throw new Error(`Unrecognized data type: '${dataItem.type}'`); - } - - public create(dataItem: DataItem, parentDataType: DataType): DataType { - const dataType = this.mapDataItemToDataType(dataItem); - if (dataType.isStatic()) { - return dataType; - } - - const pointer = new Pointer(dataType, parentDataType); - return pointer; - } -} \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder/calldata.ts b/packages/order-utils/test/abi_encoder/calldata.ts new file mode 100644 index 000000000..32278e5c5 --- /dev/null +++ b/packages/order-utils/test/abi_encoder/calldata.ts @@ -0,0 +1,520 @@ +import ethUtil = require('ethereumjs-util'); +import CommunicationChatBubbleOutline from 'material-ui/SvgIcon'; +var _ = require('lodash'); + +export interface DecodingRules { + structsAsObjects: boolean; +} + +export interface EncodingRules { + optimize?: boolean; + annotate?: boolean; +} + +export abstract class CalldataBlock { + private name: string; + private signature: string; + private offsetInBytes: number; + private headerSizeInBytes: number; + private bodySizeInBytes: number; + private relocatable: boolean; + private parentName: string; + + constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { + this.name = name; + this.signature = signature; + this.parentName = parentName; + this.offsetInBytes = 0; + this.headerSizeInBytes = headerSizeInBytes; + this.bodySizeInBytes = bodySizeInBytes; + this.relocatable = relocatable; + } + + protected setHeaderSize(headerSizeInBytes: number) { + this.headerSizeInBytes = headerSizeInBytes; + } + + protected setBodySize(bodySizeInBytes: number) { + this.bodySizeInBytes = bodySizeInBytes; + } + + protected setName(name: string) { + this.name = name; + } + + public getName(): string { + return this.name; + } + + public getParentName(): string { + return this.parentName; + } + + public getSignature(): string { + return this.signature; + } + + public isRelocatable(): boolean { + return this.relocatable; + } + + public getHeaderSizeInBytes(): number { + return this.headerSizeInBytes; + } + + public getBodySizeInBytes(): number { + return this.bodySizeInBytes; + } + + public getSizeInBytes(): number { + return this.headerSizeInBytes + this.bodySizeInBytes; + } + + public getOffsetInBytes(): number { + return this.offsetInBytes; + } + + public setOffset(offsetInBytes: number) { + this.offsetInBytes = offsetInBytes; + } + + public computeHash(): Buffer { + const rawData = this.getRawData(); + const hash = ethUtil.sha3(rawData); + return hash; + } + + public abstract toBuffer(): Buffer; + public abstract getRawData(): Buffer; +} + +export class PayloadCalldataBlock extends CalldataBlock { + private payload: Buffer; + + constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { + const headerSizeInBytes = 0; + const bodySizeInBytes = payload.byteLength; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); + this.payload = payload; + } + + public toBuffer(): Buffer { + return this.payload; + } + + public getRawData(): Buffer { + return this.payload; + } +} + +export class DependentCalldataBlock extends CalldataBlock { + public static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + public static RAW_DATA_START = new Buffer('<'); + public static RAW_DATA_END = new Buffer('>'); + private parent: CalldataBlock; + private dependency: CalldataBlock; + private aliasFor: CalldataBlock | undefined; + + constructor(name: string, signature: string, parentName: string, relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { + const headerSizeInBytes = 0; + const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); + this.parent = parent; + this.dependency = dependency; + this.aliasFor = undefined; + } + + public toBuffer(): Buffer { + const destinationOffset = (this.aliasFor !== undefined) ? this.aliasFor.getOffsetInBytes() : this.dependency.getOffsetInBytes(); + const parentOffset = this.parent.getOffsetInBytes(); + const parentHeaderSize = this.parent.getHeaderSizeInBytes(); + const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); + const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(16)}`); + const evmWordWidthInBytes = 32; + const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); + return pointerBufPadded; + } + + public getDependency(): CalldataBlock { + return this.dependency; + } + + public setAlias(block: CalldataBlock) { + this.aliasFor = block; + this.setName(`${this.getName()} (alias for ${block.getName()})`); + } + + public getAlias(): CalldataBlock | undefined { + return this.aliasFor; + } + + public getRawData(): Buffer { + const dependencyRawData = this.dependency.getRawData(); + const rawDataComponents: Buffer[] = []; + rawDataComponents.push(DependentCalldataBlock.RAW_DATA_START); + rawDataComponents.push(dependencyRawData); + rawDataComponents.push(DependentCalldataBlock.RAW_DATA_END); + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } +} + +export class MemberCalldataBlock extends CalldataBlock { + private static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + private header: Buffer | undefined; + private members: CalldataBlock[]; + private contiguous: boolean; + + constructor(name: string, signature: string, parentName: string, relocatable: boolean, contiguous: boolean) { + super(name, signature, parentName, 0, 0, relocatable); + this.members = []; + this.header = undefined; + this.contiguous = contiguous; + } + + public getRawData(): Buffer { + const rawDataComponents: Buffer[] = []; + if (this.header !== undefined) { + rawDataComponents.push(this.header); + } + _.each(this.members, (member: CalldataBlock) => { + const memberBuffer = member.getRawData(); + rawDataComponents.push(memberBuffer); + }); + + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } + + public setMembers(members: CalldataBlock[]) { + let bodySizeInBytes = 0; + _.each(members, (member: CalldataBlock) => { + bodySizeInBytes += member.getSizeInBytes(); + }); + this.members = members; + this.setBodySize(0); + } + + public isContiguous(): boolean { + return true; + } + + public setHeader(header: Buffer) { + this.setHeaderSize(header.byteLength); + this.header = header; + } + + public toBuffer(): Buffer { + if (this.header !== undefined) return this.header; + return new Buffer(''); + } + + public getMembers(): CalldataBlock[] { + return this.members; + } +} + +class Queue { + private store: T[] = []; + push(val: T) { + this.store.push(val); + } + pushFront(val: T) { + this.store.unshift(val); + } + pop(): T | undefined { + return this.store.shift(); + } + popBack(): T | undefined { + if (this.store.length === 0) return undefined; + const backElement = this.store.splice(-1, 1)[0]; + return backElement; + } + merge(q: Queue) { + this.store = this.store.concat(q.store); + } + mergeFront(q: Queue) { + this.store = q.store.concat(this.store); + } + getStore(): T[] { + return this.store; + } + peek(): T | undefined { + return this.store.length >= 0 ? this.store[0] : undefined; + } +} + +export class Calldata { + private selector: string; + private rules: EncodingRules; + private sizeInBytes: number; + private root: MemberCalldataBlock | undefined; + + constructor(rules: EncodingRules) { + this.selector = ''; + this.rules = rules; + this.sizeInBytes = 0; + this.root = undefined; + } + + private createQueue(memberBlock: MemberCalldataBlock): Queue { + const blockQueue = new Queue(); + _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { + if (member instanceof MemberCalldataBlock) { + blockQueue.mergeFront(this.createQueue(member)); + } else { + blockQueue.pushFront(member); + } + }); + + // Children + _.each(memberBlock.getMembers(), (member: CalldataBlock) => { + if (member instanceof DependentCalldataBlock && member.getAlias() === undefined) { + let dependency = member.getDependency(); + if (dependency instanceof MemberCalldataBlock) { + blockQueue.merge(this.createQueue(dependency)); + } else { + blockQueue.push(dependency); + } + } + }); + + blockQueue.pushFront(memberBlock); + return blockQueue; + } + + private generateAnnotatedHexString(): string { + let hexValue = `${this.selector}`; + if (this.root === undefined) { + throw new Error('expected root'); + } + + const valueQueue = this.createQueue(this.root); + + let block: CalldataBlock | undefined; + let offset = 0; + const functionBlock = valueQueue.peek(); + let functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); + while ((block = valueQueue.pop()) !== undefined) { + // Set f + + // Process each block 1 word at a time + const size = block.getSizeInBytes(); + const name = block.getName(); + const parentName = block.getParentName(); + + //const ancestrialNamesOffset = name.startsWith('ptr<') ? 4 : 0; + //const parentOffset = name.lastIndexOf(parentName); + const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, '');//.replace(`${parentName}[`, '['); + const signature = block.getSignature(); + + // Current offset + let offsetStr = ''; + + // If this block is empty then it's a newline + let value = ''; + let nameStr = ''; + let line = ''; + if (size === 0) { + offsetStr = ' '.repeat(10); + value = ' '.repeat(74); + nameStr = `### ${prettyName.padEnd(80)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + offsetStr = `0x${offset.toString(16)}`.padEnd(10, ' '); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(0, 32))).padEnd(74); + if (block instanceof MemberCalldataBlock) { + nameStr = `### ${prettyName.padEnd(80)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + nameStr = ` ${prettyName.padEnd(80)}`; + line = `${offsetStr}${value}${nameStr}`; + } + } + + for (let j = 32; j < size; j += 32) { + offsetStr = `0x${(offset + j).toString(16)}`.padEnd(10, ' '); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(j, j + 32))).padEnd(74); + nameStr = ' '.repeat(40); + + line = `${line}\n${offsetStr}${value}${nameStr}`; + } + + // Append to hex value + hexValue = `${hexValue}\n${line}`; + offset += size; + } + + return hexValue; + } + + private generateCondensedHexString(): string { + let selectorBuffer = ethUtil.toBuffer(this.selector); + if (this.root === undefined) { + throw new Error('expected root'); + } + + const valueQueue = this.createQueue(this.root); + const valueBufs: Buffer[] = [selectorBuffer]; + let block: CalldataBlock | undefined; + while ((block = valueQueue.pop()) !== undefined) { + valueBufs.push(block.toBuffer()); + } + + const combinedBuffers = Buffer.concat(valueBufs); + const hexValue = ethUtil.bufferToHex(combinedBuffers); + return hexValue; + } + + public optimize() { + if (this.root === undefined) { + throw new Error('expected root'); + } + + const blocksByHash: { [key: string]: CalldataBlock } = {}; + + // 1. Create a queue of subtrees by hash + // Note that they are ordered the same as + const subtreeQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; + while ((block = subtreeQueue.popBack()) !== undefined) { + if (block instanceof DependentCalldataBlock) { + const blockHashBuf = block.getDependency().computeHash(); + const blockHash = ethUtil.bufferToHex(blockHashBuf); + if (blockHash in blocksByHash) { + const blockWithSameHash = blocksByHash[blockHash]; + if (blockWithSameHash !== block.getDependency()) { + block.setAlias(blockWithSameHash); + } + } + continue; + } + + const blockHashBuf = block.computeHash(); + const blockHash = ethUtil.bufferToHex(blockHashBuf); + if (blockHash in blocksByHash === false) { + blocksByHash[blockHash] = block; + } + } + } + + public toHexString(): string { + if (this.root === undefined) { + throw new Error('expected root'); + } + + if (this.rules.optimize) this.optimize(); + + const offsetQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; + let offset = 0; + while ((block = offsetQueue.pop()) !== undefined) { + block.setOffset(offset); + offset += block.getSizeInBytes(); + } + + const hexValue = this.rules.annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); + return hexValue; + } + + public getSelectorHex(): string { + return this.selector; + } + + public getSizeInBytes(): number { + return this.sizeInBytes; + } + + public toAnnotatedString(): string { + return ""; + } + + public setRoot(block: MemberCalldataBlock) { + this.root = block; + this.sizeInBytes += block.getSizeInBytes(); + } + + public setSelector(selector: string) { + // Ensure we have a 0x prefix + if (selector.startsWith('0x')) { + this.selector = selector; + } else { + this.selector = `$0x${selector}`; + } + + // The selector must be 10 characters: '0x' followed by 4 bytes (two hex chars per byte) + if (this.selector.length !== 10) { + throw new Error(`Invalid selector '${this.selector}'`); + } + this.sizeInBytes += 8; + } +} + +export class RawCalldata { + private value: Buffer; + private offset: number; // tracks current offset into raw calldata; used for parsing + private selector: string; + private scopes: Queue; + + constructor(value: string | Buffer) { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Expected raw calldata to start with '0x'`); + } + const valueBuf = ethUtil.toBuffer(value); + this.selector = ethUtil.bufferToHex(valueBuf.slice(0, 4)); + this.value = valueBuf.slice(4); // disregard selector + this.offset = 0; + this.scopes = new Queue(); + this.scopes.push(0); + } + + public popBytes(lengthInBytes: number): Buffer { + const value = this.value.slice(this.offset, this.offset + lengthInBytes); + this.setOffset(this.offset + lengthInBytes); + return value; + } + + public popWord(): Buffer { + const wordInBytes = 32; + return this.popBytes(wordInBytes); + } + + public popWords(length: number): Buffer { + const wordInBytes = 32; + return this.popBytes(length * wordInBytes); + } + + public readBytes(from: number, to: number): Buffer { + const value = this.value.slice(from, to); + return value; + } + + public setOffset(offsetInBytes: number) { + this.offset = offsetInBytes; + } + + public startScope() { + this.scopes.pushFront(this.offset); + } + + public endScope() { + this.scopes.pop(); + } + + public getOffset(): number { + return this.offset; + } + + public toAbsoluteOffset(relativeOffset: number) { + const scopeOffset = this.scopes.peek(); + if (scopeOffset === undefined) { + throw new Error(`Tried to access undefined scope.`); + } + const absoluteOffset = relativeOffset + scopeOffset; + return absoluteOffset; + } + + public getSelector(): string { + return this.selector; + } +} \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder/data_type.ts b/packages/order-utils/test/abi_encoder/data_type.ts new file mode 100644 index 000000000..4c4537a69 --- /dev/null +++ b/packages/order-utils/test/abi_encoder/data_type.ts @@ -0,0 +1,322 @@ +import { RawCalldata, Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata"; +import { MethodAbi, DataItem } from 'ethereum-types'; +import { DecodingRules, EncodingRules } from './calldata'; +import { BigNumber } from '@0x/utils'; +import ethUtil = require('ethereumjs-util'); +var _ = require('lodash'); + +export interface DataTypeFactory { + create: (dataItem: DataItem, parentDataType: DataType) => DataType; + mapDataItemToDataType: (dataItem: DataItem) => DataType; +} + +export abstract class DataType { + private static DEFAULT_ENCODING_RULES = { optimize: false, annotate: false } as EncodingRules; + private static DEFAULT_DECODING_RULES = { structsAsObjects: false } as DecodingRules; + + private dataItem: DataItem; + private factory: DataTypeFactory; + + constructor(dataItem: DataItem, factory: DataTypeFactory) { + this.dataItem = dataItem; + this.factory = factory; + } + + public getDataItem(): DataItem { + return this.dataItem; + } + + public getFactory(): DataTypeFactory { + return this.factory; + } + + public encode(value: any, rules?: EncodingRules, selector?: string): string { + const rules_ = rules ? rules : DataType.DEFAULT_ENCODING_RULES; + const calldata = new Calldata(rules_); + if (selector) calldata.setSelector(selector); + const block = this.generateCalldataBlock(value); + calldata.setRoot(block as MemberCalldataBlock); // @TODO CHANGE + const calldataHex = calldata.toHexString(); + return calldataHex; + } + + public decode(calldata: string, rules?: DecodingRules): any { + const rawCalldata = new RawCalldata(calldata); + const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; + const value = this.generateValue(rawCalldata, rules_); + return value; + } + + public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; + public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any; + public abstract getSignature(): string; + public abstract isStatic(): boolean; +} + +export abstract class PayloadDataType extends DataType { + protected hasConstantSize: boolean; + + public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { + super(dataItem, factory); + this.hasConstantSize = hasConstantSize; + } + + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { + const encodedValue = this.encodeValue(value); + const name = this.getDataItem().name; + const signature = this.getSignature(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const relocatable = false; + const block = new PayloadCalldataBlock(name, signature, parentName, /*offsetInBytes,*/ relocatable, encodedValue); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { + const value = this.decodeValue(calldata); + return value; + } + + public isStatic(): boolean { + // If a payload has a constant size then it's static + return this.hasConstantSize; + } + + public abstract encodeValue(value: any): Buffer; + public abstract decodeValue(calldata: RawCalldata): any; +} + +export abstract class DependentDataType extends DataType { + protected dependency: DataType; + protected parent: DataType; + + public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { + super(dataItem, factory); + this.dependency = dependency; + this.parent = parent; + } + + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { + if (parentBlock === undefined) { + throw new Error(`DependentDataType requires a parent block to generate its block`); + } + const dependencyBlock = this.dependency.generateCalldataBlock(value, parentBlock); + const name = this.getDataItem().name; + const signature = this.getSignature(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const relocatable = false; + const block = new DependentCalldataBlock(name, signature, parentName, relocatable, dependencyBlock, parentBlock); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { + const destinationOffsetBuf = calldata.popWord(); + const currentOffset = calldata.getOffset(); + const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), 16); + const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); + calldata.setOffset(destinationOffsetAbsolute); + const value = this.dependency.generateValue(calldata, rules); + calldata.setOffset(currentOffset); + return value; + } + + public isStatic(): boolean { + return true; + } +} + +export interface MemberMap { + [key: string]: number; +} + +export abstract class MemberDataType extends DataType { + private memberMap: MemberMap; + private members: DataType[]; + private isArray: boolean; + protected arrayLength: number | undefined; + protected arrayElementType: string | undefined; + + + public constructor(dataItem: DataItem, factory: DataTypeFactory, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { + super(dataItem, factory); + this.memberMap = {}; + this.members = []; + this.isArray = isArray; + this.arrayLength = arrayLength; + this.arrayElementType = arrayElementType; + if (isArray && arrayLength !== undefined) { + [this.members, this.memberMap] = this.createMembersWithLength(dataItem, arrayLength); + } else if (!isArray) { + [this.members, this.memberMap] = this.createMembersWithKeys(dataItem); + } + } + + private createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { + // Sanity check + if (dataItem.components === undefined) { + throw new Error(`Expected components`); + } + + let members: DataType[] = []; + let memberMap: MemberMap = {}; + _.each(dataItem.components, (memberItem: DataItem) => { + const childDataItem = { + type: memberItem.type, + name: `${dataItem.name}.${memberItem.name}`, + } as DataItem; + const components = memberItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[memberItem.name] = members.length; + members.push(child); + }); + + return [members, memberMap]; + } + + private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { + let members: DataType[] = []; + let memberMap: MemberMap = {}; + const range = _.range(length); + _.each(range, (idx: number) => { + const childDataItem = { + type: this.arrayElementType, + name: `${dataItem.name}[${idx.toString(10)}]`, + } as DataItem; + const components = dataItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[idx.toString(10)] = members.length; + members.push(child); + }); + + return [members, memberMap]; + } + + protected generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { + // Sanity check length + if (this.arrayLength !== undefined && value.length !== this.arrayLength) { + throw new Error( + `Expected array of ${JSON.stringify( + this.arrayLength, + )} elements, but got array of length ${JSON.stringify(value.length)}`, + ); + } + + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); + + let members = this.members; + if (this.isArray && this.arrayLength === undefined) { + [members,] = this.createMembersWithLength(this.getDataItem(), value.length); + + const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(16)}`), 32); + methodBlock.setHeader(lenBuf); + } + + const memberBlocks: CalldataBlock[] = []; + _.each(members, (member: DataType, idx: number) => { + const block = member.generateCalldataBlock(value[idx], methodBlock); + memberBlocks.push(block); + }); + methodBlock.setMembers(memberBlocks); + return methodBlock; + } + + protected generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); + const memberBlocks: CalldataBlock[] = []; + let childMap = _.cloneDeep(this.memberMap); + _.forOwn(obj, (value: any, key: string) => { + if (key in childMap === false) { + throw new Error(`Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`); + } + const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock); + memberBlocks.push(block); + delete childMap[key]; + }); + + if (Object.keys(childMap).length !== 0) { + throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); + } + + methodBlock.setMembers(memberBlocks); + return methodBlock; + } + + public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value, parentBlock) : this.generateCalldataBlockFromObject(value, parentBlock); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { + let members = this.members; + if (this.isArray && this.arrayLength === undefined) { + const arrayLengthBuf = calldata.popWord(); + const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); + const hexBase = 16; + const arrayLength = new BigNumber(arrayLengthHex, hexBase); + + [members,] = this.createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); + } + + calldata.startScope(); + let value: any[] | object; + if (rules.structsAsObjects && !this.isArray) { + value = {}; + _.each(this.memberMap, (idx: number, key: string) => { + const member = this.members[idx]; + let memberValue = member.generateValue(calldata, rules); + (value as { [key: string]: any })[key] = memberValue; + }); + } else { + value = []; + _.each(members, (member: DataType, idx: number) => { + let memberValue = member.generateValue(calldata, rules); + (value as any[]).push(memberValue); + }); + } + calldata.endScope(); + return value; + } + + protected computeSignatureOfMembers(): string { + // Compute signature of members + let signature = `(`; + _.each(this.members, (member: DataType, i: number) => { + signature += member.getSignature(); + if (i < this.members.length - 1) { + signature += ','; + } + }); + signature += ')'; + return signature; + } + + public isStatic(): boolean { + /* For Tuple: + const isStaticTuple = this.children.length === 0; + return isStaticTuple; // @TODO: True in every case or only when dynamic data? + + For Array: + if isLengthDefined = false then this is false + + Otherwise if the first element is a Pointer then false + */ + + if (this.isArray && this.arrayLength === undefined) { + return false; + } + + // Search for dependent members + const dependentMember = _.find(this.members, (member: DataType) => { + return (member instanceof DependentDataType); + }); + const isStatic = (dependentMember === undefined); // static if we couldn't find a dependent member + return isStatic; + } +} diff --git a/packages/order-utils/test/abi_encoder/evm_data_types.ts b/packages/order-utils/test/abi_encoder/evm_data_types.ts new file mode 100644 index 000000000..bb7c1d81d --- /dev/null +++ b/packages/order-utils/test/abi_encoder/evm_data_types.ts @@ -0,0 +1,550 @@ +import { DataType, DataTypeFactory, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; + +import { DecodingRules, EncodingRules } from './calldata'; + +import { MethodAbi, DataItem } from 'ethereum-types'; + +import ethUtil = require('ethereumjs-util'); + +import { Calldata, RawCalldata } from './calldata'; + +import { BigNumber } from '@0x/utils'; + +var _ = require('lodash'); + +export interface DataTypeStaticInterface { + matchGrammar: (type: string) => boolean; + encodeValue: (value: any) => Buffer; + decodeValue: (rawCalldata: RawCalldata) => any; +} + +export class Address extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Address.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Address.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); + } + } + + public getSignature(): string { + return 'address'; + } + + public static matchGrammar(type: string): boolean { + return type === 'address'; + } + + public encodeValue(value: boolean): Buffer { + const evmWordWidth = 32; + const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const paddedValueBuf = calldata.popWord(); + const valueBuf = paddedValueBuf.slice(12); + const value = ethUtil.bufferToHex(valueBuf); + return value; + } +} + +export class Bool extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Bool.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Bool.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); + } + } + + public getSignature(): string { + return 'bool'; + } + + public static matchGrammar(type: string): boolean { + return type === 'bool'; + } + + public encodeValue(value: boolean): Buffer { + const evmWordWidth = 32; + const encodedValue = value === true ? '0x1' : '0x0'; + const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): boolean { + const valueBuf = calldata.popWord(); + const valueHex = ethUtil.bufferToHex(valueBuf); + const valueNumber = new BigNumber(valueHex, 16); + let value: boolean = (valueNumber.equals(0)) ? false : true; + if (!(valueNumber.equals(0) || valueNumber.equals(1))) { + throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); + } + return value; + } +} + +abstract class Number extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + static MAX_WIDTH: number = 256; + static DEFAULT_WIDTH: number = Number.MAX_WIDTH; + width: number = Number.DEFAULT_WIDTH; + + constructor(dataItem: DataItem, matcher: RegExp) { + super(dataItem, EvmDataTypeFactory.getInstance(), Number.SIZE_KNOWN_AT_COMPILE_TIME); + const matches = matcher.exec(dataItem.type); + if (matches === null) { + throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); + } + if (matches !== null && matches.length === 2 && matches[1] !== undefined) { + this.width = parseInt(matches[1]); + } else { + this.width = 256; + } + } + + public encodeValue(value: BigNumber): Buffer { + if (value.greaterThan(this.getMaxValue())) { + throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; + } else if (value.lessThan(this.getMinValue())) { + throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; + } + + const hexBase = 16; + const evmWordWidth = 32; + let valueBuf: Buffer; + if (value.greaterThanOrEqualTo(0)) { + valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); + } else { + // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. + // Step 1/3: Convert value to positive binary string + const binBase = 2; + const valueBin = value.times(-1).toString(binBase); + + // Step 2/3: Invert binary value + const bitsInEvmWord = 256; + let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, binBase); + + // Step 3/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const negativeValue = invertedValue.plus(1); + + // Convert the negated value to a hex string + valueBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), + evmWordWidth, + ); + } + + return valueBuf; + } + + public decodeValue(calldata: RawCalldata): BigNumber { + const paddedValueBuf = calldata.popWord(); + const paddedValueHex = ethUtil.bufferToHex(paddedValueBuf); + let value = new BigNumber(paddedValueHex, 16); + if (this instanceof Int) { + // Check if we're negative + const binBase = 2; + const paddedValueBin = value.toString(binBase); + const valueBin = paddedValueBin.slice(paddedValueBin.length - this.width); + if (valueBin[0].startsWith('1')) { + // Negative + // Step 1/3: Invert binary value + let invertedValueBin = ''; + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, binBase); + + // Step 2/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const positiveValue = invertedValue.plus(1); + + // Step 3/3: Invert positive value + const negativeValue = positiveValue.times(-1); + value = negativeValue; + } + } + + return value; + } + + public abstract getMaxValue(): BigNumber; + public abstract getMinValue(): BigNumber; +} + +export class Int extends Number { + static matcher = RegExp( + '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + constructor(dataItem: DataItem) { + super(dataItem, Int.matcher); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).times(-1); + } + + public getSignature(): string { + return `int${this.width}`; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class UInt extends Number { + static matcher = RegExp( + '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + constructor(dataItem: DataItem) { + super(dataItem, UInt.matcher); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(0); + } + + public getSignature(): string { + return `uint${this.width}`; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Byte extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + static matcher = RegExp( + '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', + ); + + static DEFAULT_WIDTH = 1; + width: number = Byte.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Byte.SIZE_KNOWN_AT_COMPILE_TIME); + const matches = Byte.matcher.exec(dataItem.type); + if (!Byte.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); + } + if (matches !== null && matches.length === 3 && matches[2] !== undefined) { + this.width = parseInt(matches[2]); + } else { + this.width = Byte.DEFAULT_WIDTH; + } + } + + public getSignature(): string { + // Note that `byte` reduces to `bytes1` + return `bytes${this.width}`; + } + + public encodeValue(value: string | Buffer): Buffer { + // Convert value into a buffer and do bounds checking + const valueBuf = ethUtil.toBuffer(value); + if (valueBuf.byteLength > this.width) { + throw new Error( + `Tried to assign ${value} (${ + valueBuf.byteLength + } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, + ); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + // Store value as hex + const evmWordWidth = 32; + const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); + return paddedValue; + } + + public decodeValue(calldata: RawCalldata): string { + const paddedValueBuf = calldata.popWord(); + const valueBuf = paddedValueBuf.slice(0, this.width); + const value = ethUtil.bufferToHex(valueBuf); + return value; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Bytes extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = Bytes.UNDEFINED_LENGTH; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Bytes.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Bytes.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Bytes with bad input: ${dataItem}`); + } + } + + public encodeValue(value: string | Buffer): Buffer { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); + } + const valueBuf = ethUtil.toBuffer(value); + if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + const wordsForValue = Math.ceil(valueBuf.byteLength / 32); + const paddedBytesForValue = wordsForValue * 32; + const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedBytesForValue); + const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); + const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const lengthBuf = calldata.popWord(); + const lengthHex = ethUtil.bufferToHex(lengthBuf); + const length = parseInt(lengthHex, 16); + const wordsForValue = Math.ceil(length / 32); + const paddedValueBuf = calldata.popWords(wordsForValue); + const valueBuf = paddedValueBuf.slice(0, length); + const decodedValue = ethUtil.bufferToHex(valueBuf); + return decodedValue; + } + + public getSignature(): string { + return 'bytes'; + } + + public static matchGrammar(type: string): boolean { + return type === 'bytes'; + } +} + +export class SolString extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), SolString.SIZE_KNOWN_AT_COMPILE_TIME); + if (!SolString.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); + } + } + + public encodeValue(value: string): Buffer { + const wordsForValue = Math.ceil(value.length / 32); + const paddedBytesForValue = wordsForValue * 32; + const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); + const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); + const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const lengthBuf = calldata.popWord(); + const lengthHex = ethUtil.bufferToHex(lengthBuf); + const length = parseInt(lengthHex, 16); + const wordsForValue = Math.ceil(length / 32); + const paddedValueBuf = calldata.popWords(wordsForValue); + const valueBuf = paddedValueBuf.slice(0, length); + const value = valueBuf.toString('ascii'); + return value; + } + + public getSignature(): string { + return 'string'; + } + + public static matchGrammar(type: string): boolean { + return type === 'string'; + } +} + +export class Pointer extends DependentDataType { + + constructor(destDataType: DataType, parentDataType: DataType) { + const destDataItem = destDataType.getDataItem(); + const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; + super(dataItem, EvmDataTypeFactory.getInstance(), destDataType, parentDataType); + } + + public getSignature(): string { + return this.dependency.getSignature(); + } +} + +export class Tuple extends MemberDataType { + private tupleSignature: string; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + if (!Tuple.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); + } + this.tupleSignature = this.computeSignatureOfMembers(); + } + + public getSignature(): string { + return this.tupleSignature; + } + + public static matchGrammar(type: string): boolean { + return type === 'tuple'; + } +} + +export class SolArray extends MemberDataType { + static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); + private arraySignature: string; + private elementType: string; + + constructor(dataItem: DataItem) { + // Sanity check + const matches = SolArray.matcher.exec(dataItem.type); + if (matches === null || matches.length !== 3) { + throw new Error(`Could not parse array: ${dataItem.type}`); + } else if (matches[1] === undefined) { + throw new Error(`Could not parse array type: ${dataItem.type}`); + } else if (matches[2] === undefined) { + throw new Error(`Could not parse array length: ${dataItem.type}`); + } + + const isArray = true; + const arrayElementType = matches[1]; + const arrayLength = (matches[2] === '') ? undefined : parseInt(matches[2], 10); + super(dataItem, EvmDataTypeFactory.getInstance(), isArray, arrayLength, arrayElementType); + this.elementType = arrayElementType; + this.arraySignature = this.computeSignature(); + } + + private computeSignature(): string { + let dataItem = { + type: this.elementType, + name: 'N/A', + } as DataItem; + const components = this.getDataItem().components; + if (components !== undefined) { + dataItem.components = components; + } + const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); + const type = elementDataType.getSignature(); + if (this.arrayLength === undefined) { + return `${type}[]`; + } else { + return `${type}[${this.arrayLength}]`; + } + } + + public getSignature(): string { + return this.arraySignature; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Method extends MemberDataType { + private methodSignature: string; + private methodSelector: string; + + // TMP + public selector: string; + + constructor(abi: MethodAbi) { + super({ type: 'method', name: abi.name, components: abi.inputs }, EvmDataTypeFactory.getInstance()); + this.methodSignature = this.computeSignature(); + this.selector = this.methodSelector = this.computeSelector(); + + } + + private computeSignature(): string { + const memberSignature = this.computeSignatureOfMembers(); + const methodSignature = `${this.getDataItem().name}${memberSignature}`; + return methodSignature; + } + + private computeSelector(): string { + const signature = this.computeSignature(); + const selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(signature).slice(0, 4))); + return selector; + } + + public encode(value: any, rules?: EncodingRules): string { + const calldata = super.encode(value, rules, this.selector); + return calldata; + } + + public decode(calldata: string, rules?: DecodingRules): any[] | object { + if (!calldata.startsWith(this.selector)) { + throw new Error(`Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`); + } + const value = super.decode(calldata, rules); + return value; + } + + public getSignature(): string { + return this.methodSignature; + } + + public getSelector(): string { + return this.methodSelector; + } +} + +export class EvmDataTypeFactory implements DataTypeFactory { + private static instance: DataTypeFactory; + + private constructor() { } + + public static getInstance(): DataTypeFactory { + if (!EvmDataTypeFactory.instance) { + EvmDataTypeFactory.instance = new EvmDataTypeFactory(); + } + return EvmDataTypeFactory.instance; + } + + public mapDataItemToDataType(dataItem: DataItem): DataType { + if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); + if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); + if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); + if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); + if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); + if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); + if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); + if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); + if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); + //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); + //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); + + throw new Error(`Unrecognized data type: '${dataItem.type}'`); + } + + public create(dataItem: DataItem, parentDataType: DataType): DataType { + const dataType = this.mapDataItemToDataType(dataItem); + if (dataType.isStatic()) { + return dataType; + } + + const pointer = new Pointer(dataType, parentDataType); + return pointer; + } +} \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder/index.ts b/packages/order-utils/test/abi_encoder/index.ts new file mode 100644 index 000000000..991edb8c5 --- /dev/null +++ b/packages/order-utils/test/abi_encoder/index.ts @@ -0,0 +1,2 @@ +export { EncodingRules, DecodingRules } from './calldata'; +export * from './evm_data_types'; \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts index 295311acc..f1e562bc6 100644 --- a/packages/order-utils/test/abi_encoder_test.ts +++ b/packages/order-utils/test/abi_encoder_test.ts @@ -14,10 +14,9 @@ import { BigNumber } from '@0x/utils'; import { assert } from '@0x/order-utils/src/assert'; //import * as AbiEncoder from './abi_encoder'; -import * as AbiEncoder from './abi/abi_encoder'; +import * as AbiEncoder from './abi_encoder'; import * as AbiSamples from './abi_samples'; -import { Calldata } from './abi/calldata'; chaiSetup.configure(); const expect = chai.expect; @@ -42,7 +41,7 @@ describe.only('ABI Encoder', () => { // Verify optimized calldata is expected const optimizedCalldata = method.encode(args, { optimize: true }); const expectedOptimizedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + //expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Verify args decode properly const decodedArgs = method.decode(optimizedCalldata); -- cgit v1.2.3 From 5b0d554f7baec54837d795b6568ae5ba8d8a0908 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 14 Nov 2018 15:27:07 -0800 Subject: Moved Abi Encoder into utils package --- packages/order-utils/test/abi_encoder/calldata.ts | 520 ------------ packages/order-utils/test/abi_encoder/data_type.ts | 322 ------- .../order-utils/test/abi_encoder/evm_data_types.ts | 550 ------------ packages/order-utils/test/abi_encoder/index.ts | 2 - packages/order-utils/test/abi_encoder_test.ts | 943 --------------------- packages/order-utils/test/abi_samples.ts | 814 ------------------ packages/utils/src/abi_encoder/calldata.ts | 520 ++++++++++++ packages/utils/src/abi_encoder/data_type.ts | 322 +++++++ packages/utils/src/abi_encoder/evm_data_types.ts | 550 ++++++++++++ packages/utils/src/abi_encoder/index.ts | 2 + packages/utils/src/index.ts | 1 + packages/utils/test/abi_encoder_test.ts | 935 ++++++++++++++++++++ packages/utils/test/abi_samples.ts | 814 ++++++++++++++++++ packages/utils/test/utils/chai_setup.ts | 13 + 14 files changed, 3157 insertions(+), 3151 deletions(-) delete mode 100644 packages/order-utils/test/abi_encoder/calldata.ts delete mode 100644 packages/order-utils/test/abi_encoder/data_type.ts delete mode 100644 packages/order-utils/test/abi_encoder/evm_data_types.ts delete mode 100644 packages/order-utils/test/abi_encoder/index.ts delete mode 100644 packages/order-utils/test/abi_encoder_test.ts delete mode 100644 packages/order-utils/test/abi_samples.ts create mode 100644 packages/utils/src/abi_encoder/calldata.ts create mode 100644 packages/utils/src/abi_encoder/data_type.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types.ts create mode 100644 packages/utils/src/abi_encoder/index.ts create mode 100644 packages/utils/test/abi_encoder_test.ts create mode 100644 packages/utils/test/abi_samples.ts create mode 100644 packages/utils/test/utils/chai_setup.ts diff --git a/packages/order-utils/test/abi_encoder/calldata.ts b/packages/order-utils/test/abi_encoder/calldata.ts deleted file mode 100644 index 32278e5c5..000000000 --- a/packages/order-utils/test/abi_encoder/calldata.ts +++ /dev/null @@ -1,520 +0,0 @@ -import ethUtil = require('ethereumjs-util'); -import CommunicationChatBubbleOutline from 'material-ui/SvgIcon'; -var _ = require('lodash'); - -export interface DecodingRules { - structsAsObjects: boolean; -} - -export interface EncodingRules { - optimize?: boolean; - annotate?: boolean; -} - -export abstract class CalldataBlock { - private name: string; - private signature: string; - private offsetInBytes: number; - private headerSizeInBytes: number; - private bodySizeInBytes: number; - private relocatable: boolean; - private parentName: string; - - constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { - this.name = name; - this.signature = signature; - this.parentName = parentName; - this.offsetInBytes = 0; - this.headerSizeInBytes = headerSizeInBytes; - this.bodySizeInBytes = bodySizeInBytes; - this.relocatable = relocatable; - } - - protected setHeaderSize(headerSizeInBytes: number) { - this.headerSizeInBytes = headerSizeInBytes; - } - - protected setBodySize(bodySizeInBytes: number) { - this.bodySizeInBytes = bodySizeInBytes; - } - - protected setName(name: string) { - this.name = name; - } - - public getName(): string { - return this.name; - } - - public getParentName(): string { - return this.parentName; - } - - public getSignature(): string { - return this.signature; - } - - public isRelocatable(): boolean { - return this.relocatable; - } - - public getHeaderSizeInBytes(): number { - return this.headerSizeInBytes; - } - - public getBodySizeInBytes(): number { - return this.bodySizeInBytes; - } - - public getSizeInBytes(): number { - return this.headerSizeInBytes + this.bodySizeInBytes; - } - - public getOffsetInBytes(): number { - return this.offsetInBytes; - } - - public setOffset(offsetInBytes: number) { - this.offsetInBytes = offsetInBytes; - } - - public computeHash(): Buffer { - const rawData = this.getRawData(); - const hash = ethUtil.sha3(rawData); - return hash; - } - - public abstract toBuffer(): Buffer; - public abstract getRawData(): Buffer; -} - -export class PayloadCalldataBlock extends CalldataBlock { - private payload: Buffer; - - constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { - const headerSizeInBytes = 0; - const bodySizeInBytes = payload.byteLength; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); - this.payload = payload; - } - - public toBuffer(): Buffer { - return this.payload; - } - - public getRawData(): Buffer { - return this.payload; - } -} - -export class DependentCalldataBlock extends CalldataBlock { - public static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; - public static RAW_DATA_START = new Buffer('<'); - public static RAW_DATA_END = new Buffer('>'); - private parent: CalldataBlock; - private dependency: CalldataBlock; - private aliasFor: CalldataBlock | undefined; - - constructor(name: string, signature: string, parentName: string, relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { - const headerSizeInBytes = 0; - const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); - this.parent = parent; - this.dependency = dependency; - this.aliasFor = undefined; - } - - public toBuffer(): Buffer { - const destinationOffset = (this.aliasFor !== undefined) ? this.aliasFor.getOffsetInBytes() : this.dependency.getOffsetInBytes(); - const parentOffset = this.parent.getOffsetInBytes(); - const parentHeaderSize = this.parent.getHeaderSizeInBytes(); - const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); - const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(16)}`); - const evmWordWidthInBytes = 32; - const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); - return pointerBufPadded; - } - - public getDependency(): CalldataBlock { - return this.dependency; - } - - public setAlias(block: CalldataBlock) { - this.aliasFor = block; - this.setName(`${this.getName()} (alias for ${block.getName()})`); - } - - public getAlias(): CalldataBlock | undefined { - return this.aliasFor; - } - - public getRawData(): Buffer { - const dependencyRawData = this.dependency.getRawData(); - const rawDataComponents: Buffer[] = []; - rawDataComponents.push(DependentCalldataBlock.RAW_DATA_START); - rawDataComponents.push(dependencyRawData); - rawDataComponents.push(DependentCalldataBlock.RAW_DATA_END); - const rawData = Buffer.concat(rawDataComponents); - return rawData; - } -} - -export class MemberCalldataBlock extends CalldataBlock { - private static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; - private header: Buffer | undefined; - private members: CalldataBlock[]; - private contiguous: boolean; - - constructor(name: string, signature: string, parentName: string, relocatable: boolean, contiguous: boolean) { - super(name, signature, parentName, 0, 0, relocatable); - this.members = []; - this.header = undefined; - this.contiguous = contiguous; - } - - public getRawData(): Buffer { - const rawDataComponents: Buffer[] = []; - if (this.header !== undefined) { - rawDataComponents.push(this.header); - } - _.each(this.members, (member: CalldataBlock) => { - const memberBuffer = member.getRawData(); - rawDataComponents.push(memberBuffer); - }); - - const rawData = Buffer.concat(rawDataComponents); - return rawData; - } - - public setMembers(members: CalldataBlock[]) { - let bodySizeInBytes = 0; - _.each(members, (member: CalldataBlock) => { - bodySizeInBytes += member.getSizeInBytes(); - }); - this.members = members; - this.setBodySize(0); - } - - public isContiguous(): boolean { - return true; - } - - public setHeader(header: Buffer) { - this.setHeaderSize(header.byteLength); - this.header = header; - } - - public toBuffer(): Buffer { - if (this.header !== undefined) return this.header; - return new Buffer(''); - } - - public getMembers(): CalldataBlock[] { - return this.members; - } -} - -class Queue { - private store: T[] = []; - push(val: T) { - this.store.push(val); - } - pushFront(val: T) { - this.store.unshift(val); - } - pop(): T | undefined { - return this.store.shift(); - } - popBack(): T | undefined { - if (this.store.length === 0) return undefined; - const backElement = this.store.splice(-1, 1)[0]; - return backElement; - } - merge(q: Queue) { - this.store = this.store.concat(q.store); - } - mergeFront(q: Queue) { - this.store = q.store.concat(this.store); - } - getStore(): T[] { - return this.store; - } - peek(): T | undefined { - return this.store.length >= 0 ? this.store[0] : undefined; - } -} - -export class Calldata { - private selector: string; - private rules: EncodingRules; - private sizeInBytes: number; - private root: MemberCalldataBlock | undefined; - - constructor(rules: EncodingRules) { - this.selector = ''; - this.rules = rules; - this.sizeInBytes = 0; - this.root = undefined; - } - - private createQueue(memberBlock: MemberCalldataBlock): Queue { - const blockQueue = new Queue(); - _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof MemberCalldataBlock) { - blockQueue.mergeFront(this.createQueue(member)); - } else { - blockQueue.pushFront(member); - } - }); - - // Children - _.each(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof DependentCalldataBlock && member.getAlias() === undefined) { - let dependency = member.getDependency(); - if (dependency instanceof MemberCalldataBlock) { - blockQueue.merge(this.createQueue(dependency)); - } else { - blockQueue.push(dependency); - } - } - }); - - blockQueue.pushFront(memberBlock); - return blockQueue; - } - - private generateAnnotatedHexString(): string { - let hexValue = `${this.selector}`; - if (this.root === undefined) { - throw new Error('expected root'); - } - - const valueQueue = this.createQueue(this.root); - - let block: CalldataBlock | undefined; - let offset = 0; - const functionBlock = valueQueue.peek(); - let functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); - while ((block = valueQueue.pop()) !== undefined) { - // Set f - - // Process each block 1 word at a time - const size = block.getSizeInBytes(); - const name = block.getName(); - const parentName = block.getParentName(); - - //const ancestrialNamesOffset = name.startsWith('ptr<') ? 4 : 0; - //const parentOffset = name.lastIndexOf(parentName); - const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, '');//.replace(`${parentName}[`, '['); - const signature = block.getSignature(); - - // Current offset - let offsetStr = ''; - - // If this block is empty then it's a newline - let value = ''; - let nameStr = ''; - let line = ''; - if (size === 0) { - offsetStr = ' '.repeat(10); - value = ' '.repeat(74); - nameStr = `### ${prettyName.padEnd(80)}`; - line = `\n${offsetStr}${value}${nameStr}`; - } else { - offsetStr = `0x${offset.toString(16)}`.padEnd(10, ' '); - value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(0, 32))).padEnd(74); - if (block instanceof MemberCalldataBlock) { - nameStr = `### ${prettyName.padEnd(80)}`; - line = `\n${offsetStr}${value}${nameStr}`; - } else { - nameStr = ` ${prettyName.padEnd(80)}`; - line = `${offsetStr}${value}${nameStr}`; - } - } - - for (let j = 32; j < size; j += 32) { - offsetStr = `0x${(offset + j).toString(16)}`.padEnd(10, ' '); - value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(j, j + 32))).padEnd(74); - nameStr = ' '.repeat(40); - - line = `${line}\n${offsetStr}${value}${nameStr}`; - } - - // Append to hex value - hexValue = `${hexValue}\n${line}`; - offset += size; - } - - return hexValue; - } - - private generateCondensedHexString(): string { - let selectorBuffer = ethUtil.toBuffer(this.selector); - if (this.root === undefined) { - throw new Error('expected root'); - } - - const valueQueue = this.createQueue(this.root); - const valueBufs: Buffer[] = [selectorBuffer]; - let block: CalldataBlock | undefined; - while ((block = valueQueue.pop()) !== undefined) { - valueBufs.push(block.toBuffer()); - } - - const combinedBuffers = Buffer.concat(valueBufs); - const hexValue = ethUtil.bufferToHex(combinedBuffers); - return hexValue; - } - - public optimize() { - if (this.root === undefined) { - throw new Error('expected root'); - } - - const blocksByHash: { [key: string]: CalldataBlock } = {}; - - // 1. Create a queue of subtrees by hash - // Note that they are ordered the same as - const subtreeQueue = this.createQueue(this.root); - let block: CalldataBlock | undefined; - while ((block = subtreeQueue.popBack()) !== undefined) { - if (block instanceof DependentCalldataBlock) { - const blockHashBuf = block.getDependency().computeHash(); - const blockHash = ethUtil.bufferToHex(blockHashBuf); - if (blockHash in blocksByHash) { - const blockWithSameHash = blocksByHash[blockHash]; - if (blockWithSameHash !== block.getDependency()) { - block.setAlias(blockWithSameHash); - } - } - continue; - } - - const blockHashBuf = block.computeHash(); - const blockHash = ethUtil.bufferToHex(blockHashBuf); - if (blockHash in blocksByHash === false) { - blocksByHash[blockHash] = block; - } - } - } - - public toHexString(): string { - if (this.root === undefined) { - throw new Error('expected root'); - } - - if (this.rules.optimize) this.optimize(); - - const offsetQueue = this.createQueue(this.root); - let block: CalldataBlock | undefined; - let offset = 0; - while ((block = offsetQueue.pop()) !== undefined) { - block.setOffset(offset); - offset += block.getSizeInBytes(); - } - - const hexValue = this.rules.annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); - return hexValue; - } - - public getSelectorHex(): string { - return this.selector; - } - - public getSizeInBytes(): number { - return this.sizeInBytes; - } - - public toAnnotatedString(): string { - return ""; - } - - public setRoot(block: MemberCalldataBlock) { - this.root = block; - this.sizeInBytes += block.getSizeInBytes(); - } - - public setSelector(selector: string) { - // Ensure we have a 0x prefix - if (selector.startsWith('0x')) { - this.selector = selector; - } else { - this.selector = `$0x${selector}`; - } - - // The selector must be 10 characters: '0x' followed by 4 bytes (two hex chars per byte) - if (this.selector.length !== 10) { - throw new Error(`Invalid selector '${this.selector}'`); - } - this.sizeInBytes += 8; - } -} - -export class RawCalldata { - private value: Buffer; - private offset: number; // tracks current offset into raw calldata; used for parsing - private selector: string; - private scopes: Queue; - - constructor(value: string | Buffer) { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Expected raw calldata to start with '0x'`); - } - const valueBuf = ethUtil.toBuffer(value); - this.selector = ethUtil.bufferToHex(valueBuf.slice(0, 4)); - this.value = valueBuf.slice(4); // disregard selector - this.offset = 0; - this.scopes = new Queue(); - this.scopes.push(0); - } - - public popBytes(lengthInBytes: number): Buffer { - const value = this.value.slice(this.offset, this.offset + lengthInBytes); - this.setOffset(this.offset + lengthInBytes); - return value; - } - - public popWord(): Buffer { - const wordInBytes = 32; - return this.popBytes(wordInBytes); - } - - public popWords(length: number): Buffer { - const wordInBytes = 32; - return this.popBytes(length * wordInBytes); - } - - public readBytes(from: number, to: number): Buffer { - const value = this.value.slice(from, to); - return value; - } - - public setOffset(offsetInBytes: number) { - this.offset = offsetInBytes; - } - - public startScope() { - this.scopes.pushFront(this.offset); - } - - public endScope() { - this.scopes.pop(); - } - - public getOffset(): number { - return this.offset; - } - - public toAbsoluteOffset(relativeOffset: number) { - const scopeOffset = this.scopes.peek(); - if (scopeOffset === undefined) { - throw new Error(`Tried to access undefined scope.`); - } - const absoluteOffset = relativeOffset + scopeOffset; - return absoluteOffset; - } - - public getSelector(): string { - return this.selector; - } -} \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder/data_type.ts b/packages/order-utils/test/abi_encoder/data_type.ts deleted file mode 100644 index 4c4537a69..000000000 --- a/packages/order-utils/test/abi_encoder/data_type.ts +++ /dev/null @@ -1,322 +0,0 @@ -import { RawCalldata, Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata"; -import { MethodAbi, DataItem } from 'ethereum-types'; -import { DecodingRules, EncodingRules } from './calldata'; -import { BigNumber } from '@0x/utils'; -import ethUtil = require('ethereumjs-util'); -var _ = require('lodash'); - -export interface DataTypeFactory { - create: (dataItem: DataItem, parentDataType: DataType) => DataType; - mapDataItemToDataType: (dataItem: DataItem) => DataType; -} - -export abstract class DataType { - private static DEFAULT_ENCODING_RULES = { optimize: false, annotate: false } as EncodingRules; - private static DEFAULT_DECODING_RULES = { structsAsObjects: false } as DecodingRules; - - private dataItem: DataItem; - private factory: DataTypeFactory; - - constructor(dataItem: DataItem, factory: DataTypeFactory) { - this.dataItem = dataItem; - this.factory = factory; - } - - public getDataItem(): DataItem { - return this.dataItem; - } - - public getFactory(): DataTypeFactory { - return this.factory; - } - - public encode(value: any, rules?: EncodingRules, selector?: string): string { - const rules_ = rules ? rules : DataType.DEFAULT_ENCODING_RULES; - const calldata = new Calldata(rules_); - if (selector) calldata.setSelector(selector); - const block = this.generateCalldataBlock(value); - calldata.setRoot(block as MemberCalldataBlock); // @TODO CHANGE - const calldataHex = calldata.toHexString(); - return calldataHex; - } - - public decode(calldata: string, rules?: DecodingRules): any { - const rawCalldata = new RawCalldata(calldata); - const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; - const value = this.generateValue(rawCalldata, rules_); - return value; - } - - public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; - public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any; - public abstract getSignature(): string; - public abstract isStatic(): boolean; -} - -export abstract class PayloadDataType extends DataType { - protected hasConstantSize: boolean; - - public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { - super(dataItem, factory); - this.hasConstantSize = hasConstantSize; - } - - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { - const encodedValue = this.encodeValue(value); - const name = this.getDataItem().name; - const signature = this.getSignature(); - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const relocatable = false; - const block = new PayloadCalldataBlock(name, signature, parentName, /*offsetInBytes,*/ relocatable, encodedValue); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any { - const value = this.decodeValue(calldata); - return value; - } - - public isStatic(): boolean { - // If a payload has a constant size then it's static - return this.hasConstantSize; - } - - public abstract encodeValue(value: any): Buffer; - public abstract decodeValue(calldata: RawCalldata): any; -} - -export abstract class DependentDataType extends DataType { - protected dependency: DataType; - protected parent: DataType; - - public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { - super(dataItem, factory); - this.dependency = dependency; - this.parent = parent; - } - - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { - if (parentBlock === undefined) { - throw new Error(`DependentDataType requires a parent block to generate its block`); - } - const dependencyBlock = this.dependency.generateCalldataBlock(value, parentBlock); - const name = this.getDataItem().name; - const signature = this.getSignature(); - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const relocatable = false; - const block = new DependentCalldataBlock(name, signature, parentName, relocatable, dependencyBlock, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any { - const destinationOffsetBuf = calldata.popWord(); - const currentOffset = calldata.getOffset(); - const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), 16); - const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); - calldata.setOffset(destinationOffsetAbsolute); - const value = this.dependency.generateValue(calldata, rules); - calldata.setOffset(currentOffset); - return value; - } - - public isStatic(): boolean { - return true; - } -} - -export interface MemberMap { - [key: string]: number; -} - -export abstract class MemberDataType extends DataType { - private memberMap: MemberMap; - private members: DataType[]; - private isArray: boolean; - protected arrayLength: number | undefined; - protected arrayElementType: string | undefined; - - - public constructor(dataItem: DataItem, factory: DataTypeFactory, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { - super(dataItem, factory); - this.memberMap = {}; - this.members = []; - this.isArray = isArray; - this.arrayLength = arrayLength; - this.arrayElementType = arrayElementType; - if (isArray && arrayLength !== undefined) { - [this.members, this.memberMap] = this.createMembersWithLength(dataItem, arrayLength); - } else if (!isArray) { - [this.members, this.memberMap] = this.createMembersWithKeys(dataItem); - } - } - - private createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { - // Sanity check - if (dataItem.components === undefined) { - throw new Error(`Expected components`); - } - - let members: DataType[] = []; - let memberMap: MemberMap = {}; - _.each(dataItem.components, (memberItem: DataItem) => { - const childDataItem = { - type: memberItem.type, - name: `${dataItem.name}.${memberItem.name}`, - } as DataItem; - const components = memberItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[memberItem.name] = members.length; - members.push(child); - }); - - return [members, memberMap]; - } - - private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { - let members: DataType[] = []; - let memberMap: MemberMap = {}; - const range = _.range(length); - _.each(range, (idx: number) => { - const childDataItem = { - type: this.arrayElementType, - name: `${dataItem.name}[${idx.toString(10)}]`, - } as DataItem; - const components = dataItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[idx.toString(10)] = members.length; - members.push(child); - }); - - return [members, memberMap]; - } - - protected generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { - // Sanity check length - if (this.arrayLength !== undefined && value.length !== this.arrayLength) { - throw new Error( - `Expected array of ${JSON.stringify( - this.arrayLength, - )} elements, but got array of length ${JSON.stringify(value.length)}`, - ); - } - - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); - - let members = this.members; - if (this.isArray && this.arrayLength === undefined) { - [members,] = this.createMembersWithLength(this.getDataItem(), value.length); - - const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(16)}`), 32); - methodBlock.setHeader(lenBuf); - } - - const memberBlocks: CalldataBlock[] = []; - _.each(members, (member: DataType, idx: number) => { - const block = member.generateCalldataBlock(value[idx], methodBlock); - memberBlocks.push(block); - }); - methodBlock.setMembers(memberBlocks); - return methodBlock; - } - - protected generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); - const memberBlocks: CalldataBlock[] = []; - let childMap = _.cloneDeep(this.memberMap); - _.forOwn(obj, (value: any, key: string) => { - if (key in childMap === false) { - throw new Error(`Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`); - } - const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock); - memberBlocks.push(block); - delete childMap[key]; - }); - - if (Object.keys(childMap).length !== 0) { - throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); - } - - methodBlock.setMembers(memberBlocks); - return methodBlock; - } - - public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value, parentBlock) : this.generateCalldataBlockFromObject(value, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { - let members = this.members; - if (this.isArray && this.arrayLength === undefined) { - const arrayLengthBuf = calldata.popWord(); - const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); - const hexBase = 16; - const arrayLength = new BigNumber(arrayLengthHex, hexBase); - - [members,] = this.createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); - } - - calldata.startScope(); - let value: any[] | object; - if (rules.structsAsObjects && !this.isArray) { - value = {}; - _.each(this.memberMap, (idx: number, key: string) => { - const member = this.members[idx]; - let memberValue = member.generateValue(calldata, rules); - (value as { [key: string]: any })[key] = memberValue; - }); - } else { - value = []; - _.each(members, (member: DataType, idx: number) => { - let memberValue = member.generateValue(calldata, rules); - (value as any[]).push(memberValue); - }); - } - calldata.endScope(); - return value; - } - - protected computeSignatureOfMembers(): string { - // Compute signature of members - let signature = `(`; - _.each(this.members, (member: DataType, i: number) => { - signature += member.getSignature(); - if (i < this.members.length - 1) { - signature += ','; - } - }); - signature += ')'; - return signature; - } - - public isStatic(): boolean { - /* For Tuple: - const isStaticTuple = this.children.length === 0; - return isStaticTuple; // @TODO: True in every case or only when dynamic data? - - For Array: - if isLengthDefined = false then this is false - - Otherwise if the first element is a Pointer then false - */ - - if (this.isArray && this.arrayLength === undefined) { - return false; - } - - // Search for dependent members - const dependentMember = _.find(this.members, (member: DataType) => { - return (member instanceof DependentDataType); - }); - const isStatic = (dependentMember === undefined); // static if we couldn't find a dependent member - return isStatic; - } -} diff --git a/packages/order-utils/test/abi_encoder/evm_data_types.ts b/packages/order-utils/test/abi_encoder/evm_data_types.ts deleted file mode 100644 index bb7c1d81d..000000000 --- a/packages/order-utils/test/abi_encoder/evm_data_types.ts +++ /dev/null @@ -1,550 +0,0 @@ -import { DataType, DataTypeFactory, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; - -import { DecodingRules, EncodingRules } from './calldata'; - -import { MethodAbi, DataItem } from 'ethereum-types'; - -import ethUtil = require('ethereumjs-util'); - -import { Calldata, RawCalldata } from './calldata'; - -import { BigNumber } from '@0x/utils'; - -var _ = require('lodash'); - -export interface DataTypeStaticInterface { - matchGrammar: (type: string) => boolean; - encodeValue: (value: any) => Buffer; - decodeValue: (rawCalldata: RawCalldata) => any; -} - -export class Address extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Address.SIZE_KNOWN_AT_COMPILE_TIME); - if (!Address.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); - } - } - - public getSignature(): string { - return 'address'; - } - - public static matchGrammar(type: string): boolean { - return type === 'address'; - } - - public encodeValue(value: boolean): Buffer { - const evmWordWidth = 32; - const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(12); - const value = ethUtil.bufferToHex(valueBuf); - return value; - } -} - -export class Bool extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Bool.SIZE_KNOWN_AT_COMPILE_TIME); - if (!Bool.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); - } - } - - public getSignature(): string { - return 'bool'; - } - - public static matchGrammar(type: string): boolean { - return type === 'bool'; - } - - public encodeValue(value: boolean): Buffer { - const evmWordWidth = 32; - const encodedValue = value === true ? '0x1' : '0x0'; - const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): boolean { - const valueBuf = calldata.popWord(); - const valueHex = ethUtil.bufferToHex(valueBuf); - const valueNumber = new BigNumber(valueHex, 16); - let value: boolean = (valueNumber.equals(0)) ? false : true; - if (!(valueNumber.equals(0) || valueNumber.equals(1))) { - throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); - } - return value; - } -} - -abstract class Number extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - static MAX_WIDTH: number = 256; - static DEFAULT_WIDTH: number = Number.MAX_WIDTH; - width: number = Number.DEFAULT_WIDTH; - - constructor(dataItem: DataItem, matcher: RegExp) { - super(dataItem, EvmDataTypeFactory.getInstance(), Number.SIZE_KNOWN_AT_COMPILE_TIME); - const matches = matcher.exec(dataItem.type); - if (matches === null) { - throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); - } - if (matches !== null && matches.length === 2 && matches[1] !== undefined) { - this.width = parseInt(matches[1]); - } else { - this.width = 256; - } - } - - public encodeValue(value: BigNumber): Buffer { - if (value.greaterThan(this.getMaxValue())) { - throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; - } else if (value.lessThan(this.getMinValue())) { - throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; - } - - const hexBase = 16; - const evmWordWidth = 32; - let valueBuf: Buffer; - if (value.greaterThanOrEqualTo(0)) { - valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); - } else { - // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. - // Step 1/3: Convert value to positive binary string - const binBase = 2; - const valueBin = value.times(-1).toString(binBase); - - // Step 2/3: Invert binary value - const bitsInEvmWord = 256; - let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, binBase); - - // Step 3/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const negativeValue = invertedValue.plus(1); - - // Convert the negated value to a hex string - valueBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), - evmWordWidth, - ); - } - - return valueBuf; - } - - public decodeValue(calldata: RawCalldata): BigNumber { - const paddedValueBuf = calldata.popWord(); - const paddedValueHex = ethUtil.bufferToHex(paddedValueBuf); - let value = new BigNumber(paddedValueHex, 16); - if (this instanceof Int) { - // Check if we're negative - const binBase = 2; - const paddedValueBin = value.toString(binBase); - const valueBin = paddedValueBin.slice(paddedValueBin.length - this.width); - if (valueBin[0].startsWith('1')) { - // Negative - // Step 1/3: Invert binary value - let invertedValueBin = ''; - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, binBase); - - // Step 2/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const positiveValue = invertedValue.plus(1); - - // Step 3/3: Invert positive value - const negativeValue = positiveValue.times(-1); - value = negativeValue; - } - } - - return value; - } - - public abstract getMaxValue(): BigNumber; - public abstract getMinValue(): BigNumber; -} - -export class Int extends Number { - static matcher = RegExp( - '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - constructor(dataItem: DataItem) { - super(dataItem, Int.matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).times(-1); - } - - public getSignature(): string { - return `int${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class UInt extends Number { - static matcher = RegExp( - '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - constructor(dataItem: DataItem) { - super(dataItem, UInt.matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(0); - } - - public getSignature(): string { - return `uint${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class Byte extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - static matcher = RegExp( - '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', - ); - - static DEFAULT_WIDTH = 1; - width: number = Byte.DEFAULT_WIDTH; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Byte.SIZE_KNOWN_AT_COMPILE_TIME); - const matches = Byte.matcher.exec(dataItem.type); - if (!Byte.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); - } - if (matches !== null && matches.length === 3 && matches[2] !== undefined) { - this.width = parseInt(matches[2]); - } else { - this.width = Byte.DEFAULT_WIDTH; - } - } - - public getSignature(): string { - // Note that `byte` reduces to `bytes1` - return `bytes${this.width}`; - } - - public encodeValue(value: string | Buffer): Buffer { - // Convert value into a buffer and do bounds checking - const valueBuf = ethUtil.toBuffer(value); - if (valueBuf.byteLength > this.width) { - throw new Error( - `Tried to assign ${value} (${ - valueBuf.byteLength - } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, - ); - } else if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - // Store value as hex - const evmWordWidth = 32; - const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); - return paddedValue; - } - - public decodeValue(calldata: RawCalldata): string { - const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(0, this.width); - const value = ethUtil.bufferToHex(valueBuf); - return value; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class Bytes extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = Bytes.UNDEFINED_LENGTH; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Bytes.SIZE_KNOWN_AT_COMPILE_TIME); - if (!Bytes.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Bytes with bad input: ${dataItem}`); - } - } - - public encodeValue(value: string | Buffer): Buffer { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); - } - const valueBuf = ethUtil.toBuffer(value); - if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - const wordsForValue = Math.ceil(valueBuf.byteLength / 32); - const paddedBytesForValue = wordsForValue * 32; - const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedBytesForValue); - const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); - const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const lengthBuf = calldata.popWord(); - const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, 16); - const wordsForValue = Math.ceil(length / 32); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); - const decodedValue = ethUtil.bufferToHex(valueBuf); - return decodedValue; - } - - public getSignature(): string { - return 'bytes'; - } - - public static matchGrammar(type: string): boolean { - return type === 'bytes'; - } -} - -export class SolString extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), SolString.SIZE_KNOWN_AT_COMPILE_TIME); - if (!SolString.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); - } - } - - public encodeValue(value: string): Buffer { - const wordsForValue = Math.ceil(value.length / 32); - const paddedBytesForValue = wordsForValue * 32; - const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); - const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); - const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const lengthBuf = calldata.popWord(); - const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, 16); - const wordsForValue = Math.ceil(length / 32); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); - const value = valueBuf.toString('ascii'); - return value; - } - - public getSignature(): string { - return 'string'; - } - - public static matchGrammar(type: string): boolean { - return type === 'string'; - } -} - -export class Pointer extends DependentDataType { - - constructor(destDataType: DataType, parentDataType: DataType) { - const destDataItem = destDataType.getDataItem(); - const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; - super(dataItem, EvmDataTypeFactory.getInstance(), destDataType, parentDataType); - } - - public getSignature(): string { - return this.dependency.getSignature(); - } -} - -export class Tuple extends MemberDataType { - private tupleSignature: string; - - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance()); - if (!Tuple.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); - } - this.tupleSignature = this.computeSignatureOfMembers(); - } - - public getSignature(): string { - return this.tupleSignature; - } - - public static matchGrammar(type: string): boolean { - return type === 'tuple'; - } -} - -export class SolArray extends MemberDataType { - static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); - private arraySignature: string; - private elementType: string; - - constructor(dataItem: DataItem) { - // Sanity check - const matches = SolArray.matcher.exec(dataItem.type); - if (matches === null || matches.length !== 3) { - throw new Error(`Could not parse array: ${dataItem.type}`); - } else if (matches[1] === undefined) { - throw new Error(`Could not parse array type: ${dataItem.type}`); - } else if (matches[2] === undefined) { - throw new Error(`Could not parse array length: ${dataItem.type}`); - } - - const isArray = true; - const arrayElementType = matches[1]; - const arrayLength = (matches[2] === '') ? undefined : parseInt(matches[2], 10); - super(dataItem, EvmDataTypeFactory.getInstance(), isArray, arrayLength, arrayElementType); - this.elementType = arrayElementType; - this.arraySignature = this.computeSignature(); - } - - private computeSignature(): string { - let dataItem = { - type: this.elementType, - name: 'N/A', - } as DataItem; - const components = this.getDataItem().components; - if (components !== undefined) { - dataItem.components = components; - } - const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); - const type = elementDataType.getSignature(); - if (this.arrayLength === undefined) { - return `${type}[]`; - } else { - return `${type}[${this.arrayLength}]`; - } - } - - public getSignature(): string { - return this.arraySignature; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } -} - -export class Method extends MemberDataType { - private methodSignature: string; - private methodSelector: string; - - // TMP - public selector: string; - - constructor(abi: MethodAbi) { - super({ type: 'method', name: abi.name, components: abi.inputs }, EvmDataTypeFactory.getInstance()); - this.methodSignature = this.computeSignature(); - this.selector = this.methodSelector = this.computeSelector(); - - } - - private computeSignature(): string { - const memberSignature = this.computeSignatureOfMembers(); - const methodSignature = `${this.getDataItem().name}${memberSignature}`; - return methodSignature; - } - - private computeSelector(): string { - const signature = this.computeSignature(); - const selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(signature).slice(0, 4))); - return selector; - } - - public encode(value: any, rules?: EncodingRules): string { - const calldata = super.encode(value, rules, this.selector); - return calldata; - } - - public decode(calldata: string, rules?: DecodingRules): any[] | object { - if (!calldata.startsWith(this.selector)) { - throw new Error(`Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`); - } - const value = super.decode(calldata, rules); - return value; - } - - public getSignature(): string { - return this.methodSignature; - } - - public getSelector(): string { - return this.methodSelector; - } -} - -export class EvmDataTypeFactory implements DataTypeFactory { - private static instance: DataTypeFactory; - - private constructor() { } - - public static getInstance(): DataTypeFactory { - if (!EvmDataTypeFactory.instance) { - EvmDataTypeFactory.instance = new EvmDataTypeFactory(); - } - return EvmDataTypeFactory.instance; - } - - public mapDataItemToDataType(dataItem: DataItem): DataType { - if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); - if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); - if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); - if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); - if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); - if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); - if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); - if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); - if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); - //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); - //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); - - throw new Error(`Unrecognized data type: '${dataItem.type}'`); - } - - public create(dataItem: DataItem, parentDataType: DataType): DataType { - const dataType = this.mapDataItemToDataType(dataItem); - if (dataType.isStatic()) { - return dataType; - } - - const pointer = new Pointer(dataType, parentDataType); - return pointer; - } -} \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder/index.ts b/packages/order-utils/test/abi_encoder/index.ts deleted file mode 100644 index 991edb8c5..000000000 --- a/packages/order-utils/test/abi_encoder/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { EncodingRules, DecodingRules } from './calldata'; -export * from './evm_data_types'; \ No newline at end of file diff --git a/packages/order-utils/test/abi_encoder_test.ts b/packages/order-utils/test/abi_encoder_test.ts deleted file mode 100644 index f1e562bc6..000000000 --- a/packages/order-utils/test/abi_encoder_test.ts +++ /dev/null @@ -1,943 +0,0 @@ -import * as chai from 'chai'; -import 'mocha'; -import ethUtil = require('ethereumjs-util'); - -var _ = require('lodash'); - -// import { assert } from '@0x/order-utils/src/assert'; - -import { chaiSetup } from './utils/chai_setup'; - -import { MethodAbi, DataItem } from 'ethereum-types'; - -import { BigNumber } from '@0x/utils'; -import { assert } from '@0x/order-utils/src/assert'; -//import * as AbiEncoder from './abi_encoder'; - -import * as AbiEncoder from './abi_encoder'; - -import * as AbiSamples from './abi_samples'; - -chaiSetup.configure(); -const expect = chai.expect; - -describe.only('ABI Encoder', () => { - describe.only('Optimizer', () => { - - }); - - describe.only('ABI Tests at Method Level', () => { - - it('Should reuse duplicated strings in string array', async () => { - const method = new AbiEncoder.Method(AbiSamples.stringAbi); - const strings = [ - "Test String", - "Test String 2", - "Test String", - "Test String 2", - ]; - const args = [strings]; - - // Verify optimized calldata is expected - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; - //expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - - // Verify args decode properly - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); - }); - - it('Should point array elements to a duplicated value from another parameter', async () => { - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi2); - const stringArray = [ - "Test String", - "Test String", - "Test String", - "Test String 2", - ]; - const string = 'Test String'; - const args = [stringArray, string]; - - // Verify optimized calldata is expected - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - - // Verify args decode properly - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); - }); - - - it('Optimizer #3 (tuple should point to array)', async () => { - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi3); - const uint8Array = [ - new BigNumber(100), - new BigNumber(150), - new BigNumber(200), - new BigNumber(225), - ]; - const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; - const args = [uint8Array, uintTupleArray]; - - - const TEST = method.encode(args, { optimize: true, annotate: true }); - console.log('*'.repeat(50), ' ENCODED DATA ', TEST); - - const optimizedCalldata = method.encode(args, { optimize: true }); - - console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - console.log(JSON.stringify(decodedArgs)); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - - it('Optimizer #4 (Expect no optimization)', async () => { - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi4); - const uint8Array = [ - new BigNumber(100), - new BigNumber(150), - new BigNumber(200), - new BigNumber(225), - ]; - const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; - const args = [uint8Array, uintTupleArray]; - - - const TEST = method.encode(args, { optimize: true, annotate: true }); - console.log('*'.repeat(50), ' ENCODED DATA ', TEST); - - const optimizedCalldata = method.encode(args, { optimize: true }); - - console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - console.log(JSON.stringify(decodedArgs)); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - - it('Crazy ABI', async () => { - const method = new AbiEncoder.Method(AbiSamples.crazyAbi); - console.log(method.getSignature()); - - const someStaticArray = [new BigNumber(127), new BigNumber(14), new BigNumber(54)]; - const someStaticArrayWithDynamicMembers = [ - 'the little piping piper piped a piping pipper papper', - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ]; - const someDynamicArrayWithDynamicMembers = [ - '0x38745637834987324827439287423897238947239847', - '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', - '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', - ]; - const some2DArray = [ - [ - 'some string', - 'some another string', - 'there are just too many stringsup in', - 'here', - 'yall ghonna make me lose my mind', - ], - [ - 'the little piping piper piped a piping pipper papper', - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ], - [], - ]; - const someTuple = { - someUint32: new BigNumber(4037824789), - someStr: 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.' - }; - const someTupleWithDynamicTypes = { - someUint: new BigNumber(4024789), - someStr: 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', - someBytes: '0x29384723894723843743289742389472398473289472348927489274894738427428947389facdea', - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', - }; - const someTupleWithDynamicTypes2 = { - someUint: new BigNumber(9024789), - someStr: 'ksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjkakdhjasjkdhasjkldshdjahdkjsahdaj', - someBytes: '0x29384723894398473289472348927489272384374328974238947274894738427428947389facde1', - someAddress: '0x746dafa5ebde1f4699f4981d3221892e41d24895', - }; - const someTupleWithDynamicTypes3 = { - someUint: new BigNumber(1024789), - someStr: 'sdhsajkdhsajkdhadjkashdjakdhjasjkdhasjkldshdjahdkjsahdajkksadhajkdhsajkdhsadjk', - someBytes: '0x38947238437432829384729742389472398473289472348927489274894738427428947389facdef', - someAddress: '0x89571d322189e415ebde1f4699f498d24246dafa', - }; - const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3]; - - const args = { - someStaticArray: someStaticArray, - someStaticArrayWithDynamicMembers: someStaticArrayWithDynamicMembers, - someDynamicArrayWithDynamicMembers: someDynamicArrayWithDynamicMembers, - some2DArray: some2DArray, - someTuple: someTuple, - someTupleWithDynamicTypes: someTupleWithDynamicTypes, - someArrayOfTuplesWithDynamicTypes: someArrayOfTuplesWithDynamicTypes - }; - - const calldata = method.encode(args); - console.log(calldata); - - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); - console.log(method.getSignature()); - - const expectedCalldata = '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; - //const expectedCalldata = '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata, { structsAsObjects: true }); - const decodedValueJson = JSON.stringify(decodedValue); - console.log(`DECODED`, '*'.repeat(200), '\n', decodedValueJson); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Crazy ABI #1', async () => { - const method = new AbiEncoder.Method(AbiSamples.crazyAbi1); - - const args = [ - new BigNumber(256745454), - new BigNumber(-256745454), - new BigNumber(434244), - '0x43', - '0x0001020304050607080911121314151617181920212223242526272829303132', - '0x0001020304050607080911121314151617181920212223242526272829303132080911121314151617181920212223242526272829303132', - 'Little peter piper piped a piping pepper pot', - '0xe41d2489571d322189246dafa5ebde1f4699f498', - true - ]; - - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - - it('Types with default widths', async () => { - const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); - console.log(method); - const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Array of Static Tuples (Array has defined length)', async () => { - const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDefinedLengthAbi); - - let value = 0; - const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { - arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Array of Static Tuples (Array has dynamic length)', async () => { - const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDynamicLengthAbi); - - let value = 0; - const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { - arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Array of Dynamic Tuples (Array has defined length)', async () => { - const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithDefinedLengthAbi); - - let value = 0; - const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { - arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Array of Dynamic Tuples (Array has dynamic length)', async () => { - const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithUndefinedLengthAbi); - - let value = 0; - const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { - arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Multidimensional Arrays / Static Members', async () => { - const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi); - - // Eight 3-dimensional arrays of uint8[2][2][2] - let value = 0; - const args = []; - for (let i = 0; i < 8; ++i) { - args.push( - [ - [ - [new BigNumber(++value), new BigNumber(++value)], - [new BigNumber(++value), new BigNumber(++value)], - ], - [ - [new BigNumber(++value), new BigNumber(++value)], - [new BigNumber(++value), new BigNumber(++value)], - ] - ] - ); - } - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; expect(calldata).to.be.equal(expectedCalldata); - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Multidimensional Arrays / Dynamic Members', async () => { - const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysDynamicTypeAbi); - - // Eight 3-dimensional arrays of string[2][2][2] - let value = 0; - const args = []; - for (let i = 0; i < 4; ++i) { - args.push( - [ - [ - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - ], - [ - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - ] - ] - ); - } - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Fixed Lenfgth Array / Dynamic Members', async () => { - const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); - const args = [["Brave", "New", "World"]]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); - const expectedCalldata = - '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Fixed Lenfgth Array / Dynamic Members', async () => { - const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); - const args = [["Brave", "New", "World"]]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); - const expectedCalldata = - '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Unfixed Length Array / Dynamic Members ABI', async () => { - const method = new AbiEncoder.Method(AbiSamples.dynamicArrayDynamicMembersAbi); - const args = [["Brave", "New", "World"]]; - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Unfixed Length Array / Static Members ABI', async () => { - const method = new AbiEncoder.Method(AbiSamples.dynamicArrayStaticMembersAbi); - const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; - const calldata = method.encode(args); - const expectedCalldata = '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - - it('Fixed Length Array / Static Members ABI', async () => { - const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); - const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; - const calldata = method.encode(args); - const expectedCalldata = - '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - - it('Simple ABI 2', async () => { - const method = new AbiEncoder.Method(AbiSamples.simpleAbi2); - - const args = [ - '0xaf', // e (bytes1) - '0x0001020304050607080911121314151617181920212223242526272829303132', // f (bytes32) - '0x616161616161616161616161616161616161616161616161616161616161616161616161616161611114f324567838475647382938475677448899338457668899002020202020', // g - 'My first name is Greg and my last name is Hysen, what do ya know!', // h - ]; - - const calldata = method.encode(args); - const expectedCalldata = - '0x7ac2bd96af000000000000000000000000000000000000000000000000000000000000000001020304050607080911121314151617181920212223242526272829303132000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000047616161616161616161616161616161616161616161616161616161616161616161616161616161611114f3245678384756473829384756774488993384576688990020202020200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414d79206669727374206e616d65206973204772656720616e64206d79206c617374206e616d6520697320487973656e2c207768617420646f207961206b6e6f772100000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Array ABI', async () => { - const method = new AbiEncoder.Method(AbiSamples.stringAbi); - console.log(method); - const args = [['five', 'six', 'seven']]; - const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Static Tuple', async () => { - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi); - const args = [[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]; - const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Dynamic Tuple (Array input)', async () => { - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const args = [[new BigNumber(5), 'five']]; - const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - it('Dynamic Tuple (Object input)', async () => { - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - }); - - it.skip('Nested Tuples', async () => { - // Couldn't get nested tuples to work with Remix - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.nestedTuples); - const firstTuple = { - someUint32: new BigNumber(30472), - nestedTuple: { - someUint: new BigNumber('48384725243211555532'), - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' - } - }; - const secondTuple = { - someUint: new BigNumber(2984237422), - someStr: 'This string will exceed 256 bits, so it will spill into the next word of memory.', - nestedTuple: { - someUint32: new BigNumber(23), - secondNestedTuple: { - someUint: new BigNumber(234324), - someStr: 'Im also a short string -- oops I just got loooooooooooooooooonger!', - someBytes: '0x23847287fff3472984723498ff23487324987aaa237438911873429472ba', - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' - } - }, - someBytes: '0x2834y3947289423u489aaaff4783924739847489', - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699afaf', - }; - const thirdTuple = { - 'someUint': new BigNumber(37422), - 'someStr': 'This into the next word of memory. string will exceed 256 bits, so it will spill.', - 'nestedTuple': { - someUint32: new BigNumber(23999222), - 'secondNestedTuple': { - 'someUint': new BigNumber(324), - 'someStr': 'Im also a short st', - 'someBytes': '0x723498ff2348732498723847287fff3472984aaa237438911873429472ba', - 'someAddress': '0x46dafa5ebde1f4699f498e41d2489571d3221892' - } - }, - 'someBytes': '0x947289423u489aaaff472834y383924739847489', - 'someAddress': '0x46dafa5ebde1f46e41d2489571d322189299afaf', - }; - const fourthTuple = { - 'someUint': new BigNumber(222283488822), - 'someStr': 'exceed 256 bits, so it will spill into the. This string will next word of memory.', - 'nestedTuple': { - someUint32: new BigNumber(2300), - 'secondNestedTuple': { - 'someUint': new BigNumber(343224), - 'someStr': 'The alphabet backwards is arguably easier to say if thats the way you learned the first time.', - 'someBytes': '0x87324987aaa23743891187323847287fff3472984723498ff234429472ba', - 'someAddress': '0x71d322189246dafa5ebe41d24895de1f4699f498' - } - }, - 'someBytes': '0x2783924739847488343947289423u489aaaff490', - 'someAddress': '0xebde1d322189246dafa1f4699afafe41d2489575', - }; - const args = [ - [firstTuple], - [secondTuple, thirdTuple, fourthTuple] - ]; - - console.log('*'.repeat(250), method, '*'.repeat(250)); - - - const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - console.log(JSON.stringify(args)); - - console.log(calldata); - const expectedCalldata = '0x'; - expect(calldata).to.be.equal(expectedCalldata); - }); - - it.skip('Object ABI (Object input - Missing Key)', async () => { - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const calldata = method.encode([{ someUint: new BigNumber(5) }]); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - - // @TODO: Figure out how to catch throw - expect(calldata).to.be.equal(expectedCalldata); - }); - - it.skip('Object ABI (Object input - Too Many Keys)', async () => { - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five', unwantedKey: 14 }]); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - - // @TODO: Figure out how to catch throw - expect(calldata).to.be.equal(expectedCalldata); - }); - }); - /* - describe('Array', () => { - it('sample', async () => { - const testDataItem = { name: 'testArray', type: 'int[2]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue([new BigNumber(5), new BigNumber(6)]); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - }); - - it('sample undefined size', async () => { - const testDataItem = { name: 'testArray', type: 'int[]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue([new BigNumber(5), new BigNumber(6)]); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - }); - - it('sample dynamic types', async () => { - const testDataItem = { name: 'testArray', type: 'string[]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue(['five', 'six', 'seven']); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - const calldata = new AbiEncoder.Calldata('0x01020304', 1); - dataType.bind(calldata, AbiEncoder.CalldataSection.PARAMS); - console.log('*'.repeat(60)); - console.log(calldata.getHexValue()); - }); - }); - - describe('Address', () => { - const testAddressDataItem = { name: 'testAddress', type: 'address' }; - it('Valid Address', async () => { - const addressDataType = new AbiEncoder.Address(testAddressDataItem); - addressDataType.assignValue('0xe41d2489571d322189246dafa5ebde1f4699f498'); - const expectedAbiEncodedAddress = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; - - console.log(addressDataType.getHexValue()); - console.log(expectedAbiEncodedAddress); - expect(addressDataType.getHexValue()).to.be.equal(expectedAbiEncodedAddress); - }); - }); - - describe('Bool', () => { - const testBoolDataItem = { name: 'testBool', type: 'bool' }; - it('True', async () => { - const boolDataType = new AbiEncoder.Bool(testBoolDataItem); - boolDataType.assignValue(true); - const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); - }); - - it('False', async () => { - const boolDataType = new AbiEncoder.Bool(testBoolDataItem); - boolDataType.assignValue(false); - const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000000'; - expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); - }); - }); - - describe('Integer', () => { - const testIntDataItem = { name: 'testInt', type: 'int' }; - it('Positive - Base case', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(1)); - const expectedAbiEncodedInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Positive', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(437829473)); - const expectedAbiEncodedInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Negative - Base case', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(-1)); - const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Negative', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(-437829473)); - const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - // TODO: Add bounds tests + tests for different widths - }); - - describe('Unsigned Integer', () => { - const testIntDataItem = { name: 'testUInt', type: 'uint' }; - it('Lower Bound', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(0)); - const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000000'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - it('Base Case', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(1)); - const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - it('Random value', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(437829473)); - const expectedAbiEncodedUInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - // TODO: Add bounds tests + tests for different widths - }); - - describe('Static Bytes', () => { - it('Byte (padded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x05'); - const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it.skip('Byte (no padding)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - - // @TODO: This does not catch the Error - expect(byteDataType.assignValue('0x5')).to.throw(); - }); - - it('Bytes1', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes1' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x05'); - const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it('Bytes32 (padded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x0001020304050607080911121314151617181920212223242526272829303132'); - const expectedAbiEncodedByte = '0x0001020304050607080911121314151617181920212223242526272829303132'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it('Bytes32 (unpadded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x1a18bf61'); - const expectedAbiEncodedByte = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it.skip('Bytes32 - Too long', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - - // @TODO: This does not catch the Error - expect( - byteDataType.assignValue('0x000102030405060708091112131415161718192021222324252627282930313233'), - ).to.throw( - `Tried to assign 0x000102030405060708091112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32`, - ); - }); - }); - - describe('Bytes (Dynamic)', () => { - const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; - it('Less than 32 bytes', async () => { - const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); - bytesDataType.assignValue('0x010203'); - const expectedAbiEncodedBytes = - '0x00000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000'; - - expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); - }); - - it('Greater than 32 bytes', async () => { - const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); - const testValue = '0x' + '61'.repeat(40); - bytesDataType.assignValue(testValue); - const expectedAbiEncodedBytes = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); - }); - - // @TODO: Add test for throw on half-byte - // @TODO: Test with no 0x prefix - // @TODO: Test with Buffer as input - }); - - describe('String', () => { - const testStringDataItem = { name: 'testString', type: 'string' }; - it('Less than 32 bytes', async () => { - const stringDataType = new AbiEncoder.SolString(testStringDataItem); - stringDataType.assignValue('five'); - const expectedAbiEncodedString = - '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - - console.log(stringDataType.getHexValue()); - console.log(expectedAbiEncodedString); - expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); - }); - - it('Greater than 32 bytes', async () => { - const stringDataType = new AbiEncoder.SolString(testStringDataItem); - const testValue = 'a'.repeat(40); - stringDataType.assignValue(testValue); - const expectedAbiEncodedString = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); - }); - });*/ -}); diff --git a/packages/order-utils/test/abi_samples.ts b/packages/order-utils/test/abi_samples.ts deleted file mode 100644 index 5e8268f1a..000000000 --- a/packages/order-utils/test/abi_samples.ts +++ /dev/null @@ -1,814 +0,0 @@ -import { MethodAbi } from 'ethereum-types'; - -export const simpleAbi = { - constant: false, - inputs: [ - { - name: 'greg', - type: 'uint256', - }, - { - name: 'gregStr', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const stringAbi = { - constant: false, - inputs: [ - { - name: 'greg', - type: 'string[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const optimizerAbi2 = { - constant: false, - inputs: [ - { - name: 'stringArray', - type: 'string[]', - }, - { - name: 'string', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const optimizerAbi3 = { - constant: false, - inputs: [ - { - name: 'uint8Array', - type: 'uint8[]', - }, - { - components: [ - { - name: 'uint', - type: 'uint', - }, - ], - name: 'uintTuple', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const optimizerAbi4 = { - constant: false, - inputs: [ - { - name: 'uint8Array', - type: 'uint8[4]', - }, - { - components: [ - { - name: 'uint', - type: 'uint', - }, - ], - name: 'uintTuple', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const typesWithDefaultWidthsAbi = { - constant: false, - inputs: [ - { - name: 'someUint', - type: 'uint', - }, - { - name: 'someInt', - type: 'int', - }, - { - name: 'someByte', - type: 'byte', - }, - { - name: 'someUint', - type: 'uint[]', - }, - { - name: 'someInt', - type: 'int[]', - }, - { - name: 'someByte', - type: 'byte[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const multiDimensionalArraysStaticTypeAbi = { - constant: false, - inputs: [ - { - name: 'a', - type: 'uint8[][][]', - }, - { - name: 'b', - type: 'uint8[][][2]', - }, - { - name: 'c', - type: 'uint8[][2][]', - }, - { - name: 'd', - type: 'uint8[2][][]', - }, - { - name: 'e', - type: 'uint8[][2][2]', - }, - { - name: 'f', - type: 'uint8[2][2][]', - }, - { - name: 'g', - type: 'uint8[2][][2]', - }, - { - name: 'h', - type: 'uint8[2][2][2]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const multiDimensionalArraysDynamicTypeAbi = { - constant: false, - inputs: [ - { - name: 'a', - type: 'string[][][]', - }, - { - name: 'b', - type: 'string[][][2]', - }, - { - name: 'c', - type: 'string[][2][]', - }, - { - name: 'h', - type: 'string[2][2][2]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const dynamicTupleAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - ], - name: 'order', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const arrayOfStaticTuplesWithDefinedLengthAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someUint2', - type: 'uint256', - }, - ], - name: 'order', - type: 'tuple[8]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const arrayOfStaticTuplesWithDynamicLengthAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someUint2', - type: 'uint256', - }, - ], - name: 'order', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const arrayOfDynamicTuplesWithDefinedLengthAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[8]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const arrayOfDynamicTuplesWithUndefinedLengthAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const arrayOfDynamicTuplesAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const multidimensionalArrayOfDynamicTuplesAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[][2][]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const staticTupleAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint1', - type: 'uint256', - }, - { - name: 'someUint2', - type: 'uint256', - }, - { - name: 'someUint3', - type: 'uint256', - }, - { - name: 'someBool', - type: 'bool', - }, - ], - name: 'order', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const staticArrayAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'uint8[3]', - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const staticArrayDynamicMembersAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'string[3]', - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const dynamicArrayDynamicMembersAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'string[]', - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const dynamicArrayStaticMembersAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'uint8[]', - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const crazyAbi1 = { - constant: false, - inputs: [ - { - name: 'someUInt256', - type: 'uint256', - }, - { - name: 'someInt256', - type: 'int256', - }, - { - name: 'someInt32', - type: 'int32', - }, - { - name: 'someByte', - type: 'byte', - }, - { - name: 'someBytes32', - type: 'bytes32', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someString', - type: 'string', - }, - { - name: 'someAddress', - type: 'address', - }, - { - name: 'someBool', - type: 'bool', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const crazyAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'uint8[3]', - }, - { - name: 'someStaticArrayWithDynamicMembers', - type: 'string[2]', - }, - { - name: 'someDynamicArrayWithDynamicMembers', - type: 'bytes[]', - }, - { - name: 'some2DArray', - type: 'string[][]', - }, - { - name: 'someTuple', - type: 'tuple', - components: [ - { - name: 'someUint32', - type: 'uint32', - }, - { - name: 'someStr', - type: 'string', - }, - ], - }, - { - name: 'someTupleWithDynamicTypes', - type: 'tuple', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - /*{ - name: 'someStrArray', - type: 'string[]', - },*/ - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - }, - { - name: 'someArrayOfTuplesWithDynamicTypes', - type: 'tuple[]', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - /*{ - name: 'someStrArray', - type: 'string[]', - },*/ - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const nestedTuples = { - constant: false, - inputs: [ - { - name: 'firstTuple', - type: 'tuple[1]', - components: [ - { - name: 'someUint32', - type: 'uint32', - }, - { - name: 'nestedTuple', - type: 'tuple', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - }, - ], - }, - { - name: 'secondTuple', - type: 'tuple[]', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - { - name: 'nestedTuple', - type: 'tuple', - components: [ - { - name: 'someUint32', - type: 'uint32', - }, - { - name: 'secondNestedTuple', - type: 'tuple', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - }, - ], - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - } - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const simpleAbi2 = { - constant: false, - inputs: [ - { - name: 'someByte', - type: 'byte', - }, - { - name: 'someBytes32', - type: 'bytes32', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const fillOrderAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'makerAddress', - type: 'address', - }, - { - name: 'takerAddress', - type: 'address', - }, - { - name: 'feeRecipientAddress', - type: 'address', - }, - { - name: 'senderAddress', - type: 'address', - }, - { - name: 'makerAssetAmount', - type: 'uint256', - }, - { - name: 'takerAssetAmount', - type: 'uint256', - }, - { - name: 'makerFee', - type: 'uint256', - }, - { - name: 'takerFee', - type: 'uint256', - }, - { - name: 'expirationTimeSeconds', - type: 'uint256', - }, - { - name: 'salt', - type: 'uint256', - }, - { - name: 'makerAssetData', - type: 'bytes', - }, - { - name: 'takerAssetData', - type: 'bytes', - }, - ], - name: 'order', - type: 'tuple', - }, - { - name: 'takerAssetFillAmount', - type: 'uint256', - }, - { - name: 'salt', - type: 'uint256', - }, - { - name: 'orderSignature', - type: 'bytes', - }, - { - name: 'takerSignature', - type: 'bytes', - }, - ], - name: 'fillOrder', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; diff --git a/packages/utils/src/abi_encoder/calldata.ts b/packages/utils/src/abi_encoder/calldata.ts new file mode 100644 index 000000000..32278e5c5 --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata.ts @@ -0,0 +1,520 @@ +import ethUtil = require('ethereumjs-util'); +import CommunicationChatBubbleOutline from 'material-ui/SvgIcon'; +var _ = require('lodash'); + +export interface DecodingRules { + structsAsObjects: boolean; +} + +export interface EncodingRules { + optimize?: boolean; + annotate?: boolean; +} + +export abstract class CalldataBlock { + private name: string; + private signature: string; + private offsetInBytes: number; + private headerSizeInBytes: number; + private bodySizeInBytes: number; + private relocatable: boolean; + private parentName: string; + + constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { + this.name = name; + this.signature = signature; + this.parentName = parentName; + this.offsetInBytes = 0; + this.headerSizeInBytes = headerSizeInBytes; + this.bodySizeInBytes = bodySizeInBytes; + this.relocatable = relocatable; + } + + protected setHeaderSize(headerSizeInBytes: number) { + this.headerSizeInBytes = headerSizeInBytes; + } + + protected setBodySize(bodySizeInBytes: number) { + this.bodySizeInBytes = bodySizeInBytes; + } + + protected setName(name: string) { + this.name = name; + } + + public getName(): string { + return this.name; + } + + public getParentName(): string { + return this.parentName; + } + + public getSignature(): string { + return this.signature; + } + + public isRelocatable(): boolean { + return this.relocatable; + } + + public getHeaderSizeInBytes(): number { + return this.headerSizeInBytes; + } + + public getBodySizeInBytes(): number { + return this.bodySizeInBytes; + } + + public getSizeInBytes(): number { + return this.headerSizeInBytes + this.bodySizeInBytes; + } + + public getOffsetInBytes(): number { + return this.offsetInBytes; + } + + public setOffset(offsetInBytes: number) { + this.offsetInBytes = offsetInBytes; + } + + public computeHash(): Buffer { + const rawData = this.getRawData(); + const hash = ethUtil.sha3(rawData); + return hash; + } + + public abstract toBuffer(): Buffer; + public abstract getRawData(): Buffer; +} + +export class PayloadCalldataBlock extends CalldataBlock { + private payload: Buffer; + + constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { + const headerSizeInBytes = 0; + const bodySizeInBytes = payload.byteLength; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); + this.payload = payload; + } + + public toBuffer(): Buffer { + return this.payload; + } + + public getRawData(): Buffer { + return this.payload; + } +} + +export class DependentCalldataBlock extends CalldataBlock { + public static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + public static RAW_DATA_START = new Buffer('<'); + public static RAW_DATA_END = new Buffer('>'); + private parent: CalldataBlock; + private dependency: CalldataBlock; + private aliasFor: CalldataBlock | undefined; + + constructor(name: string, signature: string, parentName: string, relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { + const headerSizeInBytes = 0; + const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); + this.parent = parent; + this.dependency = dependency; + this.aliasFor = undefined; + } + + public toBuffer(): Buffer { + const destinationOffset = (this.aliasFor !== undefined) ? this.aliasFor.getOffsetInBytes() : this.dependency.getOffsetInBytes(); + const parentOffset = this.parent.getOffsetInBytes(); + const parentHeaderSize = this.parent.getHeaderSizeInBytes(); + const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); + const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(16)}`); + const evmWordWidthInBytes = 32; + const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); + return pointerBufPadded; + } + + public getDependency(): CalldataBlock { + return this.dependency; + } + + public setAlias(block: CalldataBlock) { + this.aliasFor = block; + this.setName(`${this.getName()} (alias for ${block.getName()})`); + } + + public getAlias(): CalldataBlock | undefined { + return this.aliasFor; + } + + public getRawData(): Buffer { + const dependencyRawData = this.dependency.getRawData(); + const rawDataComponents: Buffer[] = []; + rawDataComponents.push(DependentCalldataBlock.RAW_DATA_START); + rawDataComponents.push(dependencyRawData); + rawDataComponents.push(DependentCalldataBlock.RAW_DATA_END); + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } +} + +export class MemberCalldataBlock extends CalldataBlock { + private static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + private header: Buffer | undefined; + private members: CalldataBlock[]; + private contiguous: boolean; + + constructor(name: string, signature: string, parentName: string, relocatable: boolean, contiguous: boolean) { + super(name, signature, parentName, 0, 0, relocatable); + this.members = []; + this.header = undefined; + this.contiguous = contiguous; + } + + public getRawData(): Buffer { + const rawDataComponents: Buffer[] = []; + if (this.header !== undefined) { + rawDataComponents.push(this.header); + } + _.each(this.members, (member: CalldataBlock) => { + const memberBuffer = member.getRawData(); + rawDataComponents.push(memberBuffer); + }); + + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } + + public setMembers(members: CalldataBlock[]) { + let bodySizeInBytes = 0; + _.each(members, (member: CalldataBlock) => { + bodySizeInBytes += member.getSizeInBytes(); + }); + this.members = members; + this.setBodySize(0); + } + + public isContiguous(): boolean { + return true; + } + + public setHeader(header: Buffer) { + this.setHeaderSize(header.byteLength); + this.header = header; + } + + public toBuffer(): Buffer { + if (this.header !== undefined) return this.header; + return new Buffer(''); + } + + public getMembers(): CalldataBlock[] { + return this.members; + } +} + +class Queue { + private store: T[] = []; + push(val: T) { + this.store.push(val); + } + pushFront(val: T) { + this.store.unshift(val); + } + pop(): T | undefined { + return this.store.shift(); + } + popBack(): T | undefined { + if (this.store.length === 0) return undefined; + const backElement = this.store.splice(-1, 1)[0]; + return backElement; + } + merge(q: Queue) { + this.store = this.store.concat(q.store); + } + mergeFront(q: Queue) { + this.store = q.store.concat(this.store); + } + getStore(): T[] { + return this.store; + } + peek(): T | undefined { + return this.store.length >= 0 ? this.store[0] : undefined; + } +} + +export class Calldata { + private selector: string; + private rules: EncodingRules; + private sizeInBytes: number; + private root: MemberCalldataBlock | undefined; + + constructor(rules: EncodingRules) { + this.selector = ''; + this.rules = rules; + this.sizeInBytes = 0; + this.root = undefined; + } + + private createQueue(memberBlock: MemberCalldataBlock): Queue { + const blockQueue = new Queue(); + _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { + if (member instanceof MemberCalldataBlock) { + blockQueue.mergeFront(this.createQueue(member)); + } else { + blockQueue.pushFront(member); + } + }); + + // Children + _.each(memberBlock.getMembers(), (member: CalldataBlock) => { + if (member instanceof DependentCalldataBlock && member.getAlias() === undefined) { + let dependency = member.getDependency(); + if (dependency instanceof MemberCalldataBlock) { + blockQueue.merge(this.createQueue(dependency)); + } else { + blockQueue.push(dependency); + } + } + }); + + blockQueue.pushFront(memberBlock); + return blockQueue; + } + + private generateAnnotatedHexString(): string { + let hexValue = `${this.selector}`; + if (this.root === undefined) { + throw new Error('expected root'); + } + + const valueQueue = this.createQueue(this.root); + + let block: CalldataBlock | undefined; + let offset = 0; + const functionBlock = valueQueue.peek(); + let functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); + while ((block = valueQueue.pop()) !== undefined) { + // Set f + + // Process each block 1 word at a time + const size = block.getSizeInBytes(); + const name = block.getName(); + const parentName = block.getParentName(); + + //const ancestrialNamesOffset = name.startsWith('ptr<') ? 4 : 0; + //const parentOffset = name.lastIndexOf(parentName); + const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, '');//.replace(`${parentName}[`, '['); + const signature = block.getSignature(); + + // Current offset + let offsetStr = ''; + + // If this block is empty then it's a newline + let value = ''; + let nameStr = ''; + let line = ''; + if (size === 0) { + offsetStr = ' '.repeat(10); + value = ' '.repeat(74); + nameStr = `### ${prettyName.padEnd(80)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + offsetStr = `0x${offset.toString(16)}`.padEnd(10, ' '); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(0, 32))).padEnd(74); + if (block instanceof MemberCalldataBlock) { + nameStr = `### ${prettyName.padEnd(80)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + nameStr = ` ${prettyName.padEnd(80)}`; + line = `${offsetStr}${value}${nameStr}`; + } + } + + for (let j = 32; j < size; j += 32) { + offsetStr = `0x${(offset + j).toString(16)}`.padEnd(10, ' '); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(j, j + 32))).padEnd(74); + nameStr = ' '.repeat(40); + + line = `${line}\n${offsetStr}${value}${nameStr}`; + } + + // Append to hex value + hexValue = `${hexValue}\n${line}`; + offset += size; + } + + return hexValue; + } + + private generateCondensedHexString(): string { + let selectorBuffer = ethUtil.toBuffer(this.selector); + if (this.root === undefined) { + throw new Error('expected root'); + } + + const valueQueue = this.createQueue(this.root); + const valueBufs: Buffer[] = [selectorBuffer]; + let block: CalldataBlock | undefined; + while ((block = valueQueue.pop()) !== undefined) { + valueBufs.push(block.toBuffer()); + } + + const combinedBuffers = Buffer.concat(valueBufs); + const hexValue = ethUtil.bufferToHex(combinedBuffers); + return hexValue; + } + + public optimize() { + if (this.root === undefined) { + throw new Error('expected root'); + } + + const blocksByHash: { [key: string]: CalldataBlock } = {}; + + // 1. Create a queue of subtrees by hash + // Note that they are ordered the same as + const subtreeQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; + while ((block = subtreeQueue.popBack()) !== undefined) { + if (block instanceof DependentCalldataBlock) { + const blockHashBuf = block.getDependency().computeHash(); + const blockHash = ethUtil.bufferToHex(blockHashBuf); + if (blockHash in blocksByHash) { + const blockWithSameHash = blocksByHash[blockHash]; + if (blockWithSameHash !== block.getDependency()) { + block.setAlias(blockWithSameHash); + } + } + continue; + } + + const blockHashBuf = block.computeHash(); + const blockHash = ethUtil.bufferToHex(blockHashBuf); + if (blockHash in blocksByHash === false) { + blocksByHash[blockHash] = block; + } + } + } + + public toHexString(): string { + if (this.root === undefined) { + throw new Error('expected root'); + } + + if (this.rules.optimize) this.optimize(); + + const offsetQueue = this.createQueue(this.root); + let block: CalldataBlock | undefined; + let offset = 0; + while ((block = offsetQueue.pop()) !== undefined) { + block.setOffset(offset); + offset += block.getSizeInBytes(); + } + + const hexValue = this.rules.annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); + return hexValue; + } + + public getSelectorHex(): string { + return this.selector; + } + + public getSizeInBytes(): number { + return this.sizeInBytes; + } + + public toAnnotatedString(): string { + return ""; + } + + public setRoot(block: MemberCalldataBlock) { + this.root = block; + this.sizeInBytes += block.getSizeInBytes(); + } + + public setSelector(selector: string) { + // Ensure we have a 0x prefix + if (selector.startsWith('0x')) { + this.selector = selector; + } else { + this.selector = `$0x${selector}`; + } + + // The selector must be 10 characters: '0x' followed by 4 bytes (two hex chars per byte) + if (this.selector.length !== 10) { + throw new Error(`Invalid selector '${this.selector}'`); + } + this.sizeInBytes += 8; + } +} + +export class RawCalldata { + private value: Buffer; + private offset: number; // tracks current offset into raw calldata; used for parsing + private selector: string; + private scopes: Queue; + + constructor(value: string | Buffer) { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Expected raw calldata to start with '0x'`); + } + const valueBuf = ethUtil.toBuffer(value); + this.selector = ethUtil.bufferToHex(valueBuf.slice(0, 4)); + this.value = valueBuf.slice(4); // disregard selector + this.offset = 0; + this.scopes = new Queue(); + this.scopes.push(0); + } + + public popBytes(lengthInBytes: number): Buffer { + const value = this.value.slice(this.offset, this.offset + lengthInBytes); + this.setOffset(this.offset + lengthInBytes); + return value; + } + + public popWord(): Buffer { + const wordInBytes = 32; + return this.popBytes(wordInBytes); + } + + public popWords(length: number): Buffer { + const wordInBytes = 32; + return this.popBytes(length * wordInBytes); + } + + public readBytes(from: number, to: number): Buffer { + const value = this.value.slice(from, to); + return value; + } + + public setOffset(offsetInBytes: number) { + this.offset = offsetInBytes; + } + + public startScope() { + this.scopes.pushFront(this.offset); + } + + public endScope() { + this.scopes.pop(); + } + + public getOffset(): number { + return this.offset; + } + + public toAbsoluteOffset(relativeOffset: number) { + const scopeOffset = this.scopes.peek(); + if (scopeOffset === undefined) { + throw new Error(`Tried to access undefined scope.`); + } + const absoluteOffset = relativeOffset + scopeOffset; + return absoluteOffset; + } + + public getSelector(): string { + return this.selector; + } +} \ No newline at end of file diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts new file mode 100644 index 000000000..3b4028abd --- /dev/null +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -0,0 +1,322 @@ +import { RawCalldata, Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata"; +import { MethodAbi, DataItem } from 'ethereum-types'; +import { DecodingRules, EncodingRules } from './calldata'; +import { BigNumber } from '../configured_bignumber'; +import ethUtil = require('ethereumjs-util'); +var _ = require('lodash'); + +export interface DataTypeFactory { + create: (dataItem: DataItem, parentDataType: DataType) => DataType; + mapDataItemToDataType: (dataItem: DataItem) => DataType; +} + +export abstract class DataType { + private static DEFAULT_ENCODING_RULES = { optimize: false, annotate: false } as EncodingRules; + private static DEFAULT_DECODING_RULES = { structsAsObjects: false } as DecodingRules; + + private dataItem: DataItem; + private factory: DataTypeFactory; + + constructor(dataItem: DataItem, factory: DataTypeFactory) { + this.dataItem = dataItem; + this.factory = factory; + } + + public getDataItem(): DataItem { + return this.dataItem; + } + + public getFactory(): DataTypeFactory { + return this.factory; + } + + public encode(value: any, rules?: EncodingRules, selector?: string): string { + const rules_ = rules ? rules : DataType.DEFAULT_ENCODING_RULES; + const calldata = new Calldata(rules_); + if (selector) calldata.setSelector(selector); + const block = this.generateCalldataBlock(value); + calldata.setRoot(block as MemberCalldataBlock); // @TODO CHANGE + const calldataHex = calldata.toHexString(); + return calldataHex; + } + + public decode(calldata: string, rules?: DecodingRules): any { + const rawCalldata = new RawCalldata(calldata); + const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; + const value = this.generateValue(rawCalldata, rules_); + return value; + } + + public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; + public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any; + public abstract getSignature(): string; + public abstract isStatic(): boolean; +} + +export abstract class PayloadDataType extends DataType { + protected hasConstantSize: boolean; + + public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { + super(dataItem, factory); + this.hasConstantSize = hasConstantSize; + } + + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { + const encodedValue = this.encodeValue(value); + const name = this.getDataItem().name; + const signature = this.getSignature(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const relocatable = false; + const block = new PayloadCalldataBlock(name, signature, parentName, /*offsetInBytes,*/ relocatable, encodedValue); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { + const value = this.decodeValue(calldata); + return value; + } + + public isStatic(): boolean { + // If a payload has a constant size then it's static + return this.hasConstantSize; + } + + public abstract encodeValue(value: any): Buffer; + public abstract decodeValue(calldata: RawCalldata): any; +} + +export abstract class DependentDataType extends DataType { + protected dependency: DataType; + protected parent: DataType; + + public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { + super(dataItem, factory); + this.dependency = dependency; + this.parent = parent; + } + + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { + if (parentBlock === undefined) { + throw new Error(`DependentDataType requires a parent block to generate its block`); + } + const dependencyBlock = this.dependency.generateCalldataBlock(value, parentBlock); + const name = this.getDataItem().name; + const signature = this.getSignature(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const relocatable = false; + const block = new DependentCalldataBlock(name, signature, parentName, relocatable, dependencyBlock, parentBlock); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { + const destinationOffsetBuf = calldata.popWord(); + const currentOffset = calldata.getOffset(); + const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), 16); + const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); + calldata.setOffset(destinationOffsetAbsolute); + const value = this.dependency.generateValue(calldata, rules); + calldata.setOffset(currentOffset); + return value; + } + + public isStatic(): boolean { + return true; + } +} + +export interface MemberMap { + [key: string]: number; +} + +export abstract class MemberDataType extends DataType { + private memberMap: MemberMap; + private members: DataType[]; + private isArray: boolean; + protected arrayLength: number | undefined; + protected arrayElementType: string | undefined; + + + public constructor(dataItem: DataItem, factory: DataTypeFactory, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { + super(dataItem, factory); + this.memberMap = {}; + this.members = []; + this.isArray = isArray; + this.arrayLength = arrayLength; + this.arrayElementType = arrayElementType; + if (isArray && arrayLength !== undefined) { + [this.members, this.memberMap] = this.createMembersWithLength(dataItem, arrayLength); + } else if (!isArray) { + [this.members, this.memberMap] = this.createMembersWithKeys(dataItem); + } + } + + private createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { + // Sanity check + if (dataItem.components === undefined) { + throw new Error(`Expected components`); + } + + let members: DataType[] = []; + let memberMap: MemberMap = {}; + _.each(dataItem.components, (memberItem: DataItem) => { + const childDataItem = { + type: memberItem.type, + name: `${dataItem.name}.${memberItem.name}`, + } as DataItem; + const components = memberItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[memberItem.name] = members.length; + members.push(child); + }); + + return [members, memberMap]; + } + + private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { + let members: DataType[] = []; + let memberMap: MemberMap = {}; + const range = _.range(length); + _.each(range, (idx: number) => { + const childDataItem = { + type: this.arrayElementType, + name: `${dataItem.name}[${idx.toString(10)}]`, + } as DataItem; + const components = dataItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[idx.toString(10)] = members.length; + members.push(child); + }); + + return [members, memberMap]; + } + + protected generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { + // Sanity check length + if (this.arrayLength !== undefined && value.length !== this.arrayLength) { + throw new Error( + `Expected array of ${JSON.stringify( + this.arrayLength, + )} elements, but got array of length ${JSON.stringify(value.length)}`, + ); + } + + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); + + let members = this.members; + if (this.isArray && this.arrayLength === undefined) { + [members,] = this.createMembersWithLength(this.getDataItem(), value.length); + + const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(16)}`), 32); + methodBlock.setHeader(lenBuf); + } + + const memberBlocks: CalldataBlock[] = []; + _.each(members, (member: DataType, idx: number) => { + const block = member.generateCalldataBlock(value[idx], methodBlock); + memberBlocks.push(block); + }); + methodBlock.setMembers(memberBlocks); + return methodBlock; + } + + protected generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); + const memberBlocks: CalldataBlock[] = []; + let childMap = _.cloneDeep(this.memberMap); + _.forOwn(obj, (value: any, key: string) => { + if (key in childMap === false) { + throw new Error(`Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`); + } + const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock); + memberBlocks.push(block); + delete childMap[key]; + }); + + if (Object.keys(childMap).length !== 0) { + throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); + } + + methodBlock.setMembers(memberBlocks); + return methodBlock; + } + + public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value, parentBlock) : this.generateCalldataBlockFromObject(value, parentBlock); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { + let members = this.members; + if (this.isArray && this.arrayLength === undefined) { + const arrayLengthBuf = calldata.popWord(); + const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); + const hexBase = 16; + const arrayLength = new BigNumber(arrayLengthHex, hexBase); + + [members,] = this.createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); + } + + calldata.startScope(); + let value: any[] | object; + if (rules.structsAsObjects && !this.isArray) { + value = {}; + _.each(this.memberMap, (idx: number, key: string) => { + const member = this.members[idx]; + let memberValue = member.generateValue(calldata, rules); + (value as { [key: string]: any })[key] = memberValue; + }); + } else { + value = []; + _.each(members, (member: DataType, idx: number) => { + let memberValue = member.generateValue(calldata, rules); + (value as any[]).push(memberValue); + }); + } + calldata.endScope(); + return value; + } + + protected computeSignatureOfMembers(): string { + // Compute signature of members + let signature = `(`; + _.each(this.members, (member: DataType, i: number) => { + signature += member.getSignature(); + if (i < this.members.length - 1) { + signature += ','; + } + }); + signature += ')'; + return signature; + } + + public isStatic(): boolean { + /* For Tuple: + const isStaticTuple = this.children.length === 0; + return isStaticTuple; // @TODO: True in every case or only when dynamic data? + + For Array: + if isLengthDefined = false then this is false + + Otherwise if the first element is a Pointer then false + */ + + if (this.isArray && this.arrayLength === undefined) { + return false; + } + + // Search for dependent members + const dependentMember = _.find(this.members, (member: DataType) => { + return (member instanceof DependentDataType); + }); + const isStatic = (dependentMember === undefined); // static if we couldn't find a dependent member + return isStatic; + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts new file mode 100644 index 000000000..2973596fe --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -0,0 +1,550 @@ +import { DataType, DataTypeFactory, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; + +import { DecodingRules, EncodingRules } from './calldata'; + +import { MethodAbi, DataItem } from 'ethereum-types'; + +import ethUtil = require('ethereumjs-util'); + +import { Calldata, RawCalldata } from './calldata'; + +import { BigNumber } from '../configured_bignumber'; + +var _ = require('lodash'); + +export interface DataTypeStaticInterface { + matchGrammar: (type: string) => boolean; + encodeValue: (value: any) => Buffer; + decodeValue: (rawCalldata: RawCalldata) => any; +} + +export class Address extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Address.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Address.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); + } + } + + public getSignature(): string { + return 'address'; + } + + public static matchGrammar(type: string): boolean { + return type === 'address'; + } + + public encodeValue(value: boolean): Buffer { + const evmWordWidth = 32; + const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const paddedValueBuf = calldata.popWord(); + const valueBuf = paddedValueBuf.slice(12); + const value = ethUtil.bufferToHex(valueBuf); + return value; + } +} + +export class Bool extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Bool.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Bool.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); + } + } + + public getSignature(): string { + return 'bool'; + } + + public static matchGrammar(type: string): boolean { + return type === 'bool'; + } + + public encodeValue(value: boolean): Buffer { + const evmWordWidth = 32; + const encodedValue = value === true ? '0x1' : '0x0'; + const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): boolean { + const valueBuf = calldata.popWord(); + const valueHex = ethUtil.bufferToHex(valueBuf); + const valueNumber = new BigNumber(valueHex, 16); + let value: boolean = (valueNumber.equals(0)) ? false : true; + if (!(valueNumber.equals(0) || valueNumber.equals(1))) { + throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); + } + return value; + } +} + +abstract class Number extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + static MAX_WIDTH: number = 256; + static DEFAULT_WIDTH: number = Number.MAX_WIDTH; + width: number = Number.DEFAULT_WIDTH; + + constructor(dataItem: DataItem, matcher: RegExp) { + super(dataItem, EvmDataTypeFactory.getInstance(), Number.SIZE_KNOWN_AT_COMPILE_TIME); + const matches = matcher.exec(dataItem.type); + if (matches === null) { + throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); + } + if (matches !== null && matches.length === 2 && matches[1] !== undefined) { + this.width = parseInt(matches[1]); + } else { + this.width = 256; + } + } + + public encodeValue(value: BigNumber): Buffer { + if (value.greaterThan(this.getMaxValue())) { + throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; + } else if (value.lessThan(this.getMinValue())) { + throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; + } + + const hexBase = 16; + const evmWordWidth = 32; + let valueBuf: Buffer; + if (value.greaterThanOrEqualTo(0)) { + valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); + } else { + // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. + // Step 1/3: Convert value to positive binary string + const binBase = 2; + const valueBin = value.times(-1).toString(binBase); + + // Step 2/3: Invert binary value + const bitsInEvmWord = 256; + let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, binBase); + + // Step 3/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const negativeValue = invertedValue.plus(1); + + // Convert the negated value to a hex string + valueBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), + evmWordWidth, + ); + } + + return valueBuf; + } + + public decodeValue(calldata: RawCalldata): BigNumber { + const paddedValueBuf = calldata.popWord(); + const paddedValueHex = ethUtil.bufferToHex(paddedValueBuf); + let value = new BigNumber(paddedValueHex, 16); + if (this instanceof Int) { + // Check if we're negative + const binBase = 2; + const paddedValueBin = value.toString(binBase); + const valueBin = paddedValueBin.slice(paddedValueBin.length - this.width); + if (valueBin[0].startsWith('1')) { + // Negative + // Step 1/3: Invert binary value + let invertedValueBin = ''; + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, binBase); + + // Step 2/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const positiveValue = invertedValue.plus(1); + + // Step 3/3: Invert positive value + const negativeValue = positiveValue.times(-1); + value = negativeValue; + } + } + + return value; + } + + public abstract getMaxValue(): BigNumber; + public abstract getMinValue(): BigNumber; +} + +export class Int extends Number { + static matcher = RegExp( + '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + constructor(dataItem: DataItem) { + super(dataItem, Int.matcher); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(2).toPower(this.width - 1).times(-1); + } + + public getSignature(): string { + return `int${this.width}`; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class UInt extends Number { + static matcher = RegExp( + '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + constructor(dataItem: DataItem) { + super(dataItem, UInt.matcher); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this.width).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(0); + } + + public getSignature(): string { + return `uint${this.width}`; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Byte extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + static matcher = RegExp( + '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', + ); + + static DEFAULT_WIDTH = 1; + width: number = Byte.DEFAULT_WIDTH; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Byte.SIZE_KNOWN_AT_COMPILE_TIME); + const matches = Byte.matcher.exec(dataItem.type); + if (!Byte.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); + } + if (matches !== null && matches.length === 3 && matches[2] !== undefined) { + this.width = parseInt(matches[2]); + } else { + this.width = Byte.DEFAULT_WIDTH; + } + } + + public getSignature(): string { + // Note that `byte` reduces to `bytes1` + return `bytes${this.width}`; + } + + public encodeValue(value: string | Buffer): Buffer { + // Convert value into a buffer and do bounds checking + const valueBuf = ethUtil.toBuffer(value); + if (valueBuf.byteLength > this.width) { + throw new Error( + `Tried to assign ${value} (${ + valueBuf.byteLength + } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, + ); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + // Store value as hex + const evmWordWidth = 32; + const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); + return paddedValue; + } + + public decodeValue(calldata: RawCalldata): string { + const paddedValueBuf = calldata.popWord(); + const valueBuf = paddedValueBuf.slice(0, this.width); + const value = ethUtil.bufferToHex(valueBuf); + return value; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Bytes extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; + static UNDEFINED_LENGTH = new BigNumber(-1); + length: BigNumber = Bytes.UNDEFINED_LENGTH; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Bytes.SIZE_KNOWN_AT_COMPILE_TIME); + if (!Bytes.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Bytes with bad input: ${dataItem}`); + } + } + + public encodeValue(value: string | Buffer): Buffer { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); + } + const valueBuf = ethUtil.toBuffer(value); + if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + const wordsForValue = Math.ceil(valueBuf.byteLength / 32); + const paddedBytesForValue = wordsForValue * 32; + const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedBytesForValue); + const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); + const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const lengthBuf = calldata.popWord(); + const lengthHex = ethUtil.bufferToHex(lengthBuf); + const length = parseInt(lengthHex, 16); + const wordsForValue = Math.ceil(length / 32); + const paddedValueBuf = calldata.popWords(wordsForValue); + const valueBuf = paddedValueBuf.slice(0, length); + const decodedValue = ethUtil.bufferToHex(valueBuf); + return decodedValue; + } + + public getSignature(): string { + return 'bytes'; + } + + public static matchGrammar(type: string): boolean { + return type === 'bytes'; + } +} + +export class SolString extends PayloadDataType { + private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), SolString.SIZE_KNOWN_AT_COMPILE_TIME); + if (!SolString.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); + } + } + + public encodeValue(value: string): Buffer { + const wordsForValue = Math.ceil(value.length / 32); + const paddedBytesForValue = wordsForValue * 32; + const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); + const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); + const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const lengthBuf = calldata.popWord(); + const lengthHex = ethUtil.bufferToHex(lengthBuf); + const length = parseInt(lengthHex, 16); + const wordsForValue = Math.ceil(length / 32); + const paddedValueBuf = calldata.popWords(wordsForValue); + const valueBuf = paddedValueBuf.slice(0, length); + const value = valueBuf.toString('ascii'); + return value; + } + + public getSignature(): string { + return 'string'; + } + + public static matchGrammar(type: string): boolean { + return type === 'string'; + } +} + +export class Pointer extends DependentDataType { + + constructor(destDataType: DataType, parentDataType: DataType) { + const destDataItem = destDataType.getDataItem(); + const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; + super(dataItem, EvmDataTypeFactory.getInstance(), destDataType, parentDataType); + } + + public getSignature(): string { + return this.dependency.getSignature(); + } +} + +export class Tuple extends MemberDataType { + private tupleSignature: string; + + constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + if (!Tuple.matchGrammar(dataItem.type)) { + throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); + } + this.tupleSignature = this.computeSignatureOfMembers(); + } + + public getSignature(): string { + return this.tupleSignature; + } + + public static matchGrammar(type: string): boolean { + return type === 'tuple'; + } +} + +export class SolArray extends MemberDataType { + static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); + private arraySignature: string; + private elementType: string; + + constructor(dataItem: DataItem) { + // Sanity check + const matches = SolArray.matcher.exec(dataItem.type); + if (matches === null || matches.length !== 3) { + throw new Error(`Could not parse array: ${dataItem.type}`); + } else if (matches[1] === undefined) { + throw new Error(`Could not parse array type: ${dataItem.type}`); + } else if (matches[2] === undefined) { + throw new Error(`Could not parse array length: ${dataItem.type}`); + } + + const isArray = true; + const arrayElementType = matches[1]; + const arrayLength = (matches[2] === '') ? undefined : parseInt(matches[2], 10); + super(dataItem, EvmDataTypeFactory.getInstance(), isArray, arrayLength, arrayElementType); + this.elementType = arrayElementType; + this.arraySignature = this.computeSignature(); + } + + private computeSignature(): string { + let dataItem = { + type: this.elementType, + name: 'N/A', + } as DataItem; + const components = this.getDataItem().components; + if (components !== undefined) { + dataItem.components = components; + } + const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); + const type = elementDataType.getSignature(); + if (this.arrayLength === undefined) { + return `${type}[]`; + } else { + return `${type}[${this.arrayLength}]`; + } + } + + public getSignature(): string { + return this.arraySignature; + } + + public static matchGrammar(type: string): boolean { + return this.matcher.test(type); + } +} + +export class Method extends MemberDataType { + private methodSignature: string; + private methodSelector: string; + + // TMP + public selector: string; + + constructor(abi: MethodAbi) { + super({ type: 'method', name: abi.name, components: abi.inputs }, EvmDataTypeFactory.getInstance()); + this.methodSignature = this.computeSignature(); + this.selector = this.methodSelector = this.computeSelector(); + + } + + private computeSignature(): string { + const memberSignature = this.computeSignatureOfMembers(); + const methodSignature = `${this.getDataItem().name}${memberSignature}`; + return methodSignature; + } + + private computeSelector(): string { + const signature = this.computeSignature(); + const selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(signature).slice(0, 4))); + return selector; + } + + public encode(value: any, rules?: EncodingRules): string { + const calldata = super.encode(value, rules, this.selector); + return calldata; + } + + public decode(calldata: string, rules?: DecodingRules): any[] | object { + if (!calldata.startsWith(this.selector)) { + throw new Error(`Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`); + } + const value = super.decode(calldata, rules); + return value; + } + + public getSignature(): string { + return this.methodSignature; + } + + public getSelector(): string { + return this.methodSelector; + } +} + +export class EvmDataTypeFactory implements DataTypeFactory { + private static instance: DataTypeFactory; + + private constructor() { } + + public static getInstance(): DataTypeFactory { + if (!EvmDataTypeFactory.instance) { + EvmDataTypeFactory.instance = new EvmDataTypeFactory(); + } + return EvmDataTypeFactory.instance; + } + + public mapDataItemToDataType(dataItem: DataItem): DataType { + if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); + if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); + if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); + if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); + if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); + if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); + if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); + if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); + if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); + //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); + //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); + + throw new Error(`Unrecognized data type: '${dataItem.type}'`); + } + + public create(dataItem: DataItem, parentDataType: DataType): DataType { + const dataType = this.mapDataItemToDataType(dataItem); + if (dataType.isStatic()) { + return dataType; + } + + const pointer = new Pointer(dataType, parentDataType); + return pointer; + } +} \ No newline at end of file diff --git a/packages/utils/src/abi_encoder/index.ts b/packages/utils/src/abi_encoder/index.ts new file mode 100644 index 000000000..991edb8c5 --- /dev/null +++ b/packages/utils/src/abi_encoder/index.ts @@ -0,0 +1,2 @@ +export { EncodingRules, DecodingRules } from './calldata'; +export * from './evm_data_types'; \ No newline at end of file diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 0723e5788..c44530bbc 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -10,3 +10,4 @@ export { NULL_BYTES } from './constants'; export { errorUtils } from './error_utils'; export { fetchAsync } from './fetch_async'; export { signTypedDataUtils } from './sign_typed_data_utils'; +export * from './abi_encoder'; diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts new file mode 100644 index 000000000..2a8fba450 --- /dev/null +++ b/packages/utils/test/abi_encoder_test.ts @@ -0,0 +1,935 @@ +import * as chai from 'chai'; +import 'mocha'; + +// import { assert } from '@0x/order-utils/src/assert'; + +import { chaiSetup } from './utils/chai_setup'; +import { BigNumber } from '../src/'; +//import * as AbiEncoder from './abi_encoder'; + +import * as AbiEncoder from '../src/abi_encoder'; +import * as AbiSamples from './abi_samples'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe.only('ABI Encoder', () => { + describe.only('Optimizer', () => { + + }); + + describe.only('ABI Tests at Method Level', () => { + + it('Should reuse duplicated strings in string array', async () => { + const method = new AbiEncoder.Method(AbiSamples.stringAbi); + const strings = [ + "Test String", + "Test String 2", + "Test String", + "Test String 2", + ]; + const args = [strings]; + + // Verify optimized calldata is expected + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; + //expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + + // Verify args decode properly + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); + }); + + it('Should point array elements to a duplicated value from another parameter', async () => { + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi2); + const stringArray = [ + "Test String", + "Test String", + "Test String", + "Test String 2", + ]; + const string = 'Test String'; + const args = [stringArray, string]; + + // Verify optimized calldata is expected + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + + // Verify args decode properly + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); + }); + + + it('Optimizer #3 (tuple should point to array)', async () => { + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi3); + const uint8Array = [ + new BigNumber(100), + new BigNumber(150), + new BigNumber(200), + new BigNumber(225), + ]; + const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; + const args = [uint8Array, uintTupleArray]; + + + const TEST = method.encode(args, { optimize: true, annotate: true }); + console.log('*'.repeat(50), ' ENCODED DATA ', TEST); + + const optimizedCalldata = method.encode(args, { optimize: true }); + + console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + console.log(JSON.stringify(decodedArgs)); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + + it('Optimizer #4 (Expect no optimization)', async () => { + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi4); + const uint8Array = [ + new BigNumber(100), + new BigNumber(150), + new BigNumber(200), + new BigNumber(225), + ]; + const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; + const args = [uint8Array, uintTupleArray]; + + + const TEST = method.encode(args, { optimize: true, annotate: true }); + console.log('*'.repeat(50), ' ENCODED DATA ', TEST); + + const optimizedCalldata = method.encode(args, { optimize: true }); + + console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + console.log(JSON.stringify(decodedArgs)); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + + it('Crazy ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.crazyAbi); + console.log(method.getSignature()); + + const someStaticArray = [new BigNumber(127), new BigNumber(14), new BigNumber(54)]; + const someStaticArrayWithDynamicMembers = [ + 'the little piping piper piped a piping pipper papper', + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ]; + const someDynamicArrayWithDynamicMembers = [ + '0x38745637834987324827439287423897238947239847', + '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', + '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', + ]; + const some2DArray = [ + [ + 'some string', + 'some another string', + 'there are just too many stringsup in', + 'here', + 'yall ghonna make me lose my mind', + ], + [ + 'the little piping piper piped a piping pipper papper', + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ], + [], + ]; + const someTuple = { + someUint32: new BigNumber(4037824789), + someStr: 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.' + }; + const someTupleWithDynamicTypes = { + someUint: new BigNumber(4024789), + someStr: 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', + someBytes: '0x29384723894723843743289742389472398473289472348927489274894738427428947389facdea', + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', + }; + const someTupleWithDynamicTypes2 = { + someUint: new BigNumber(9024789), + someStr: 'ksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjkakdhjasjkdhasjkldshdjahdkjsahdaj', + someBytes: '0x29384723894398473289472348927489272384374328974238947274894738427428947389facde1', + someAddress: '0x746dafa5ebde1f4699f4981d3221892e41d24895', + }; + const someTupleWithDynamicTypes3 = { + someUint: new BigNumber(1024789), + someStr: 'sdhsajkdhsajkdhadjkashdjakdhjasjkdhasjkldshdjahdkjsahdajkksadhajkdhsajkdhsadjk', + someBytes: '0x38947238437432829384729742389472398473289472348927489274894738427428947389facdef', + someAddress: '0x89571d322189e415ebde1f4699f498d24246dafa', + }; + const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3]; + + const args = { + someStaticArray: someStaticArray, + someStaticArrayWithDynamicMembers: someStaticArrayWithDynamicMembers, + someDynamicArrayWithDynamicMembers: someDynamicArrayWithDynamicMembers, + some2DArray: some2DArray, + someTuple: someTuple, + someTupleWithDynamicTypes: someTupleWithDynamicTypes, + someArrayOfTuplesWithDynamicTypes: someArrayOfTuplesWithDynamicTypes + }; + + const calldata = method.encode(args); + console.log(calldata); + + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + console.log(method.getSignature()); + + const expectedCalldata = '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; + //const expectedCalldata = '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata, { structsAsObjects: true }); + const decodedValueJson = JSON.stringify(decodedValue); + console.log(`DECODED`, '*'.repeat(200), '\n', decodedValueJson); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Crazy ABI #1', async () => { + const method = new AbiEncoder.Method(AbiSamples.crazyAbi1); + + const args = [ + new BigNumber(256745454), + new BigNumber(-256745454), + new BigNumber(434244), + '0x43', + '0x0001020304050607080911121314151617181920212223242526272829303132', + '0x0001020304050607080911121314151617181920212223242526272829303132080911121314151617181920212223242526272829303132', + 'Little peter piper piped a piping pepper pot', + '0xe41d2489571d322189246dafa5ebde1f4699f498', + true + ]; + + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + + it('Types with default widths', async () => { + const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); + console.log(method); + const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Array of Static Tuples (Array has defined length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDefinedLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Array of Static Tuples (Array has dynamic length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDynamicLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Array of Dynamic Tuples (Array has defined length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithDefinedLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Array of Dynamic Tuples (Array has dynamic length)', async () => { + const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithUndefinedLengthAbi); + + let value = 0; + const arrayOfTuples = []; + for (let i = 0; i < 8; ++i) { + arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Multidimensional Arrays / Static Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi); + + // Eight 3-dimensional arrays of uint8[2][2][2] + let value = 0; + const args = []; + for (let i = 0; i < 8; ++i) { + args.push( + [ + [ + [new BigNumber(++value), new BigNumber(++value)], + [new BigNumber(++value), new BigNumber(++value)], + ], + [ + [new BigNumber(++value), new BigNumber(++value)], + [new BigNumber(++value), new BigNumber(++value)], + ] + ] + ); + } + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; expect(calldata).to.be.equal(expectedCalldata); + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Multidimensional Arrays / Dynamic Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysDynamicTypeAbi); + + // Eight 3-dimensional arrays of string[2][2][2] + let value = 0; + const args = []; + for (let i = 0; i < 4; ++i) { + args.push( + [ + [ + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + ], + [ + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + ] + ] + ); + } + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(method.getSignature()); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Fixed Lenfgth Array / Dynamic Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); + const args = [["Brave", "New", "World"]]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + const expectedCalldata = + '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Fixed Lenfgth Array / Dynamic Members', async () => { + const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); + const args = [["Brave", "New", "World"]]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + const expectedCalldata = + '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Unfixed Length Array / Dynamic Members ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.dynamicArrayDynamicMembersAbi); + const args = [["Brave", "New", "World"]]; + const calldata = method.encode(args); + console.log(calldata); + console.log('*'.repeat(40)); + console.log(JSON.stringify(args)); + const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Unfixed Length Array / Static Members ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.dynamicArrayStaticMembersAbi); + const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; + const calldata = method.encode(args); + const expectedCalldata = '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + + it('Fixed Length Array / Static Members ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); + const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; + const calldata = method.encode(args); + const expectedCalldata = + '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + + it('Simple ABI 2', async () => { + const method = new AbiEncoder.Method(AbiSamples.simpleAbi2); + + const args = [ + '0xaf', // e (bytes1) + '0x0001020304050607080911121314151617181920212223242526272829303132', // f (bytes32) + '0x616161616161616161616161616161616161616161616161616161616161616161616161616161611114f324567838475647382938475677448899338457668899002020202020', // g + 'My first name is Greg and my last name is Hysen, what do ya know!', // h + ]; + + const calldata = method.encode(args); + const expectedCalldata = + '0x7ac2bd96af000000000000000000000000000000000000000000000000000000000000000001020304050607080911121314151617181920212223242526272829303132000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000047616161616161616161616161616161616161616161616161616161616161616161616161616161611114f3245678384756473829384756774488993384576688990020202020200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414d79206669727374206e616d65206973204772656720616e64206d79206c617374206e616d6520697320487973656e2c207768617420646f207961206b6e6f772100000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Array ABI', async () => { + const method = new AbiEncoder.Method(AbiSamples.stringAbi); + console.log(method); + const args = [['five', 'six', 'seven']]; + const calldata = method.encode(args); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Static Tuple', async () => { + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi); + const args = [[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]; + const calldata = method.encode(args); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Dynamic Tuple (Array input)', async () => { + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); + const args = [[new BigNumber(5), 'five']]; + const calldata = method.encode(args); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + + // Test decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + + it('Dynamic Tuple (Object input)', async () => { + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); + const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it.skip('Nested Tuples', async () => { + // Couldn't get nested tuples to work with Remix + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.nestedTuples); + const firstTuple = { + someUint32: new BigNumber(30472), + nestedTuple: { + someUint: new BigNumber('48384725243211555532'), + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' + } + }; + const secondTuple = { + someUint: new BigNumber(2984237422), + someStr: 'This string will exceed 256 bits, so it will spill into the next word of memory.', + nestedTuple: { + someUint32: new BigNumber(23), + secondNestedTuple: { + someUint: new BigNumber(234324), + someStr: 'Im also a short string -- oops I just got loooooooooooooooooonger!', + someBytes: '0x23847287fff3472984723498ff23487324987aaa237438911873429472ba', + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' + } + }, + someBytes: '0x2834y3947289423u489aaaff4783924739847489', + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699afaf', + }; + const thirdTuple = { + 'someUint': new BigNumber(37422), + 'someStr': 'This into the next word of memory. string will exceed 256 bits, so it will spill.', + 'nestedTuple': { + someUint32: new BigNumber(23999222), + 'secondNestedTuple': { + 'someUint': new BigNumber(324), + 'someStr': 'Im also a short st', + 'someBytes': '0x723498ff2348732498723847287fff3472984aaa237438911873429472ba', + 'someAddress': '0x46dafa5ebde1f4699f498e41d2489571d3221892' + } + }, + 'someBytes': '0x947289423u489aaaff472834y383924739847489', + 'someAddress': '0x46dafa5ebde1f46e41d2489571d322189299afaf', + }; + const fourthTuple = { + 'someUint': new BigNumber(222283488822), + 'someStr': 'exceed 256 bits, so it will spill into the. This string will next word of memory.', + 'nestedTuple': { + someUint32: new BigNumber(2300), + 'secondNestedTuple': { + 'someUint': new BigNumber(343224), + 'someStr': 'The alphabet backwards is arguably easier to say if thats the way you learned the first time.', + 'someBytes': '0x87324987aaa23743891187323847287fff3472984723498ff234429472ba', + 'someAddress': '0x71d322189246dafa5ebe41d24895de1f4699f498' + } + }, + 'someBytes': '0x2783924739847488343947289423u489aaaff490', + 'someAddress': '0xebde1d322189246dafa1f4699afafe41d2489575', + }; + const args = [ + [firstTuple], + [secondTuple, thirdTuple, fourthTuple] + ]; + + console.log('*'.repeat(250), method, '*'.repeat(250)); + + + const calldata = method.encode(args); + console.log(method.getSignature()); + console.log(method.selector); + console.log(JSON.stringify(args)); + + console.log(calldata); + const expectedCalldata = '0x'; + expect(calldata).to.be.equal(expectedCalldata); + }); + + it.skip('Object ABI (Object input - Missing Key)', async () => { + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); + const calldata = method.encode([{ someUint: new BigNumber(5) }]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + + // @TODO: Figure out how to catch throw + expect(calldata).to.be.equal(expectedCalldata); + }); + + it.skip('Object ABI (Object input - Too Many Keys)', async () => { + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); + const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five', unwantedKey: 14 }]); + console.log(method.getSignature()); + console.log(method.selector); + + console.log(calldata); + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + + // @TODO: Figure out how to catch throw + expect(calldata).to.be.equal(expectedCalldata); + }); + }); + /* + describe('Array', () => { + it('sample', async () => { + const testDataItem = { name: 'testArray', type: 'int[2]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue([new BigNumber(5), new BigNumber(6)]); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + }); + + it('sample undefined size', async () => { + const testDataItem = { name: 'testArray', type: 'int[]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue([new BigNumber(5), new BigNumber(6)]); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + }); + + it('sample dynamic types', async () => { + const testDataItem = { name: 'testArray', type: 'string[]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue(['five', 'six', 'seven']); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + const calldata = new AbiEncoder.Calldata('0x01020304', 1); + dataType.bind(calldata, AbiEncoder.CalldataSection.PARAMS); + console.log('*'.repeat(60)); + console.log(calldata.getHexValue()); + }); + }); + + describe('Address', () => { + const testAddressDataItem = { name: 'testAddress', type: 'address' }; + it('Valid Address', async () => { + const addressDataType = new AbiEncoder.Address(testAddressDataItem); + addressDataType.assignValue('0xe41d2489571d322189246dafa5ebde1f4699f498'); + const expectedAbiEncodedAddress = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; + + console.log(addressDataType.getHexValue()); + console.log(expectedAbiEncodedAddress); + expect(addressDataType.getHexValue()).to.be.equal(expectedAbiEncodedAddress); + }); + }); + + describe('Bool', () => { + const testBoolDataItem = { name: 'testBool', type: 'bool' }; + it('True', async () => { + const boolDataType = new AbiEncoder.Bool(testBoolDataItem); + boolDataType.assignValue(true); + const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + }); + + it('False', async () => { + const boolDataType = new AbiEncoder.Bool(testBoolDataItem); + boolDataType.assignValue(false); + const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + }); + }); + + describe('Integer', () => { + const testIntDataItem = { name: 'testInt', type: 'int' }; + it('Positive - Base case', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(1)); + const expectedAbiEncodedInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Positive', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(437829473)); + const expectedAbiEncodedInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Negative - Base case', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(-1)); + const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Negative', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(-437829473)); + const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + // TODO: Add bounds tests + tests for different widths + }); + + describe('Unsigned Integer', () => { + const testIntDataItem = { name: 'testUInt', type: 'uint' }; + it('Lower Bound', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(0)); + const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + it('Base Case', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(1)); + const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + it('Random value', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(437829473)); + const expectedAbiEncodedUInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + // TODO: Add bounds tests + tests for different widths + }); + + describe('Static Bytes', () => { + it('Byte (padded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x05'); + const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it.skip('Byte (no padding)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + + // @TODO: This does not catch the Error + expect(byteDataType.assignValue('0x5')).to.throw(); + }); + + it('Bytes1', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes1' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x05'); + const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it('Bytes32 (padded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x0001020304050607080911121314151617181920212223242526272829303132'); + const expectedAbiEncodedByte = '0x0001020304050607080911121314151617181920212223242526272829303132'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it('Bytes32 (unpadded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x1a18bf61'); + const expectedAbiEncodedByte = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it.skip('Bytes32 - Too long', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + + // @TODO: This does not catch the Error + expect( + byteDataType.assignValue('0x000102030405060708091112131415161718192021222324252627282930313233'), + ).to.throw( + `Tried to assign 0x000102030405060708091112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32`, + ); + }); + }); + + describe('Bytes (Dynamic)', () => { + const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; + it('Less than 32 bytes', async () => { + const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); + bytesDataType.assignValue('0x010203'); + const expectedAbiEncodedBytes = + '0x00000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000'; + + expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + }); + + it('Greater than 32 bytes', async () => { + const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); + const testValue = '0x' + '61'.repeat(40); + bytesDataType.assignValue(testValue); + const expectedAbiEncodedBytes = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + }); + + // @TODO: Add test for throw on half-byte + // @TODO: Test with no 0x prefix + // @TODO: Test with Buffer as input + }); + + describe('String', () => { + const testStringDataItem = { name: 'testString', type: 'string' }; + it('Less than 32 bytes', async () => { + const stringDataType = new AbiEncoder.SolString(testStringDataItem); + stringDataType.assignValue('five'); + const expectedAbiEncodedString = + '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + + console.log(stringDataType.getHexValue()); + console.log(expectedAbiEncodedString); + expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + }); + + it('Greater than 32 bytes', async () => { + const stringDataType = new AbiEncoder.SolString(testStringDataItem); + const testValue = 'a'.repeat(40); + stringDataType.assignValue(testValue); + const expectedAbiEncodedString = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + }); + });*/ +}); diff --git a/packages/utils/test/abi_samples.ts b/packages/utils/test/abi_samples.ts new file mode 100644 index 000000000..5e8268f1a --- /dev/null +++ b/packages/utils/test/abi_samples.ts @@ -0,0 +1,814 @@ +import { MethodAbi } from 'ethereum-types'; + +export const simpleAbi = { + constant: false, + inputs: [ + { + name: 'greg', + type: 'uint256', + }, + { + name: 'gregStr', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const stringAbi = { + constant: false, + inputs: [ + { + name: 'greg', + type: 'string[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const optimizerAbi2 = { + constant: false, + inputs: [ + { + name: 'stringArray', + type: 'string[]', + }, + { + name: 'string', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const optimizerAbi3 = { + constant: false, + inputs: [ + { + name: 'uint8Array', + type: 'uint8[]', + }, + { + components: [ + { + name: 'uint', + type: 'uint', + }, + ], + name: 'uintTuple', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const optimizerAbi4 = { + constant: false, + inputs: [ + { + name: 'uint8Array', + type: 'uint8[4]', + }, + { + components: [ + { + name: 'uint', + type: 'uint', + }, + ], + name: 'uintTuple', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const typesWithDefaultWidthsAbi = { + constant: false, + inputs: [ + { + name: 'someUint', + type: 'uint', + }, + { + name: 'someInt', + type: 'int', + }, + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someUint', + type: 'uint[]', + }, + { + name: 'someInt', + type: 'int[]', + }, + { + name: 'someByte', + type: 'byte[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const multiDimensionalArraysStaticTypeAbi = { + constant: false, + inputs: [ + { + name: 'a', + type: 'uint8[][][]', + }, + { + name: 'b', + type: 'uint8[][][2]', + }, + { + name: 'c', + type: 'uint8[][2][]', + }, + { + name: 'd', + type: 'uint8[2][][]', + }, + { + name: 'e', + type: 'uint8[][2][2]', + }, + { + name: 'f', + type: 'uint8[2][2][]', + }, + { + name: 'g', + type: 'uint8[2][][2]', + }, + { + name: 'h', + type: 'uint8[2][2][2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const multiDimensionalArraysDynamicTypeAbi = { + constant: false, + inputs: [ + { + name: 'a', + type: 'string[][][]', + }, + { + name: 'b', + type: 'string[][][2]', + }, + { + name: 'c', + type: 'string[][2][]', + }, + { + name: 'h', + type: 'string[2][2][2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const dynamicTupleAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + ], + name: 'order', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfStaticTuplesWithDefinedLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + ], + name: 'order', + type: 'tuple[8]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfStaticTuplesWithDynamicLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfDynamicTuplesWithDefinedLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[8]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfDynamicTuplesWithUndefinedLengthAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayOfDynamicTuplesAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const multidimensionalArrayOfDynamicTuplesAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[][2][]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const staticTupleAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint1', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + { + name: 'someUint3', + type: 'uint256', + }, + { + name: 'someBool', + type: 'bool', + }, + ], + name: 'order', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const staticArrayAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[3]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const staticArrayDynamicMembersAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'string[3]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const dynamicArrayDynamicMembersAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'string[]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const dynamicArrayStaticMembersAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[]', + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const crazyAbi1 = { + constant: false, + inputs: [ + { + name: 'someUInt256', + type: 'uint256', + }, + { + name: 'someInt256', + type: 'int256', + }, + { + name: 'someInt32', + type: 'int32', + }, + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someBytes32', + type: 'bytes32', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someString', + type: 'string', + }, + { + name: 'someAddress', + type: 'address', + }, + { + name: 'someBool', + type: 'bool', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const crazyAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[3]', + }, + { + name: 'someStaticArrayWithDynamicMembers', + type: 'string[2]', + }, + { + name: 'someDynamicArrayWithDynamicMembers', + type: 'bytes[]', + }, + { + name: 'some2DArray', + type: 'string[][]', + }, + { + name: 'someTuple', + type: 'tuple', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'someStr', + type: 'string', + }, + ], + }, + { + name: 'someTupleWithDynamicTypes', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + /*{ + name: 'someStrArray', + type: 'string[]', + },*/ + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + { + name: 'someArrayOfTuplesWithDynamicTypes', + type: 'tuple[]', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + /*{ + name: 'someStrArray', + type: 'string[]', + },*/ + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const nestedTuples = { + constant: false, + inputs: [ + { + name: 'firstTuple', + type: 'tuple[1]', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'nestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + ], + }, + { + name: 'secondTuple', + type: 'tuple[]', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + { + name: 'nestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'secondNestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + ], + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const simpleAbi2 = { + constant: false, + inputs: [ + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someBytes32', + type: 'bytes32', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const fillOrderAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'makerAddress', + type: 'address', + }, + { + name: 'takerAddress', + type: 'address', + }, + { + name: 'feeRecipientAddress', + type: 'address', + }, + { + name: 'senderAddress', + type: 'address', + }, + { + name: 'makerAssetAmount', + type: 'uint256', + }, + { + name: 'takerAssetAmount', + type: 'uint256', + }, + { + name: 'makerFee', + type: 'uint256', + }, + { + name: 'takerFee', + type: 'uint256', + }, + { + name: 'expirationTimeSeconds', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'makerAssetData', + type: 'bytes', + }, + { + name: 'takerAssetData', + type: 'bytes', + }, + ], + name: 'order', + type: 'tuple', + }, + { + name: 'takerAssetFillAmount', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'orderSignature', + type: 'bytes', + }, + { + name: 'takerSignature', + type: 'bytes', + }, + ], + name: 'fillOrder', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; diff --git a/packages/utils/test/utils/chai_setup.ts b/packages/utils/test/utils/chai_setup.ts new file mode 100644 index 000000000..1a8733093 --- /dev/null +++ b/packages/utils/test/utils/chai_setup.ts @@ -0,0 +1,13 @@ +import * as chai from 'chai'; +import chaiAsPromised = require('chai-as-promised'); +import ChaiBigNumber = require('chai-bignumber'); +import * as dirtyChai from 'dirty-chai'; + +export const chaiSetup = { + configure(): void { + chai.config.includeStack = true; + chai.use(ChaiBigNumber()); + chai.use(dirtyChai); + chai.use(chaiAsPromised); + }, +}; -- cgit v1.2.3 From 1f9ea7ed1b3d56a13b5e518adeed907eb830c02a Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 14 Nov 2018 15:34:27 -0800 Subject: Removed unnecessary imports from abi encoder tets --- packages/utils/src/index.ts | 3 +-- packages/utils/test/abi_encoder_test.ts | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index c44530bbc..2edc6a331 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -9,5 +9,4 @@ export { abiUtils } from './abi_utils'; export { NULL_BYTES } from './constants'; export { errorUtils } from './error_utils'; export { fetchAsync } from './fetch_async'; -export { signTypedDataUtils } from './sign_typed_data_utils'; -export * from './abi_encoder'; +export { signTypedDataUtils } from './sign_typed_data_utils'; \ No newline at end of file diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 2a8fba450..0961458da 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -1,12 +1,8 @@ import * as chai from 'chai'; import 'mocha'; -// import { assert } from '@0x/order-utils/src/assert'; - import { chaiSetup } from './utils/chai_setup'; import { BigNumber } from '../src/'; -//import * as AbiEncoder from './abi_encoder'; - import * as AbiEncoder from '../src/abi_encoder'; import * as AbiSamples from './abi_samples'; -- cgit v1.2.3 From ebd4dbc6c6ebdf776ba1582367d3eda32717ba65 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 14 Nov 2018 15:55:10 -0800 Subject: Exports AbiEncoder as 1 unit --- packages/utils/src/index.ts | 4 +++- packages/utils/test/abi_encoder_test.ts | 5 ++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 2edc6a331..f59cbec8c 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -9,4 +9,6 @@ export { abiUtils } from './abi_utils'; export { NULL_BYTES } from './constants'; export { errorUtils } from './error_utils'; export { fetchAsync } from './fetch_async'; -export { signTypedDataUtils } from './sign_typed_data_utils'; \ No newline at end of file +export { signTypedDataUtils } from './sign_typed_data_utils'; +import * as AbiEncoder from './abi_encoder'; +export { AbiEncoder }; diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 0961458da..8d78b61b5 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -2,8 +2,7 @@ import * as chai from 'chai'; import 'mocha'; import { chaiSetup } from './utils/chai_setup'; -import { BigNumber } from '../src/'; -import * as AbiEncoder from '../src/abi_encoder'; +import { BigNumber, AbiEncoder } from '../src/'; import * as AbiSamples from './abi_samples'; chaiSetup.configure(); @@ -29,7 +28,7 @@ describe.only('ABI Encoder', () => { // Verify optimized calldata is expected const optimizedCalldata = method.encode(args, { optimize: true }); const expectedOptimizedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; - //expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Verify args decode properly const decodedArgs = method.decode(optimizedCalldata); -- cgit v1.2.3 From 8a8b904a292063d1adb3df0a84023610a3985f7f Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Thu, 15 Nov 2018 14:33:40 -0800 Subject: Use new ABI Encoder for contracts --- packages/utils/src/abi_encoder/calldata.ts | 12 ++++++--- packages/utils/src/abi_encoder/data_type.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types.ts | 32 +++++++++++++++++++--- packages/utils/test/abi_encoder_test.ts | 29 ++++++++++++++++++++ packages/utils/test/abi_samples.ts | 34 ++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 7 deletions(-) diff --git a/packages/utils/src/abi_encoder/calldata.ts b/packages/utils/src/abi_encoder/calldata.ts index 32278e5c5..078767c22 100644 --- a/packages/utils/src/abi_encoder/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata.ts @@ -456,13 +456,19 @@ export class RawCalldata { private selector: string; private scopes: Queue; - constructor(value: string | Buffer) { + constructor(value: string | Buffer, hasSelectorPrefix: boolean = true) { if (typeof value === 'string' && !value.startsWith('0x')) { throw new Error(`Expected raw calldata to start with '0x'`); } const valueBuf = ethUtil.toBuffer(value); - this.selector = ethUtil.bufferToHex(valueBuf.slice(0, 4)); - this.value = valueBuf.slice(4); // disregard selector + if (hasSelectorPrefix) { + this.selector = ethUtil.bufferToHex(valueBuf.slice(0, 4)); + this.value = valueBuf.slice(4); // disregard selector + } else { + this.selector = '0x'; + this.value = valueBuf; + } + this.offset = 0; this.scopes = new Queue(); this.scopes.push(0); diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index 3b4028abd..f5a8087ea 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -6,7 +6,7 @@ import ethUtil = require('ethereumjs-util'); var _ = require('lodash'); export interface DataTypeFactory { - create: (dataItem: DataItem, parentDataType: DataType) => DataType; + create: (dataItem: DataItem, parentDataType?: DataType) => DataType; mapDataItemToDataType: (dataItem: DataItem) => DataType; } diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index 2973596fe..d024a9bfa 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -106,7 +106,8 @@ abstract class Number extends PayloadDataType { } } - public encodeValue(value: BigNumber): Buffer { + public encodeValue(value_: BigNumber | string | number): Buffer { + const value = new BigNumber(value_, 10); if (value.greaterThan(this.getMaxValue())) { throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; } else if (value.lessThan(this.getMinValue())) { @@ -465,6 +466,7 @@ export class SolArray extends MemberDataType { export class Method extends MemberDataType { private methodSignature: string; private methodSelector: string; + private returnDataTypes: DataType[]; // TMP public selector: string; @@ -473,7 +475,11 @@ export class Method extends MemberDataType { super({ type: 'method', name: abi.name, components: abi.inputs }, EvmDataTypeFactory.getInstance()); this.methodSignature = this.computeSignature(); this.selector = this.methodSelector = this.computeSelector(); - + this.returnDataTypes = []; + const dummy = new Byte({ type: 'byte', name: 'DUMMY' }); // @TODO TMP + _.each(abi.outputs, (dataItem: DataItem) => { + this.returnDataTypes.push(this.getFactory().create(dataItem, dummy)); + }); } private computeSignature(): string { @@ -501,6 +507,23 @@ export class Method extends MemberDataType { return value; } + public decodeReturnValues(returndata: string, rules?: DecodingRules): any { + //console.log('O'.repeat(100), '\n', returndata, '\n', this.returnDataTypes, 'P'.repeat(100)); + + const returnValues: any[] = []; + const rules_ = rules ? rules : { structsAsObjects: false } as DecodingRules; + const rawReturnData = new RawCalldata(returndata, false); + _.each(this.returnDataTypes, (dataType: DataType) => { + returnValues.push(dataType.generateValue(rawReturnData, rules_)); + }); + + //console.log('*'.repeat(40), '\n', JSON.stringify(returnValues), '\n', '*'.repeat(100)); + /*if (returnValues.length === 1) { + return returnValues[0]; + }*/ + return returnValues; + } + public getSignature(): string { return this.methodSignature; } @@ -538,12 +561,15 @@ export class EvmDataTypeFactory implements DataTypeFactory { throw new Error(`Unrecognized data type: '${dataItem.type}'`); } - public create(dataItem: DataItem, parentDataType: DataType): DataType { + public create(dataItem: DataItem, parentDataType?: DataType): DataType { const dataType = this.mapDataItemToDataType(dataItem); if (dataType.isStatic()) { return dataType; } + if (parentDataType === undefined) { // @Todo -- will this work for return values? + throw new Error(`Trying to create a pointer`); + } const pointer = new Pointer(dataType, parentDataType); return pointer; } diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 8d78b61b5..8add41af6 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -15,6 +15,35 @@ describe.only('ABI Encoder', () => { describe.only('ABI Tests at Method Level', () => { + it.only('Should reuse duplicated strings in string array', async () => { + const method = new AbiEncoder.Method(AbiSamples.GAbi); + + const args = [ + { + a: new BigNumber(5), + e: '0x616161', + b: 'aaa', + f: '0xe41d2489571d322189246dafa5ebde1f4699f498' + } + ] + + // Verify optimized calldata is expected + const optimizedCalldata = method.encode(args, { optimize: true }); + //const expectedOptimizedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; + //expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + + // Verify args decode properly + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + //expect(decodedArgsJson).to.be.equal(argsJson); + + console.log(method.getSignature()); + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); + console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); + + }); + it('Should reuse duplicated strings in string array', async () => { const method = new AbiEncoder.Method(AbiSamples.stringAbi); const strings = [ diff --git a/packages/utils/test/abi_samples.ts b/packages/utils/test/abi_samples.ts index 5e8268f1a..aa38711cd 100644 --- a/packages/utils/test/abi_samples.ts +++ b/packages/utils/test/abi_samples.ts @@ -34,6 +34,40 @@ export const stringAbi = { type: 'function', } as MethodAbi; + +export const GAbi = { + constant: false, + inputs: [ + { + components: [{ + name: 'a', + type: 'uint256', + }, + { + name: 'b', + type: 'string', + }, + { + name: 'e', + type: 'bytes', + }, + { + name: 'f', + type: 'address', + }], + + name: 'f', + type: 'tuple', + + } + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + export const optimizerAbi2 = { constant: false, inputs: [ -- cgit v1.2.3 From 880540f4b8fd8dd7a7f1a93cf7fbcdc17e1fbb13 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 10:34:04 -0800 Subject: Fixed issue with decoding negative ints with width < 256 --- packages/utils/src/abi_encoder/data_type.ts | 4 +- packages/utils/src/abi_encoder/evm_data_types.ts | 5 +- packages/utils/test/abi_encoder_test.ts | 475 ++++++++++++----------- 3 files changed, 246 insertions(+), 238 deletions(-) diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index f5a8087ea..15de12a2f 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -41,7 +41,8 @@ export abstract class DataType { } public decode(calldata: string, rules?: DecodingRules): any { - const rawCalldata = new RawCalldata(calldata); + const rawCalldata = new RawCalldata(calldata, false); + console.log(`HERE DUDE ${JSON.stringify(rawCalldata)}`); const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; const value = this.generateValue(rawCalldata, rules_); return value; @@ -277,6 +278,7 @@ export abstract class MemberDataType extends DataType { value = []; _.each(members, (member: DataType, idx: number) => { let memberValue = member.generateValue(calldata, rules); + console.log(`MEMBER VALUE: ${memberValue}`); (value as any[]).push(memberValue); }); } diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index d024a9bfa..bfb2808da 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -154,9 +154,8 @@ abstract class Number extends PayloadDataType { if (this instanceof Int) { // Check if we're negative const binBase = 2; - const paddedValueBin = value.toString(binBase); - const valueBin = paddedValueBin.slice(paddedValueBin.length - this.width); - if (valueBin[0].startsWith('1')) { + const valueBin = value.toString(2); + if (valueBin.length === 256 && valueBin[0].startsWith('1')) { // Negative // Step 1/3: Invert binary value let invertedValueBin = ''; diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 8add41af6..d2d7b9825 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -13,9 +13,9 @@ describe.only('ABI Encoder', () => { }); - describe.only('ABI Tests at Method Level', () => { + describe('ABI Tests at Method Level', () => { - it.only('Should reuse duplicated strings in string array', async () => { + it('Should reuse duplicated strings in string array', async () => { const method = new AbiEncoder.Method(AbiSamples.GAbi); const args = [ @@ -723,237 +723,244 @@ describe.only('ABI Encoder', () => { expect(calldata).to.be.equal(expectedCalldata); }); }); - /* - describe('Array', () => { - it('sample', async () => { - const testDataItem = { name: 'testArray', type: 'int[2]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue([new BigNumber(5), new BigNumber(6)]); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - }); - - it('sample undefined size', async () => { - const testDataItem = { name: 'testArray', type: 'int[]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue([new BigNumber(5), new BigNumber(6)]); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - }); - - it('sample dynamic types', async () => { - const testDataItem = { name: 'testArray', type: 'string[]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue(['five', 'six', 'seven']); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - const calldata = new AbiEncoder.Calldata('0x01020304', 1); - dataType.bind(calldata, AbiEncoder.CalldataSection.PARAMS); - console.log('*'.repeat(60)); - console.log(calldata.getHexValue()); - }); - }); - - describe('Address', () => { - const testAddressDataItem = { name: 'testAddress', type: 'address' }; - it('Valid Address', async () => { - const addressDataType = new AbiEncoder.Address(testAddressDataItem); - addressDataType.assignValue('0xe41d2489571d322189246dafa5ebde1f4699f498'); - const expectedAbiEncodedAddress = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; - - console.log(addressDataType.getHexValue()); - console.log(expectedAbiEncodedAddress); - expect(addressDataType.getHexValue()).to.be.equal(expectedAbiEncodedAddress); - }); - }); - - describe('Bool', () => { - const testBoolDataItem = { name: 'testBool', type: 'bool' }; - it('True', async () => { - const boolDataType = new AbiEncoder.Bool(testBoolDataItem); - boolDataType.assignValue(true); - const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); - }); - - it('False', async () => { - const boolDataType = new AbiEncoder.Bool(testBoolDataItem); - boolDataType.assignValue(false); - const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000000'; - expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); - }); - }); - - describe('Integer', () => { - const testIntDataItem = { name: 'testInt', type: 'int' }; - it('Positive - Base case', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(1)); - const expectedAbiEncodedInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Positive', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(437829473)); - const expectedAbiEncodedInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Negative - Base case', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(-1)); - const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - it('Negative', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(-437829473)); - const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); - }); - - // TODO: Add bounds tests + tests for different widths - }); - - describe('Unsigned Integer', () => { - const testIntDataItem = { name: 'testUInt', type: 'uint' }; - it('Lower Bound', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(0)); - const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000000'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - it('Base Case', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(1)); - const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - it('Random value', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(437829473)); - const expectedAbiEncodedUInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); - }); - - // TODO: Add bounds tests + tests for different widths - }); - - describe('Static Bytes', () => { - it('Byte (padded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x05'); - const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it.skip('Byte (no padding)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - - // @TODO: This does not catch the Error - expect(byteDataType.assignValue('0x5')).to.throw(); - }); - - it('Bytes1', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes1' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x05'); - const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it('Bytes32 (padded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x0001020304050607080911121314151617181920212223242526272829303132'); - const expectedAbiEncodedByte = '0x0001020304050607080911121314151617181920212223242526272829303132'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it('Bytes32 (unpadded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x1a18bf61'); - const expectedAbiEncodedByte = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); - }); - - it.skip('Bytes32 - Too long', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - - // @TODO: This does not catch the Error - expect( - byteDataType.assignValue('0x000102030405060708091112131415161718192021222324252627282930313233'), - ).to.throw( - `Tried to assign 0x000102030405060708091112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32`, - ); - }); - }); - - describe('Bytes (Dynamic)', () => { - const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; - it('Less than 32 bytes', async () => { - const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); - bytesDataType.assignValue('0x010203'); - const expectedAbiEncodedBytes = - '0x00000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000'; - - expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); - }); - - it('Greater than 32 bytes', async () => { - const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); - const testValue = '0x' + '61'.repeat(40); - bytesDataType.assignValue(testValue); - const expectedAbiEncodedBytes = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); - }); - - // @TODO: Add test for throw on half-byte - // @TODO: Test with no 0x prefix - // @TODO: Test with Buffer as input - }); - - describe('String', () => { - const testStringDataItem = { name: 'testString', type: 'string' }; - it('Less than 32 bytes', async () => { - const stringDataType = new AbiEncoder.SolString(testStringDataItem); - stringDataType.assignValue('five'); - const expectedAbiEncodedString = - '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - - console.log(stringDataType.getHexValue()); - console.log(expectedAbiEncodedString); - expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); - }); - - it('Greater than 32 bytes', async () => { - const stringDataType = new AbiEncoder.SolString(testStringDataItem); - const testValue = 'a'.repeat(40); - stringDataType.assignValue(testValue); - const expectedAbiEncodedString = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); - }); + + describe.only('Array', () => { + it.only('sample', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'int[2]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + // Construct args to be encoded + const args = [new BigNumber(5), new BigNumber(6)]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + /* + it('sample undefined size', async () => { + const testDataItem = { name: 'testArray', type: 'int[]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue([new BigNumber(5), new BigNumber(6)]); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + }); + + it('sample dynamic types', async () => { + const testDataItem = { name: 'testArray', type: 'string[]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + console.log(JSON.stringify(dataType, null, 4)); + console.log('*'.repeat(60)); + dataType.assignValue(['five', 'six', 'seven']); + console.log(JSON.stringify(dataType, null, 4)); + const hexValue = dataType.getHexValue(); + console.log('*'.repeat(60)); + console.log(hexValue); + const calldata = new AbiEncoder.Calldata('0x01020304', 1); + dataType.bind(calldata, AbiEncoder.CalldataSection.PARAMS); + console.log('*'.repeat(60)); + console.log(calldata.getHexValue()); });*/ + }); + + /* + describe('Address', () => { + const testAddressDataItem = { name: 'testAddress', type: 'address' }; + it('Valid Address', async () => { + const addressDataType = new AbiEncoder.Address(testAddressDataItem); + addressDataType.assignValue('0xe41d2489571d322189246dafa5ebde1f4699f498'); + const expectedAbiEncodedAddress = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; + + console.log(addressDataType.getHexValue()); + console.log(expectedAbiEncodedAddress); + expect(addressDataType.getHexValue()).to.be.equal(expectedAbiEncodedAddress); + }); + }); + + describe('Bool', () => { + const testBoolDataItem = { name: 'testBool', type: 'bool' }; + it('True', async () => { + const boolDataType = new AbiEncoder.Bool(testBoolDataItem); + boolDataType.assignValue(true); + const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + }); + + it('False', async () => { + const boolDataType = new AbiEncoder.Bool(testBoolDataItem); + boolDataType.assignValue(false); + const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + }); + }); + + describe('Integer', () => { + const testIntDataItem = { name: 'testInt', type: 'int' }; + it('Positive - Base case', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(1)); + const expectedAbiEncodedInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Positive', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(437829473)); + const expectedAbiEncodedInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Negative - Base case', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(-1)); + const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + it('Negative', async () => { + const intDataType = new AbiEncoder.Int(testIntDataItem); + intDataType.assignValue(new BigNumber(-437829473)); + const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f'; + expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + }); + + // TODO: Add bounds tests + tests for different widths + }); + + describe('Unsigned Integer', () => { + const testIntDataItem = { name: 'testUInt', type: 'uint' }; + it('Lower Bound', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(0)); + const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + it('Base Case', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(1)); + const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + it('Random value', async () => { + const uintDataType = new AbiEncoder.UInt(testIntDataItem); + uintDataType.assignValue(new BigNumber(437829473)); + const expectedAbiEncodedUInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; + expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + }); + + // TODO: Add bounds tests + tests for different widths + }); + + describe('Static Bytes', () => { + it('Byte (padded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x05'); + const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it.skip('Byte (no padding)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + + // @TODO: This does not catch the Error + expect(byteDataType.assignValue('0x5')).to.throw(); + }); + + it('Bytes1', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes1' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x05'); + const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it('Bytes32 (padded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x0001020304050607080911121314151617181920212223242526272829303132'); + const expectedAbiEncodedByte = '0x0001020304050607080911121314151617181920212223242526272829303132'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it('Bytes32 (unpadded)', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + byteDataType.assignValue('0x1a18bf61'); + const expectedAbiEncodedByte = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; + expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + }); + + it.skip('Bytes32 - Too long', async () => { + const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; + const byteDataType = new AbiEncoder.Byte(testByteDataItem); + + // @TODO: This does not catch the Error + expect( + byteDataType.assignValue('0x000102030405060708091112131415161718192021222324252627282930313233'), + ).to.throw( + `Tried to assign 0x000102030405060708091112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32`, + ); + }); + }); + + describe('Bytes (Dynamic)', () => { + const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; + it('Less than 32 bytes', async () => { + const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); + bytesDataType.assignValue('0x010203'); + const expectedAbiEncodedBytes = + '0x00000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000'; + + expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + }); + + it('Greater than 32 bytes', async () => { + const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); + const testValue = '0x' + '61'.repeat(40); + bytesDataType.assignValue(testValue); + const expectedAbiEncodedBytes = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + }); + + // @TODO: Add test for throw on half-byte + // @TODO: Test with no 0x prefix + // @TODO: Test with Buffer as input + }); + + describe('String', () => { + const testStringDataItem = { name: 'testString', type: 'string' }; + it('Less than 32 bytes', async () => { + const stringDataType = new AbiEncoder.SolString(testStringDataItem); + stringDataType.assignValue('five'); + const expectedAbiEncodedString = + '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + + console.log(stringDataType.getHexValue()); + console.log(expectedAbiEncodedString); + expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + }); + + it('Greater than 32 bytes', async () => { + const stringDataType = new AbiEncoder.SolString(testStringDataItem); + const testValue = 'a'.repeat(40); + stringDataType.assignValue(testValue); + const expectedAbiEncodedString = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + }); + });*/ }); -- cgit v1.2.3 From bc538c71fcab68578a82b08b4467240f8e79b96b Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 10:44:17 -0800 Subject: Tests - Encode/Decode Array --- packages/utils/src/abi_encoder/data_type.ts | 4 +- packages/utils/test/abi_encoder_test.ts | 68 +++++++++++++++++++---------- 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index 15de12a2f..022f5621f 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -41,8 +41,7 @@ export abstract class DataType { } public decode(calldata: string, rules?: DecodingRules): any { - const rawCalldata = new RawCalldata(calldata, false); - console.log(`HERE DUDE ${JSON.stringify(rawCalldata)}`); + const rawCalldata = new RawCalldata(calldata, false); // @TODO Sohuld not hardcode false here const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; const value = this.generateValue(rawCalldata, rules_); return value; @@ -278,7 +277,6 @@ export abstract class MemberDataType extends DataType { value = []; _.each(members, (member: DataType, idx: number) => { let memberValue = member.generateValue(calldata, rules); - console.log(`MEMBER VALUE: ${memberValue}`); (value as any[]).push(memberValue); }); } diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index d2d7b9825..bf6250713 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -725,7 +725,7 @@ describe.only('ABI Encoder', () => { }); describe.only('Array', () => { - it.only('sample', async () => { + it.only('Fixed size; Static elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'int[2]' }; const dataType = new AbiEncoder.SolArray(testDataItem); @@ -742,34 +742,56 @@ describe.only('ABI Encoder', () => { expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - /* - it('sample undefined size', async () => { + it.only('Dynamic size; Static elements', async () => { + // Create DataType object const testDataItem = { name: 'testArray', type: 'int[]' }; const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue([new BigNumber(5), new BigNumber(6)]); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); + // Construct args to be encoded + const args = [new BigNumber(5), new BigNumber(6)]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('sample dynamic types', async () => { + it.only('Fixed size; Dynamic elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'string[2]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + // Construct args to be encoded + const args = ["Hello", "world"]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + it.only('Dynamic size; Dynamic elements', async () => { + // Create DataType object const testDataItem = { name: 'testArray', type: 'string[]' }; const dataType = new AbiEncoder.SolArray(testDataItem); - console.log(JSON.stringify(dataType, null, 4)); - console.log('*'.repeat(60)); - dataType.assignValue(['five', 'six', 'seven']); - console.log(JSON.stringify(dataType, null, 4)); - const hexValue = dataType.getHexValue(); - console.log('*'.repeat(60)); - console.log(hexValue); - const calldata = new AbiEncoder.Calldata('0x01020304', 1); - dataType.bind(calldata, AbiEncoder.CalldataSection.PARAMS); - console.log('*'.repeat(60)); - console.log(calldata.getHexValue()); - });*/ + // Construct args to be encoded + const args = ["Hello", "world"]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); }); /* -- cgit v1.2.3 From a630312074758b31951c194533dcae596424592d Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 10:57:08 -0800 Subject: Tests for Tuple --- packages/utils/test/abi_encoder_test.ts | 67 ++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index bf6250713..cf1f0327a 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -4,6 +4,7 @@ import 'mocha'; import { chaiSetup } from './utils/chai_setup'; import { BigNumber, AbiEncoder } from '../src/'; import * as AbiSamples from './abi_samples'; +import { DecodingRules } from '../src/abi_encoder'; chaiSetup.configure(); const expect = chai.expect; @@ -724,8 +725,8 @@ describe.only('ABI Encoder', () => { }); }); - describe.only('Array', () => { - it.only('Fixed size; Static elements', async () => { + describe('Array', () => { + it('Fixed size; Static elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'int[2]' }; const dataType = new AbiEncoder.SolArray(testDataItem); @@ -742,7 +743,7 @@ describe.only('ABI Encoder', () => { expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it.only('Dynamic size; Static elements', async () => { + it('Dynamic size; Static elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'int[]' }; const dataType = new AbiEncoder.SolArray(testDataItem); @@ -759,7 +760,7 @@ describe.only('ABI Encoder', () => { expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it.only('Fixed size; Dynamic elements', async () => { + it('Fixed size; Dynamic elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'string[2]' }; const dataType = new AbiEncoder.SolArray(testDataItem); @@ -776,7 +777,7 @@ describe.only('ABI Encoder', () => { expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it.only('Dynamic size; Dynamic elements', async () => { + it('Dynamic size; Dynamic elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'string[]' }; const dataType = new AbiEncoder.SolArray(testDataItem); @@ -794,6 +795,62 @@ describe.only('ABI Encoder', () => { }); }); + describe.only('Tuple', () => { + it('Static elements only', async () => { + // Create DataType object + const testDataItem = { name: 'Tuple', type: 'tuple', components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }] }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { field_1: new BigNumber(-5), field_2: true }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + it('Dynamic elements only', async () => { + // Create DataType object + const testDataItem = { name: 'Tuple', type: 'tuple', components: [{ name: 'field_1', type: 'string' }, { name: 'field_2', type: 'bytes' }] }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { field_1: "Hello, World!", field_2: '0xabcdef0123456789' }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + it('Static and dynamic elements mixed', async () => { + // Create DataType object + const testDataItem = { name: 'Tuple', type: 'tuple', components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'string' }, { name: 'field_3', type: 'bool' }, { name: 'field_4', type: 'bytes' }] }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { field_1: new BigNumber(-5), field_2: "Hello, World!", field_3: true, field_4: '0xabcdef0123456789' }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + }); + /* describe('Address', () => { const testAddressDataItem = { name: 'testAddress', type: 'address' }; -- cgit v1.2.3 From a2ad15be0dc500fa196021a5b32e80af8500043c Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 11:18:34 -0800 Subject: Tests for Address --- packages/utils/src/abi_encoder/calldata.ts | 15 +++++-- packages/utils/src/abi_encoder/data_type.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types.ts | 13 +++++- packages/utils/test/abi_encoder_test.ts | 52 +++++++++++++++++++----- 4 files changed, 66 insertions(+), 16 deletions(-) diff --git a/packages/utils/src/abi_encoder/calldata.ts b/packages/utils/src/abi_encoder/calldata.ts index 078767c22..11288064e 100644 --- a/packages/utils/src/abi_encoder/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata.ts @@ -248,7 +248,7 @@ export class Calldata { private selector: string; private rules: EncodingRules; private sizeInBytes: number; - private root: MemberCalldataBlock | undefined; + private root: CalldataBlock | undefined; constructor(rules: EncodingRules) { this.selector = ''; @@ -257,8 +257,17 @@ export class Calldata { this.root = undefined; } - private createQueue(memberBlock: MemberCalldataBlock): Queue { + private createQueue(block: CalldataBlock): Queue { const blockQueue = new Queue(); + + // Base Case + if (block instanceof MemberCalldataBlock === false) { + blockQueue.push(block); + return blockQueue; + } + + // This is a Member Block + const memberBlock = block as MemberCalldataBlock; _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { if (member instanceof MemberCalldataBlock) { blockQueue.mergeFront(this.createQueue(member)); @@ -429,7 +438,7 @@ export class Calldata { return ""; } - public setRoot(block: MemberCalldataBlock) { + public setRoot(block: CalldataBlock) { this.root = block; this.sizeInBytes += block.getSizeInBytes(); } diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index 022f5621f..0f3cecac5 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -35,7 +35,7 @@ export abstract class DataType { const calldata = new Calldata(rules_); if (selector) calldata.setSelector(selector); const block = this.generateCalldataBlock(value); - calldata.setRoot(block as MemberCalldataBlock); // @TODO CHANGE + calldata.setRoot(block); // @TODO CHANGE const calldataHex = calldata.toHexString(); return calldataHex; } diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index bfb2808da..1ee95863b 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -20,6 +20,8 @@ export interface DataTypeStaticInterface { export class Address extends PayloadDataType { private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + public static ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X = "Address must start with '0x'"; + public static ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES = "Address must be 20 bytes"; constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance(), Address.SIZE_KNOWN_AT_COMPILE_TIME); @@ -36,9 +38,16 @@ export class Address extends PayloadDataType { return type === 'address'; } - public encodeValue(value: boolean): Buffer { + public encodeValue(value: string): Buffer { + if (value.startsWith('0x') === false) { + throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); + } + const valueAsBuffer = ethUtil.toBuffer(value); + if (valueAsBuffer.byteLength !== 20) { + throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); + } const evmWordWidth = 32; - const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value), evmWordWidth); + const encodedValueBuf = ethUtil.setLengthLeft(valueAsBuffer, evmWordWidth); return encodedValueBuf; } diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index cf1f0327a..5b341545e 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -725,7 +725,7 @@ describe.only('ABI Encoder', () => { }); }); - describe('Array', () => { + describe.only('Array', () => { it('Fixed size; Static elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'int[2]' }; @@ -793,6 +793,9 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); + + // @TODO: Add test that fails if we pass in the wrong number of elements + // @TODO: Add test that fails if we pass in an element of incorrecrt type }); describe.only('Tuple', () => { @@ -849,22 +852,51 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); + + // @TODO: Add test that fails if we pass in the wrong number of elements + // @TODO: Add test that fails if we pass in arguments in wrong order }); - /* - describe('Address', () => { - const testAddressDataItem = { name: 'testAddress', type: 'address' }; + describe.only('Address', () => { it('Valid Address', async () => { - const addressDataType = new AbiEncoder.Address(testAddressDataItem); - addressDataType.assignValue('0xe41d2489571d322189246dafa5ebde1f4699f498'); - const expectedAbiEncodedAddress = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; + // Create DataType object + const testDataItem = { name: 'Address', type: 'address' }; + const dataType = new AbiEncoder.Address(testDataItem); + // Construct args to be encoded + const args = '0xe41d2489571d322189246dafa5ebde1f4699f498'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); - console.log(addressDataType.getHexValue()); - console.log(expectedAbiEncodedAddress); - expect(addressDataType.getHexValue()).to.be.equal(expectedAbiEncodedAddress); + it('Invalid Address - input is not valid hex', async () => { + // Create DataType object + const testDataItem = { name: 'Address', type: 'address' }; + const dataType = new AbiEncoder.Address(testDataItem); + // Construct args to be encoded + const args = 'e4'; + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); + }); + + it('Invalid Address - input is not 20 bytes', async () => { + // Create DataType object + const testDataItem = { name: 'Address', type: 'address' }; + const dataType = new AbiEncoder.Address(testDataItem); + // Construct args to be encoded + const args = '0xe4'; + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); }); }); + /* describe('Bool', () => { const testBoolDataItem = { name: 'testBool', type: 'bool' }; it('True', async () => { -- cgit v1.2.3 From b28f26916fc51fa13308f335e3cd0bd5d3b075fc Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 11:20:35 -0800 Subject: Tests for Bool --- packages/utils/test/abi_encoder_test.ts | 43 ++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 5b341545e..123ebe11d 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -896,24 +896,43 @@ describe.only('ABI Encoder', () => { }); }); - /* - describe('Bool', () => { - const testBoolDataItem = { name: 'testBool', type: 'bool' }; + describe.only('Bool', () => { it('True', async () => { - const boolDataType = new AbiEncoder.Bool(testBoolDataItem); - boolDataType.assignValue(true); - const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + // Create DataType object + const testDataItem = { name: 'Boolean', type: 'bool' }; + const dataType = new AbiEncoder.Bool(testDataItem); + // Construct args to be encoded + const args = true; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); it('False', async () => { - const boolDataType = new AbiEncoder.Bool(testBoolDataItem); - boolDataType.assignValue(false); - const expectedAbiEncodedBool = '0x0000000000000000000000000000000000000000000000000000000000000000'; - expect(boolDataType.getHexValue()).to.be.equal(expectedAbiEncodedBool); + // Create DataType object + const testDataItem = { name: 'Boolean', type: 'bool' }; + const dataType = new AbiEncoder.Bool(testDataItem); + // Construct args to be encoded + const args = false; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); }); + /* describe('Integer', () => { const testIntDataItem = { name: 'testInt', type: 'int' }; it('Positive - Base case', async () => { @@ -1073,5 +1092,5 @@ describe.only('ABI Encoder', () => { '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); }); - });*/ + }); */ }); -- cgit v1.2.3 From 666075a87e8f7dfd5ef3553c5f8e08d826ac0641 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 11:47:32 -0800 Subject: Tests for Integer (tested 256 / 32 bit integers) --- packages/utils/src/abi_encoder/evm_data_types.ts | 4 +- packages/utils/test/abi_encoder_test.ts | 202 ++++++++++++++++++++--- 2 files changed, 180 insertions(+), 26 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index 1ee95863b..38878f63e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -118,9 +118,9 @@ abstract class Number extends PayloadDataType { public encodeValue(value_: BigNumber | string | number): Buffer { const value = new BigNumber(value_, 10); if (value.greaterThan(this.getMaxValue())) { - throw `tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; + throw `Tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; } else if (value.lessThan(this.getMinValue())) { - throw `tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; + throw `Tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; } const hexBase = 16; diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 123ebe11d..21ad89711 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -932,40 +932,194 @@ describe.only('ABI Encoder', () => { }); }); - /* - describe('Integer', () => { - const testIntDataItem = { name: 'testInt', type: 'int' }; - it('Positive - Base case', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(1)); - const expectedAbiEncodedInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + describe.only('Integer', () => { + it('Int256 - Positive Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = new BigNumber(1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Positive', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(437829473)); - const expectedAbiEncodedInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + it('Int256 - Negative Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = new BigNumber(-1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Negative - Base case', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(-1)); - const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + it('Int256 - Positive Value', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const max256BitInteger = (new BigNumber(2)).pow(255).minus(1); + const args = max256BitInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Negative', async () => { - const intDataType = new AbiEncoder.Int(testIntDataItem); - intDataType.assignValue(new BigNumber(-437829473)); - const expectedAbiEncodedInt = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffe5e7409f'; - expect(intDataType.getHexValue()).to.be.equal(expectedAbiEncodedInt); + it('Int256 - Negative Value', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const min256BitInteger = (new BigNumber(2)).pow(255).times(-1); + const args = min256BitInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = `0x8000000000000000000000000000000000000000000000000000000000000000`; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - // TODO: Add bounds tests + tests for different widths + it('Int256 - Value too large', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const max256BitInteger = (new BigNumber(2)).pow(255).minus(1); + const args = max256BitInteger.plus(1); + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw(); + }); + + it('Int256 - Value too small', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const min256BitInteger = (new BigNumber(2)).pow(255).times(-1); + const args = min256BitInteger.minus(1); + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw(); + }); + + it('Int32 - Positive Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = new BigNumber(1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + it('Int32 - Negative Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = new BigNumber(-1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + it('Int32 - Positive Value', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const max32BitInteger = (new BigNumber(2)).pow(31).minus(1); + const args = max32BitInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000007fffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + it('Int32 - Negative Value', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const min32BitInteger = (new BigNumber(2)).pow(31).times(-1); + const args = min32BitInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000`; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + it('Int32 - Value too large', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const max32BitInteger = (new BigNumber(2)).pow(31).minus(1); + const args = max32BitInteger.plus(1); + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw(); + }); + + it('Int32 - Value too small', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const min32BitInteger = (new BigNumber(2)).pow(31).times(-1); + const args = min32BitInteger.minus(1); + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw(); + }); }); + /* + describe('Unsigned Integer', () => { const testIntDataItem = { name: 'testUInt', type: 'uint' }; it('Lower Bound', async () => { -- cgit v1.2.3 From 9a0bd05c4ca98dd24d71cdbd53175bfdd5250107 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 11:55:56 -0800 Subject: Unsigned Integers --- packages/utils/test/abi_encoder_test.ts | 164 ++++++++++++++++++++++++++++---- 1 file changed, 145 insertions(+), 19 deletions(-) diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 21ad89711..4b85ad938 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -1118,34 +1118,160 @@ describe.only('ABI Encoder', () => { }); }); - /* + describe.only('Unsigned Integer', () => { + it('UInt256 - Positive Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = new BigNumber(1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + it('UInt256 - Positive Value', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const max256BitUnsignedInteger = (new BigNumber(2)).pow(256).minus(1); + const args = max256BitUnsignedInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + it('UInt256 - Zero Value', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const min256BitUnsignedInteger = new BigNumber(0); + const args = min256BitUnsignedInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = `0x0000000000000000000000000000000000000000000000000000000000000000`; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + it('UInt256 - Value too large', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const max256BitUnsignedInteger = (new BigNumber(2)).pow(256).minus(1); + const args = max256BitUnsignedInteger.plus(1); + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw(); + }); + + it('UInt256 - Value too small', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const min256BitUnsignedInteger = new BigNumber(0) + const args = min256BitUnsignedInteger.minus(1); + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw(); + }); + + it('UInt32 - Positive Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = new BigNumber(1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); - describe('Unsigned Integer', () => { - const testIntDataItem = { name: 'testUInt', type: 'uint' }; - it('Lower Bound', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(0)); - const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000000'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + it('UInt32 - Positive Value', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const max32BitUnsignedInteger = (new BigNumber(2)).pow(32).minus(1); + const args = max32BitUnsignedInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000ffffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Base Case', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(1)); - const expectedAbiEncodedUInt = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + it('UInt32 - Zero Value', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const min32BitUnsignedInteger = new BigNumber(0); + const args = min32BitUnsignedInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = `0x0000000000000000000000000000000000000000000000000000000000000000`; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Random value', async () => { - const uintDataType = new AbiEncoder.UInt(testIntDataItem); - uintDataType.assignValue(new BigNumber(437829473)); - const expectedAbiEncodedUInt = '0x000000000000000000000000000000000000000000000000000000001a18bf61'; - expect(uintDataType.getHexValue()).to.be.equal(expectedAbiEncodedUInt); + it('UInt32 - Value too large', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const max32BitUnsignedInteger = (new BigNumber(2)).pow(32).minus(1); + const args = max32BitUnsignedInteger.plus(1); + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw(); }); - // TODO: Add bounds tests + tests for different widths + it('UInt32 - Value too small', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const min32BitUnsignedInteger = new BigNumber(0); + const args = min32BitUnsignedInteger.minus(1); + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw(); + }); }); + /* + describe('Static Bytes', () => { it('Byte (padded)', async () => { const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; -- cgit v1.2.3 From d70d07366fcc9ed2a750a5427af9d210c486f344 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 12:43:16 -0800 Subject: Tests for Static Bytes --- packages/utils/src/abi_encoder/evm_data_types.ts | 4 + packages/utils/test/abi_encoder_test.ts | 176 +++++++++++++++++------ 2 files changed, 140 insertions(+), 40 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index 38878f63e..ff1bc594d 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -270,6 +270,10 @@ export class Byte extends PayloadDataType { } public encodeValue(value: string | Buffer): Buffer { + // Sanity check if string + if (typeof value === 'string' && value.startsWith('0x') === false) { + throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); + } // Convert value into a buffer and do bounds checking const valueBuf = ethUtil.toBuffer(value); if (valueBuf.byteLength > this.width) { diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 4b85ad938..5fe625092 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -1270,62 +1270,158 @@ describe.only('ABI Encoder', () => { }); }); - /* + describe.only('Static Bytes', () => { + it('Single Byte (byte)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Byte', type: 'byte' }; + const dataType = new AbiEncoder.Byte(testDataItem); + // Construct args to be encoded + const args = '0x05'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); - describe('Static Bytes', () => { - it('Byte (padded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x05'); - const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + it('Single Byte (bytes1)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes1', type: 'bytes1' }; + const dataType = new AbiEncoder.Byte(testDataItem); + // Construct args to be encoded + const args = '0x05'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it.skip('Byte (no padding)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'byte' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); + it('4 Bytes (bytes4)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; + const dataType = new AbiEncoder.Byte(testDataItem); + // Construct args to be encoded + const args = '0x00010203'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0001020300000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); - // @TODO: This does not catch the Error - expect(byteDataType.assignValue('0x5')).to.throw(); + it('4 Bytes (bytes4); Encoder must pad input', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; + const dataType = new AbiEncoder.Byte(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = '0x1a18'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x1a18000000000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const paddedArgs = '0x1a180000'; + const paddedArgsAsJson = JSON.stringify(paddedArgs); + expect(decodedArgsAsJson).to.be.equal(paddedArgsAsJson); }); - it('Bytes1', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes1' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x05'); - const expectedAbiEncodedByte = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + it('32 Bytes (bytes32)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; + const dataType = new AbiEncoder.Byte(testDataItem); + // Construct args to be encoded + const args = '0x0001020304050607080911121314151617181920212223242526272829303132'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0001020304050607080911121314151617181920212223242526272829303132'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + + it('32 Bytes (bytes32); Encoder must pad input', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; + const dataType = new AbiEncoder.Byte(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = '0x1a18bf61'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const paddedArgs = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; + const paddedArgsAsJson = JSON.stringify(paddedArgs); + expect(decodedArgsAsJson).to.be.equal(paddedArgsAsJson); }); - it('Bytes32 (padded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x0001020304050607080911121314151617181920212223242526272829303132'); - const expectedAbiEncodedByte = '0x0001020304050607080911121314151617181920212223242526272829303132'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + it('Should throw when pass in too many bytes (bytes4)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; + const dataType = new AbiEncoder.Byte(testDataItem); + // Construct args to be encoded + const args = '0x0102030405'; + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw('Tried to assign 0x0102030405 (5 bytes), which exceeds max bytes that can be stored in a bytes4'); }); - it('Bytes32 (unpadded)', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); - byteDataType.assignValue('0x1a18bf61'); - const expectedAbiEncodedByte = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; - expect(byteDataType.getHexValue()).to.be.equal(expectedAbiEncodedByte); + it('Should throw when pass in too many bytes (bytes32)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; + const dataType = new AbiEncoder.Byte(testDataItem); + // Construct args to be encoded + const args = '0x010203040506070809101112131415161718192021222324252627282930313233'; + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw('Tried to assign 0x010203040506070809101112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32'); }); - it.skip('Bytes32 - Too long', async () => { - const testByteDataItem = { name: 'testStaticBytes', type: 'bytes32' }; - const byteDataType = new AbiEncoder.Byte(testByteDataItem); + it('Should throw when pass in bad hex (no 0x prefix)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; + const dataType = new AbiEncoder.Byte(testDataItem); + // Construct args to be encoded + const args = '0102030405060708091011121314151617181920212223242526272829303132'; + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix."); + }); - // @TODO: This does not catch the Error - expect( - byteDataType.assignValue('0x000102030405060708091112131415161718192021222324252627282930313233'), - ).to.throw( - `Tried to assign 0x000102030405060708091112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32`, - ); + it('Should throw when pass in bad hex (include a half-byte)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; + const dataType = new AbiEncoder.Byte(testDataItem); + // Construct args to be encoded + const args = '0x010'; + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); }); }); + /* + + + describe('Bytes (Dynamic)', () => { const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; it('Less than 32 bytes', async () => { -- cgit v1.2.3 From 5b187935dc6bf194555e1652dbf53d1c637d4add Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 13:01:26 -0800 Subject: tests for String --- packages/utils/src/abi_encoder/evm_data_types.ts | 4 +- packages/utils/test/abi_encoder_test.ts | 160 +++++++++++++++++------ 2 files changed, 123 insertions(+), 41 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index ff1bc594d..b6a62a5d4 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -318,7 +318,7 @@ export class Bytes extends PayloadDataType { public encodeValue(value: string | Buffer): Buffer { if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Input value must be hex (prefixed with 0x). Actual value is '${value}'`); + throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '${value}'`); } const valueBuf = ethUtil.toBuffer(value); if (value.length % 2 !== 0) { @@ -365,7 +365,7 @@ export class SolString extends PayloadDataType { public encodeValue(value: string): Buffer { const wordsForValue = Math.ceil(value.length / 32); const paddedBytesForValue = wordsForValue * 32; - const valueBuf = ethUtil.setLengthRight(ethUtil.toBuffer(value), paddedBytesForValue); + const valueBuf = ethUtil.setLengthRight(new Buffer(value), paddedBytesForValue); const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); return encodedValueBuf; diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 5fe625092..b541cd758 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -5,6 +5,7 @@ import { chaiSetup } from './utils/chai_setup'; import { BigNumber, AbiEncoder } from '../src/'; import * as AbiSamples from './abi_samples'; import { DecodingRules } from '../src/abi_encoder'; +import ethUtil = require('ethereumjs-util'); chaiSetup.configure(); const expect = chai.expect; @@ -1418,55 +1419,136 @@ describe.only('ABI Encoder', () => { }); }); - /* - - + describe.only('Dynamic Bytes Dynamic', () => { + it('Fits into one EVM word', async () => { + // Create DataType object + const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; + const dataType = new AbiEncoder.Bytes(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = '0x1a18bf61'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); - describe('Bytes (Dynamic)', () => { - const testBytesDataItem = { name: 'testBytes', type: 'bytes' }; - it('Less than 32 bytes', async () => { - const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); - bytesDataType.assignValue('0x010203'); - const expectedAbiEncodedBytes = - '0x00000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000'; + it('Spans multiple EVM words', async () => { + // Create DataType object + const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; + const dataType = new AbiEncoder.Bytes(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = '0x' + '61'.repeat(40); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); - expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + it('Input as Buffer', async () => { + // Create DataType object + const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; + const dataType = new AbiEncoder.Bytes(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = '0x1a18bf61'; + const argsAsBuffer = ethUtil.toBuffer(args); + // Encode Args and validate result + const encodedArgs = dataType.encode(argsAsBuffer); + const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Greater than 32 bytes', async () => { - const bytesDataType = new AbiEncoder.Bytes(testBytesDataItem); - const testValue = '0x' + '61'.repeat(40); - bytesDataType.assignValue(testValue); - const expectedAbiEncodedBytes = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(bytesDataType.getHexValue()).to.be.equal(expectedAbiEncodedBytes); + it('Should throw when pass in bad hex (no 0x prefix)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes', type: 'bytes' }; + const dataType = new AbiEncoder.Bytes(testDataItem); + // Construct args to be encoded + const args = '01'; + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '01'"); }); - // @TODO: Add test for throw on half-byte - // @TODO: Test with no 0x prefix - // @TODO: Test with Buffer as input + it('Should throw when pass in bad hex (include a half-byte)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes', type: 'bytes' }; + const dataType = new AbiEncoder.Bytes(testDataItem); + // Construct args to be encoded + const args = '0x010'; + // Encode Args and validate result + expect(() => { dataType.encode(args) }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); + }); }); - describe('String', () => { - const testStringDataItem = { name: 'testString', type: 'string' }; - it('Less than 32 bytes', async () => { - const stringDataType = new AbiEncoder.SolString(testStringDataItem); - stringDataType.assignValue('five'); - const expectedAbiEncodedString = - '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + describe.only('String', () => { + it('Fits into one EVM word', async () => { + // Create DataType object + const testDataItem = { name: 'String', type: 'string' }; + const dataType = new AbiEncoder.SolString(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = 'five'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); - console.log(stringDataType.getHexValue()); - console.log(expectedAbiEncodedString); - expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + it('Spans multiple EVM words', async () => { + // Create DataType object + const testDataItem = { name: 'String', type: 'string' }; + const dataType = new AbiEncoder.SolString(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = 'a'.repeat(40); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Greater than 32 bytes', async () => { - const stringDataType = new AbiEncoder.SolString(testStringDataItem); - const testValue = 'a'.repeat(40); - stringDataType.assignValue(testValue); - const expectedAbiEncodedString = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(stringDataType.getHexValue()).to.be.equal(expectedAbiEncodedString); + it('String that begins with 0x prefix', async () => { + // Create DataType object + const testDataItem = { name: 'String', type: 'string' }; + const dataType = new AbiEncoder.SolString(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = '0x' + 'a'.repeat(40); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000002a30786161616161616161616161616161616161616161616161616161616161616161616161616161616100000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - }); */ + }); }); -- cgit v1.2.3 From b213cb39747ba0b78138d5a1db07b511bd9fd3b8 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 13:05:36 -0800 Subject: Temporary change for testing functions --- packages/utils/src/abi_encoder/data_type.ts | 2 +- packages/utils/test/abi_encoder_test.ts | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index 0f3cecac5..21c08ef54 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -41,7 +41,7 @@ export abstract class DataType { } public decode(calldata: string, rules?: DecodingRules): any { - const rawCalldata = new RawCalldata(calldata, false); // @TODO Sohuld not hardcode false here + const rawCalldata = new RawCalldata(calldata, true); // @TODO Sohuld not hardcode false here const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; const value = this.generateValue(rawCalldata, rules_); return value; diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index b541cd758..6d0d0c390 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -11,12 +11,7 @@ chaiSetup.configure(); const expect = chai.expect; describe.only('ABI Encoder', () => { - describe.only('Optimizer', () => { - - }); - - describe('ABI Tests at Method Level', () => { - + describe('Optimizer', () => { it('Should reuse duplicated strings in string array', async () => { const method = new AbiEncoder.Method(AbiSamples.GAbi); @@ -147,7 +142,9 @@ describe.only('ABI Encoder', () => { console.log(JSON.stringify(decodedArgs)); expect(decodedArgsJson).to.be.equal(argsJson); }); + }); + describe.only('ABI Tests at Method Level', () => { it('Crazy ABI', async () => { const method = new AbiEncoder.Method(AbiSamples.crazyAbi); console.log(method.getSignature()); -- cgit v1.2.3 From 0c0bcb44d3ef6d68c9c7c05be25641ef57a7287a Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 13:07:34 -0800 Subject: All 71 tests passing. Both for function encoding and individual types. --- packages/utils/src/abi_encoder/data_type.ts | 4 ++-- packages/utils/src/abi_encoder/evm_data_types.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index 21c08ef54..243b221ef 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -40,8 +40,8 @@ export abstract class DataType { return calldataHex; } - public decode(calldata: string, rules?: DecodingRules): any { - const rawCalldata = new RawCalldata(calldata, true); // @TODO Sohuld not hardcode false here + public decode(calldata: string, rules?: DecodingRules, hasSelector: boolean = false): any { + const rawCalldata = new RawCalldata(calldata, hasSelector); const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; const value = this.generateValue(rawCalldata, rules_); return value; diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index b6a62a5d4..2895ee00f 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -515,7 +515,8 @@ export class Method extends MemberDataType { if (!calldata.startsWith(this.selector)) { throw new Error(`Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`); } - const value = super.decode(calldata, rules); + const hasSelector = true; + const value = super.decode(calldata, rules, hasSelector); return value; } -- cgit v1.2.3 From 67dd062a2f6a936cad18ff81afac398cd6a1ab97 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 14:35:40 -0800 Subject: Cleaning up optimizer tests --- packages/utils/src/abi_encoder/calldata.ts | 37 +- packages/utils/src/abi_encoder/data_type.ts | 68 +- packages/utils/src/abi_encoder/evm_data_types.ts | 27 +- packages/utils/src/abi_encoder/index.ts | 2 +- packages/utils/test/abi_encoder_test.ts | 1085 +++++++++++----------- packages/utils/test/abi_samples.ts | 73 +- 6 files changed, 687 insertions(+), 605 deletions(-) diff --git a/packages/utils/src/abi_encoder/calldata.ts b/packages/utils/src/abi_encoder/calldata.ts index 11288064e..e6ce58957 100644 --- a/packages/utils/src/abi_encoder/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata.ts @@ -20,7 +20,14 @@ export abstract class CalldataBlock { private relocatable: boolean; private parentName: string; - constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ headerSizeInBytes: number, bodySizeInBytes: number, relocatable: boolean) { + constructor( + name: string, + signature: string, + parentName: string, + /*offsetInBytes: number,*/ headerSizeInBytes: number, + bodySizeInBytes: number, + relocatable: boolean, + ) { this.name = name; this.signature = signature; this.parentName = parentName; @@ -91,7 +98,13 @@ export abstract class CalldataBlock { export class PayloadCalldataBlock extends CalldataBlock { private payload: Buffer; - constructor(name: string, signature: string, parentName: string, /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer) { + constructor( + name: string, + signature: string, + parentName: string, + /*offsetInBytes: number,*/ relocatable: boolean, + payload: Buffer, + ) { const headerSizeInBytes = 0; const bodySizeInBytes = payload.byteLength; super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); @@ -115,7 +128,14 @@ export class DependentCalldataBlock extends CalldataBlock { private dependency: CalldataBlock; private aliasFor: CalldataBlock | undefined; - constructor(name: string, signature: string, parentName: string, relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock) { + constructor( + name: string, + signature: string, + parentName: string, + relocatable: boolean, + dependency: CalldataBlock, + parent: CalldataBlock, + ) { const headerSizeInBytes = 0; const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); @@ -125,7 +145,8 @@ export class DependentCalldataBlock extends CalldataBlock { } public toBuffer(): Buffer { - const destinationOffset = (this.aliasFor !== undefined) ? this.aliasFor.getOffsetInBytes() : this.dependency.getOffsetInBytes(); + const destinationOffset = + this.aliasFor !== undefined ? this.aliasFor.getOffsetInBytes() : this.dependency.getOffsetInBytes(); const parentOffset = this.parent.getOffsetInBytes(); const parentHeaderSize = this.parent.getHeaderSizeInBytes(); const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); @@ -314,7 +335,7 @@ export class Calldata { //const ancestrialNamesOffset = name.startsWith('ptr<') ? 4 : 0; //const parentOffset = name.lastIndexOf(parentName); - const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, '');//.replace(`${parentName}[`, '['); + const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, ''); //.replace(`${parentName}[`, '['); const signature = block.getSignature(); // Current offset @@ -383,7 +404,7 @@ export class Calldata { const blocksByHash: { [key: string]: CalldataBlock } = {}; // 1. Create a queue of subtrees by hash - // Note that they are ordered the same as + // Note that they are ordered the same as const subtreeQueue = this.createQueue(this.root); let block: CalldataBlock | undefined; while ((block = subtreeQueue.popBack()) !== undefined) { @@ -435,7 +456,7 @@ export class Calldata { } public toAnnotatedString(): string { - return ""; + return ''; } public setRoot(block: CalldataBlock) { @@ -532,4 +553,4 @@ export class RawCalldata { public getSelector(): string { return this.selector; } -} \ No newline at end of file +} diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index 243b221ef..80797c563 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -1,4 +1,11 @@ -import { RawCalldata, Calldata, CalldataBlock, PayloadCalldataBlock, DependentCalldataBlock, MemberCalldataBlock } from "./calldata"; +import { + RawCalldata, + Calldata, + CalldataBlock, + PayloadCalldataBlock, + DependentCalldataBlock, + MemberCalldataBlock, +} from './calldata'; import { MethodAbi, DataItem } from 'ethereum-types'; import { DecodingRules, EncodingRules } from './calldata'; import { BigNumber } from '../configured_bignumber'; @@ -67,7 +74,13 @@ export abstract class PayloadDataType extends DataType { const signature = this.getSignature(); const parentName = parentBlock === undefined ? '' : parentBlock.getName(); const relocatable = false; - const block = new PayloadCalldataBlock(name, signature, parentName, /*offsetInBytes,*/ relocatable, encodedValue); + const block = new PayloadCalldataBlock( + name, + signature, + parentName, + /*offsetInBytes,*/ relocatable, + encodedValue, + ); return block; } @@ -104,7 +117,14 @@ export abstract class DependentDataType extends DataType { const signature = this.getSignature(); const parentName = parentBlock === undefined ? '' : parentBlock.getName(); const relocatable = false; - const block = new DependentCalldataBlock(name, signature, parentName, relocatable, dependencyBlock, parentBlock); + const block = new DependentCalldataBlock( + name, + signature, + parentName, + relocatable, + dependencyBlock, + parentBlock, + ); return block; } @@ -135,8 +155,13 @@ export abstract class MemberDataType extends DataType { protected arrayLength: number | undefined; protected arrayElementType: string | undefined; - - public constructor(dataItem: DataItem, factory: DataTypeFactory, isArray: boolean = false, arrayLength?: number, arrayElementType?: string) { + public constructor( + dataItem: DataItem, + factory: DataTypeFactory, + isArray: boolean = false, + arrayLength?: number, + arrayElementType?: string, + ) { super(dataItem, factory); this.memberMap = {}; this.members = []; @@ -207,11 +232,17 @@ export abstract class MemberDataType extends DataType { } const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( + this.getDataItem().name, + this.getSignature(), + parentName, + this.isStatic(), + false, + ); let members = this.members; if (this.isArray && this.arrayLength === undefined) { - [members,] = this.createMembersWithLength(this.getDataItem(), value.length); + [members] = this.createMembersWithLength(this.getDataItem(), value.length); const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(16)}`), 32); methodBlock.setHeader(lenBuf); @@ -228,12 +259,20 @@ export abstract class MemberDataType extends DataType { protected generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock(this.getDataItem().name, this.getSignature(), parentName, this.isStatic(), false); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( + this.getDataItem().name, + this.getSignature(), + parentName, + this.isStatic(), + false, + ); const memberBlocks: CalldataBlock[] = []; let childMap = _.cloneDeep(this.memberMap); _.forOwn(obj, (value: any, key: string) => { if (key in childMap === false) { - throw new Error(`Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`); + throw new Error( + `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`, + ); } const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock); memberBlocks.push(block); @@ -249,7 +288,10 @@ export abstract class MemberDataType extends DataType { } public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const block = (value instanceof Array) ? this.generateCalldataBlockFromArray(value, parentBlock) : this.generateCalldataBlockFromObject(value, parentBlock); + const block = + value instanceof Array + ? this.generateCalldataBlockFromArray(value, parentBlock) + : this.generateCalldataBlockFromObject(value, parentBlock); return block; } @@ -261,7 +303,7 @@ export abstract class MemberDataType extends DataType { const hexBase = 16; const arrayLength = new BigNumber(arrayLengthHex, hexBase); - [members,] = this.createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); + [members] = this.createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); } calldata.startScope(); @@ -314,9 +356,9 @@ export abstract class MemberDataType extends DataType { // Search for dependent members const dependentMember = _.find(this.members, (member: DataType) => { - return (member instanceof DependentDataType); + return member instanceof DependentDataType; }); - const isStatic = (dependentMember === undefined); // static if we couldn't find a dependent member + const isStatic = dependentMember === undefined; // static if we couldn't find a dependent member return isStatic; } } diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index 2895ee00f..10e7b987b 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -21,7 +21,7 @@ export interface DataTypeStaticInterface { export class Address extends PayloadDataType { private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; public static ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X = "Address must start with '0x'"; - public static ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES = "Address must be 20 bytes"; + public static ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES = 'Address must be 20 bytes'; constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance(), Address.SIZE_KNOWN_AT_COMPILE_TIME); @@ -88,7 +88,7 @@ export class Bool extends PayloadDataType { const valueBuf = calldata.popWord(); const valueHex = ethUtil.bufferToHex(valueBuf); const valueNumber = new BigNumber(valueHex, 16); - let value: boolean = (valueNumber.equals(0)) ? false : true; + let value: boolean = valueNumber.equals(0) ? false : true; if (!(valueNumber.equals(0) || valueNumber.equals(1))) { throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); } @@ -147,10 +147,7 @@ abstract class Number extends PayloadDataType { const negativeValue = invertedValue.plus(1); // Convert the negated value to a hex string - valueBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), - evmWordWidth, - ); + valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), evmWordWidth); } return valueBuf; @@ -279,7 +276,7 @@ export class Byte extends PayloadDataType { if (valueBuf.byteLength > this.width) { throw new Error( `Tried to assign ${value} (${ - valueBuf.byteLength + valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); } else if (value.length % 2 !== 0) { @@ -392,7 +389,6 @@ export class SolString extends PayloadDataType { } export class Pointer extends DependentDataType { - constructor(destDataType: DataType, parentDataType: DataType) { const destDataItem = destDataType.getDataItem(); const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; @@ -442,7 +438,7 @@ export class SolArray extends MemberDataType { const isArray = true; const arrayElementType = matches[1]; - const arrayLength = (matches[2] === '') ? undefined : parseInt(matches[2], 10); + const arrayLength = matches[2] === '' ? undefined : parseInt(matches[2], 10); super(dataItem, EvmDataTypeFactory.getInstance(), isArray, arrayLength, arrayElementType); this.elementType = arrayElementType; this.arraySignature = this.computeSignature(); @@ -513,7 +509,9 @@ export class Method extends MemberDataType { public decode(calldata: string, rules?: DecodingRules): any[] | object { if (!calldata.startsWith(this.selector)) { - throw new Error(`Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`); + throw new Error( + `Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`, + ); } const hasSelector = true; const value = super.decode(calldata, rules, hasSelector); @@ -524,7 +522,7 @@ export class Method extends MemberDataType { //console.log('O'.repeat(100), '\n', returndata, '\n', this.returnDataTypes, 'P'.repeat(100)); const returnValues: any[] = []; - const rules_ = rules ? rules : { structsAsObjects: false } as DecodingRules; + const rules_ = rules ? rules : ({ structsAsObjects: false } as DecodingRules); const rawReturnData = new RawCalldata(returndata, false); _.each(this.returnDataTypes, (dataType: DataType) => { returnValues.push(dataType.generateValue(rawReturnData, rules_)); @@ -549,7 +547,7 @@ export class Method extends MemberDataType { export class EvmDataTypeFactory implements DataTypeFactory { private static instance: DataTypeFactory; - private constructor() { } + private constructor() {} public static getInstance(): DataTypeFactory { if (!EvmDataTypeFactory.instance) { @@ -580,10 +578,11 @@ export class EvmDataTypeFactory implements DataTypeFactory { return dataType; } - if (parentDataType === undefined) { // @Todo -- will this work for return values? + if (parentDataType === undefined) { + // @Todo -- will this work for return values? throw new Error(`Trying to create a pointer`); } const pointer = new Pointer(dataType, parentDataType); return pointer; } -} \ No newline at end of file +} diff --git a/packages/utils/src/abi_encoder/index.ts b/packages/utils/src/abi_encoder/index.ts index 991edb8c5..95ad84ac9 100644 --- a/packages/utils/src/abi_encoder/index.ts +++ b/packages/utils/src/abi_encoder/index.ts @@ -1,2 +1,2 @@ export { EncodingRules, DecodingRules } from './calldata'; -export * from './evm_data_types'; \ No newline at end of file +export * from './evm_data_types'; diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 6d0d0c390..9925abcc3 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -12,273 +12,188 @@ const expect = chai.expect; describe.only('ABI Encoder', () => { describe('Optimizer', () => { - it('Should reuse duplicated strings in string array', async () => { - const method = new AbiEncoder.Method(AbiSamples.GAbi); - - const args = [ - { - a: new BigNumber(5), - e: '0x616161', - b: 'aaa', - f: '0xe41d2489571d322189246dafa5ebde1f4699f498' - } - ] - - // Verify optimized calldata is expected - const optimizedCalldata = method.encode(args, { optimize: true }); - //const expectedOptimizedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; - //expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - - // Verify args decode properly - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - //expect(decodedArgsJson).to.be.equal(argsJson); - - console.log(method.getSignature()); - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); - - }); - - it('Should reuse duplicated strings in string array', async () => { + it('Should reuse duplicate strings in string array', async () => { + // Description: + // There are two unique values in the array `strings`. + // There should exist only one copy of each string in the optimized calldata. + // In unoptimized calldata, two copies of each string will be created. + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.stringAbi); - const strings = [ - "Test String", - "Test String 2", - "Test String", - "Test String 2", - ]; + const strings = ['Test String', 'Test String 2', 'Test String', 'Test String 2']; const args = [strings]; - - // Verify optimized calldata is expected + // Validate calldata const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; + const expectedOptimizedCalldata = + '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - - // Verify args decode properly + // Validate decoding const decodedArgs = method.decode(optimizedCalldata); const decodedArgsJson = JSON.stringify(decodedArgs); const argsJson = JSON.stringify(args); expect(decodedArgsJson).to.be.equal(argsJson); - - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); }); - - it('Should point array elements to a duplicated value from another parameter', async () => { + it('Should point array elements to a duplicate value from another parameter', async () => { + // Description: + // There are two unique values in the array `strings`. + // The value "Test String" appears three times in this array. + // There should exist only one copy of this string in the optimized calldata. + // In unoptimized calldata, "Test String" would be written three times. + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.optimizerAbi2); - const stringArray = [ - "Test String", - "Test String", - "Test String", - "Test String 2", - ]; + const strings = ['Test String', 'Test String', 'Test String', 'Test String 2']; const string = 'Test String'; - const args = [stringArray, string]; - - // Verify optimized calldata is expected + const args = [strings, string]; + // Validate calldata const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000'; + const expectedOptimizedCalldata = + '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - - // Verify args decode properly + // Validate decoding const decodedArgs = method.decode(optimizedCalldata); const decodedArgsJson = JSON.stringify(decodedArgs); const argsJson = JSON.stringify(args); expect(decodedArgsJson).to.be.equal(argsJson); - - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true, annotate: true }), '\n', '*'.repeat(100)); - console.log('*'.repeat(100), '\n', method.encode(args, { optimize: true }), '\n', '*'.repeat(100)); }); - - - it('Optimizer #3 (tuple should point to array)', async () => { + it('Dynamic Array of uints should point to Dynamic Array of Tuple(Uint)s', async () => { + // Description: + // There are two dynamic arrays, one of uint's and one of tuples. + // Each tuple is simply a wrapper for a uint - tuple := {key: uintValue} + // While the elements of these arrays have different types, they + // have the same representation in calldata. + // That is, a `uint` and a `tuple{uint}` both consume exactly one word of calldata. + // In the optimized calldata, only the elements of one array should be included. + // Both arrays will then point to the same set of elements. + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.optimizerAbi3); - const uint8Array = [ - new BigNumber(100), - new BigNumber(150), - new BigNumber(200), - new BigNumber(225), - ]; + const uint8Array = [new BigNumber(100), new BigNumber(150), new BigNumber(200), new BigNumber(225)]; const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; const args = [uint8Array, uintTupleArray]; - - - const TEST = method.encode(args, { optimize: true, annotate: true }); - console.log('*'.repeat(50), ' ENCODED DATA ', TEST); - + // Validata calldata const optimizedCalldata = method.encode(args, { optimize: true }); - - console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); + const expectedOptimizedCalldata = + '0x5b5c78fd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000e1'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding const decodedArgs = method.decode(optimizedCalldata); const decodedArgsJson = JSON.stringify(decodedArgs); const argsJson = JSON.stringify(args); - console.log(JSON.stringify(decodedArgs)); expect(decodedArgsJson).to.be.equal(argsJson); }); - - it('Optimizer #4 (Expect no optimization)', async () => { - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi4); - const uint8Array = [ - new BigNumber(100), - new BigNumber(150), - new BigNumber(200), - new BigNumber(225), - ]; - const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; - const args = [uint8Array, uintTupleArray]; - - - const TEST = method.encode(args, { optimize: true, annotate: true }); - console.log('*'.repeat(50), ' ENCODED DATA ', TEST); - + it('Duplicate Dynamic Arrays', async () => { + // Description: + // Two dynamic arrays with the same values. + // In the optimized calldata, only one set of elements should be included. + // Both arrays should point to this set. + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi5); + const array1 = [new BigNumber(100), new BigNumber(150)]; + const array2 = [array1[0], array1[1]]; + const args = [array1, array2]; + // Validata calldata const optimizedCalldata = method.encode(args, { optimize: true }); - - console.log(`OPTIMIZED CALLDATA == '${optimizedCalldata}'`); + const expectedOptimizedCalldata = + '0x7bc4226e00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding const decodedArgs = method.decode(optimizedCalldata); const decodedArgsJson = JSON.stringify(decodedArgs); const argsJson = JSON.stringify(args); - console.log(JSON.stringify(decodedArgs)); expect(decodedArgsJson).to.be.equal(argsJson); }); - }); - describe.only('ABI Tests at Method Level', () => { - it('Crazy ABI', async () => { - const method = new AbiEncoder.Method(AbiSamples.crazyAbi); - console.log(method.getSignature()); - - const someStaticArray = [new BigNumber(127), new BigNumber(14), new BigNumber(54)]; - const someStaticArrayWithDynamicMembers = [ - 'the little piping piper piped a piping pipper papper', - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ]; - const someDynamicArrayWithDynamicMembers = [ - '0x38745637834987324827439287423897238947239847', - '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', - '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', - ]; - const some2DArray = [ - [ - 'some string', - 'some another string', - 'there are just too many stringsup in', - 'here', - 'yall ghonna make me lose my mind', - ], - [ - 'the little piping piper piped a piping pipper papper', - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ], - [], - ]; - const someTuple = { - someUint32: new BigNumber(4037824789), - someStr: 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.' - }; - const someTupleWithDynamicTypes = { - someUint: new BigNumber(4024789), - someStr: 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', - someBytes: '0x29384723894723843743289742389472398473289472348927489274894738427428947389facdea', - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', - }; - const someTupleWithDynamicTypes2 = { - someUint: new BigNumber(9024789), - someStr: 'ksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjkakdhjasjkdhasjkldshdjahdkjsahdaj', - someBytes: '0x29384723894398473289472348927489272384374328974238947274894738427428947389facde1', - someAddress: '0x746dafa5ebde1f4699f4981d3221892e41d24895', - }; - const someTupleWithDynamicTypes3 = { - someUint: new BigNumber(1024789), - someStr: 'sdhsajkdhsajkdhadjkashdjakdhjasjkdhasjkldshdjahdkjsahdajkksadhajkdhsajkdhsadjk', - someBytes: '0x38947238437432829384729742389472398473289472348927489274894738427428947389facdef', - someAddress: '0x89571d322189e415ebde1f4699f498d24246dafa', - }; - const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3]; - - const args = { - someStaticArray: someStaticArray, - someStaticArrayWithDynamicMembers: someStaticArrayWithDynamicMembers, - someDynamicArrayWithDynamicMembers: someDynamicArrayWithDynamicMembers, - some2DArray: some2DArray, - someTuple: someTuple, - someTupleWithDynamicTypes: someTupleWithDynamicTypes, - someArrayOfTuplesWithDynamicTypes: someArrayOfTuplesWithDynamicTypes - }; - - const calldata = method.encode(args); - console.log(calldata); - - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); - console.log(method.getSignature()); - - const expectedCalldata = '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; - //const expectedCalldata = '0x30e1f844000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000042000000000000000000000000000000000000000000000000000000000000008600000000000000000000000000000000000000000000000000000000000000960000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cf5763d5ec63d500600000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f7484848484848484848484848484848484848384757687980943399445858584893209100000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata, { structsAsObjects: true }); - const decodedValueJson = JSON.stringify(decodedValue); - console.log(`DECODED`, '*'.repeat(200), '\n', decodedValueJson); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + /* + it.only('Duplicate Static Arrays', async () => { + // Description: + // Two dynamic arrays with the same values. + // In the optimized calldata, only one set of elements should be included. + // Both arrays should point to this set. + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi5); + const array1 = [new BigNumber(100), new BigNumber(150)]; + const array2 = [array1[0], array1[1]]; + const args = [array1, array2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x7bc4226e00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); }); - it('Crazy ABI #1', async () => { - const method = new AbiEncoder.Method(AbiSamples.crazyAbi1); - - const args = [ - new BigNumber(256745454), - new BigNumber(-256745454), - new BigNumber(434244), - '0x43', - '0x0001020304050607080911121314151617181920212223242526272829303132', - '0x0001020304050607080911121314151617181920212223242526272829303132080911121314151617181920212223242526272829303132', - 'Little peter piper piped a piping pepper pot', - '0xe41d2489571d322189246dafa5ebde1f4699f498', - true - ]; - - const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); + it.only('Duplicate Tuples', async () => { + // Description: + // Two dynamic arrays with the same values. + // In the optimized calldata, only one set of elements should be included. + // Both arrays should point to this set. + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi5); + const array1 = [new BigNumber(100), new BigNumber(150)]; + const array2 = [array1[0], array1[1]]; + const args = [array1, array2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x7bc4226e00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + });*/ - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + it('Static Array of static types should not be optimized', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.optimizerAbi4); + const uint8Array = [new BigNumber(100), new BigNumber(150), new BigNumber(200), new BigNumber(225)]; + const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; + const args = [uint8Array, uintTupleArray]; + // Validate calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const unoptimizedCalldata = method.encode(args); + expect(optimizedCalldata).to.be.equal(unoptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); }); + // Todo: Unfixed array points to fixed array + // Todo: Unfixed to unfixed array + // Todo: Duplicate tuples + }); + describe('Method ABIs', () => { it('Types with default widths', async () => { + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); - console.log(method); - const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; + const args = [ + new BigNumber(1), + new BigNumber(-1), + '0x56', + [new BigNumber(1)], + [new BigNumber(-1)], + ['0x56'], + ]; const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; + // Validate calldata + const expectedCalldata = + '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Array of Static Tuples (Array has defined length)', async () => { + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDefinedLengthAbi); - let value = 0; const arrayOfTuples = []; for (let i = 0; i < 8; ++i) { @@ -286,23 +201,19 @@ describe.only('ABI Encoder', () => { } const args = [arrayOfTuples]; const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; + // Validate calldata + const expectedCalldata = + '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Array of Static Tuples (Array has dynamic length)', async () => { + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDynamicLengthAbi); - let value = 0; const arrayOfTuples = []; for (let i = 0; i < 8; ++i) { @@ -310,316 +221,352 @@ describe.only('ABI Encoder', () => { } const args = [arrayOfTuples]; const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; + // Validate calldata + const expectedCalldata = + '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Array of Dynamic Tuples (Array has defined length)', async () => { + // Generate Calldata const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithDefinedLengthAbi); - let value = 0; const arrayOfTuples = []; for (let i = 0; i < 8; ++i) { - arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]); } const args = [arrayOfTuples]; const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; + // Validate calldata + const expectedCalldata = + '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Array of Dynamic Tuples (Array has dynamic length)', async () => { + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithUndefinedLengthAbi); - let value = 0; const arrayOfTuples = []; for (let i = 0; i < 8; ++i) { - arrayOfTuples.push([new BigNumber(++value), (new BigNumber(++value)).toString()]); + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]); } const args = [arrayOfTuples]; const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; + // Validate calldata + const expectedCalldata = + '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Multidimensional Arrays / Static Members', async () => { + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi); - // Eight 3-dimensional arrays of uint8[2][2][2] let value = 0; const args = []; for (let i = 0; i < 8; ++i) { - args.push( + args.push([ [ - [ - [new BigNumber(++value), new BigNumber(++value)], - [new BigNumber(++value), new BigNumber(++value)], - ], - [ - [new BigNumber(++value), new BigNumber(++value)], - [new BigNumber(++value), new BigNumber(++value)], - ] - ] - ); + [new BigNumber(++value), new BigNumber(++value)], + [new BigNumber(++value), new BigNumber(++value)], + ], + [ + [new BigNumber(++value), new BigNumber(++value)], + [new BigNumber(++value), new BigNumber(++value)], + ], + ]); } const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; expect(calldata).to.be.equal(expectedCalldata); + // Validate calldata + const expectedCalldata = + '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Multidimensional Arrays / Dynamic Members', async () => { + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysDynamicTypeAbi); - // Eight 3-dimensional arrays of string[2][2][2] let value = 0; const args = []; for (let i = 0; i < 4; ++i) { - args.push( + args.push([ [ - [ - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - ], - [ - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - ] - ] - ); + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + ], + [ + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + ], + ]); } const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(method.getSignature()); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; + // Validate calldata + const expectedCalldata = + '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - - it('Fixed Lenfgth Array / Dynamic Members', async () => { + it('Fixed Length Array / Dynamic Members', async () => { + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); - const args = [["Brave", "New", "World"]]; + const args = [['Brave', 'New', 'World']]; const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); + // Validate calldata const expectedCalldata = '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - - it('Fixed Lenfgth Array / Dynamic Members', async () => { + it('Fixed Length Array / Dynamic Members', async () => { + // Generaet calldata const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); - const args = [["Brave", "New", "World"]]; + const args = [['Brave', 'New', 'World']]; const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); + // Validate calldata const expectedCalldata = '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Unfixed Length Array / Dynamic Members ABI', async () => { + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.dynamicArrayDynamicMembersAbi); - const args = [["Brave", "New", "World"]]; + const args = [['Brave', 'New', 'World']]; const calldata = method.encode(args); - console.log(calldata); - console.log('*'.repeat(40)); - console.log(JSON.stringify(args)); - const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + // Validate calldata + const expectedCalldata = + '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Unfixed Length Array / Static Members ABI', async () => { + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.dynamicArrayStaticMembersAbi); const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; const calldata = method.encode(args); - const expectedCalldata = '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; + // Validate calldata + const expectedCalldata = + '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - - it('Fixed Length Array / Static Members ABI', async () => { + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; const calldata = method.encode(args); + // Validate calldata const expectedCalldata = '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - - - it('Simple ABI 2', async () => { - const method = new AbiEncoder.Method(AbiSamples.simpleAbi2); - - const args = [ - '0xaf', // e (bytes1) - '0x0001020304050607080911121314151617181920212223242526272829303132', // f (bytes32) - '0x616161616161616161616161616161616161616161616161616161616161616161616161616161611114f324567838475647382938475677448899338457668899002020202020', // g - 'My first name is Greg and my last name is Hysen, what do ya know!', // h - ]; - - const calldata = method.encode(args); - const expectedCalldata = - '0x7ac2bd96af000000000000000000000000000000000000000000000000000000000000000001020304050607080911121314151617181920212223242526272829303132000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000047616161616161616161616161616161616161616161616161616161616161616161616161616161611114f3245678384756473829384756774488993384576688990020202020200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414d79206669727374206e616d65206973204772656720616e64206d79206c617374206e616d6520697320487973656e2c207768617420646f207961206b6e6f772100000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Array ABI', async () => { + // Generate calldata const method = new AbiEncoder.Method(AbiSamples.stringAbi); - console.log(method); const args = [['five', 'six', 'seven']]; const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); + // Validate calldata const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Static Tuple', async () => { + // Generate calldata // This is dynamic because it has dynamic members const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi); const args = [[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]; const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; + // Validate calldata + const expectedCalldata = + '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Dynamic Tuple (Array input)', async () => { + // Generate calldata // This is dynamic because it has dynamic members const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); const args = [[new BigNumber(5), 'five']]; const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); + // Validate calldata const expectedCalldata = '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); - - // Test decoding + // Validate decoding const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - it('Dynamic Tuple (Object input)', async () => { + // Generate Calldata // This is dynamic because it has dynamic members const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five' }]); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); + const args = [[new BigNumber(5), 'five']]; + const calldata = method.encode(args); + // Validate calldata const expectedCalldata = '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Large, Flat ABI', async () => { + // Construct calldata + const method = new AbiEncoder.Method(AbiSamples.largeFlatAbi); + const args = [ + new BigNumber(256745454), + new BigNumber(-256745454), + new BigNumber(434244), + '0x43', + '0x0001020304050607080911121314151617181920212223242526272829303132', + '0x0001020304050607080911121314151617181920212223242526272829303132080911121314151617181920212223242526272829303132', + 'Little peter piper piped a piping pepper pot', + '0xe41d2489571d322189246dafa5ebde1f4699f498', + true, + ]; + // Validate calldata + const calldata = method.encode(args); + const expectedCalldata = + '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Large, Nested ABI', async () => { + // Construct Calldata + const method = new AbiEncoder.Method(AbiSamples.largeNestedAbi); + const someStaticArray = [new BigNumber(127), new BigNumber(14), new BigNumber(54)]; + const someStaticArrayWithDynamicMembers = [ + 'the little piping piper piped a piping pipper papper', + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ]; + const someDynamicArrayWithDynamicMembers = [ + '0x38745637834987324827439287423897238947239847', + '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', + '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', + ]; + const some2DArray = [ + [ + 'some string', + 'some another string', + 'there are just too many stringsup in', + 'here', + 'yall ghonna make me lose my mind', + ], + [ + 'the little piping piper piped a piping pipper papper', + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ], + [], + ]; + const someTuple = { + someUint32: new BigNumber(4037824789), + someStr: + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + }; + const someTupleWithDynamicTypes = { + someUint: new BigNumber(4024789), + someStr: 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', + someBytes: '0x29384723894723843743289742389472398473289472348927489274894738427428947389facdea', + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', + }; + const someTupleWithDynamicTypes2 = { + someUint: new BigNumber(9024789), + someStr: 'ksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjkakdhjasjkdhasjkldshdjahdkjsahdaj', + someBytes: '0x29384723894398473289472348927489272384374328974238947274894738427428947389facde1', + someAddress: '0x746dafa5ebde1f4699f4981d3221892e41d24895', + }; + const someTupleWithDynamicTypes3 = { + someUint: new BigNumber(1024789), + someStr: 'sdhsajkdhsajkdhadjkashdjakdhjasjkdhasjkldshdjahdkjsahdajkksadhajkdhsajkdhsadjk', + someBytes: '0x38947238437432829384729742389472398473289472348927489274894738427428947389facdef', + someAddress: '0x89571d322189e415ebde1f4699f498d24246dafa', + }; + const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3]; + const args = { + someStaticArray: someStaticArray, + someStaticArrayWithDynamicMembers: someStaticArrayWithDynamicMembers, + someDynamicArrayWithDynamicMembers: someDynamicArrayWithDynamicMembers, + some2DArray: some2DArray, + someTuple: someTuple, + someTupleWithDynamicTypes: someTupleWithDynamicTypes, + someArrayOfTuplesWithDynamicTypes: someArrayOfTuplesWithDynamicTypes, + }; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata, { structsAsObjects: true }); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); + // @TODO: Test Nested Tuples (Not Supported on ) + it.skip('Nested Tuples', async () => { // Couldn't get nested tuples to work with Remix // This is dynamic because it has dynamic members @@ -628,8 +575,8 @@ describe.only('ABI Encoder', () => { someUint32: new BigNumber(30472), nestedTuple: { someUint: new BigNumber('48384725243211555532'), - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' - } + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', + }, }; const secondTuple = { someUint: new BigNumber(2984237422), @@ -640,50 +587,47 @@ describe.only('ABI Encoder', () => { someUint: new BigNumber(234324), someStr: 'Im also a short string -- oops I just got loooooooooooooooooonger!', someBytes: '0x23847287fff3472984723498ff23487324987aaa237438911873429472ba', - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498' - } + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', + }, }, someBytes: '0x2834y3947289423u489aaaff4783924739847489', someAddress: '0xe41d2489571d322189246dafa5ebde1f4699afaf', }; const thirdTuple = { - 'someUint': new BigNumber(37422), - 'someStr': 'This into the next word of memory. string will exceed 256 bits, so it will spill.', - 'nestedTuple': { + someUint: new BigNumber(37422), + someStr: 'This into the next word of memory. string will exceed 256 bits, so it will spill.', + nestedTuple: { someUint32: new BigNumber(23999222), - 'secondNestedTuple': { - 'someUint': new BigNumber(324), - 'someStr': 'Im also a short st', - 'someBytes': '0x723498ff2348732498723847287fff3472984aaa237438911873429472ba', - 'someAddress': '0x46dafa5ebde1f4699f498e41d2489571d3221892' - } + secondNestedTuple: { + someUint: new BigNumber(324), + someStr: 'Im also a short st', + someBytes: '0x723498ff2348732498723847287fff3472984aaa237438911873429472ba', + someAddress: '0x46dafa5ebde1f4699f498e41d2489571d3221892', + }, }, - 'someBytes': '0x947289423u489aaaff472834y383924739847489', - 'someAddress': '0x46dafa5ebde1f46e41d2489571d322189299afaf', + someBytes: '0x947289423u489aaaff472834y383924739847489', + someAddress: '0x46dafa5ebde1f46e41d2489571d322189299afaf', }; const fourthTuple = { - 'someUint': new BigNumber(222283488822), - 'someStr': 'exceed 256 bits, so it will spill into the. This string will next word of memory.', - 'nestedTuple': { + someUint: new BigNumber(222283488822), + someStr: 'exceed 256 bits, so it will spill into the. This string will next word of memory.', + nestedTuple: { someUint32: new BigNumber(2300), - 'secondNestedTuple': { - 'someUint': new BigNumber(343224), - 'someStr': 'The alphabet backwards is arguably easier to say if thats the way you learned the first time.', - 'someBytes': '0x87324987aaa23743891187323847287fff3472984723498ff234429472ba', - 'someAddress': '0x71d322189246dafa5ebe41d24895de1f4699f498' - } + secondNestedTuple: { + someUint: new BigNumber(343224), + someStr: + 'The alphabet backwards is arguably easier to say if thats the way you learned the first time.', + someBytes: '0x87324987aaa23743891187323847287fff3472984723498ff234429472ba', + someAddress: '0x71d322189246dafa5ebe41d24895de1f4699f498', + }, }, - 'someBytes': '0x2783924739847488343947289423u489aaaff490', - 'someAddress': '0xebde1d322189246dafa1f4699afafe41d2489575', + someBytes: '0x2783924739847488343947289423u489aaaff490', + someAddress: '0xebde1d322189246dafa1f4699afafe41d2489575', }; - const args = [ - [firstTuple], - [secondTuple, thirdTuple, fourthTuple] - ]; + const args = [[firstTuple], [secondTuple, thirdTuple, fourthTuple]]; console.log('*'.repeat(250), method, '*'.repeat(250)); - const calldata = method.encode(args); console.log(method.getSignature()); console.log(method.selector); @@ -693,37 +637,9 @@ describe.only('ABI Encoder', () => { const expectedCalldata = '0x'; expect(calldata).to.be.equal(expectedCalldata); }); - - it.skip('Object ABI (Object input - Missing Key)', async () => { - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const calldata = method.encode([{ someUint: new BigNumber(5) }]); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - - // @TODO: Figure out how to catch throw - expect(calldata).to.be.equal(expectedCalldata); - }); - - it.skip('Object ABI (Object input - Too Many Keys)', async () => { - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const calldata = method.encode([{ someUint: new BigNumber(5), someStr: 'five', unwantedKey: 14 }]); - console.log(method.getSignature()); - console.log(method.selector); - - console.log(calldata); - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - - // @TODO: Figure out how to catch throw - expect(calldata).to.be.equal(expectedCalldata); - }); }); - describe.only('Array', () => { + describe('Array', () => { it('Fixed size; Static elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'int[2]' }; @@ -732,7 +648,8 @@ describe.only('ABI Encoder', () => { const args = [new BigNumber(5), new BigNumber(6)]; // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); @@ -740,7 +657,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Dynamic size; Static elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'int[]' }; @@ -749,7 +665,8 @@ describe.only('ABI Encoder', () => { const args = [new BigNumber(5), new BigNumber(6)]; // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); @@ -757,16 +674,16 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Fixed size; Dynamic elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'string[2]' }; const dataType = new AbiEncoder.SolArray(testDataItem); // Construct args to be encoded - const args = ["Hello", "world"]; + const args = ['Hello', 'world']; // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); @@ -774,16 +691,16 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Dynamic size; Dynamic elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'string[]' }; const dataType = new AbiEncoder.SolArray(testDataItem); // Construct args to be encoded - const args = ["Hello", "world"]; + const args = ['Hello', 'world']; // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); @@ -791,21 +708,56 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - - // @TODO: Add test that fails if we pass in the wrong number of elements - // @TODO: Add test that fails if we pass in an element of incorrecrt type + it('Static size; Too Few Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'string[3]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + // Construct args to be encoded + const args = ['Hello', 'world']; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw('Expected array of 3 elements, but got array of length 2'); + }); + it('Static size; Too Many Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'string[1]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + // Construct args to be encoded + const args = ['Hello', 'world']; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw('Expected array of 1 elements, but got array of length 2'); + }); + it('Element Type Mismatch', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'uint[]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + // Construct args to be encoded + const args = [new BigNumber(1), 'Bad Argument']; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(); + }); }); - describe.only('Tuple', () => { + describe('Tuple', () => { it('Static elements only', async () => { // Create DataType object - const testDataItem = { name: 'Tuple', type: 'tuple', components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }] }; + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }], + }; const dataType = new AbiEncoder.Tuple(testDataItem); // Construct args to be encoded const args = { field_1: new BigNumber(-5), field_2: true }; // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000001'; + const expectedEncodedArgs = + '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000001'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodingRules = { structsAsObjects: true } as DecodingRules; @@ -814,16 +766,20 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Dynamic elements only', async () => { // Create DataType object - const testDataItem = { name: 'Tuple', type: 'tuple', components: [{ name: 'field_1', type: 'string' }, { name: 'field_2', type: 'bytes' }] }; + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field_1', type: 'string' }, { name: 'field_2', type: 'bytes' }], + }; const dataType = new AbiEncoder.Tuple(testDataItem); // Construct args to be encoded - const args = { field_1: "Hello, World!", field_2: '0xabcdef0123456789' }; + const args = { field_1: 'Hello, World!', field_2: '0xabcdef0123456789' }; // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodingRules = { structsAsObjects: true } as DecodingRules; @@ -832,16 +788,30 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Static and dynamic elements mixed', async () => { // Create DataType object - const testDataItem = { name: 'Tuple', type: 'tuple', components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'string' }, { name: 'field_3', type: 'bool' }, { name: 'field_4', type: 'bytes' }] }; + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [ + { name: 'field_1', type: 'int32' }, + { name: 'field_2', type: 'string' }, + { name: 'field_3', type: 'bool' }, + { name: 'field_4', type: 'bytes' }, + ], + }; const dataType = new AbiEncoder.Tuple(testDataItem); // Construct args to be encoded - const args = { field_1: new BigNumber(-5), field_2: "Hello, World!", field_3: true, field_4: '0xabcdef0123456789' }; + const args = { + field_1: new BigNumber(-5), + field_2: 'Hello, World!', + field_3: true, + field_4: '0xabcdef0123456789', + }; // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; + const expectedEncodedArgs = + '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodingRules = { structsAsObjects: true } as DecodingRules; @@ -850,12 +820,39 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - - // @TODO: Add test that fails if we pass in the wrong number of elements - // @TODO: Add test that fails if we pass in arguments in wrong order + it('Missing Key', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { field_1: new BigNumber(-5) }; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw('Could not assign tuple to object: missing keys field_2'); + }); + it('Bad Key', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { unknown_field: new BigNumber(-5) }; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw("Could not assign tuple to object: unrecognized key 'unknown_field' in object Tuple"); + }); }); - describe.only('Address', () => { + describe('Address', () => { it('Valid Address', async () => { // Create DataType object const testDataItem = { name: 'Address', type: 'address' }; @@ -872,7 +869,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Invalid Address - input is not valid hex', async () => { // Create DataType object const testDataItem = { name: 'Address', type: 'address' }; @@ -880,9 +876,10 @@ describe.only('ABI Encoder', () => { // Construct args to be encoded const args = 'e4'; // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); + expect(() => { + dataType.encode(args); + }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); }); - it('Invalid Address - input is not 20 bytes', async () => { // Create DataType object const testDataItem = { name: 'Address', type: 'address' }; @@ -890,11 +887,13 @@ describe.only('ABI Encoder', () => { // Construct args to be encoded const args = '0xe4'; // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); + expect(() => { + dataType.encode(args); + }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); }); }); - describe.only('Bool', () => { + describe('Bool', () => { it('True', async () => { // Create DataType object const testDataItem = { name: 'Boolean', type: 'bool' }; @@ -911,7 +910,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('False', async () => { // Create DataType object const testDataItem = { name: 'Boolean', type: 'bool' }; @@ -930,7 +928,7 @@ describe.only('ABI Encoder', () => { }); }); - describe.only('Integer', () => { + describe('Integer', () => { it('Int256 - Positive Base Case', async () => { // Create DataType object const testDataItem = { name: 'Integer (256)', type: 'int' }; @@ -947,7 +945,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Int256 - Negative Base Case', async () => { // Create DataType object const testDataItem = { name: 'Integer (256)', type: 'int' }; @@ -964,13 +961,12 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Int256 - Positive Value', async () => { // Create DataType object const testDataItem = { name: 'Integer (256)', type: 'int' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const max256BitInteger = (new BigNumber(2)).pow(255).minus(1); + const max256BitInteger = new BigNumber(2).pow(255).minus(1); const args = max256BitInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -982,13 +978,12 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Int256 - Negative Value', async () => { // Create DataType object const testDataItem = { name: 'Integer (256)', type: 'int' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const min256BitInteger = (new BigNumber(2)).pow(255).times(-1); + const min256BitInteger = new BigNumber(2).pow(255).times(-1); const args = min256BitInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1000,29 +995,30 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Int256 - Value too large', async () => { // Create DataType object const testDataItem = { name: 'Integer (256)', type: 'int' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const max256BitInteger = (new BigNumber(2)).pow(255).minus(1); + const max256BitInteger = new BigNumber(2).pow(255).minus(1); const args = max256BitInteger.plus(1); // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw(); + expect(() => { + dataType.encode(args); + }).to.throw(); }); - it('Int256 - Value too small', async () => { // Create DataType object const testDataItem = { name: 'Integer (256)', type: 'int' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const min256BitInteger = (new BigNumber(2)).pow(255).times(-1); + const min256BitInteger = new BigNumber(2).pow(255).times(-1); const args = min256BitInteger.minus(1); // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw(); + expect(() => { + dataType.encode(args); + }).to.throw(); }); - it('Int32 - Positive Base Case', async () => { // Create DataType object const testDataItem = { name: 'Integer (32)', type: 'int32' }; @@ -1039,7 +1035,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Int32 - Negative Base Case', async () => { // Create DataType object const testDataItem = { name: 'Integer (32)', type: 'int32' }; @@ -1056,13 +1051,12 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Int32 - Positive Value', async () => { // Create DataType object const testDataItem = { name: 'Integer (32)', type: 'int32' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const max32BitInteger = (new BigNumber(2)).pow(31).minus(1); + const max32BitInteger = new BigNumber(2).pow(31).minus(1); const args = max32BitInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1074,13 +1068,12 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Int32 - Negative Value', async () => { // Create DataType object const testDataItem = { name: 'Integer (32)', type: 'int32' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const min32BitInteger = (new BigNumber(2)).pow(31).times(-1); + const min32BitInteger = new BigNumber(2).pow(31).times(-1); const args = min32BitInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1092,31 +1085,33 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Int32 - Value too large', async () => { // Create DataType object const testDataItem = { name: 'Integer (32)', type: 'int32' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const max32BitInteger = (new BigNumber(2)).pow(31).minus(1); + const max32BitInteger = new BigNumber(2).pow(31).minus(1); const args = max32BitInteger.plus(1); // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw(); + expect(() => { + dataType.encode(args); + }).to.throw(); }); - it('Int32 - Value too small', async () => { // Create DataType object const testDataItem = { name: 'Integer (32)', type: 'int32' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const min32BitInteger = (new BigNumber(2)).pow(31).times(-1); + const min32BitInteger = new BigNumber(2).pow(31).times(-1); const args = min32BitInteger.minus(1); // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw(); + expect(() => { + dataType.encode(args); + }).to.throw(); }); }); - describe.only('Unsigned Integer', () => { + describe('Unsigned Integer', () => { it('UInt256 - Positive Base Case', async () => { // Create DataType object const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; @@ -1133,13 +1128,12 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('UInt256 - Positive Value', async () => { // Create DataType object const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const max256BitUnsignedInteger = (new BigNumber(2)).pow(256).minus(1); + const max256BitUnsignedInteger = new BigNumber(2).pow(256).minus(1); const args = max256BitUnsignedInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1151,7 +1145,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('UInt256 - Zero Value', async () => { // Create DataType object const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; @@ -1169,29 +1162,30 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('UInt256 - Value too large', async () => { // Create DataType object const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const max256BitUnsignedInteger = (new BigNumber(2)).pow(256).minus(1); + const max256BitUnsignedInteger = new BigNumber(2).pow(256).minus(1); const args = max256BitUnsignedInteger.plus(1); // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw(); + expect(() => { + dataType.encode(args); + }).to.throw(); }); - it('UInt256 - Value too small', async () => { // Create DataType object const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const min256BitUnsignedInteger = new BigNumber(0) + const min256BitUnsignedInteger = new BigNumber(0); const args = min256BitUnsignedInteger.minus(1); // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw(); + expect(() => { + dataType.encode(args); + }).to.throw(); }); - it('UInt32 - Positive Base Case', async () => { // Create DataType object const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; @@ -1208,13 +1202,12 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('UInt32 - Positive Value', async () => { // Create DataType object const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const max32BitUnsignedInteger = (new BigNumber(2)).pow(32).minus(1); + const max32BitUnsignedInteger = new BigNumber(2).pow(32).minus(1); const args = max32BitUnsignedInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1226,7 +1219,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('UInt32 - Zero Value', async () => { // Create DataType object const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; @@ -1244,18 +1236,18 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('UInt32 - Value too large', async () => { // Create DataType object const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const max32BitUnsignedInteger = (new BigNumber(2)).pow(32).minus(1); + const max32BitUnsignedInteger = new BigNumber(2).pow(32).minus(1); const args = max32BitUnsignedInteger.plus(1); // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw(); + expect(() => { + dataType.encode(args); + }).to.throw(); }); - it('UInt32 - Value too small', async () => { // Create DataType object const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; @@ -1264,11 +1256,13 @@ describe.only('ABI Encoder', () => { const min32BitUnsignedInteger = new BigNumber(0); const args = min32BitUnsignedInteger.minus(1); // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw(); + expect(() => { + dataType.encode(args); + }).to.throw(); }); }); - describe.only('Static Bytes', () => { + describe('Static Bytes', () => { it('Single Byte (byte)', async () => { // Create DataType object const testDataItem = { name: 'Static Byte', type: 'byte' }; @@ -1285,7 +1279,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Single Byte (bytes1)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes1', type: 'bytes1' }; @@ -1302,7 +1295,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('4 Bytes (bytes4)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; @@ -1319,7 +1311,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('4 Bytes (bytes4); Encoder must pad input', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; @@ -1338,7 +1329,6 @@ describe.only('ABI Encoder', () => { const paddedArgsAsJson = JSON.stringify(paddedArgs); expect(decodedArgsAsJson).to.be.equal(paddedArgsAsJson); }); - it('32 Bytes (bytes32)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; @@ -1355,7 +1345,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('32 Bytes (bytes32); Encoder must pad input', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; @@ -1374,7 +1363,6 @@ describe.only('ABI Encoder', () => { const paddedArgsAsJson = JSON.stringify(paddedArgs); expect(decodedArgsAsJson).to.be.equal(paddedArgsAsJson); }); - it('Should throw when pass in too many bytes (bytes4)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; @@ -1382,9 +1370,12 @@ describe.only('ABI Encoder', () => { // Construct args to be encoded const args = '0x0102030405'; // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw('Tried to assign 0x0102030405 (5 bytes), which exceeds max bytes that can be stored in a bytes4'); + expect(() => { + dataType.encode(args); + }).to.throw( + 'Tried to assign 0x0102030405 (5 bytes), which exceeds max bytes that can be stored in a bytes4', + ); }); - it('Should throw when pass in too many bytes (bytes32)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; @@ -1392,9 +1383,12 @@ describe.only('ABI Encoder', () => { // Construct args to be encoded const args = '0x010203040506070809101112131415161718192021222324252627282930313233'; // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw('Tried to assign 0x010203040506070809101112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32'); + expect(() => { + dataType.encode(args); + }).to.throw( + 'Tried to assign 0x010203040506070809101112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32', + ); }); - it('Should throw when pass in bad hex (no 0x prefix)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; @@ -1402,9 +1396,10 @@ describe.only('ABI Encoder', () => { // Construct args to be encoded const args = '0102030405060708091011121314151617181920212223242526272829303132'; // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix."); + expect(() => { + dataType.encode(args); + }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix."); }); - it('Should throw when pass in bad hex (include a half-byte)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; @@ -1412,11 +1407,13 @@ describe.only('ABI Encoder', () => { // Construct args to be encoded const args = '0x010'; // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); + expect(() => { + dataType.encode(args); + }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); }); }); - describe.only('Dynamic Bytes Dynamic', () => { + describe('Dynamic Bytes', () => { it('Fits into one EVM word', async () => { // Create DataType object const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; @@ -1426,7 +1423,8 @@ describe.only('ABI Encoder', () => { const args = '0x1a18bf61'; // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000'; + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); @@ -1434,7 +1432,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Spans multiple EVM words', async () => { // Create DataType object const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; @@ -1444,7 +1441,8 @@ describe.only('ABI Encoder', () => { const args = '0x' + '61'.repeat(40); // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); @@ -1452,7 +1450,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Input as Buffer', async () => { // Create DataType object const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; @@ -1463,7 +1460,8 @@ describe.only('ABI Encoder', () => { const argsAsBuffer = ethUtil.toBuffer(args); // Encode Args and validate result const encodedArgs = dataType.encode(argsAsBuffer); - const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000'; + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); @@ -1471,7 +1469,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Should throw when pass in bad hex (no 0x prefix)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes', type: 'bytes' }; @@ -1479,9 +1476,10 @@ describe.only('ABI Encoder', () => { // Construct args to be encoded const args = '01'; // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '01'"); + expect(() => { + dataType.encode(args); + }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '01'"); }); - it('Should throw when pass in bad hex (include a half-byte)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes', type: 'bytes' }; @@ -1489,11 +1487,13 @@ describe.only('ABI Encoder', () => { // Construct args to be encoded const args = '0x010'; // Encode Args and validate result - expect(() => { dataType.encode(args) }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); + expect(() => { + dataType.encode(args); + }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); }); }); - describe.only('String', () => { + describe('String', () => { it('Fits into one EVM word', async () => { // Create DataType object const testDataItem = { name: 'String', type: 'string' }; @@ -1503,7 +1503,8 @@ describe.only('ABI Encoder', () => { const args = 'five'; // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); @@ -1511,7 +1512,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('Spans multiple EVM words', async () => { // Create DataType object const testDataItem = { name: 'String', type: 'string' }; @@ -1521,7 +1521,8 @@ describe.only('ABI Encoder', () => { const args = 'a'.repeat(40); // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); @@ -1529,7 +1530,6 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); - it('String that begins with 0x prefix', async () => { // Create DataType object const testDataItem = { name: 'String', type: 'string' }; @@ -1539,7 +1539,8 @@ describe.only('ABI Encoder', () => { const args = '0x' + 'a'.repeat(40); // Encode Args and validate result const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000002a30786161616161616161616161616161616161616161616161616161616161616161616161616161616100000000000000000000000000000000000000000000'; + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000002a30786161616161616161616161616161616161616161616161616161616161616161616161616161616100000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); diff --git a/packages/utils/test/abi_samples.ts b/packages/utils/test/abi_samples.ts index aa38711cd..806ad1700 100644 --- a/packages/utils/test/abi_samples.ts +++ b/packages/utils/test/abi_samples.ts @@ -34,32 +34,32 @@ export const stringAbi = { type: 'function', } as MethodAbi; - export const GAbi = { constant: false, inputs: [ { - components: [{ - name: 'a', - type: 'uint256', - }, - { - name: 'b', - type: 'string', - }, - { - name: 'e', - type: 'bytes', - }, - { - name: 'f', - type: 'address', - }], + components: [ + { + name: 'a', + type: 'uint256', + }, + { + name: 'b', + type: 'string', + }, + { + name: 'e', + type: 'bytes', + }, + { + name: 'f', + type: 'address', + }, + ], name: 'f', type: 'tuple', - - } + }, ], name: 'simpleFunction', outputs: [], @@ -137,6 +137,25 @@ export const optimizerAbi4 = { type: 'function', } as MethodAbi; +export const optimizerAbi5 = { + constant: false, + inputs: [ + { + name: 'array1', + type: 'uint8[]', + }, + { + name: 'array2', + type: 'uint8[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + export const typesWithDefaultWidthsAbi = { constant: false, inputs: [ @@ -456,7 +475,7 @@ export const staticArrayAbi = { { name: 'someStaticArray', type: 'uint8[3]', - } + }, ], name: 'simpleFunction', outputs: [], @@ -471,7 +490,7 @@ export const staticArrayDynamicMembersAbi = { { name: 'someStaticArray', type: 'string[3]', - } + }, ], name: 'simpleFunction', outputs: [], @@ -486,7 +505,7 @@ export const dynamicArrayDynamicMembersAbi = { { name: 'someStaticArray', type: 'string[]', - } + }, ], name: 'simpleFunction', outputs: [], @@ -501,7 +520,7 @@ export const dynamicArrayStaticMembersAbi = { { name: 'someStaticArray', type: 'uint8[]', - } + }, ], name: 'simpleFunction', outputs: [], @@ -510,7 +529,7 @@ export const dynamicArrayStaticMembersAbi = { type: 'function', } as MethodAbi; -export const crazyAbi1 = { +export const largeFlatAbi = { constant: false, inputs: [ { @@ -557,7 +576,7 @@ export const crazyAbi1 = { type: 'function', } as MethodAbi; -export const crazyAbi = { +export const largeNestedAbi = { constant: false, inputs: [ { @@ -641,7 +660,7 @@ export const crazyAbi = { type: 'address', }, ], - } + }, ], name: 'simpleFunction', outputs: [], @@ -730,7 +749,7 @@ export const nestedTuples = { type: 'address', }, ], - } + }, ], name: 'simpleFunction', outputs: [], -- cgit v1.2.3 From 0d65c9da4ad6d925779b9b6e8ff99bea4c25eedf Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 16:52:45 -0800 Subject: Optimizer Tests --- packages/utils/test/abi_encoder_test.ts | 272 +++++++++++++++++++++++--------- packages/utils/test/abi_samples.ts | 88 ----------- 2 files changed, 200 insertions(+), 160 deletions(-) diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 9925abcc3..c5be35d5e 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -4,6 +4,7 @@ import 'mocha'; import { chaiSetup } from './utils/chai_setup'; import { BigNumber, AbiEncoder } from '../src/'; import * as AbiSamples from './abi_samples'; +import * as OptimizedAbis from './optimizer_abis'; import { DecodingRules } from '../src/abi_encoder'; import ethUtil = require('ethereumjs-util'); @@ -12,19 +13,17 @@ const expect = chai.expect; describe.only('ABI Encoder', () => { describe('Optimizer', () => { - it('Should reuse duplicate strings in string array', async () => { - // Description: - // There are two unique values in the array `strings`. - // There should exist only one copy of each string in the optimized calldata. - // In unoptimized calldata, two copies of each string will be created. + it('Duplicate Dynamic Arrays with Static Elements', async () => { // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.stringAbi); - const strings = ['Test String', 'Test String 2', 'Test String', 'Test String 2']; - const args = [strings]; - // Validate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithStaticElements); + const array1 = [new BigNumber(100), new BigNumber(150)]; + const array2 = array1; + const args = [array1, array2]; + // Validata calldata const optimizedCalldata = method.encode(args, { optimize: true }); + console.log(optimizedCalldata); const expectedOptimizedCalldata = - '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000'; + '0x7221063300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); @@ -32,21 +31,68 @@ describe.only('ABI Encoder', () => { const argsJson = JSON.stringify(args); expect(decodedArgsJson).to.be.equal(argsJson); }); - it('Should point array elements to a duplicate value from another parameter', async () => { - // Description: - // There are two unique values in the array `strings`. - // The value "Test String" appears three times in this array. - // There should exist only one copy of this string in the optimized calldata. - // In unoptimized calldata, "Test String" would be written three times. + it('Duplicate Dynamic Arrays with Dynamic Elements', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithDynamicElements); + const array1 = ["Hello", "World"]; + const array2 = array1; + const args = [array1, array2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0xbb4f12e300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Static Arrays with Static Elements (should not optimize)', async () => { // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi2); - const strings = ['Test String', 'Test String', 'Test String', 'Test String 2']; - const string = 'Test String'; - const args = [strings, string]; + const method = new AbiEncoder.Method(OptimizedAbis.duplicateStaticArraysWithStaticElements); + const array1 = [new BigNumber(100), new BigNumber(150)]; + const array2 = array1; + const args = [array1, array2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x7f8130430000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + const unoptimizedCalldata = method.encode(args); + expect(optimizedCalldata).to.be.equal(unoptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Static Arrays with Dynamic Elements', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateStaticArraysWithDynamicElements); + const array1 = ["Hello", "World"]; + const array2 = array1; + const args = [array1, array2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x9fe31f8e0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Array Elements (should optimize)', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateArrayElements); + const strings = ['Hello', 'World', 'Hello', 'World']; + const args = [strings]; // Validate calldata const optimizedCalldata = method.encode(args, { optimize: true }); const expectedOptimizedCalldata = - '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d5465737420537472696e67203200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5465737420537472696e67000000000000000000000000000000000000000000'; + '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); @@ -54,24 +100,15 @@ describe.only('ABI Encoder', () => { const argsJson = JSON.stringify(args); expect(decodedArgsJson).to.be.equal(argsJson); }); - it('Dynamic Array of uints should point to Dynamic Array of Tuple(Uint)s', async () => { - // Description: - // There are two dynamic arrays, one of uint's and one of tuples. - // Each tuple is simply a wrapper for a uint - tuple := {key: uintValue} - // While the elements of these arrays have different types, they - // have the same representation in calldata. - // That is, a `uint` and a `tuple{uint}` both consume exactly one word of calldata. - // In the optimized calldata, only the elements of one array should be included. - // Both arrays will then point to the same set of elements. + it('Duplicate Tuple Fields', async () => { // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi3); - const uint8Array = [new BigNumber(100), new BigNumber(150), new BigNumber(200), new BigNumber(225)]; - const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; - const args = [uint8Array, uintTupleArray]; + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTupleFields); + const tuple = ['Hello', 'Hello']; + const args = [tuple]; // Validata calldata const optimizedCalldata = method.encode(args, { optimize: true }); const expectedOptimizedCalldata = - '0x5b5c78fd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000e1'; + '0x16780a5e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); @@ -79,20 +116,18 @@ describe.only('ABI Encoder', () => { const argsJson = JSON.stringify(args); expect(decodedArgsJson).to.be.equal(argsJson); }); - it('Duplicate Dynamic Arrays', async () => { + it('Duplicate Strings', async () => { // Description: // Two dynamic arrays with the same values. // In the optimized calldata, only one set of elements should be included. // Both arrays should point to this set. // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi5); - const array1 = [new BigNumber(100), new BigNumber(150)]; - const array2 = [array1[0], array1[1]]; - const args = [array1, array2]; + const method = new AbiEncoder.Method(OptimizedAbis.duplicateStrings); + const args = ['Hello', 'Hello']; // Validata calldata const optimizedCalldata = method.encode(args, { optimize: true }); const expectedOptimizedCalldata = - '0x7bc4226e00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; + '0x07370bfa00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); @@ -100,22 +135,19 @@ describe.only('ABI Encoder', () => { const argsJson = JSON.stringify(args); expect(decodedArgsJson).to.be.equal(argsJson); }); - - /* - it.only('Duplicate Static Arrays', async () => { + it('Duplicate Bytes', async () => { // Description: // Two dynamic arrays with the same values. // In the optimized calldata, only one set of elements should be included. // Both arrays should point to this set. // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi5); - const array1 = [new BigNumber(100), new BigNumber(150)]; - const array2 = [array1[0], array1[1]]; - const args = [array1, array2]; + const method = new AbiEncoder.Method(OptimizedAbis.duplicateBytes); + const value = '0x01020304050607080910111213141516171819202122232425262728293031323334353637383940'; + const args = [value, value]; // Validata calldata const optimizedCalldata = method.encode(args, { optimize: true }); const expectedOptimizedCalldata = - '0x7bc4226e00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; + '0x6045e42900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002801020304050607080910111213141516171819202122232425262728293031323334353637383940000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); @@ -123,49 +155,145 @@ describe.only('ABI Encoder', () => { const argsJson = JSON.stringify(args); expect(decodedArgsJson).to.be.equal(argsJson); }); - - it.only('Duplicate Tuples', async () => { + it('Duplicate Tuples', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuples); + const tuple1 = ['Hello, World!', new BigNumber(424234)]; + const tuple2 = tuple1; + const args = [tuple1, tuple2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x564f826d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000006792a000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c642100000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Fields Across Two Tuples', async () => { // Description: - // Two dynamic arrays with the same values. - // In the optimized calldata, only one set of elements should be included. - // Both arrays should point to this set. // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi5); - const array1 = [new BigNumber(100), new BigNumber(150)]; - const array2 = [array1[0], array1[1]]; - const args = [array1, array2]; + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuples); + const tuple1 = ['Hello, World!', new BigNumber(1)]; + const tuple2 = [tuple1[0], new BigNumber(2)]; + const args = [tuple1, tuple2]; // Validata calldata const optimizedCalldata = method.encode(args, { optimize: true }); const expectedOptimizedCalldata = - '0x7bc4226e00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; + '0x564f826d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c642100000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); const decodedArgsJson = JSON.stringify(decodedArgs); const argsJson = JSON.stringify(args); expect(decodedArgsJson).to.be.equal(argsJson); - });*/ - - it('Static Array of static types should not be optimized', async () => { + }); + it('Duplicate Arrays, Nested in Separate Tuples', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateArraysNestedInTuples); + const array = [new BigNumber(100), new BigNumber(150), new BigNumber(200)]; + const tuple1 = [array]; + const tuple2 = [array, 'extra argument to prevent exactly matching the tuples']; + const args = [tuple1, tuple2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x18970a9e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000035657874726120617267756d656e7420746f2070726576656e742065786163746c79206d61746368696e6720746865207475706c65730000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Tuples, Nested in Separate Tuples', async () => { // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.optimizerAbi4); - const uint8Array = [new BigNumber(100), new BigNumber(150), new BigNumber(200), new BigNumber(225)]; - const uintTupleArray = [[uint8Array[0]], [uint8Array[1]], [uint8Array[2]], [uint8Array[3]]]; - const args = [uint8Array, uintTupleArray]; + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuplesNestedInTuples); + const nestedTuple = ['Hello, World!']; + const tuple1 = [nestedTuple]; + const tuple2 = [nestedTuple, 'extra argument to prevent exactly matching the tuples']; + const args = [tuple1, tuple2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x0b4d2e6a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035657874726120617267756d656e7420746f2070726576656e742065786163746c79206d61746368696e6720746865207475706c65730000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Two-Dimensional Arrays', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTwoDimensionalArrays); + const twoDimArray1 = [['Hello', 'World'], ['Foo', 'Bar', 'Zaa']]; + const twoDimArray2 = twoDimArray1; + const args = [twoDimArray1, twoDimArray2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: false }); + const expectedOptimizedCalldata = + '0x0d28c4f9000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Array, Nested within Separate Two-Dimensional Arrays', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTwoDimensionalArrays); + const twoDimArray1 = [['Hello', 'World'], ['Foo']]; + const twoDimArray2 = [['Hello', 'World'], ['Bar']]; + const args = [twoDimArray1, twoDimArray2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x0d28c4f900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003466f6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034261720000000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Array Elements Duplicated as Tuple Fields', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.arrayElementsDuplicatedAsTupleFields); + const array = [new BigNumber(100), new BigNumber(150), new BigNumber(200), new BigNumber(225)]; + const tuple = [[array[0]], [array[1]], [array[2]], [array[3]]]; + const args = [array, tuple]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x5b5c78fd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000e1'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Array Elements Duplicated as Separate Parameter', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.arrayElementsDuplicatedAsSeparateParameter); + const array = ['Hello', 'Hello', 'Hello', 'World']; + const string = 'Hello'; + const args = [array, string]; // Validate calldata const optimizedCalldata = method.encode(args, { optimize: true }); - const unoptimizedCalldata = method.encode(args); - expect(optimizedCalldata).to.be.equal(unoptimizedCalldata); + const expectedOptimizedCalldata = + '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); const decodedArgsJson = JSON.stringify(decodedArgs); const argsJson = JSON.stringify(args); expect(decodedArgsJson).to.be.equal(argsJson); }); - - // Todo: Unfixed array points to fixed array - // Todo: Unfixed to unfixed array - // Todo: Duplicate tuples }); describe('Method ABIs', () => { diff --git a/packages/utils/test/abi_samples.ts b/packages/utils/test/abi_samples.ts index 806ad1700..0c3354044 100644 --- a/packages/utils/test/abi_samples.ts +++ b/packages/utils/test/abi_samples.ts @@ -68,94 +68,6 @@ export const GAbi = { type: 'function', } as MethodAbi; -export const optimizerAbi2 = { - constant: false, - inputs: [ - { - name: 'stringArray', - type: 'string[]', - }, - { - name: 'string', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const optimizerAbi3 = { - constant: false, - inputs: [ - { - name: 'uint8Array', - type: 'uint8[]', - }, - { - components: [ - { - name: 'uint', - type: 'uint', - }, - ], - name: 'uintTuple', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const optimizerAbi4 = { - constant: false, - inputs: [ - { - name: 'uint8Array', - type: 'uint8[4]', - }, - { - components: [ - { - name: 'uint', - type: 'uint', - }, - ], - name: 'uintTuple', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - -export const optimizerAbi5 = { - constant: false, - inputs: [ - { - name: 'array1', - type: 'uint8[]', - }, - { - name: 'array2', - type: 'uint8[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -} as MethodAbi; - export const typesWithDefaultWidthsAbi = { constant: false, inputs: [ -- cgit v1.2.3 From acd570b2b30b9ffcc2333291dead9ffd5d1bd62a Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 17:31:32 -0800 Subject: Multidimensional Array tests --- packages/utils/test/abi_encoder_test.ts | 154 +++++++++++++++++--------------- 1 file changed, 80 insertions(+), 74 deletions(-) diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index c5be35d5e..d75d1ae50 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -21,7 +21,6 @@ describe.only('ABI Encoder', () => { const args = [array1, array2]; // Validata calldata const optimizedCalldata = method.encode(args, { optimize: true }); - console.log(optimizedCalldata); const expectedOptimizedCalldata = '0x7221063300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -692,79 +691,6 @@ describe.only('ABI Encoder', () => { const decodedValueJson = JSON.stringify(decodedValue); expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); }); - - // @TODO: Test Nested Tuples (Not Supported on ) - - it.skip('Nested Tuples', async () => { - // Couldn't get nested tuples to work with Remix - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.nestedTuples); - const firstTuple = { - someUint32: new BigNumber(30472), - nestedTuple: { - someUint: new BigNumber('48384725243211555532'), - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', - }, - }; - const secondTuple = { - someUint: new BigNumber(2984237422), - someStr: 'This string will exceed 256 bits, so it will spill into the next word of memory.', - nestedTuple: { - someUint32: new BigNumber(23), - secondNestedTuple: { - someUint: new BigNumber(234324), - someStr: 'Im also a short string -- oops I just got loooooooooooooooooonger!', - someBytes: '0x23847287fff3472984723498ff23487324987aaa237438911873429472ba', - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', - }, - }, - someBytes: '0x2834y3947289423u489aaaff4783924739847489', - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699afaf', - }; - const thirdTuple = { - someUint: new BigNumber(37422), - someStr: 'This into the next word of memory. string will exceed 256 bits, so it will spill.', - nestedTuple: { - someUint32: new BigNumber(23999222), - secondNestedTuple: { - someUint: new BigNumber(324), - someStr: 'Im also a short st', - someBytes: '0x723498ff2348732498723847287fff3472984aaa237438911873429472ba', - someAddress: '0x46dafa5ebde1f4699f498e41d2489571d3221892', - }, - }, - someBytes: '0x947289423u489aaaff472834y383924739847489', - someAddress: '0x46dafa5ebde1f46e41d2489571d322189299afaf', - }; - const fourthTuple = { - someUint: new BigNumber(222283488822), - someStr: 'exceed 256 bits, so it will spill into the. This string will next word of memory.', - nestedTuple: { - someUint32: new BigNumber(2300), - secondNestedTuple: { - someUint: new BigNumber(343224), - someStr: - 'The alphabet backwards is arguably easier to say if thats the way you learned the first time.', - someBytes: '0x87324987aaa23743891187323847287fff3472984723498ff234429472ba', - someAddress: '0x71d322189246dafa5ebe41d24895de1f4699f498', - }, - }, - someBytes: '0x2783924739847488343947289423u489aaaff490', - someAddress: '0xebde1d322189246dafa1f4699afafe41d2489575', - }; - const args = [[firstTuple], [secondTuple, thirdTuple, fourthTuple]]; - - console.log('*'.repeat(250), method, '*'.repeat(250)); - - const calldata = method.encode(args); - console.log(method.getSignature()); - console.log(method.selector); - console.log(JSON.stringify(args)); - - console.log(calldata); - const expectedCalldata = '0x'; - expect(calldata).to.be.equal(expectedCalldata); - }); }); describe('Array', () => { @@ -836,6 +762,86 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); + it('Dynamic Size; Multidimensional; Dynamic Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'bytes[][]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708', '0x09101112']; + const array2 = ['0x10111213', '0x14151617']; + const array3 = ['0x18192021']; + const args = [array1, array2, array3]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000405060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000041011121300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414151617000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000041819202100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Dynamic Size; Multidimensional; Static Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'bytes4[][]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708', '0x09101112']; + const array2 = ['0x10111213', '0x14151617']; + const array3 = ['0x18192021']; + const args = [array1, array2, array3]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000301020304000000000000000000000000000000000000000000000000000000000506070800000000000000000000000000000000000000000000000000000000091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021011121300000000000000000000000000000000000000000000000000000000141516170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011819202100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Static Size; Multidimensional; Static Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'bytes4[3][2]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708', '0x09101112']; + const array2 = ['0x10111213', '0x14151617', '0x18192021']; + const args = [array1, array2]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x010203040000000000000000000000000000000000000000000000000000000005060708000000000000000000000000000000000000000000000000000000000910111200000000000000000000000000000000000000000000000000000000101112130000000000000000000000000000000000000000000000000000000014151617000000000000000000000000000000000000000000000000000000001819202100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Static Size; Multidimensional; Dynamic Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'bytes[3][2]' }; + const dataType = new AbiEncoder.SolArray(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708', '0x09101112']; + const array2 = ['0x10111213', '0x14151617', '0x18192021']; + const args = [array1, array2]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + console.log(encodedArgs); + console.log(dataType.encode(args, { annotate: true })); + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000401020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004050607080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040910111200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000410111213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004141516170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041819202100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); it('Static size; Too Few Elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'string[3]' }; -- cgit v1.2.3 From 6daa79ec12557c06a5d4d6876dd086d554addb24 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 17:43:30 -0800 Subject: Arrays nested in tuples --- packages/utils/test/abi_encoder_test.ts | 92 +++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index d75d1ae50..b672d0023 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -922,6 +922,98 @@ describe.only('ABI Encoder', () => { const argsAsJson = JSON.stringify(args); expect(decodedArgsAsJson).to.be.equal(argsAsJson); }); + it('Nested Static Array', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field', type: 'uint[2]' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { field: [new BigNumber(1), new BigNumber(2)] }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Nested Dynamic Array', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field', type: 'uint[]' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { field: [new BigNumber(1), new BigNumber(2)] }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Nested Static Multidimensional Array', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field', type: 'bytes4[2][2]' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708']; + const array2 = ['0x09101112', '0x13141516']; + const args = { field: [array1, array2] }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x0102030400000000000000000000000000000000000000000000000000000000050607080000000000000000000000000000000000000000000000000000000009101112000000000000000000000000000000000000000000000000000000001314151600000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Nested Dynamic Multidimensional Array', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field', type: 'bytes[2][2]' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708']; + const array2 = ['0x09101112', '0x13141516']; + const args = { field: [array1, array2] }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040506070800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041314151600000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); it('Static and dynamic elements mixed', async () => { // Create DataType object const testDataItem = { -- cgit v1.2.3 From b0ebc6fa14adc08c074bea4d275cc31504c95d55 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 19 Nov 2018 18:42:40 -0800 Subject: Tests for decoding return values + Ability to encode return values --- packages/utils/src/abi_encoder/evm_data_types.ts | 19 +++---- packages/utils/test/abi_encoder_test.ts | 69 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index 10e7b987b..b862e9396 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -276,7 +276,7 @@ export class Byte extends PayloadDataType { if (valueBuf.byteLength > this.width) { throw new Error( `Tried to assign ${value} (${ - valueBuf.byteLength + valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); } else if (value.length % 2 !== 0) { @@ -475,6 +475,7 @@ export class Method extends MemberDataType { private methodSignature: string; private methodSelector: string; private returnDataTypes: DataType[]; + private returnDataItem: DataItem; // TMP public selector: string; @@ -484,6 +485,7 @@ export class Method extends MemberDataType { this.methodSignature = this.computeSignature(); this.selector = this.methodSelector = this.computeSelector(); this.returnDataTypes = []; + this.returnDataItem = { type: 'tuple', name: abi.name, components: abi.outputs }; const dummy = new Byte({ type: 'byte', name: 'DUMMY' }); // @TODO TMP _.each(abi.outputs, (dataItem: DataItem) => { this.returnDataTypes.push(this.getFactory().create(dataItem, dummy)); @@ -518,20 +520,19 @@ export class Method extends MemberDataType { return value; } - public decodeReturnValues(returndata: string, rules?: DecodingRules): any { - //console.log('O'.repeat(100), '\n', returndata, '\n', this.returnDataTypes, 'P'.repeat(100)); + public encodeReturnValues(value: any, rules?: EncodingRules): string { + const returnDataType = new Tuple(this.returnDataItem); + const returndata = returnDataType.encode(value, rules); + return returndata; + } + public decodeReturnValues(returndata: string, rules?: DecodingRules): any { const returnValues: any[] = []; const rules_ = rules ? rules : ({ structsAsObjects: false } as DecodingRules); const rawReturnData = new RawCalldata(returndata, false); _.each(this.returnDataTypes, (dataType: DataType) => { returnValues.push(dataType.generateValue(rawReturnData, rules_)); }); - - //console.log('*'.repeat(40), '\n', JSON.stringify(returnValues), '\n', '*'.repeat(100)); - /*if (returnValues.length === 1) { - return returnValues[0]; - }*/ return returnValues; } @@ -547,7 +548,7 @@ export class Method extends MemberDataType { export class EvmDataTypeFactory implements DataTypeFactory { private static instance: DataTypeFactory; - private constructor() {} + private constructor() { } public static getInstance(): DataTypeFactory { if (!EvmDataTypeFactory.instance) { diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index b672d0023..0220984b0 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -5,6 +5,7 @@ import { chaiSetup } from './utils/chai_setup'; import { BigNumber, AbiEncoder } from '../src/'; import * as AbiSamples from './abi_samples'; import * as OptimizedAbis from './optimizer_abis'; +import * as ReturnValueAbis from './return_value_abis'; import { DecodingRules } from '../src/abi_encoder'; import ethUtil = require('ethereumjs-util'); @@ -12,6 +13,74 @@ chaiSetup.configure(); const expect = chai.expect; describe.only('ABI Encoder', () => { + describe('Decode Return Values', () => { + it('No Return Value', async () => { + // Decode return value + const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues); + const returnValue = '0x'; + const decodedReturnValue = method.decodeReturnValues(returnValue); + const expectedDecodedReturnValue: any[] = []; + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(expectedDecodedReturnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Single static return value', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.singleStaticReturnValue); + const returnValue = ['0x01020304']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Multiple static return values', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.multipleStaticReturnValues); + const returnValue = ['0x01020304', '0x05060708']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Single dynamic return value', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.singleDynamicReturnValue); + const returnValue = ['0x01020304']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Multiple dynamic return values', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.multipleDynamicReturnValues); + const returnValue = ['0x01020304', '0x05060708']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Mixed static/dynamic return values', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.mixedStaticAndDynamicReturnValues); + const returnValue = ['0x01020304', '0x05060708']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + }); + describe('Optimizer', () => { it('Duplicate Dynamic Arrays with Static Elements', async () => { // Generate calldata -- cgit v1.2.3 From 2164b34bf9ca6f38fff93a527ee162b0624b818e Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 10:07:57 -0800 Subject: Ran linter on Calldata --- packages/utils/src/abi_encoder/calldata.ts | 505 +++++++++++++--------------- packages/utils/src/abi_encoder/data_type.ts | 11 +- 2 files changed, 241 insertions(+), 275 deletions(-) diff --git a/packages/utils/src/abi_encoder/calldata.ts b/packages/utils/src/abi_encoder/calldata.ts index e6ce58957..9f91f8495 100644 --- a/packages/utils/src/abi_encoder/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata.ts @@ -1,6 +1,6 @@ import ethUtil = require('ethereumjs-util'); -import CommunicationChatBubbleOutline from 'material-ui/SvgIcon'; -var _ = require('lodash'); +const _ = require('lodash'); +import * as Constants from './constants'; export interface DecodingRules { structsAsObjects: boolean; @@ -12,77 +12,69 @@ export interface EncodingRules { } export abstract class CalldataBlock { - private name: string; - private signature: string; - private offsetInBytes: number; - private headerSizeInBytes: number; - private bodySizeInBytes: number; - private relocatable: boolean; - private parentName: string; + private readonly _signature: string; + private readonly _parentName: string; + private _name: string; + private _offsetInBytes: number; + private _headerSizeInBytes: number; + private _bodySizeInBytes: number; constructor( name: string, signature: string, parentName: string, - /*offsetInBytes: number,*/ headerSizeInBytes: number, + headerSizeInBytes: number, bodySizeInBytes: number, - relocatable: boolean, ) { - this.name = name; - this.signature = signature; - this.parentName = parentName; - this.offsetInBytes = 0; - this.headerSizeInBytes = headerSizeInBytes; - this.bodySizeInBytes = bodySizeInBytes; - this.relocatable = relocatable; + this._name = name; + this._signature = signature; + this._parentName = parentName; + this._offsetInBytes = 0; + this._headerSizeInBytes = headerSizeInBytes; + this._bodySizeInBytes = bodySizeInBytes; } - protected setHeaderSize(headerSizeInBytes: number) { - this.headerSizeInBytes = headerSizeInBytes; + protected _setHeaderSize(headerSizeInBytes: number): void { + this._headerSizeInBytes = headerSizeInBytes; } - protected setBodySize(bodySizeInBytes: number) { - this.bodySizeInBytes = bodySizeInBytes; + protected _setBodySize(bodySizeInBytes: number): void { + this._bodySizeInBytes = bodySizeInBytes; } - protected setName(name: string) { - this.name = name; + protected _setName(name: string): void { + this._name = name; } public getName(): string { - return this.name; + return this._name; } public getParentName(): string { - return this.parentName; + return this._parentName; } public getSignature(): string { - return this.signature; + return this._signature; } - - public isRelocatable(): boolean { - return this.relocatable; - } - public getHeaderSizeInBytes(): number { - return this.headerSizeInBytes; + return this._headerSizeInBytes; } public getBodySizeInBytes(): number { - return this.bodySizeInBytes; + return this._bodySizeInBytes; } public getSizeInBytes(): number { - return this.headerSizeInBytes + this.bodySizeInBytes; + return this.getHeaderSizeInBytes() + this.getBodySizeInBytes(); } public getOffsetInBytes(): number { - return this.offsetInBytes; + return this._offsetInBytes; } - public setOffset(offsetInBytes: number) { - this.offsetInBytes = offsetInBytes; + public setOffset(offsetInBytes: number): void { + this._offsetInBytes = offsetInBytes; } public computeHash(): Buffer { @@ -96,81 +88,80 @@ export abstract class CalldataBlock { } export class PayloadCalldataBlock extends CalldataBlock { - private payload: Buffer; + private readonly _payload: Buffer; constructor( name: string, signature: string, parentName: string, - /*offsetInBytes: number,*/ relocatable: boolean, payload: Buffer, ) { const headerSizeInBytes = 0; const bodySizeInBytes = payload.byteLength; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); - this.payload = payload; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); + this._payload = payload; } public toBuffer(): Buffer { - return this.payload; + return this._payload; } public getRawData(): Buffer { - return this.payload; + return this._payload; } } export class DependentCalldataBlock extends CalldataBlock { - public static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; - public static RAW_DATA_START = new Buffer('<'); - public static RAW_DATA_END = new Buffer('>'); - private parent: CalldataBlock; - private dependency: CalldataBlock; - private aliasFor: CalldataBlock | undefined; + public static readonly RAW_DATA_START = new Buffer('<'); + public static readonly RAW_DATA_END = new Buffer('>'); + private static readonly _DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + private static readonly _EMPTY_HEADER_SIZE = 0; + private readonly _parent: CalldataBlock; + private readonly _dependency: CalldataBlock; + private _aliasFor: CalldataBlock | undefined; constructor( name: string, signature: string, parentName: string, - relocatable: boolean, dependency: CalldataBlock, parent: CalldataBlock, ) { - const headerSizeInBytes = 0; - const bodySizeInBytes = DependentCalldataBlock.DEPENDENT_PAYLOAD_SIZE_IN_BYTES; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes, relocatable); - this.parent = parent; - this.dependency = dependency; - this.aliasFor = undefined; + const headerSizeInBytes = DependentCalldataBlock._EMPTY_HEADER_SIZE; + const bodySizeInBytes = DependentCalldataBlock._DEPENDENT_PAYLOAD_SIZE_IN_BYTES; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); + this._parent = parent; + this._dependency = dependency; + this._aliasFor = undefined; } public toBuffer(): Buffer { const destinationOffset = - this.aliasFor !== undefined ? this.aliasFor.getOffsetInBytes() : this.dependency.getOffsetInBytes(); - const parentOffset = this.parent.getOffsetInBytes(); - const parentHeaderSize = this.parent.getHeaderSizeInBytes(); + this._aliasFor !== undefined ? this._aliasFor.getOffsetInBytes() : this._dependency.getOffsetInBytes(); + const parentOffset = this._parent.getOffsetInBytes(); + const parentHeaderSize = this._parent.getHeaderSizeInBytes(); const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); - const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(16)}`); + const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(Constants.HEX_BASE)}`); const evmWordWidthInBytes = 32; const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); return pointerBufPadded; } public getDependency(): CalldataBlock { - return this.dependency; + return this._dependency; } - public setAlias(block: CalldataBlock) { - this.aliasFor = block; - this.setName(`${this.getName()} (alias for ${block.getName()})`); + public setAlias(block: CalldataBlock): void { + this._aliasFor = block; + this._setName(`${this.getName()} (alias for ${block.getName()})`); } public getAlias(): CalldataBlock | undefined { - return this.aliasFor; + return this._aliasFor; } public getRawData(): Buffer { - const dependencyRawData = this.dependency.getRawData(); + const dependencyRawData = this._dependency.getRawData(); const rawDataComponents: Buffer[] = []; rawDataComponents.push(DependentCalldataBlock.RAW_DATA_START); rawDataComponents.push(dependencyRawData); @@ -181,24 +172,21 @@ export class DependentCalldataBlock extends CalldataBlock { } export class MemberCalldataBlock extends CalldataBlock { - private static DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; - private header: Buffer | undefined; - private members: CalldataBlock[]; - private contiguous: boolean; + private _header: Buffer | undefined; + private _members: CalldataBlock[]; - constructor(name: string, signature: string, parentName: string, relocatable: boolean, contiguous: boolean) { - super(name, signature, parentName, 0, 0, relocatable); - this.members = []; - this.header = undefined; - this.contiguous = contiguous; + constructor(name: string, signature: string, parentName: string) { + super(name, signature, parentName, 0, 0); + this._members = []; + this._header = undefined; } public getRawData(): Buffer { const rawDataComponents: Buffer[] = []; - if (this.header !== undefined) { - rawDataComponents.push(this.header); + if (this._header !== undefined) { + rawDataComponents.push(this._header); } - _.each(this.members, (member: CalldataBlock) => { + _.each(this._members, (member: CalldataBlock) => { const memberBuffer = member.getRawData(); rawDataComponents.push(memberBuffer); }); @@ -207,91 +195,79 @@ export class MemberCalldataBlock extends CalldataBlock { return rawData; } - public setMembers(members: CalldataBlock[]) { - let bodySizeInBytes = 0; - _.each(members, (member: CalldataBlock) => { - bodySizeInBytes += member.getSizeInBytes(); - }); - this.members = members; - this.setBodySize(0); - } - - public isContiguous(): boolean { - return true; + public setMembers(members: CalldataBlock[]): void { + this._members = members; } - public setHeader(header: Buffer) { - this.setHeaderSize(header.byteLength); - this.header = header; + public setHeader(header: Buffer): void { + this._setHeaderSize(header.byteLength); + this._header = header; } public toBuffer(): Buffer { - if (this.header !== undefined) return this.header; + if (this._header !== undefined) { + return this._header; + } return new Buffer(''); } public getMembers(): CalldataBlock[] { - return this.members; + return this._members; } } class Queue { - private store: T[] = []; - push(val: T) { - this.store.push(val); + private _store: T[] = []; + public push(val: T): void { + this._store.push(val); } - pushFront(val: T) { - this.store.unshift(val); + public pushFront(val: T): void { + this._store.unshift(val); } - pop(): T | undefined { - return this.store.shift(); + public pop(): T | undefined { + return this._store.shift(); } - popBack(): T | undefined { - if (this.store.length === 0) return undefined; - const backElement = this.store.splice(-1, 1)[0]; + public popBack(): T | undefined { + if (this._store.length === 0) { + return undefined; + } + const backElement = this._store.splice(-1, 1)[0]; return backElement; } - merge(q: Queue) { - this.store = this.store.concat(q.store); + public merge(q: Queue): void { + this._store = this._store.concat(q._store); } - mergeFront(q: Queue) { - this.store = q.store.concat(this.store); + public mergeFront(q: Queue): void { + this._store = q._store.concat(this._store); } - getStore(): T[] { - return this.store; + public getStore(): T[] { + return this._store; } - peek(): T | undefined { - return this.store.length >= 0 ? this.store[0] : undefined; + public peek(): T | undefined { + return this._store.length >= 0 ? this._store[0] : undefined; } } export class Calldata { - private selector: string; - private rules: EncodingRules; - private sizeInBytes: number; - private root: CalldataBlock | undefined; - - constructor(rules: EncodingRules) { - this.selector = ''; - this.rules = rules; - this.sizeInBytes = 0; - this.root = undefined; - } + private readonly _rules: EncodingRules; + private _selector: string; + private _sizeInBytes: number; + private _root: CalldataBlock | undefined; - private createQueue(block: CalldataBlock): Queue { + private static _createQueue(block: CalldataBlock): Queue { const blockQueue = new Queue(); // Base Case - if (block instanceof MemberCalldataBlock === false) { + if (!(block instanceof MemberCalldataBlock)) { blockQueue.push(block); return blockQueue; } // This is a Member Block - const memberBlock = block as MemberCalldataBlock; + const memberBlock = block; _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { if (member instanceof MemberCalldataBlock) { - blockQueue.mergeFront(this.createQueue(member)); + blockQueue.mergeFront(Calldata._createQueue(member)); } else { blockQueue.pushFront(member); } @@ -300,9 +276,9 @@ export class Calldata { // Children _.each(memberBlock.getMembers(), (member: CalldataBlock) => { if (member instanceof DependentCalldataBlock && member.getAlias() === undefined) { - let dependency = member.getDependency(); + const dependency = member.getDependency(); if (dependency instanceof MemberCalldataBlock) { - blockQueue.merge(this.createQueue(dependency)); + blockQueue.merge(Calldata._createQueue(dependency)); } else { blockQueue.push(dependency); } @@ -313,91 +289,15 @@ export class Calldata { return blockQueue; } - private generateAnnotatedHexString(): string { - let hexValue = `${this.selector}`; - if (this.root === undefined) { - throw new Error('expected root'); - } - - const valueQueue = this.createQueue(this.root); - - let block: CalldataBlock | undefined; - let offset = 0; - const functionBlock = valueQueue.peek(); - let functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); - while ((block = valueQueue.pop()) !== undefined) { - // Set f - - // Process each block 1 word at a time - const size = block.getSizeInBytes(); - const name = block.getName(); - const parentName = block.getParentName(); - - //const ancestrialNamesOffset = name.startsWith('ptr<') ? 4 : 0; - //const parentOffset = name.lastIndexOf(parentName); - const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, ''); //.replace(`${parentName}[`, '['); - const signature = block.getSignature(); - - // Current offset - let offsetStr = ''; - - // If this block is empty then it's a newline - let value = ''; - let nameStr = ''; - let line = ''; - if (size === 0) { - offsetStr = ' '.repeat(10); - value = ' '.repeat(74); - nameStr = `### ${prettyName.padEnd(80)}`; - line = `\n${offsetStr}${value}${nameStr}`; - } else { - offsetStr = `0x${offset.toString(16)}`.padEnd(10, ' '); - value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(0, 32))).padEnd(74); - if (block instanceof MemberCalldataBlock) { - nameStr = `### ${prettyName.padEnd(80)}`; - line = `\n${offsetStr}${value}${nameStr}`; - } else { - nameStr = ` ${prettyName.padEnd(80)}`; - line = `${offsetStr}${value}${nameStr}`; - } - } - - for (let j = 32; j < size; j += 32) { - offsetStr = `0x${(offset + j).toString(16)}`.padEnd(10, ' '); - value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(j, j + 32))).padEnd(74); - nameStr = ' '.repeat(40); - - line = `${line}\n${offsetStr}${value}${nameStr}`; - } - - // Append to hex value - hexValue = `${hexValue}\n${line}`; - offset += size; - } - - return hexValue; - } - - private generateCondensedHexString(): string { - let selectorBuffer = ethUtil.toBuffer(this.selector); - if (this.root === undefined) { - throw new Error('expected root'); - } - - const valueQueue = this.createQueue(this.root); - const valueBufs: Buffer[] = [selectorBuffer]; - let block: CalldataBlock | undefined; - while ((block = valueQueue.pop()) !== undefined) { - valueBufs.push(block.toBuffer()); - } - - const combinedBuffers = Buffer.concat(valueBufs); - const hexValue = ethUtil.bufferToHex(combinedBuffers); - return hexValue; + public constructor(rules: EncodingRules) { + this._rules = rules; + this._selector = ''; + this._sizeInBytes = 0; + this._root = undefined; } - public optimize() { - if (this.root === undefined) { + public optimize(): void { + if (this._root === undefined) { throw new Error('expected root'); } @@ -405,14 +305,14 @@ export class Calldata { // 1. Create a queue of subtrees by hash // Note that they are ordered the same as - const subtreeQueue = this.createQueue(this.root); + const subtreeQueue = Calldata._createQueue(this._root); let block: CalldataBlock | undefined; - while ((block = subtreeQueue.popBack()) !== undefined) { + for (block = subtreeQueue.popBack(); block !== undefined; block = subtreeQueue.popBack()) { if (block instanceof DependentCalldataBlock) { - const blockHashBuf = block.getDependency().computeHash(); - const blockHash = ethUtil.bufferToHex(blockHashBuf); - if (blockHash in blocksByHash) { - const blockWithSameHash = blocksByHash[blockHash]; + const dependencyBlockHashBuf = block.getDependency().computeHash(); + const dependencyBlockHash = ethUtil.bufferToHex(dependencyBlockHashBuf); + if (dependencyBlockHash in blocksByHash) { + const blockWithSameHash = blocksByHash[dependencyBlockHash]; if (blockWithSameHash !== block.getDependency()) { block.setAlias(blockWithSameHash); } @@ -422,69 +322,142 @@ export class Calldata { const blockHashBuf = block.computeHash(); const blockHash = ethUtil.bufferToHex(blockHashBuf); - if (blockHash in blocksByHash === false) { + if (!(blockHash in blocksByHash)) { blocksByHash[blockHash] = block; } } } public toHexString(): string { - if (this.root === undefined) { + if (this._root === undefined) { throw new Error('expected root'); } - if (this.rules.optimize) this.optimize(); + if (this._rules.optimize) { + this.optimize(); + } - const offsetQueue = this.createQueue(this.root); + const offsetQueue = Calldata._createQueue(this._root); let block: CalldataBlock | undefined; let offset = 0; - while ((block = offsetQueue.pop()) !== undefined) { + for (block = offsetQueue.pop(); block !== undefined; block = offsetQueue.pop()) { block.setOffset(offset); offset += block.getSizeInBytes(); } - const hexValue = this.rules.annotate ? this.generateAnnotatedHexString() : this.generateCondensedHexString(); + const hexValue = this._rules.annotate ? this._generateAnnotatedHexString() : this._generateCondensedHexString(); return hexValue; } public getSelectorHex(): string { - return this.selector; + return this._selector; } public getSizeInBytes(): number { - return this.sizeInBytes; + return this._sizeInBytes; } - public toAnnotatedString(): string { - return ''; + public setRoot(block: CalldataBlock): void { + this._root = block; + this._sizeInBytes += block.getSizeInBytes(); } - public setRoot(block: CalldataBlock) { - this.root = block; - this.sizeInBytes += block.getSizeInBytes(); + public setSelector(selector: string): void { + this._selector = selector.startsWith('0x') ? selector : `$0x${selector}`; + if (this._selector.length !== Constants.HEX_SELECTOR_LENGTH_IN_CHARS) { + throw new Error(`Invalid selector '${this._selector}'`); + } + this._sizeInBytes += Constants.HEX_SELECTOR_LENGTH_IN_BYTES; // @TODO: Used to be += 8. Bad? } - public setSelector(selector: string) { - // Ensure we have a 0x prefix - if (selector.startsWith('0x')) { - this.selector = selector; - } else { - this.selector = `$0x${selector}`; + private _generateAnnotatedHexString(): string { + let hexValue = `${this._selector}`; + if (this._root === undefined) { + throw new Error('expected root'); } - // The selector must be 10 characters: '0x' followed by 4 bytes (two hex chars per byte) - if (this.selector.length !== 10) { - throw new Error(`Invalid selector '${this.selector}'`); + const valueQueue = Calldata._createQueue(this._root); + + let block: CalldataBlock | undefined; + let offset = 0; + const functionBlock = valueQueue.peek(); + const functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); + for (block = valueQueue.pop(); block !== undefined; block = valueQueue.pop()) { + // Process each block 1 word at a time + const size = block.getSizeInBytes(); + const name = block.getName(); + const parentName = block.getParentName(); + const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, ''); + + // Current offset + let offsetStr = ''; + + // If this block is empty then it's a newline + const offsetPadding = 10; + const valuePadding = 74; + const namePadding = 80; + const evmWordStartIndex = 0; + const emptySize = 0; + let value = ''; + let nameStr = ''; + let line = ''; + if (size === emptySize) { + offsetStr = ' '.repeat(offsetPadding); + value = ' '.repeat(valuePadding); + nameStr = `### ${prettyName.padEnd(namePadding)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + offsetStr = `0x${offset.toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(evmWordStartIndex, Constants.EVM_WORD_WIDTH_IN_BYTES))).padEnd(valuePadding); + if (block instanceof MemberCalldataBlock) { + nameStr = `### ${prettyName.padEnd(namePadding)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + nameStr = ` ${prettyName.padEnd(namePadding)}`; + line = `${offsetStr}${value}${nameStr}`; + } + } + + for (let j = Constants.EVM_WORD_WIDTH_IN_BYTES; j < size; j += Constants.EVM_WORD_WIDTH_IN_BYTES) { + offsetStr = `0x${(offset + j).toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); + value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(j, j + Constants.EVM_WORD_WIDTH_IN_BYTES))).padEnd(valuePadding); + nameStr = ' '.repeat(namePadding); + line = `${line}\n${offsetStr}${value}${nameStr}`; + } + + // Append to hex value + hexValue = `${hexValue}\n${line}`; + offset += size; + } + + return hexValue; + } + + private _generateCondensedHexString(): string { + const selectorBuffer = ethUtil.toBuffer(this._selector); + if (this._root === undefined) { + throw new Error('expected root'); + } + + const valueQueue = Calldata._createQueue(this._root); + const valueBufs: Buffer[] = [selectorBuffer]; + let block: CalldataBlock | undefined; + for (block = valueQueue.pop(); block !== undefined; block = valueQueue.pop()) { + valueBufs.push(block.toBuffer()); } - this.sizeInBytes += 8; + + const combinedBuffers = Buffer.concat(valueBufs); + const hexValue = ethUtil.bufferToHex(combinedBuffers); + return hexValue; } } export class RawCalldata { - private value: Buffer; - private offset: number; // tracks current offset into raw calldata; used for parsing - private selector: string; - private scopes: Queue; + private static readonly _INITIAL_OFFSET = 0; + private readonly _value: Buffer; + private readonly _selector: string; + private readonly _scopes: Queue; + private _offset: number; // tracks current offset into raw calldata; used for parsing constructor(value: string | Buffer, hasSelectorPrefix: boolean = true) { if (typeof value === 'string' && !value.startsWith('0x')) { @@ -492,21 +465,21 @@ export class RawCalldata { } const valueBuf = ethUtil.toBuffer(value); if (hasSelectorPrefix) { - this.selector = ethUtil.bufferToHex(valueBuf.slice(0, 4)); - this.value = valueBuf.slice(4); // disregard selector + this._selector = ethUtil.bufferToHex(valueBuf.slice(Constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, Constants.HEX_SELECTOR_LENGTH_IN_BYTES)); + this._value = valueBuf.slice(Constants.HEX_SELECTOR_LENGTH_IN_BYTES); // disregard selector } else { - this.selector = '0x'; - this.value = valueBuf; + this._selector = '0x'; + this._value = valueBuf; } - this.offset = 0; - this.scopes = new Queue(); - this.scopes.push(0); + this._scopes = new Queue(); + this._scopes.push(RawCalldata._INITIAL_OFFSET); + this._offset = RawCalldata._INITIAL_OFFSET; } public popBytes(lengthInBytes: number): Buffer { - const value = this.value.slice(this.offset, this.offset + lengthInBytes); - this.setOffset(this.offset + lengthInBytes); + const value = this._value.slice(this._offset, this._offset + lengthInBytes); + this.setOffset(this._offset + lengthInBytes); return value; } @@ -521,28 +494,28 @@ export class RawCalldata { } public readBytes(from: number, to: number): Buffer { - const value = this.value.slice(from, to); + const value = this._value.slice(from, to); return value; } - public setOffset(offsetInBytes: number) { - this.offset = offsetInBytes; + public setOffset(offsetInBytes: number): void { + this._offset = offsetInBytes; } - public startScope() { - this.scopes.pushFront(this.offset); + public startScope(): void { + this._scopes.pushFront(this._offset); } - public endScope() { - this.scopes.pop(); + public endScope(): void { + this._scopes.pop(); } public getOffset(): number { - return this.offset; + return this._offset; } - public toAbsoluteOffset(relativeOffset: number) { - const scopeOffset = this.scopes.peek(); + public toAbsoluteOffset(relativeOffset: number): number { + const scopeOffset = this._scopes.peek(); if (scopeOffset === undefined) { throw new Error(`Tried to access undefined scope.`); } @@ -551,6 +524,6 @@ export class RawCalldata { } public getSelector(): string { - return this.selector; + return this._selector; } } diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index 80797c563..926023468 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -78,7 +78,6 @@ export abstract class PayloadDataType extends DataType { name, signature, parentName, - /*offsetInBytes,*/ relocatable, encodedValue, ); return block; @@ -116,12 +115,10 @@ export abstract class DependentDataType extends DataType { const name = this.getDataItem().name; const signature = this.getSignature(); const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const relocatable = false; const block = new DependentCalldataBlock( name, signature, parentName, - relocatable, dependencyBlock, parentBlock, ); @@ -235,9 +232,7 @@ export abstract class MemberDataType extends DataType { const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( this.getDataItem().name, this.getSignature(), - parentName, - this.isStatic(), - false, + parentName ); let members = this.members; @@ -262,9 +257,7 @@ export abstract class MemberDataType extends DataType { const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( this.getDataItem().name, this.getSignature(), - parentName, - this.isStatic(), - false, + parentName ); const memberBlocks: CalldataBlock[] = []; let childMap = _.cloneDeep(this.memberMap); -- cgit v1.2.3 From 0f7abcd59bcbad5a74c7984b199158f5c9f03934 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 10:08:17 -0800 Subject: Moved global constants to separate file --- packages/utils/src/abi_encoder/constants.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 packages/utils/src/abi_encoder/constants.ts diff --git a/packages/utils/src/abi_encoder/constants.ts b/packages/utils/src/abi_encoder/constants.ts new file mode 100644 index 000000000..b52630d74 --- /dev/null +++ b/packages/utils/src/abi_encoder/constants.ts @@ -0,0 +1,6 @@ +export const EVM_WORD_WIDTH_IN_BYTES = 32; +export const HEX_BASE = 16; +export const BIN_BASE = 2; +export const HEX_SELECTOR_LENGTH_IN_CHARS = 10; +export const HEX_SELECTOR_LENGTH_IN_BYTES = 4; +export const HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA = 0; \ No newline at end of file -- cgit v1.2.3 From fbbca8e2837b8036b8dfd7985c702fd05073daa3 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 10:09:38 -0800 Subject: Ran prettier on utils --- packages/utils/src/abi_encoder/calldata.ts | 33 +-- packages/utils/src/abi_encoder/constants.ts | 2 +- packages/utils/src/abi_encoder/data_type.ts | 19 +- packages/utils/src/abi_encoder/evm_data_types.ts | 4 +- packages/utils/test/abi_encoder_test.ts | 4 +- packages/utils/test/optimizer_abis.ts | 339 +++++++++++++++++++++++ packages/utils/test/return_value_abis.ts | 98 +++++++ 7 files changed, 463 insertions(+), 36 deletions(-) create mode 100644 packages/utils/test/optimizer_abis.ts create mode 100644 packages/utils/test/return_value_abis.ts diff --git a/packages/utils/src/abi_encoder/calldata.ts b/packages/utils/src/abi_encoder/calldata.ts index 9f91f8495..0c45d6198 100644 --- a/packages/utils/src/abi_encoder/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata.ts @@ -90,12 +90,7 @@ export abstract class CalldataBlock { export class PayloadCalldataBlock extends CalldataBlock { private readonly _payload: Buffer; - constructor( - name: string, - signature: string, - parentName: string, - payload: Buffer, - ) { + constructor(name: string, signature: string, parentName: string, payload: Buffer) { const headerSizeInBytes = 0; const bodySizeInBytes = payload.byteLength; super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); @@ -120,13 +115,7 @@ export class DependentCalldataBlock extends CalldataBlock { private readonly _dependency: CalldataBlock; private _aliasFor: CalldataBlock | undefined; - constructor( - name: string, - signature: string, - parentName: string, - dependency: CalldataBlock, - parent: CalldataBlock, - ) { + constructor(name: string, signature: string, parentName: string, dependency: CalldataBlock, parent: CalldataBlock) { const headerSizeInBytes = DependentCalldataBlock._EMPTY_HEADER_SIZE; const bodySizeInBytes = DependentCalldataBlock._DEPENDENT_PAYLOAD_SIZE_IN_BYTES; super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); @@ -408,7 +397,13 @@ export class Calldata { line = `\n${offsetStr}${value}${nameStr}`; } else { offsetStr = `0x${offset.toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); - value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(evmWordStartIndex, Constants.EVM_WORD_WIDTH_IN_BYTES))).padEnd(valuePadding); + value = ethUtil + .stripHexPrefix( + ethUtil.bufferToHex( + block.toBuffer().slice(evmWordStartIndex, Constants.EVM_WORD_WIDTH_IN_BYTES), + ), + ) + .padEnd(valuePadding); if (block instanceof MemberCalldataBlock) { nameStr = `### ${prettyName.padEnd(namePadding)}`; line = `\n${offsetStr}${value}${nameStr}`; @@ -420,7 +415,11 @@ export class Calldata { for (let j = Constants.EVM_WORD_WIDTH_IN_BYTES; j < size; j += Constants.EVM_WORD_WIDTH_IN_BYTES) { offsetStr = `0x${(offset + j).toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); - value = ethUtil.stripHexPrefix(ethUtil.bufferToHex(block.toBuffer().slice(j, j + Constants.EVM_WORD_WIDTH_IN_BYTES))).padEnd(valuePadding); + value = ethUtil + .stripHexPrefix( + ethUtil.bufferToHex(block.toBuffer().slice(j, j + Constants.EVM_WORD_WIDTH_IN_BYTES)), + ) + .padEnd(valuePadding); nameStr = ' '.repeat(namePadding); line = `${line}\n${offsetStr}${value}${nameStr}`; } @@ -465,7 +464,9 @@ export class RawCalldata { } const valueBuf = ethUtil.toBuffer(value); if (hasSelectorPrefix) { - this._selector = ethUtil.bufferToHex(valueBuf.slice(Constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, Constants.HEX_SELECTOR_LENGTH_IN_BYTES)); + this._selector = ethUtil.bufferToHex( + valueBuf.slice(Constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, Constants.HEX_SELECTOR_LENGTH_IN_BYTES), + ); this._value = valueBuf.slice(Constants.HEX_SELECTOR_LENGTH_IN_BYTES); // disregard selector } else { this._selector = '0x'; diff --git a/packages/utils/src/abi_encoder/constants.ts b/packages/utils/src/abi_encoder/constants.ts index b52630d74..bc5b985e0 100644 --- a/packages/utils/src/abi_encoder/constants.ts +++ b/packages/utils/src/abi_encoder/constants.ts @@ -3,4 +3,4 @@ export const HEX_BASE = 16; export const BIN_BASE = 2; export const HEX_SELECTOR_LENGTH_IN_CHARS = 10; export const HEX_SELECTOR_LENGTH_IN_BYTES = 4; -export const HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA = 0; \ No newline at end of file +export const HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA = 0; diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index 926023468..4370cb253 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -74,12 +74,7 @@ export abstract class PayloadDataType extends DataType { const signature = this.getSignature(); const parentName = parentBlock === undefined ? '' : parentBlock.getName(); const relocatable = false; - const block = new PayloadCalldataBlock( - name, - signature, - parentName, - encodedValue, - ); + const block = new PayloadCalldataBlock(name, signature, parentName, encodedValue); return block; } @@ -115,13 +110,7 @@ export abstract class DependentDataType extends DataType { const name = this.getDataItem().name; const signature = this.getSignature(); const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const block = new DependentCalldataBlock( - name, - signature, - parentName, - dependencyBlock, - parentBlock, - ); + const block = new DependentCalldataBlock(name, signature, parentName, dependencyBlock, parentBlock); return block; } @@ -232,7 +221,7 @@ export abstract class MemberDataType extends DataType { const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( this.getDataItem().name, this.getSignature(), - parentName + parentName, ); let members = this.members; @@ -257,7 +246,7 @@ export abstract class MemberDataType extends DataType { const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( this.getDataItem().name, this.getSignature(), - parentName + parentName, ); const memberBlocks: CalldataBlock[] = []; let childMap = _.cloneDeep(this.memberMap); diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index b862e9396..76837361e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -276,7 +276,7 @@ export class Byte extends PayloadDataType { if (valueBuf.byteLength > this.width) { throw new Error( `Tried to assign ${value} (${ - valueBuf.byteLength + valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); } else if (value.length % 2 !== 0) { @@ -548,7 +548,7 @@ export class Method extends MemberDataType { export class EvmDataTypeFactory implements DataTypeFactory { private static instance: DataTypeFactory; - private constructor() { } + private constructor() {} public static getInstance(): DataTypeFactory { if (!EvmDataTypeFactory.instance) { diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index 0220984b0..c7986fa00 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -102,7 +102,7 @@ describe.only('ABI Encoder', () => { it('Duplicate Dynamic Arrays with Dynamic Elements', async () => { // Generate calldata const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithDynamicElements); - const array1 = ["Hello", "World"]; + const array1 = ['Hello', 'World']; const array2 = array1; const args = [array1, array2]; // Validata calldata @@ -138,7 +138,7 @@ describe.only('ABI Encoder', () => { it('Duplicate Static Arrays with Dynamic Elements', async () => { // Generate calldata const method = new AbiEncoder.Method(OptimizedAbis.duplicateStaticArraysWithDynamicElements); - const array1 = ["Hello", "World"]; + const array1 = ['Hello', 'World']; const array2 = array1; const args = [array1, array2]; // Validata calldata diff --git a/packages/utils/test/optimizer_abis.ts b/packages/utils/test/optimizer_abis.ts new file mode 100644 index 000000000..ea562e5b5 --- /dev/null +++ b/packages/utils/test/optimizer_abis.ts @@ -0,0 +1,339 @@ +import { MethodAbi } from 'ethereum-types'; + +export const duplicateDynamicArraysWithStaticElements = { + constant: false, + inputs: [ + { + name: 'array1', + type: 'uint[]', + }, + { + name: 'array2', + type: 'uint[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const duplicateDynamicArraysWithDynamicElements = { + constant: false, + inputs: [ + { + name: 'array1', + type: 'string[]', + }, + { + name: 'array2', + type: 'string[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const duplicateStaticArraysWithStaticElements = { + constant: false, + inputs: [ + { + name: 'array1', + type: 'uint[2]', + }, + { + name: 'array2', + type: 'uint[2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const duplicateStaticArraysWithDynamicElements = { + constant: false, + inputs: [ + { + name: 'array1', + type: 'string[2]', + }, + { + name: 'array2', + type: 'string[2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const duplicateArrayElements = { + constant: false, + inputs: [ + { + name: 'array', + type: 'string[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const duplicateTupleFields = { + constant: false, + inputs: [ + { + components: [ + { + name: 'field1', + type: 'string', + }, + { + name: 'field2', + type: 'string', + }, + ], + name: 'Tuple', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const duplicateStrings = { + constant: false, + inputs: [ + { + name: 'string1', + type: 'string', + }, + { + name: 'string2', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const duplicateBytes = { + constant: false, + inputs: [ + { + name: 'bytes1', + type: 'bytes', + }, + { + name: 'bytes2', + type: 'bytes', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const duplicateTuples = { + constant: false, + inputs: [ + { + components: [ + { + name: 'field1', + type: 'string', + }, + { + name: 'field2', + type: 'uint', + }, + ], + name: 'Tuple', + type: 'tuple', + }, + { + components: [ + { + name: 'field1', + type: 'string', + }, + { + name: 'field2', + type: 'uint', + }, + ], + name: 'Tuple', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const duplicateArraysNestedInTuples = { + constant: false, + inputs: [ + { + components: [ + { + name: 'field', + type: 'uint[]', + }, + ], + name: 'Tuple1', + type: 'tuple', + }, + { + components: [ + { + name: 'field', + type: 'uint[]', + }, + { + name: 'extraField', + type: 'string', + }, + ], + name: 'Tuple2', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const duplicateTuplesNestedInTuples = { + constant: false, + inputs: [ + { + components: [ + { + components: [ + { + name: 'nestedField', + type: 'string', + }, + ], + name: 'field', + type: 'tuple', + }, + ], + name: 'Tuple1', + type: 'tuple', + }, + { + components: [ + { + components: [ + { + name: 'nestedField', + type: 'string', + }, + ], + name: 'field', + type: 'tuple', + }, + { + name: 'extraField', + type: 'string', + }, + ], + name: 'Tuple1', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const duplicateTwoDimensionalArrays = { + constant: false, + inputs: [ + { + name: 'array1', + type: 'string[][]', + }, + { + name: 'array2', + type: 'string[][]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayElementsDuplicatedAsSeparateParameter = { + constant: false, + inputs: [ + { + name: 'stringArray', + type: 'string[]', + }, + { + name: 'string', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const arrayElementsDuplicatedAsTupleFields = { + constant: false, + inputs: [ + { + name: 'uint8Array', + type: 'uint8[]', + }, + { + components: [ + { + name: 'uint', + type: 'uint', + }, + ], + name: 'uintTuple', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; diff --git a/packages/utils/test/return_value_abis.ts b/packages/utils/test/return_value_abis.ts new file mode 100644 index 000000000..847559dac --- /dev/null +++ b/packages/utils/test/return_value_abis.ts @@ -0,0 +1,98 @@ +import { MethodAbi } from 'ethereum-types'; + +export const noReturnValues = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const singleStaticReturnValue = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [ + { + name: 'Bytes4', + type: 'bytes4', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const multipleStaticReturnValues = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [ + { + name: 'val1', + type: 'bytes4', + }, + { + name: 'val2', + type: 'bytes4', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const singleDynamicReturnValue = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [ + { + name: 'val', + type: 'bytes', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const multipleDynamicReturnValues = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [ + { + name: 'val1', + type: 'bytes', + }, + { + name: 'val2', + type: 'bytes', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; + +export const mixedStaticAndDynamicReturnValues = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [ + { + name: 'val1', + type: 'bytes4', + }, + { + name: 'val2', + type: 'bytes', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +} as MethodAbi; -- cgit v1.2.3 From 406b5739be91d2a25337b247d31cef720ddd0ae3 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 10:32:31 -0800 Subject: Fixed linter errors on data_type.ts --- packages/utils/src/abi_encoder/calldata.ts | 6 +- packages/utils/src/abi_encoder/constants.ts | 1 + packages/utils/src/abi_encoder/data_type.ts | 312 ++++++++++++----------- packages/utils/src/abi_encoder/evm_data_types.ts | 14 +- 4 files changed, 171 insertions(+), 162 deletions(-) diff --git a/packages/utils/src/abi_encoder/calldata.ts b/packages/utils/src/abi_encoder/calldata.ts index 0c45d6198..994b0fb81 100644 --- a/packages/utils/src/abi_encoder/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata.ts @@ -1,5 +1,7 @@ -import ethUtil = require('ethereumjs-util'); -const _ = require('lodash'); + +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + import * as Constants from './constants'; export interface DecodingRules { diff --git a/packages/utils/src/abi_encoder/constants.ts b/packages/utils/src/abi_encoder/constants.ts index bc5b985e0..52029695f 100644 --- a/packages/utils/src/abi_encoder/constants.ts +++ b/packages/utils/src/abi_encoder/constants.ts @@ -1,5 +1,6 @@ export const EVM_WORD_WIDTH_IN_BYTES = 32; export const HEX_BASE = 16; +export const DEC_BASE = 10; export const BIN_BASE = 2; export const HEX_SELECTOR_LENGTH_IN_CHARS = 10; export const HEX_SELECTOR_LENGTH_IN_BYTES = 4; diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index 4370cb253..ce7b11ef6 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -1,16 +1,21 @@ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { BigNumber } from '../configured_bignumber'; + +import * as Constants from './constants'; + import { - RawCalldata, Calldata, CalldataBlock, - PayloadCalldataBlock, + DecodingRules, DependentCalldataBlock, + EncodingRules, MemberCalldataBlock, + PayloadCalldataBlock, + RawCalldata, } from './calldata'; -import { MethodAbi, DataItem } from 'ethereum-types'; -import { DecodingRules, EncodingRules } from './calldata'; -import { BigNumber } from '../configured_bignumber'; -import ethUtil = require('ethereumjs-util'); -var _ = require('lodash'); export interface DataTypeFactory { create: (dataItem: DataItem, parentDataType?: DataType) => DataType; @@ -18,29 +23,30 @@ export interface DataTypeFactory { } export abstract class DataType { - private static DEFAULT_ENCODING_RULES = { optimize: false, annotate: false } as EncodingRules; - private static DEFAULT_DECODING_RULES = { structsAsObjects: false } as DecodingRules; - - private dataItem: DataItem; - private factory: DataTypeFactory; + private static readonly _DEFAULT_ENCODING_RULES: EncodingRules = { optimize: false, annotate: false }; + private static readonly _DEFAULT_DECODING_RULES: DecodingRules = { structsAsObjects: false }; + private readonly _dataItem: DataItem; + private readonly _factory: DataTypeFactory; constructor(dataItem: DataItem, factory: DataTypeFactory) { - this.dataItem = dataItem; - this.factory = factory; + this._dataItem = dataItem; + this._factory = factory; } public getDataItem(): DataItem { - return this.dataItem; + return this._dataItem; } public getFactory(): DataTypeFactory { - return this.factory; + return this._factory; } public encode(value: any, rules?: EncodingRules, selector?: string): string { - const rules_ = rules ? rules : DataType.DEFAULT_ENCODING_RULES; + const rules_ = rules ? rules : DataType._DEFAULT_ENCODING_RULES; const calldata = new Calldata(rules_); - if (selector) calldata.setSelector(selector); + if (selector) { + calldata.setSelector(selector); + } const block = this.generateCalldataBlock(value); calldata.setRoot(block); // @TODO CHANGE const calldataHex = calldata.toHexString(); @@ -49,7 +55,7 @@ export abstract class DataType { public decode(calldata: string, rules?: DecodingRules, hasSelector: boolean = false): any { const rawCalldata = new RawCalldata(calldata, hasSelector); - const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; + const rules_ = rules ? rules : DataType._DEFAULT_DECODING_RULES; const value = this.generateValue(rawCalldata, rules_); return value; } @@ -61,11 +67,11 @@ export abstract class DataType { } export abstract class PayloadDataType extends DataType { - protected hasConstantSize: boolean; + protected _hasConstantSize: boolean; public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { super(dataItem, factory); - this.hasConstantSize = hasConstantSize; + this._hasConstantSize = hasConstantSize; } public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { @@ -73,7 +79,6 @@ export abstract class PayloadDataType extends DataType { const name = this.getDataItem().name; const signature = this.getSignature(); const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const relocatable = false; const block = new PayloadCalldataBlock(name, signature, parentName, encodedValue); return block; } @@ -84,8 +89,7 @@ export abstract class PayloadDataType extends DataType { } public isStatic(): boolean { - // If a payload has a constant size then it's static - return this.hasConstantSize; + return this._hasConstantSize; } public abstract encodeValue(value: any): Buffer; @@ -93,20 +97,22 @@ export abstract class PayloadDataType extends DataType { } export abstract class DependentDataType extends DataType { - protected dependency: DataType; - protected parent: DataType; + protected _dependency: DataType; + protected _parent: DataType; + private readonly _isStatic: boolean; public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { super(dataItem, factory); - this.dependency = dependency; - this.parent = parent; + this._dependency = dependency; + this._parent = parent; + this._isStatic = true; } public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { if (parentBlock === undefined) { throw new Error(`DependentDataType requires a parent block to generate its block`); } - const dependencyBlock = this.dependency.generateCalldataBlock(value, parentBlock); + const dependencyBlock = this._dependency.generateCalldataBlock(value, parentBlock); const name = this.getDataItem().name; const signature = this.getSignature(); const parentName = parentBlock === undefined ? '' : parentBlock.getName(); @@ -117,16 +123,16 @@ export abstract class DependentDataType extends DataType { public generateValue(calldata: RawCalldata, rules: DecodingRules): any { const destinationOffsetBuf = calldata.popWord(); const currentOffset = calldata.getOffset(); - const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), 16); + const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), Constants.HEX_BASE); const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); calldata.setOffset(destinationOffsetAbsolute); - const value = this.dependency.generateValue(calldata, rules); + const value = this._dependency.generateValue(calldata, rules); calldata.setOffset(currentOffset); return value; } public isStatic(): boolean { - return true; + return this._isStatic; } } @@ -135,11 +141,11 @@ export interface MemberMap { } export abstract class MemberDataType extends DataType { - private memberMap: MemberMap; - private members: DataType[]; - private isArray: boolean; - protected arrayLength: number | undefined; - protected arrayElementType: string | undefined; + protected readonly _arrayLength: number | undefined; + protected readonly _arrayElementType: string | undefined; + private readonly _memberMap: MemberMap; + private readonly _members: DataType[]; + private readonly _isArray: boolean; public constructor( dataItem: DataItem, @@ -149,70 +155,86 @@ export abstract class MemberDataType extends DataType { arrayElementType?: string, ) { super(dataItem, factory); - this.memberMap = {}; - this.members = []; - this.isArray = isArray; - this.arrayLength = arrayLength; - this.arrayElementType = arrayElementType; + this._memberMap = {}; + this._members = []; + this._isArray = isArray; + this._arrayLength = arrayLength; + this._arrayElementType = arrayElementType; if (isArray && arrayLength !== undefined) { - [this.members, this.memberMap] = this.createMembersWithLength(dataItem, arrayLength); + [this._members, this._memberMap] = this._createMembersWithLength(dataItem, arrayLength); } else if (!isArray) { - [this.members, this.memberMap] = this.createMembersWithKeys(dataItem); + [this._members, this._memberMap] = this._createMembersWithKeys(dataItem); } } - private createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { - // Sanity check - if (dataItem.components === undefined) { - throw new Error(`Expected components`); - } + public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const block = + value instanceof Array + ? this._generateCalldataBlockFromArray(value, parentBlock) + : this._generateCalldataBlockFromObject(value, parentBlock); + return block; + } - let members: DataType[] = []; - let memberMap: MemberMap = {}; - _.each(dataItem.components, (memberItem: DataItem) => { - const childDataItem = { - type: memberItem.type, - name: `${dataItem.name}.${memberItem.name}`, - } as DataItem; - const components = memberItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[memberItem.name] = members.length; - members.push(child); - }); + public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { + let members = this._members; + if (this._isArray && this._arrayLength === undefined) { + const arrayLengthBuf = calldata.popWord(); + const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); + const hexBase = 16; + const arrayLength = new BigNumber(arrayLengthHex, hexBase); - return [members, memberMap]; + [members] = this._createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); + } + + calldata.startScope(); + let value: any[] | object; + if (rules.structsAsObjects && !this._isArray) { + value = {}; + _.each(this._memberMap, (idx: number, key: string) => { + const member = this._members[idx]; + const memberValue = member.generateValue(calldata, rules); + (value as { [key: string]: any })[key] = memberValue; + }); + } else { + value = []; + _.each(members, (member: DataType, idx: number) => { + const memberValue = member.generateValue(calldata, rules); + (value as any[]).push(memberValue); + }); + } + calldata.endScope(); + return value; } - private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { - let members: DataType[] = []; - let memberMap: MemberMap = {}; - const range = _.range(length); - _.each(range, (idx: number) => { - const childDataItem = { - type: this.arrayElementType, - name: `${dataItem.name}[${idx.toString(10)}]`, - } as DataItem; - const components = dataItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[idx.toString(10)] = members.length; - members.push(child); - }); + public isStatic(): boolean { + /* For Tuple: + const isStaticTuple = this.children.length === 0; + return isStaticTuple; // @TODO: True in every case or only when dynamic data? - return [members, memberMap]; + For Array: + if isLengthDefined = false then this is false + + Otherwise if the first element is a Pointer then false + */ + + if (this._isArray && this._arrayLength === undefined) { + return false; + } + + // Search for dependent members + const dependentMember = _.find(this._members, (member: DataType) => { + return member instanceof DependentDataType; + }); + const isStatic = dependentMember === undefined; // static if we couldn't find a dependent member + return isStatic; } - protected generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { + protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { // Sanity check length - if (this.arrayLength !== undefined && value.length !== this.arrayLength) { + if (this._arrayLength !== undefined && value.length !== this._arrayLength) { throw new Error( `Expected array of ${JSON.stringify( - this.arrayLength, + this._arrayLength, )} elements, but got array of length ${JSON.stringify(value.length)}`, ); } @@ -224,11 +246,11 @@ export abstract class MemberDataType extends DataType { parentName, ); - let members = this.members; - if (this.isArray && this.arrayLength === undefined) { - [members] = this.createMembersWithLength(this.getDataItem(), value.length); + let members = this._members; + if (this._isArray && this._arrayLength === undefined) { + [members] = this._createMembersWithLength(this.getDataItem(), value.length); - const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(16)}`), 32); + const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(Constants.HEX_BASE)}`), Constants.EVM_WORD_WIDTH_IN_BYTES); methodBlock.setHeader(lenBuf); } @@ -241,7 +263,7 @@ export abstract class MemberDataType extends DataType { return methodBlock; } - protected generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { + protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { const parentName = parentBlock === undefined ? '' : parentBlock.getName(); const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( this.getDataItem().name, @@ -249,14 +271,14 @@ export abstract class MemberDataType extends DataType { parentName, ); const memberBlocks: CalldataBlock[] = []; - let childMap = _.cloneDeep(this.memberMap); + const childMap = _.cloneDeep(this._memberMap); _.forOwn(obj, (value: any, key: string) => { - if (key in childMap === false) { + if (!(key in childMap)) { throw new Error( `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`, ); } - const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock); + const block = this._members[this._memberMap[key]].generateCalldataBlock(value, methodBlock); memberBlocks.push(block); delete childMap[key]; }); @@ -269,51 +291,12 @@ export abstract class MemberDataType extends DataType { return methodBlock; } - public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const block = - value instanceof Array - ? this.generateCalldataBlockFromArray(value, parentBlock) - : this.generateCalldataBlockFromObject(value, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { - let members = this.members; - if (this.isArray && this.arrayLength === undefined) { - const arrayLengthBuf = calldata.popWord(); - const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); - const hexBase = 16; - const arrayLength = new BigNumber(arrayLengthHex, hexBase); - - [members] = this.createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); - } - - calldata.startScope(); - let value: any[] | object; - if (rules.structsAsObjects && !this.isArray) { - value = {}; - _.each(this.memberMap, (idx: number, key: string) => { - const member = this.members[idx]; - let memberValue = member.generateValue(calldata, rules); - (value as { [key: string]: any })[key] = memberValue; - }); - } else { - value = []; - _.each(members, (member: DataType, idx: number) => { - let memberValue = member.generateValue(calldata, rules); - (value as any[]).push(memberValue); - }); - } - calldata.endScope(); - return value; - } - - protected computeSignatureOfMembers(): string { + protected _computeSignatureOfMembers(): string { // Compute signature of members let signature = `(`; - _.each(this.members, (member: DataType, i: number) => { + _.each(this._members, (member: DataType, i: number) => { signature += member.getSignature(); - if (i < this.members.length - 1) { + if (i < this._members.length - 1) { signature += ','; } }); @@ -321,26 +304,49 @@ export abstract class MemberDataType extends DataType { return signature; } - public isStatic(): boolean { - /* For Tuple: - const isStaticTuple = this.children.length === 0; - return isStaticTuple; // @TODO: True in every case or only when dynamic data? - - For Array: - if isLengthDefined = false then this is false + private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { + // Sanity check + if (dataItem.components === undefined) { + throw new Error(`Expected components`); + } - Otherwise if the first element is a Pointer then false - */ + const members: DataType[] = []; + const memberMap: MemberMap = {}; + _.each(dataItem.components, (memberItem: DataItem) => { + const childDataItem: DataItem = { + type: memberItem.type, + name: `${dataItem.name}.${memberItem.name}`, + }; + const components = memberItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[memberItem.name] = members.length; + members.push(child); + }); - if (this.isArray && this.arrayLength === undefined) { - return false; - } + return [members, memberMap]; + } - // Search for dependent members - const dependentMember = _.find(this.members, (member: DataType) => { - return member instanceof DependentDataType; + private _createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { + const members: DataType[] = []; + const memberMap: MemberMap = {}; + const range = _.range(length); + _.each(range, (idx: number) => { + const childDataItem: DataItem = { + type: this._arrayElementType ? this._arrayElementType : '', + name: `${dataItem.name}[${idx.toString(Constants.DEC_BASE)}]`, + }; + const components = dataItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[idx.toString(Constants.DEC_BASE)] = members.length; + members.push(child); }); - const isStatic = dependentMember === undefined; // static if we couldn't find a dependent member - return isStatic; + + return [members, memberMap]; } } diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index 76837361e..1f5dff56f 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -276,7 +276,7 @@ export class Byte extends PayloadDataType { if (valueBuf.byteLength > this.width) { throw new Error( `Tried to assign ${value} (${ - valueBuf.byteLength + valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); } else if (value.length % 2 !== 0) { @@ -396,7 +396,7 @@ export class Pointer extends DependentDataType { } public getSignature(): string { - return this.dependency.getSignature(); + return this._dependency.getSignature(); } } @@ -408,7 +408,7 @@ export class Tuple extends MemberDataType { if (!Tuple.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); } - this.tupleSignature = this.computeSignatureOfMembers(); + this.tupleSignature = this._computeSignatureOfMembers(); } public getSignature(): string { @@ -455,10 +455,10 @@ export class SolArray extends MemberDataType { } const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); const type = elementDataType.getSignature(); - if (this.arrayLength === undefined) { + if (this._arrayLength === undefined) { return `${type}[]`; } else { - return `${type}[${this.arrayLength}]`; + return `${type}[${this._arrayLength}]`; } } @@ -493,7 +493,7 @@ export class Method extends MemberDataType { } private computeSignature(): string { - const memberSignature = this.computeSignatureOfMembers(); + const memberSignature = this._computeSignatureOfMembers(); const methodSignature = `${this.getDataItem().name}${memberSignature}`; return methodSignature; } @@ -548,7 +548,7 @@ export class Method extends MemberDataType { export class EvmDataTypeFactory implements DataTypeFactory { private static instance: DataTypeFactory; - private constructor() {} + private constructor() { } public static getInstance(): DataTypeFactory { if (!EvmDataTypeFactory.instance) { -- cgit v1.2.3 From 0ed1819143fa82163502fb412bcb5ed44c041456 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 11:14:02 -0800 Subject: Fixed linter errors on evm_data_types --- packages/utils/src/abi_encoder/constants.ts | 1 + packages/utils/src/abi_encoder/evm_data_types.ts | 383 +++++++++++------------ 2 files changed, 190 insertions(+), 194 deletions(-) diff --git a/packages/utils/src/abi_encoder/constants.ts b/packages/utils/src/abi_encoder/constants.ts index 52029695f..3d85fbdb8 100644 --- a/packages/utils/src/abi_encoder/constants.ts +++ b/packages/utils/src/abi_encoder/constants.ts @@ -1,4 +1,5 @@ export const EVM_WORD_WIDTH_IN_BYTES = 32; +export const EVM_WORD_WIDTH_IN_BITS = 256; export const HEX_BASE = 16; export const DEC_BASE = 10; export const BIN_BASE = 2; diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index 1f5dff56f..24f6051b0 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -1,16 +1,12 @@ -import { DataType, DataTypeFactory, PayloadDataType, DependentDataType, MemberDataType } from './data_type'; - -import { DecodingRules, EncodingRules } from './calldata'; - -import { MethodAbi, DataItem } from 'ethereum-types'; - -import ethUtil = require('ethereumjs-util'); - -import { Calldata, RawCalldata } from './calldata'; +import { DataItem, MethodAbi } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; import { BigNumber } from '../configured_bignumber'; -var _ = require('lodash'); +import { DecodingRules, EncodingRules, RawCalldata } from './calldata'; +import * as Constants from './constants'; +import { DataType, DataTypeFactory, DependentDataType, MemberDataType, PayloadDataType } from './data_type'; export interface DataTypeStaticInterface { matchGrammar: (type: string) => boolean; @@ -19,12 +15,18 @@ export interface DataTypeStaticInterface { } export class Address extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; public static ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X = "Address must start with '0x'"; public static ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES = 'Address must be 20 bytes'; + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + private static readonly _ADDRESS_SIZE_IN_BYTES = 20; + private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = Constants.EVM_WORD_WIDTH_IN_BYTES - Address._ADDRESS_SIZE_IN_BYTES; + + public static matchGrammar(type: string): boolean { + return type === 'address'; + } - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Address.SIZE_KNOWN_AT_COMPILE_TIME); + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Address._SIZE_KNOWN_AT_COMPILE_TIME); if (!Address.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); } @@ -34,36 +36,35 @@ export class Address extends PayloadDataType { return 'address'; } - public static matchGrammar(type: string): boolean { - return type === 'address'; - } - public encodeValue(value: string): Buffer { - if (value.startsWith('0x') === false) { + if (!value.startsWith('0x')) { throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); } const valueAsBuffer = ethUtil.toBuffer(value); - if (valueAsBuffer.byteLength !== 20) { + if (valueAsBuffer.byteLength !== Address._ADDRESS_SIZE_IN_BYTES) { throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); } - const evmWordWidth = 32; - const encodedValueBuf = ethUtil.setLengthLeft(valueAsBuffer, evmWordWidth); + const encodedValueBuf = ethUtil.setLengthLeft(valueAsBuffer, Constants.EVM_WORD_WIDTH_IN_BYTES); return encodedValueBuf; } public decodeValue(calldata: RawCalldata): string { const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(12); + const valueBuf = paddedValueBuf.slice(Address._DECODED_ADDRESS_OFFSET_IN_BYTES); const value = ethUtil.bufferToHex(valueBuf); return value; } } export class Bool extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Bool.SIZE_KNOWN_AT_COMPILE_TIME); + public static matchGrammar(type: string): boolean { + return type === 'bool'; + } + + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Bool._SIZE_KNOWN_AT_COMPILE_TIME); if (!Bool.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); } @@ -73,14 +74,9 @@ export class Bool extends PayloadDataType { return 'bool'; } - public static matchGrammar(type: string): boolean { - return type === 'bool'; - } - public encodeValue(value: boolean): Buffer { - const evmWordWidth = 32; - const encodedValue = value === true ? '0x1' : '0x0'; - const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), evmWordWidth); + const encodedValue = value ? '0x1' : '0x0'; + const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), Constants.EVM_WORD_WIDTH_IN_BYTES); return encodedValueBuf; } @@ -88,46 +84,44 @@ export class Bool extends PayloadDataType { const valueBuf = calldata.popWord(); const valueHex = ethUtil.bufferToHex(valueBuf); const valueNumber = new BigNumber(valueHex, 16); - let value: boolean = valueNumber.equals(0) ? false : true; if (!(valueNumber.equals(0) || valueNumber.equals(1))) { throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); } + /* tslint:disable boolean-naming */ + const value: boolean = valueNumber.equals(0) ? false : true; + /* tslint:enable boolean-naming */ return value; } } abstract class Number extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - static MAX_WIDTH: number = 256; - static DEFAULT_WIDTH: number = Number.MAX_WIDTH; - width: number = Number.DEFAULT_WIDTH; + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + private static readonly _MAX_WIDTH: number = 256; + private static readonly _DEFAULT_WIDTH: number = Number._MAX_WIDTH; + protected _width: number; constructor(dataItem: DataItem, matcher: RegExp) { - super(dataItem, EvmDataTypeFactory.getInstance(), Number.SIZE_KNOWN_AT_COMPILE_TIME); + super(dataItem, EvmDataTypeFactory.getInstance(), Number._SIZE_KNOWN_AT_COMPILE_TIME); const matches = matcher.exec(dataItem.type); if (matches === null) { throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); } - if (matches !== null && matches.length === 2 && matches[1] !== undefined) { - this.width = parseInt(matches[1]); - } else { - this.width = 256; - } + this._width = (matches !== null && matches.length === 2 && matches[1] !== undefined) ? + parseInt(matches[1], Constants.DEC_BASE) : + this._width = Number._DEFAULT_WIDTH; } public encodeValue(value_: BigNumber | string | number): Buffer { const value = new BigNumber(value_, 10); if (value.greaterThan(this.getMaxValue())) { - throw `Tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`; + throw new Error(`Tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`); } else if (value.lessThan(this.getMinValue())) { - throw `Tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`; + throw new Error(`Tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`); } - const hexBase = 16; - const evmWordWidth = 32; let valueBuf: Buffer; if (value.greaterThanOrEqualTo(0)) { - valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(hexBase)}`), evmWordWidth); + valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(Constants.HEX_BASE)}`), Constants.EVM_WORD_WIDTH_IN_BYTES); } else { // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. // Step 1/3: Convert value to positive binary string @@ -135,8 +129,7 @@ abstract class Number extends PayloadDataType { const valueBin = value.times(-1).toString(binBase); // Step 2/3: Invert binary value - const bitsInEvmWord = 256; - let invertedValueBin = '1'.repeat(bitsInEvmWord - valueBin.length); + let invertedValueBin = '1'.repeat(Constants.EVM_WORD_WIDTH_IN_BITS - valueBin.length); _.each(valueBin, (bit: string) => { invertedValueBin += bit === '1' ? '0' : '1'; }); @@ -147,7 +140,7 @@ abstract class Number extends PayloadDataType { const negativeValue = invertedValue.plus(1); // Convert the negated value to a hex string - valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${negativeValue.toString(hexBase)}`), evmWordWidth); + valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${negativeValue.toString(Constants.HEX_BASE)}`), Constants.EVM_WORD_WIDTH_IN_BYTES); } return valueBuf; @@ -159,16 +152,15 @@ abstract class Number extends PayloadDataType { let value = new BigNumber(paddedValueHex, 16); if (this instanceof Int) { // Check if we're negative - const binBase = 2; - const valueBin = value.toString(2); - if (valueBin.length === 256 && valueBin[0].startsWith('1')) { + const valueBin = value.toString(Constants.BIN_BASE); + if (valueBin.length === Constants.EVM_WORD_WIDTH_IN_BITS && valueBin[0].startsWith('1')) { // Negative // Step 1/3: Invert binary value let invertedValueBin = ''; _.each(valueBin, (bit: string) => { invertedValueBin += bit === '1' ? '0' : '1'; }); - const invertedValue = new BigNumber(invertedValueBin, binBase); + const invertedValue = new BigNumber(invertedValueBin, Constants.BIN_BASE); // Step 2/3: Add 1 to inverted value // The result is the two's-complement represent of the input value. @@ -188,42 +180,46 @@ abstract class Number extends PayloadDataType { } export class Int extends Number { - static matcher = RegExp( + private static readonly _matcher = RegExp( '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); - constructor(dataItem: DataItem) { - super(dataItem, Int.matcher); + public static matchGrammar(type: string): boolean { + return Int._matcher.test(type); + } + + public constructor(dataItem: DataItem) { + super(dataItem, Int._matcher); } public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).sub(1); + return new BigNumber(2).toPower(this._width - 1).sub(1); } public getMinValue(): BigNumber { - return new BigNumber(2).toPower(this.width - 1).times(-1); + return new BigNumber(2).toPower(this._width - 1).times(-1); } public getSignature(): string { - return `int${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); + return `int${this._width}`; } } export class UInt extends Number { - static matcher = RegExp( + private static readonly _matcher = RegExp( '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); - constructor(dataItem: DataItem) { - super(dataItem, UInt.matcher); + public static matchGrammar(type: string): boolean { + return UInt._matcher.test(type); + } + + public constructor(dataItem: DataItem) { + super(dataItem, UInt._matcher); } public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this.width).sub(1); + return new BigNumber(2).toPower(this._width).sub(1); } public getMinValue(): BigNumber { @@ -231,49 +227,45 @@ export class UInt extends Number { } public getSignature(): string { - return `uint${this.width}`; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); + return `uint${this._width}`; } } export class Byte extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - static matcher = RegExp( + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + private static readonly _matcher = RegExp( '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', ); - static DEFAULT_WIDTH = 1; - width: number = Byte.DEFAULT_WIDTH; + private static readonly _DEFAULT_WIDTH = 1; + private readonly _width: number; + + public static matchGrammar(type: string): boolean { + return Byte._matcher.test(type); + } - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Byte.SIZE_KNOWN_AT_COMPILE_TIME); - const matches = Byte.matcher.exec(dataItem.type); + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Byte._SIZE_KNOWN_AT_COMPILE_TIME); + const matches = Byte._matcher.exec(dataItem.type); if (!Byte.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); } - if (matches !== null && matches.length === 3 && matches[2] !== undefined) { - this.width = parseInt(matches[2]); - } else { - this.width = Byte.DEFAULT_WIDTH; - } + this._width = (matches !== null && matches.length === 3 && matches[2] !== undefined) ? parseInt(matches[2], Constants.DEC_BASE) : Byte._DEFAULT_WIDTH; } public getSignature(): string { // Note that `byte` reduces to `bytes1` - return `bytes${this.width}`; + return `bytes${this._width}`; } public encodeValue(value: string | Buffer): Buffer { // Sanity check if string - if (typeof value === 'string' && value.startsWith('0x') === false) { + if (typeof value === 'string' && !value.startsWith('0x')) { throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); } // Convert value into a buffer and do bounds checking const valueBuf = ethUtil.toBuffer(value); - if (valueBuf.byteLength > this.width) { + if (valueBuf.byteLength > this._width) { throw new Error( `Tried to assign ${value} (${ valueBuf.byteLength @@ -291,23 +283,21 @@ export class Byte extends PayloadDataType { public decodeValue(calldata: RawCalldata): string { const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(0, this.width); + const valueBuf = paddedValueBuf.slice(0, this._width); const value = ethUtil.bufferToHex(valueBuf); return value; } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } } export class Bytes extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - static UNDEFINED_LENGTH = new BigNumber(-1); - length: BigNumber = Bytes.UNDEFINED_LENGTH; + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Bytes.SIZE_KNOWN_AT_COMPILE_TIME); + public static matchGrammar(type: string): boolean { + return type === 'bytes'; + } + + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), Bytes._SIZE_KNOWN_AT_COMPILE_TIME); if (!Bytes.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Bytes with bad input: ${dataItem}`); } @@ -322,10 +312,10 @@ export class Bytes extends PayloadDataType { throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); } - const wordsForValue = Math.ceil(valueBuf.byteLength / 32); - const paddedBytesForValue = wordsForValue * 32; + const wordsForValue = Math.ceil(valueBuf.byteLength / Constants.EVM_WORD_WIDTH_IN_BYTES); + const paddedBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedBytesForValue); - const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), 32); + const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), Constants.EVM_WORD_WIDTH_IN_BYTES); const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); return encodedValueBuf; } @@ -333,8 +323,8 @@ export class Bytes extends PayloadDataType { public decodeValue(calldata: RawCalldata): string { const lengthBuf = calldata.popWord(); const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, 16); - const wordsForValue = Math.ceil(length / 32); + const length = parseInt(lengthHex, Constants.HEX_BASE); + const wordsForValue = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); const paddedValueBuf = calldata.popWords(wordsForValue); const valueBuf = paddedValueBuf.slice(0, length); const decodedValue = ethUtil.bufferToHex(valueBuf); @@ -344,26 +334,27 @@ export class Bytes extends PayloadDataType { public getSignature(): string { return 'bytes'; } +} + +export class SolString extends PayloadDataType { + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; public static matchGrammar(type: string): boolean { - return type === 'bytes'; + return type === 'string'; } -} -export class SolString extends PayloadDataType { - private static SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), SolString.SIZE_KNOWN_AT_COMPILE_TIME); + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance(), SolString._SIZE_KNOWN_AT_COMPILE_TIME); if (!SolString.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); } } public encodeValue(value: string): Buffer { - const wordsForValue = Math.ceil(value.length / 32); - const paddedBytesForValue = wordsForValue * 32; + const wordsForValue = Math.ceil(value.length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const paddedBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; const valueBuf = ethUtil.setLengthRight(new Buffer(value), paddedBytesForValue); - const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), 32); + const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), Constants.EVM_WORD_WIDTH_IN_BYTES); const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); return encodedValueBuf; } @@ -371,8 +362,8 @@ export class SolString extends PayloadDataType { public decodeValue(calldata: RawCalldata): string { const lengthBuf = calldata.popWord(); const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, 16); - const wordsForValue = Math.ceil(length / 32); + const length = parseInt(lengthHex, Constants.HEX_BASE); + const wordsForValue = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); const paddedValueBuf = calldata.popWords(wordsForValue); const valueBuf = paddedValueBuf.slice(0, length); const value = valueBuf.toString('ascii'); @@ -382,16 +373,12 @@ export class SolString extends PayloadDataType { public getSignature(): string { return 'string'; } - - public static matchGrammar(type: string): boolean { - return type === 'string'; - } } export class Pointer extends DependentDataType { constructor(destDataType: DataType, parentDataType: DataType) { const destDataItem = destDataType.getDataItem(); - const dataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` } as DataItem; + const dataItem: DataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` }; super(dataItem, EvmDataTypeFactory.getInstance(), destDataType, parentDataType); } @@ -401,33 +388,37 @@ export class Pointer extends DependentDataType { } export class Tuple extends MemberDataType { - private tupleSignature: string; + private readonly _tupleSignature: string; - constructor(dataItem: DataItem) { + public static matchGrammar(type: string): boolean { + return type === 'tuple'; + } + + public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance()); if (!Tuple.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); } - this.tupleSignature = this._computeSignatureOfMembers(); + this._tupleSignature = this._computeSignatureOfMembers(); } public getSignature(): string { - return this.tupleSignature; - } - - public static matchGrammar(type: string): boolean { - return type === 'tuple'; + return this._tupleSignature; } } export class SolArray extends MemberDataType { - static matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); - private arraySignature: string; - private elementType: string; + private static readonly _matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); + private readonly _arraySignature: string; + private readonly _elementType: string; - constructor(dataItem: DataItem) { + public static matchGrammar(type: string): boolean { + return SolArray._matcher.test(type); + } + + public constructor(dataItem: DataItem) { // Sanity check - const matches = SolArray.matcher.exec(dataItem.type); + const matches = SolArray._matcher.exec(dataItem.type); if (matches === null || matches.length !== 3) { throw new Error(`Could not parse array: ${dataItem.type}`); } else if (matches[1] === undefined) { @@ -438,17 +429,21 @@ export class SolArray extends MemberDataType { const isArray = true; const arrayElementType = matches[1]; - const arrayLength = matches[2] === '' ? undefined : parseInt(matches[2], 10); + const arrayLength = matches[2] === '' ? undefined : parseInt(matches[2], Constants.DEC_BASE); super(dataItem, EvmDataTypeFactory.getInstance(), isArray, arrayLength, arrayElementType); - this.elementType = arrayElementType; - this.arraySignature = this.computeSignature(); + this._elementType = arrayElementType; + this._arraySignature = this._computeSignature(); } - private computeSignature(): string { - let dataItem = { - type: this.elementType, + public getSignature(): string { + return this._arraySignature; + } + + private _computeSignature(): string { + const dataItem: DataItem = { + type: this._elementType, name: 'N/A', - } as DataItem; + }; const components = this.getDataItem().components; if (components !== undefined) { dataItem.components = components; @@ -461,49 +456,29 @@ export class SolArray extends MemberDataType { return `${type}[${this._arrayLength}]`; } } - - public getSignature(): string { - return this.arraySignature; - } - - public static matchGrammar(type: string): boolean { - return this.matcher.test(type); - } } export class Method extends MemberDataType { - private methodSignature: string; - private methodSelector: string; - private returnDataTypes: DataType[]; - private returnDataItem: DataItem; - // TMP public selector: string; - constructor(abi: MethodAbi) { + private readonly _methodSignature: string; + private readonly _methodSelector: string; + private readonly _returnDataTypes: DataType[]; + private readonly _returnDataItem: DataItem; + + public constructor(abi: MethodAbi) { super({ type: 'method', name: abi.name, components: abi.inputs }, EvmDataTypeFactory.getInstance()); - this.methodSignature = this.computeSignature(); - this.selector = this.methodSelector = this.computeSelector(); - this.returnDataTypes = []; - this.returnDataItem = { type: 'tuple', name: abi.name, components: abi.outputs }; + this._methodSignature = this._computeSignature(); + this.selector = this._methodSelector = this._computeSelector(); + this._returnDataTypes = []; + this._returnDataItem = { type: 'tuple', name: abi.name, components: abi.outputs }; const dummy = new Byte({ type: 'byte', name: 'DUMMY' }); // @TODO TMP _.each(abi.outputs, (dataItem: DataItem) => { - this.returnDataTypes.push(this.getFactory().create(dataItem, dummy)); + this._returnDataTypes.push(this.getFactory().create(dataItem, dummy)); }); } - private computeSignature(): string { - const memberSignature = this._computeSignatureOfMembers(); - const methodSignature = `${this.getDataItem().name}${memberSignature}`; - return methodSignature; - } - - private computeSelector(): string { - const signature = this.computeSignature(); - const selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(signature).slice(0, 4))); - return selector; - } - public encode(value: any, rules?: EncodingRules): string { const calldata = super.encode(value, rules, this.selector); return calldata; @@ -521,55 +496,73 @@ export class Method extends MemberDataType { } public encodeReturnValues(value: any, rules?: EncodingRules): string { - const returnDataType = new Tuple(this.returnDataItem); + const returnDataType = new Tuple(this._returnDataItem); const returndata = returnDataType.encode(value, rules); return returndata; } public decodeReturnValues(returndata: string, rules?: DecodingRules): any { const returnValues: any[] = []; - const rules_ = rules ? rules : ({ structsAsObjects: false } as DecodingRules); + const rules_: DecodingRules = rules ? rules : { structsAsObjects: false }; const rawReturnData = new RawCalldata(returndata, false); - _.each(this.returnDataTypes, (dataType: DataType) => { + _.each(this._returnDataTypes, (dataType: DataType) => { returnValues.push(dataType.generateValue(rawReturnData, rules_)); }); return returnValues; } public getSignature(): string { - return this.methodSignature; + return this._methodSignature; } public getSelector(): string { - return this.methodSelector; + return this._methodSelector; + } + + private _computeSignature(): string { + const memberSignature = this._computeSignatureOfMembers(); + const methodSignature = `${this.getDataItem().name}${memberSignature}`; + return methodSignature; + } + + private _computeSelector(): string { + const signature = this._computeSignature(); + const selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(signature).slice(Constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, Constants.HEX_SELECTOR_LENGTH_IN_BYTES))); + return selector; } } export class EvmDataTypeFactory implements DataTypeFactory { - private static instance: DataTypeFactory; - - private constructor() { } + private static _instance: DataTypeFactory; public static getInstance(): DataTypeFactory { - if (!EvmDataTypeFactory.instance) { - EvmDataTypeFactory.instance = new EvmDataTypeFactory(); + if (!EvmDataTypeFactory._instance) { + EvmDataTypeFactory._instance = new EvmDataTypeFactory(); } - return EvmDataTypeFactory.instance; + return EvmDataTypeFactory._instance; } public mapDataItemToDataType(dataItem: DataItem): DataType { - if (SolArray.matchGrammar(dataItem.type)) return new SolArray(dataItem); - if (Address.matchGrammar(dataItem.type)) return new Address(dataItem); - if (Bool.matchGrammar(dataItem.type)) return new Bool(dataItem); - if (Int.matchGrammar(dataItem.type)) return new Int(dataItem); - if (UInt.matchGrammar(dataItem.type)) return new UInt(dataItem); - if (Byte.matchGrammar(dataItem.type)) return new Byte(dataItem); - if (Tuple.matchGrammar(dataItem.type)) return new Tuple(dataItem); - if (Bytes.matchGrammar(dataItem.type)) return new Bytes(dataItem); - if (SolString.matchGrammar(dataItem.type)) return new SolString(dataItem); - //if (Fixed.matchGrammar(dataItem.type)) return Fixed(dataItem); - //if (UFixed.matchGrammar(dataItem.type)) return UFixed(dataItem); - + if (SolArray.matchGrammar(dataItem.type)) { + return new SolArray(dataItem); + } else if (Address.matchGrammar(dataItem.type)) { + return new Address(dataItem); + } else if (Bool.matchGrammar(dataItem.type)) { + return new Bool(dataItem); + } else if (Int.matchGrammar(dataItem.type)) { + return new Int(dataItem); + } else if (UInt.matchGrammar(dataItem.type)) { + return new UInt(dataItem); + } else if (Byte.matchGrammar(dataItem.type)) { + return new Byte(dataItem); + } else if (Tuple.matchGrammar(dataItem.type)) { + return new Tuple(dataItem); + } else if (Bytes.matchGrammar(dataItem.type)) { + return new Bytes(dataItem); + } else if (SolString.matchGrammar(dataItem.type)) { + return new SolString(dataItem); + } + // @TODO: Implement Fixed/UFixed types throw new Error(`Unrecognized data type: '${dataItem.type}'`); } @@ -586,4 +579,6 @@ export class EvmDataTypeFactory implements DataTypeFactory { const pointer = new Pointer(dataType, parentDataType); return pointer; } + + private constructor() { } } -- cgit v1.2.3 From 62e6b22789b349dda7885d3317c76c90ecb3c882 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 11:35:16 -0800 Subject: Fixed linter errors on tests --- packages/utils/test/abi_encoder_test.ts | 99 +++++++++++++++++--------------- packages/utils/test/abi_samples.ts | 93 +++++++++++++++--------------- packages/utils/test/optimizer_abis.ts | 57 +++++++++--------- packages/utils/test/return_value_abis.ts | 25 ++++---- 4 files changed, 141 insertions(+), 133 deletions(-) diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index c7986fa00..e99528b06 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -1,13 +1,13 @@ import * as chai from 'chai'; +import * as ethUtil from 'ethereumjs-util'; import 'mocha'; -import { chaiSetup } from './utils/chai_setup'; -import { BigNumber, AbiEncoder } from '../src/'; +import { AbiEncoder, BigNumber } from '../src/'; + import * as AbiSamples from './abi_samples'; import * as OptimizedAbis from './optimizer_abis'; import * as ReturnValueAbis from './return_value_abis'; -import { DecodingRules } from '../src/abi_encoder'; -import ethUtil = require('ethereumjs-util'); +import { chaiSetup } from './utils/chai_setup'; chaiSetup.configure(); const expect = chai.expect; @@ -349,8 +349,8 @@ describe.only('ABI Encoder', () => { // Generate calldata const method = new AbiEncoder.Method(OptimizedAbis.arrayElementsDuplicatedAsSeparateParameter); const array = ['Hello', 'Hello', 'Hello', 'World']; - const string = 'Hello'; - const args = [array, string]; + const str = 'Hello'; + const args = [array, str]; // Validate calldata const optimizedCalldata = method.encode(args, { optimize: true }); const expectedOptimizedCalldata = @@ -392,7 +392,8 @@ describe.only('ABI Encoder', () => { const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDefinedLengthAbi); let value = 0; const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { + const arrayOfTuplesLength = 8; + for (let i = 0; i < arrayOfTuplesLength; ++i) { arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); } const args = [arrayOfTuples]; @@ -412,7 +413,8 @@ describe.only('ABI Encoder', () => { const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDynamicLengthAbi); let value = 0; const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { + const arrayOfTuplesLength = 8; + for (let i = 0; i < arrayOfTuplesLength; ++i) { arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); } const args = [arrayOfTuples]; @@ -432,7 +434,8 @@ describe.only('ABI Encoder', () => { const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithDefinedLengthAbi); let value = 0; const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { + const arrayOfTuplesLength = 8; + for (let i = 0; i < arrayOfTuplesLength; ++i) { arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]); } const args = [arrayOfTuples]; @@ -452,7 +455,8 @@ describe.only('ABI Encoder', () => { const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithUndefinedLengthAbi); let value = 0; const arrayOfTuples = []; - for (let i = 0; i < 8; ++i) { + const arrayOfTuplesLength = 8; + for (let i = 0; i < arrayOfTuplesLength; ++i) { arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]); } const args = [arrayOfTuples]; @@ -473,7 +477,8 @@ describe.only('ABI Encoder', () => { // Eight 3-dimensional arrays of uint8[2][2][2] let value = 0; const args = []; - for (let i = 0; i < 8; ++i) { + const argsLength = 8; + for (let i = 0; i < argsLength; ++i) { args.push([ [ [new BigNumber(++value), new BigNumber(++value)], @@ -503,7 +508,8 @@ describe.only('ABI Encoder', () => { // Eight 3-dimensional arrays of string[2][2][2] let value = 0; const args = []; - for (let i = 0; i < 4; ++i) { + const argsLength = 4; + for (let i = 0; i < argsLength; ++i) { args.push([ [ [new BigNumber(++value).toString(), new BigNumber(++value).toString()], @@ -741,13 +747,13 @@ describe.only('ABI Encoder', () => { }; const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3]; const args = { - someStaticArray: someStaticArray, - someStaticArrayWithDynamicMembers: someStaticArrayWithDynamicMembers, - someDynamicArrayWithDynamicMembers: someDynamicArrayWithDynamicMembers, - some2DArray: some2DArray, - someTuple: someTuple, - someTupleWithDynamicTypes: someTupleWithDynamicTypes, - someArrayOfTuplesWithDynamicTypes: someArrayOfTuplesWithDynamicTypes, + someStaticArray, + someStaticArrayWithDynamicMembers, + someDynamicArrayWithDynamicMembers, + some2DArray, + someTuple, + someTupleWithDynamicTypes, + someArrayOfTuplesWithDynamicTypes, }; const calldata = method.encode(args); // Validate calldata @@ -900,8 +906,6 @@ describe.only('ABI Encoder', () => { const args = [array1, array2]; // Encode Args and validate result const encodedArgs = dataType.encode(args); - console.log(encodedArgs); - console.log(dataType.encode(args, { annotate: true })); const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000401020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004050607080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040910111200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000410111213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004141516170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041819202100000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -963,7 +967,7 @@ describe.only('ABI Encoder', () => { '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000001'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); const decodedArgsAsJson = JSON.stringify(decodedArgs); const argsAsJson = JSON.stringify(args); @@ -985,7 +989,7 @@ describe.only('ABI Encoder', () => { '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); const decodedArgsAsJson = JSON.stringify(decodedArgs); const argsAsJson = JSON.stringify(args); @@ -1007,7 +1011,7 @@ describe.only('ABI Encoder', () => { '0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); const decodedArgsAsJson = JSON.stringify(decodedArgs); const argsAsJson = JSON.stringify(args); @@ -1029,7 +1033,7 @@ describe.only('ABI Encoder', () => { '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); const decodedArgsAsJson = JSON.stringify(decodedArgs); const argsAsJson = JSON.stringify(args); @@ -1053,7 +1057,7 @@ describe.only('ABI Encoder', () => { '0x0102030400000000000000000000000000000000000000000000000000000000050607080000000000000000000000000000000000000000000000000000000009101112000000000000000000000000000000000000000000000000000000001314151600000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); const decodedArgsAsJson = JSON.stringify(decodedArgs); const argsAsJson = JSON.stringify(args); @@ -1077,7 +1081,7 @@ describe.only('ABI Encoder', () => { '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040506070800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041314151600000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); const decodedArgsAsJson = JSON.stringify(decodedArgs); const argsAsJson = JSON.stringify(args); @@ -1109,7 +1113,7 @@ describe.only('ABI Encoder', () => { '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result - const decodingRules = { structsAsObjects: true } as DecodingRules; + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); const decodedArgsAsJson = JSON.stringify(decodedArgs); const argsAsJson = JSON.stringify(args); @@ -1224,6 +1228,13 @@ describe.only('ABI Encoder', () => { }); describe('Integer', () => { + /* tslint:disable custom-no-magic-numbers */ + const max256BitInteger = new BigNumber(2).pow(255).minus(1); + const min256BitInteger = new BigNumber(2).pow(255).times(-1); + const max32BitInteger = new BigNumber(2).pow(31).minus(1); + const min32BitInteger = new BigNumber(2).pow(31).times(-1); + /* tslint:enable custom-no-magic-numbers */ + it('Int256 - Positive Base Case', async () => { // Create DataType object const testDataItem = { name: 'Integer (256)', type: 'int' }; @@ -1261,7 +1272,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Integer (256)', type: 'int' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const max256BitInteger = new BigNumber(2).pow(255).minus(1); const args = max256BitInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1278,7 +1288,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Integer (256)', type: 'int' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const min256BitInteger = new BigNumber(2).pow(255).times(-1); const args = min256BitInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1295,7 +1304,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Integer (256)', type: 'int' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const max256BitInteger = new BigNumber(2).pow(255).minus(1); const args = max256BitInteger.plus(1); // Encode Args and validate result expect(() => { @@ -1307,7 +1315,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Integer (256)', type: 'int' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const min256BitInteger = new BigNumber(2).pow(255).times(-1); const args = min256BitInteger.minus(1); // Encode Args and validate result expect(() => { @@ -1351,7 +1358,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Integer (32)', type: 'int32' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const max32BitInteger = new BigNumber(2).pow(31).minus(1); const args = max32BitInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1368,7 +1374,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Integer (32)', type: 'int32' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const min32BitInteger = new BigNumber(2).pow(31).times(-1); const args = min32BitInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1385,7 +1390,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Integer (32)', type: 'int32' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const max32BitInteger = new BigNumber(2).pow(31).minus(1); const args = max32BitInteger.plus(1); // Encode Args and validate result expect(() => { @@ -1397,7 +1401,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Integer (32)', type: 'int32' }; const dataType = new AbiEncoder.Int(testDataItem); // Construct args to be encoded - const min32BitInteger = new BigNumber(2).pow(31).times(-1); const args = min32BitInteger.minus(1); // Encode Args and validate result expect(() => { @@ -1407,6 +1410,13 @@ describe.only('ABI Encoder', () => { }); describe('Unsigned Integer', () => { + /* tslint:disable custom-no-magic-numbers */ + const max256BitUnsignedInteger = new BigNumber(2).pow(256).minus(1); + const min256BitUnsignedInteger = new BigNumber(0); + const max32BitUnsignedInteger = new BigNumber(2).pow(32).minus(1); + const min32BitUnsignedInteger = new BigNumber(0); + /* tslint:enable custom-no-magic-numbers */ + it('UInt256 - Positive Base Case', async () => { // Create DataType object const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; @@ -1428,7 +1438,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const max256BitUnsignedInteger = new BigNumber(2).pow(256).minus(1); const args = max256BitUnsignedInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1445,7 +1454,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const min256BitUnsignedInteger = new BigNumber(0); const args = min256BitUnsignedInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1462,7 +1470,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const max256BitUnsignedInteger = new BigNumber(2).pow(256).minus(1); const args = max256BitUnsignedInteger.plus(1); // Encode Args and validate result expect(() => { @@ -1474,7 +1481,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const min256BitUnsignedInteger = new BigNumber(0); const args = min256BitUnsignedInteger.minus(1); // Encode Args and validate result expect(() => { @@ -1502,7 +1508,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const max32BitUnsignedInteger = new BigNumber(2).pow(32).minus(1); const args = max32BitUnsignedInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1519,7 +1524,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const min32BitUnsignedInteger = new BigNumber(0); const args = min32BitUnsignedInteger; // Encode Args and validate result const encodedArgs = dataType.encode(args); @@ -1536,7 +1540,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const max32BitUnsignedInteger = new BigNumber(2).pow(32).minus(1); const args = max32BitUnsignedInteger.plus(1); // Encode Args and validate result expect(() => { @@ -1548,7 +1551,6 @@ describe.only('ABI Encoder', () => { const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; const dataType = new AbiEncoder.UInt(testDataItem); // Construct args to be encoded - const min32BitUnsignedInteger = new BigNumber(0); const args = min32BitUnsignedInteger.minus(1); // Encode Args and validate result expect(() => { @@ -1733,7 +1735,8 @@ describe.only('ABI Encoder', () => { const dataType = new AbiEncoder.Bytes(testDataItem); // Construct args to be encoded // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. - const args = '0x' + '61'.repeat(40); + const bytesLength = 40; + const args = '0x' + '61'.repeat(bytesLength); // Encode Args and validate result const encodedArgs = dataType.encode(args); const expectedEncodedArgs = @@ -1813,7 +1816,8 @@ describe.only('ABI Encoder', () => { const dataType = new AbiEncoder.SolString(testDataItem); // Construct args to be encoded // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. - const args = 'a'.repeat(40); + const bytesLength = 40; + const args = 'a'.repeat(bytesLength); // Encode Args and validate result const encodedArgs = dataType.encode(args); const expectedEncodedArgs = @@ -1831,7 +1835,8 @@ describe.only('ABI Encoder', () => { const dataType = new AbiEncoder.SolString(testDataItem); // Construct args to be encoded // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. - const args = '0x' + 'a'.repeat(40); + const strLength = 40; + const args = '0x' + 'a'.repeat(strLength); // Encode Args and validate result const encodedArgs = dataType.encode(args); const expectedEncodedArgs = diff --git a/packages/utils/test/abi_samples.ts b/packages/utils/test/abi_samples.ts index 0c3354044..fc552c127 100644 --- a/packages/utils/test/abi_samples.ts +++ b/packages/utils/test/abi_samples.ts @@ -1,6 +1,7 @@ +/* tslint:disable max-file-line-count */ import { MethodAbi } from 'ethereum-types'; -export const simpleAbi = { +export const simpleAbi: MethodAbi = { constant: false, inputs: [ { @@ -17,9 +18,9 @@ export const simpleAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const stringAbi = { +export const stringAbi: MethodAbi = { constant: false, inputs: [ { @@ -32,9 +33,9 @@ export const stringAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const GAbi = { +export const GAbi: MethodAbi = { constant: false, inputs: [ { @@ -66,9 +67,9 @@ export const GAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const typesWithDefaultWidthsAbi = { +export const typesWithDefaultWidthsAbi: MethodAbi = { constant: false, inputs: [ { @@ -101,9 +102,9 @@ export const typesWithDefaultWidthsAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const multiDimensionalArraysStaticTypeAbi = { +export const multiDimensionalArraysStaticTypeAbi: MethodAbi = { constant: false, inputs: [ { @@ -144,9 +145,9 @@ export const multiDimensionalArraysStaticTypeAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const multiDimensionalArraysDynamicTypeAbi = { +export const multiDimensionalArraysDynamicTypeAbi: MethodAbi = { constant: false, inputs: [ { @@ -171,9 +172,9 @@ export const multiDimensionalArraysDynamicTypeAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const dynamicTupleAbi = { +export const dynamicTupleAbi: MethodAbi = { constant: false, inputs: [ { @@ -196,9 +197,9 @@ export const dynamicTupleAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const arrayOfStaticTuplesWithDefinedLengthAbi = { +export const arrayOfStaticTuplesWithDefinedLengthAbi: MethodAbi = { constant: false, inputs: [ { @@ -221,9 +222,9 @@ export const arrayOfStaticTuplesWithDefinedLengthAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const arrayOfStaticTuplesWithDynamicLengthAbi = { +export const arrayOfStaticTuplesWithDynamicLengthAbi: MethodAbi = { constant: false, inputs: [ { @@ -246,9 +247,9 @@ export const arrayOfStaticTuplesWithDynamicLengthAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const arrayOfDynamicTuplesWithDefinedLengthAbi = { +export const arrayOfDynamicTuplesWithDefinedLengthAbi: MethodAbi = { constant: false, inputs: [ { @@ -271,9 +272,9 @@ export const arrayOfDynamicTuplesWithDefinedLengthAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const arrayOfDynamicTuplesWithUndefinedLengthAbi = { +export const arrayOfDynamicTuplesWithUndefinedLengthAbi: MethodAbi = { constant: false, inputs: [ { @@ -296,9 +297,9 @@ export const arrayOfDynamicTuplesWithUndefinedLengthAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const arrayOfDynamicTuplesAbi = { +export const arrayOfDynamicTuplesAbi: MethodAbi = { constant: false, inputs: [ { @@ -321,9 +322,9 @@ export const arrayOfDynamicTuplesAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const multidimensionalArrayOfDynamicTuplesAbi = { +export const multidimensionalArrayOfDynamicTuplesAbi: MethodAbi = { constant: false, inputs: [ { @@ -346,9 +347,9 @@ export const multidimensionalArrayOfDynamicTuplesAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const staticTupleAbi = { +export const staticTupleAbi: MethodAbi = { constant: false, inputs: [ { @@ -379,9 +380,9 @@ export const staticTupleAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const staticArrayAbi = { +export const staticArrayAbi: MethodAbi = { constant: false, inputs: [ { @@ -394,9 +395,9 @@ export const staticArrayAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const staticArrayDynamicMembersAbi = { +export const staticArrayDynamicMembersAbi: MethodAbi = { constant: false, inputs: [ { @@ -409,9 +410,9 @@ export const staticArrayDynamicMembersAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const dynamicArrayDynamicMembersAbi = { +export const dynamicArrayDynamicMembersAbi: MethodAbi = { constant: false, inputs: [ { @@ -424,9 +425,9 @@ export const dynamicArrayDynamicMembersAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const dynamicArrayStaticMembersAbi = { +export const dynamicArrayStaticMembersAbi: MethodAbi = { constant: false, inputs: [ { @@ -439,9 +440,9 @@ export const dynamicArrayStaticMembersAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const largeFlatAbi = { +export const largeFlatAbi: MethodAbi = { constant: false, inputs: [ { @@ -486,9 +487,9 @@ export const largeFlatAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const largeNestedAbi = { +export const largeNestedAbi: MethodAbi = { constant: false, inputs: [ { @@ -579,9 +580,9 @@ export const largeNestedAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const nestedTuples = { +export const nestedTuples: MethodAbi = { constant: false, inputs: [ { @@ -668,9 +669,9 @@ export const nestedTuples = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const simpleAbi2 = { +export const simpleAbi2: MethodAbi = { constant: false, inputs: [ { @@ -695,9 +696,9 @@ export const simpleAbi2 = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const fillOrderAbi = { +export const fillOrderAbi: MethodAbi = { constant: false, inputs: [ { @@ -776,4 +777,4 @@ export const fillOrderAbi = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; diff --git a/packages/utils/test/optimizer_abis.ts b/packages/utils/test/optimizer_abis.ts index ea562e5b5..7cfd7a118 100644 --- a/packages/utils/test/optimizer_abis.ts +++ b/packages/utils/test/optimizer_abis.ts @@ -1,6 +1,7 @@ +/* tslint:disable max-file-line-count */ import { MethodAbi } from 'ethereum-types'; -export const duplicateDynamicArraysWithStaticElements = { +export const duplicateDynamicArraysWithStaticElements: MethodAbi = { constant: false, inputs: [ { @@ -17,9 +18,9 @@ export const duplicateDynamicArraysWithStaticElements = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const duplicateDynamicArraysWithDynamicElements = { +export const duplicateDynamicArraysWithDynamicElements: MethodAbi = { constant: false, inputs: [ { @@ -36,9 +37,9 @@ export const duplicateDynamicArraysWithDynamicElements = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const duplicateStaticArraysWithStaticElements = { +export const duplicateStaticArraysWithStaticElements: MethodAbi = { constant: false, inputs: [ { @@ -55,9 +56,9 @@ export const duplicateStaticArraysWithStaticElements = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const duplicateStaticArraysWithDynamicElements = { +export const duplicateStaticArraysWithDynamicElements: MethodAbi = { constant: false, inputs: [ { @@ -74,9 +75,9 @@ export const duplicateStaticArraysWithDynamicElements = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const duplicateArrayElements = { +export const duplicateArrayElements: MethodAbi = { constant: false, inputs: [ { @@ -89,9 +90,9 @@ export const duplicateArrayElements = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const duplicateTupleFields = { +export const duplicateTupleFields: MethodAbi = { constant: false, inputs: [ { @@ -114,9 +115,9 @@ export const duplicateTupleFields = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const duplicateStrings = { +export const duplicateStrings: MethodAbi = { constant: false, inputs: [ { @@ -133,9 +134,9 @@ export const duplicateStrings = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const duplicateBytes = { +export const duplicateBytes: MethodAbi = { constant: false, inputs: [ { @@ -152,9 +153,9 @@ export const duplicateBytes = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const duplicateTuples = { +export const duplicateTuples: MethodAbi = { constant: false, inputs: [ { @@ -191,9 +192,9 @@ export const duplicateTuples = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const duplicateArraysNestedInTuples = { +export const duplicateArraysNestedInTuples: MethodAbi = { constant: false, inputs: [ { @@ -226,9 +227,9 @@ export const duplicateArraysNestedInTuples = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const duplicateTuplesNestedInTuples = { +export const duplicateTuplesNestedInTuples: MethodAbi = { constant: false, inputs: [ { @@ -273,9 +274,9 @@ export const duplicateTuplesNestedInTuples = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const duplicateTwoDimensionalArrays = { +export const duplicateTwoDimensionalArrays: MethodAbi = { constant: false, inputs: [ { @@ -292,9 +293,9 @@ export const duplicateTwoDimensionalArrays = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const arrayElementsDuplicatedAsSeparateParameter = { +export const arrayElementsDuplicatedAsSeparateParameter: MethodAbi = { constant: false, inputs: [ { @@ -311,9 +312,9 @@ export const arrayElementsDuplicatedAsSeparateParameter = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const arrayElementsDuplicatedAsTupleFields = { +export const arrayElementsDuplicatedAsTupleFields: MethodAbi = { constant: false, inputs: [ { @@ -336,4 +337,4 @@ export const arrayElementsDuplicatedAsTupleFields = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; diff --git a/packages/utils/test/return_value_abis.ts b/packages/utils/test/return_value_abis.ts index 847559dac..ac2124011 100644 --- a/packages/utils/test/return_value_abis.ts +++ b/packages/utils/test/return_value_abis.ts @@ -1,6 +1,7 @@ +/* tslint:disable max-file-line-count */ import { MethodAbi } from 'ethereum-types'; -export const noReturnValues = { +export const noReturnValues: MethodAbi = { constant: false, inputs: [], name: 'simpleFunction', @@ -8,9 +9,9 @@ export const noReturnValues = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const singleStaticReturnValue = { +export const singleStaticReturnValue: MethodAbi = { constant: false, inputs: [], name: 'simpleFunction', @@ -23,9 +24,9 @@ export const singleStaticReturnValue = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const multipleStaticReturnValues = { +export const multipleStaticReturnValues: MethodAbi = { constant: false, inputs: [], name: 'simpleFunction', @@ -42,9 +43,9 @@ export const multipleStaticReturnValues = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const singleDynamicReturnValue = { +export const singleDynamicReturnValue: MethodAbi = { constant: false, inputs: [], name: 'simpleFunction', @@ -57,9 +58,9 @@ export const singleDynamicReturnValue = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const multipleDynamicReturnValues = { +export const multipleDynamicReturnValues: MethodAbi = { constant: false, inputs: [], name: 'simpleFunction', @@ -76,9 +77,9 @@ export const multipleDynamicReturnValues = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -export const mixedStaticAndDynamicReturnValues = { +export const mixedStaticAndDynamicReturnValues: MethodAbi = { constant: false, inputs: [], name: 'simpleFunction', @@ -95,4 +96,4 @@ export const mixedStaticAndDynamicReturnValues = { payable: false, stateMutability: 'nonpayable', type: 'function', -} as MethodAbi; +}; -- cgit v1.2.3 From 29d63cdf976e4cb10b738f7cbb1a57f43c5530ef Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 11:38:47 -0800 Subject: Fixed linter errors in package.json --- packages/utils/package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/utils/package.json b/packages/utils/package.json index 8dc1f0739..1f4d85843 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -33,6 +33,9 @@ "@types/lodash": "4.14.104", "@types/mocha": "^2.2.42", "chai": "^4.0.1", + "chai-as-promised": "^7.1.0", + "chai-bignumber": "^2.0.1", + "dirty-chai": "^2.0.1", "make-promises-safe": "^1.1.0", "mocha": "^4.1.0", "npm-run-all": "^4.1.2", @@ -57,4 +60,4 @@ "publishConfig": { "access": "public" } -} +} \ No newline at end of file -- cgit v1.2.3 From 5934e5a57b5897594e22efc9e6ff28841c6951bf Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 11:45:24 -0800 Subject: Renaming some EVM data types for clarity --- packages/utils/src/abi_encoder/evm_data_types.ts | 94 ++++++++++++------------ packages/utils/test/abi_encoder_test.ts | 58 +++++++-------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index 24f6051b0..7ad7f403e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -9,7 +9,7 @@ import * as Constants from './constants'; import { DataType, DataTypeFactory, DependentDataType, MemberDataType, PayloadDataType } from './data_type'; export interface DataTypeStaticInterface { - matchGrammar: (type: string) => boolean; + matchType: (type: string) => boolean; encodeValue: (value: any) => Buffer; decodeValue: (rawCalldata: RawCalldata) => any; } @@ -21,13 +21,13 @@ export class Address extends PayloadDataType { private static readonly _ADDRESS_SIZE_IN_BYTES = 20; private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = Constants.EVM_WORD_WIDTH_IN_BYTES - Address._ADDRESS_SIZE_IN_BYTES; - public static matchGrammar(type: string): boolean { + public static matchType(type: string): boolean { return type === 'address'; } public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance(), Address._SIZE_KNOWN_AT_COMPILE_TIME); - if (!Address.matchGrammar(dataItem.type)) { + if (!Address.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); } } @@ -59,13 +59,13 @@ export class Address extends PayloadDataType { export class Bool extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - public static matchGrammar(type: string): boolean { + public static matchType(type: string): boolean { return type === 'bool'; } public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance(), Bool._SIZE_KNOWN_AT_COMPILE_TIME); - if (!Bool.matchGrammar(dataItem.type)) { + if (!Bool.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); } } @@ -184,7 +184,7 @@ export class Int extends Number { '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); - public static matchGrammar(type: string): boolean { + public static matchType(type: string): boolean { return Int._matcher.test(type); } @@ -210,7 +210,7 @@ export class UInt extends Number { '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); - public static matchGrammar(type: string): boolean { + public static matchType(type: string): boolean { return UInt._matcher.test(type); } @@ -231,7 +231,7 @@ export class UInt extends Number { } } -export class Byte extends PayloadDataType { +export class StaticBytes extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _matcher = RegExp( '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', @@ -240,17 +240,17 @@ export class Byte extends PayloadDataType { private static readonly _DEFAULT_WIDTH = 1; private readonly _width: number; - public static matchGrammar(type: string): boolean { - return Byte._matcher.test(type); + public static matchType(type: string): boolean { + return StaticBytes._matcher.test(type); } public constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Byte._SIZE_KNOWN_AT_COMPILE_TIME); - const matches = Byte._matcher.exec(dataItem.type); - if (!Byte.matchGrammar(dataItem.type)) { + super(dataItem, EvmDataTypeFactory.getInstance(), StaticBytes._SIZE_KNOWN_AT_COMPILE_TIME); + const matches = StaticBytes._matcher.exec(dataItem.type); + if (!StaticBytes.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); } - this._width = (matches !== null && matches.length === 3 && matches[2] !== undefined) ? parseInt(matches[2], Constants.DEC_BASE) : Byte._DEFAULT_WIDTH; + this._width = (matches !== null && matches.length === 3 && matches[2] !== undefined) ? parseInt(matches[2], Constants.DEC_BASE) : StaticBytes._DEFAULT_WIDTH; } public getSignature(): string { @@ -289,17 +289,17 @@ export class Byte extends PayloadDataType { } } -export class Bytes extends PayloadDataType { +export class DynamicBytes extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - public static matchGrammar(type: string): boolean { + public static matchType(type: string): boolean { return type === 'bytes'; } public constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Bytes._SIZE_KNOWN_AT_COMPILE_TIME); - if (!Bytes.matchGrammar(dataItem.type)) { - throw new Error(`Tried to instantiate Bytes with bad input: ${dataItem}`); + super(dataItem, EvmDataTypeFactory.getInstance(), DynamicBytes._SIZE_KNOWN_AT_COMPILE_TIME); + if (!DynamicBytes.matchType(dataItem.type)) { + throw new Error(`Tried to instantiate DynamicBytes with bad input: ${dataItem}`); } } @@ -313,8 +313,8 @@ export class Bytes extends PayloadDataType { } const wordsForValue = Math.ceil(valueBuf.byteLength / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; - const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedBytesForValue); + const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; + const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedDynamicBytesForValue); const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), Constants.EVM_WORD_WIDTH_IN_BYTES); const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); return encodedValueBuf; @@ -336,24 +336,24 @@ export class Bytes extends PayloadDataType { } } -export class SolString extends PayloadDataType { +export class String extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - public static matchGrammar(type: string): boolean { + public static matchType(type: string): boolean { return type === 'string'; } public constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), SolString._SIZE_KNOWN_AT_COMPILE_TIME); - if (!SolString.matchGrammar(dataItem.type)) { + super(dataItem, EvmDataTypeFactory.getInstance(), String._SIZE_KNOWN_AT_COMPILE_TIME); + if (!String.matchType(dataItem.type)) { throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); } } public encodeValue(value: string): Buffer { const wordsForValue = Math.ceil(value.length / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; - const valueBuf = ethUtil.setLengthRight(new Buffer(value), paddedBytesForValue); + const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; + const valueBuf = ethUtil.setLengthRight(new Buffer(value), paddedDynamicBytesForValue); const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), Constants.EVM_WORD_WIDTH_IN_BYTES); const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); return encodedValueBuf; @@ -390,13 +390,13 @@ export class Pointer extends DependentDataType { export class Tuple extends MemberDataType { private readonly _tupleSignature: string; - public static matchGrammar(type: string): boolean { + public static matchType(type: string): boolean { return type === 'tuple'; } public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance()); - if (!Tuple.matchGrammar(dataItem.type)) { + if (!Tuple.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); } this._tupleSignature = this._computeSignatureOfMembers(); @@ -407,18 +407,18 @@ export class Tuple extends MemberDataType { } } -export class SolArray extends MemberDataType { +export class Array extends MemberDataType { private static readonly _matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); private readonly _arraySignature: string; private readonly _elementType: string; - public static matchGrammar(type: string): boolean { - return SolArray._matcher.test(type); + public static matchType(type: string): boolean { + return Array._matcher.test(type); } public constructor(dataItem: DataItem) { // Sanity check - const matches = SolArray._matcher.exec(dataItem.type); + const matches = Array._matcher.exec(dataItem.type); if (matches === null || matches.length !== 3) { throw new Error(`Could not parse array: ${dataItem.type}`); } else if (matches[1] === undefined) { @@ -473,7 +473,7 @@ export class Method extends MemberDataType { this.selector = this._methodSelector = this._computeSelector(); this._returnDataTypes = []; this._returnDataItem = { type: 'tuple', name: abi.name, components: abi.outputs }; - const dummy = new Byte({ type: 'byte', name: 'DUMMY' }); // @TODO TMP + const dummy = new StaticBytes({ type: 'byte', name: 'DUMMY' }); // @TODO TMP _.each(abi.outputs, (dataItem: DataItem) => { this._returnDataTypes.push(this.getFactory().create(dataItem, dummy)); }); @@ -543,24 +543,24 @@ export class EvmDataTypeFactory implements DataTypeFactory { } public mapDataItemToDataType(dataItem: DataItem): DataType { - if (SolArray.matchGrammar(dataItem.type)) { - return new SolArray(dataItem); - } else if (Address.matchGrammar(dataItem.type)) { + if (Array.matchType(dataItem.type)) { + return new Array(dataItem); + } else if (Address.matchType(dataItem.type)) { return new Address(dataItem); - } else if (Bool.matchGrammar(dataItem.type)) { + } else if (Bool.matchType(dataItem.type)) { return new Bool(dataItem); - } else if (Int.matchGrammar(dataItem.type)) { + } else if (Int.matchType(dataItem.type)) { return new Int(dataItem); - } else if (UInt.matchGrammar(dataItem.type)) { + } else if (UInt.matchType(dataItem.type)) { return new UInt(dataItem); - } else if (Byte.matchGrammar(dataItem.type)) { - return new Byte(dataItem); - } else if (Tuple.matchGrammar(dataItem.type)) { + } else if (StaticBytes.matchType(dataItem.type)) { + return new StaticBytes(dataItem); + } else if (Tuple.matchType(dataItem.type)) { return new Tuple(dataItem); - } else if (Bytes.matchGrammar(dataItem.type)) { - return new Bytes(dataItem); - } else if (SolString.matchGrammar(dataItem.type)) { - return new SolString(dataItem); + } else if (DynamicBytes.matchType(dataItem.type)) { + return new DynamicBytes(dataItem); + } else if (String.matchType(dataItem.type)) { + return new String(dataItem); } // @TODO: Implement Fixed/UFixed types throw new Error(`Unrecognized data type: '${dataItem.type}'`); diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts index e99528b06..35eb8d0a9 100644 --- a/packages/utils/test/abi_encoder_test.ts +++ b/packages/utils/test/abi_encoder_test.ts @@ -772,7 +772,7 @@ describe.only('ABI Encoder', () => { it('Fixed size; Static elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'int[2]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); + const dataType = new AbiEncoder.Array(testDataItem); // Construct args to be encoded const args = [new BigNumber(5), new BigNumber(6)]; // Encode Args and validate result @@ -789,7 +789,7 @@ describe.only('ABI Encoder', () => { it('Dynamic size; Static elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'int[]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); + const dataType = new AbiEncoder.Array(testDataItem); // Construct args to be encoded const args = [new BigNumber(5), new BigNumber(6)]; // Encode Args and validate result @@ -806,7 +806,7 @@ describe.only('ABI Encoder', () => { it('Fixed size; Dynamic elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'string[2]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); + const dataType = new AbiEncoder.Array(testDataItem); // Construct args to be encoded const args = ['Hello', 'world']; // Encode Args and validate result @@ -823,7 +823,7 @@ describe.only('ABI Encoder', () => { it('Dynamic size; Dynamic elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'string[]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); + const dataType = new AbiEncoder.Array(testDataItem); // Construct args to be encoded const args = ['Hello', 'world']; // Encode Args and validate result @@ -840,7 +840,7 @@ describe.only('ABI Encoder', () => { it('Dynamic Size; Multidimensional; Dynamic Elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'bytes[][]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); + const dataType = new AbiEncoder.Array(testDataItem); // Construct args to be encoded const array1 = ['0x01020304', '0x05060708', '0x09101112']; const array2 = ['0x10111213', '0x14151617']; @@ -860,7 +860,7 @@ describe.only('ABI Encoder', () => { it('Dynamic Size; Multidimensional; Static Elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'bytes4[][]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); + const dataType = new AbiEncoder.Array(testDataItem); // Construct args to be encoded const array1 = ['0x01020304', '0x05060708', '0x09101112']; const array2 = ['0x10111213', '0x14151617']; @@ -880,7 +880,7 @@ describe.only('ABI Encoder', () => { it('Static Size; Multidimensional; Static Elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'bytes4[3][2]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); + const dataType = new AbiEncoder.Array(testDataItem); // Construct args to be encoded const array1 = ['0x01020304', '0x05060708', '0x09101112']; const array2 = ['0x10111213', '0x14151617', '0x18192021']; @@ -899,7 +899,7 @@ describe.only('ABI Encoder', () => { it('Static Size; Multidimensional; Dynamic Elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'bytes[3][2]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); + const dataType = new AbiEncoder.Array(testDataItem); // Construct args to be encoded const array1 = ['0x01020304', '0x05060708', '0x09101112']; const array2 = ['0x10111213', '0x14151617', '0x18192021']; @@ -918,7 +918,7 @@ describe.only('ABI Encoder', () => { it('Static size; Too Few Elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'string[3]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); + const dataType = new AbiEncoder.Array(testDataItem); // Construct args to be encoded const args = ['Hello', 'world']; // Encode Args and validate result @@ -929,7 +929,7 @@ describe.only('ABI Encoder', () => { it('Static size; Too Many Elements', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'string[1]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); + const dataType = new AbiEncoder.Array(testDataItem); // Construct args to be encoded const args = ['Hello', 'world']; // Encode Args and validate result @@ -940,7 +940,7 @@ describe.only('ABI Encoder', () => { it('Element Type Mismatch', async () => { // Create DataType object const testDataItem = { name: 'testArray', type: 'uint[]' }; - const dataType = new AbiEncoder.SolArray(testDataItem); + const dataType = new AbiEncoder.Array(testDataItem); // Construct args to be encoded const args = [new BigNumber(1), 'Bad Argument']; // Encode Args and validate result @@ -1563,7 +1563,7 @@ describe.only('ABI Encoder', () => { it('Single Byte (byte)', async () => { // Create DataType object const testDataItem = { name: 'Static Byte', type: 'byte' }; - const dataType = new AbiEncoder.Byte(testDataItem); + const dataType = new AbiEncoder.StaticBytes(testDataItem); // Construct args to be encoded const args = '0x05'; // Encode Args and validate result @@ -1579,7 +1579,7 @@ describe.only('ABI Encoder', () => { it('Single Byte (bytes1)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes1', type: 'bytes1' }; - const dataType = new AbiEncoder.Byte(testDataItem); + const dataType = new AbiEncoder.StaticBytes(testDataItem); // Construct args to be encoded const args = '0x05'; // Encode Args and validate result @@ -1595,7 +1595,7 @@ describe.only('ABI Encoder', () => { it('4 Bytes (bytes4)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; - const dataType = new AbiEncoder.Byte(testDataItem); + const dataType = new AbiEncoder.StaticBytes(testDataItem); // Construct args to be encoded const args = '0x00010203'; // Encode Args and validate result @@ -1611,7 +1611,7 @@ describe.only('ABI Encoder', () => { it('4 Bytes (bytes4); Encoder must pad input', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; - const dataType = new AbiEncoder.Byte(testDataItem); + const dataType = new AbiEncoder.StaticBytes(testDataItem); // Construct args to be encoded // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const args = '0x1a18'; @@ -1629,7 +1629,7 @@ describe.only('ABI Encoder', () => { it('32 Bytes (bytes32)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; - const dataType = new AbiEncoder.Byte(testDataItem); + const dataType = new AbiEncoder.StaticBytes(testDataItem); // Construct args to be encoded const args = '0x0001020304050607080911121314151617181920212223242526272829303132'; // Encode Args and validate result @@ -1645,7 +1645,7 @@ describe.only('ABI Encoder', () => { it('32 Bytes (bytes32); Encoder must pad input', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; - const dataType = new AbiEncoder.Byte(testDataItem); + const dataType = new AbiEncoder.StaticBytes(testDataItem); // Construct args to be encoded // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const args = '0x1a18bf61'; @@ -1663,7 +1663,7 @@ describe.only('ABI Encoder', () => { it('Should throw when pass in too many bytes (bytes4)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; - const dataType = new AbiEncoder.Byte(testDataItem); + const dataType = new AbiEncoder.StaticBytes(testDataItem); // Construct args to be encoded const args = '0x0102030405'; // Encode Args and validate result @@ -1676,7 +1676,7 @@ describe.only('ABI Encoder', () => { it('Should throw when pass in too many bytes (bytes32)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; - const dataType = new AbiEncoder.Byte(testDataItem); + const dataType = new AbiEncoder.StaticBytes(testDataItem); // Construct args to be encoded const args = '0x010203040506070809101112131415161718192021222324252627282930313233'; // Encode Args and validate result @@ -1689,7 +1689,7 @@ describe.only('ABI Encoder', () => { it('Should throw when pass in bad hex (no 0x prefix)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; - const dataType = new AbiEncoder.Byte(testDataItem); + const dataType = new AbiEncoder.StaticBytes(testDataItem); // Construct args to be encoded const args = '0102030405060708091011121314151617181920212223242526272829303132'; // Encode Args and validate result @@ -1700,7 +1700,7 @@ describe.only('ABI Encoder', () => { it('Should throw when pass in bad hex (include a half-byte)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; - const dataType = new AbiEncoder.Byte(testDataItem); + const dataType = new AbiEncoder.StaticBytes(testDataItem); // Construct args to be encoded const args = '0x010'; // Encode Args and validate result @@ -1714,7 +1714,7 @@ describe.only('ABI Encoder', () => { it('Fits into one EVM word', async () => { // Create DataType object const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; - const dataType = new AbiEncoder.Bytes(testDataItem); + const dataType = new AbiEncoder.DynamicBytes(testDataItem); // Construct args to be encoded // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const args = '0x1a18bf61'; @@ -1732,7 +1732,7 @@ describe.only('ABI Encoder', () => { it('Spans multiple EVM words', async () => { // Create DataType object const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; - const dataType = new AbiEncoder.Bytes(testDataItem); + const dataType = new AbiEncoder.DynamicBytes(testDataItem); // Construct args to be encoded // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const bytesLength = 40; @@ -1751,7 +1751,7 @@ describe.only('ABI Encoder', () => { it('Input as Buffer', async () => { // Create DataType object const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; - const dataType = new AbiEncoder.Bytes(testDataItem); + const dataType = new AbiEncoder.DynamicBytes(testDataItem); // Construct args to be encoded // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const args = '0x1a18bf61'; @@ -1770,7 +1770,7 @@ describe.only('ABI Encoder', () => { it('Should throw when pass in bad hex (no 0x prefix)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes', type: 'bytes' }; - const dataType = new AbiEncoder.Bytes(testDataItem); + const dataType = new AbiEncoder.DynamicBytes(testDataItem); // Construct args to be encoded const args = '01'; // Encode Args and validate result @@ -1781,7 +1781,7 @@ describe.only('ABI Encoder', () => { it('Should throw when pass in bad hex (include a half-byte)', async () => { // Create DataType object const testDataItem = { name: 'Static Bytes', type: 'bytes' }; - const dataType = new AbiEncoder.Bytes(testDataItem); + const dataType = new AbiEncoder.DynamicBytes(testDataItem); // Construct args to be encoded const args = '0x010'; // Encode Args and validate result @@ -1795,7 +1795,7 @@ describe.only('ABI Encoder', () => { it('Fits into one EVM word', async () => { // Create DataType object const testDataItem = { name: 'String', type: 'string' }; - const dataType = new AbiEncoder.SolString(testDataItem); + const dataType = new AbiEncoder.String(testDataItem); // Construct args to be encoded // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const args = 'five'; @@ -1813,7 +1813,7 @@ describe.only('ABI Encoder', () => { it('Spans multiple EVM words', async () => { // Create DataType object const testDataItem = { name: 'String', type: 'string' }; - const dataType = new AbiEncoder.SolString(testDataItem); + const dataType = new AbiEncoder.String(testDataItem); // Construct args to be encoded // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const bytesLength = 40; @@ -1832,7 +1832,7 @@ describe.only('ABI Encoder', () => { it('String that begins with 0x prefix', async () => { // Create DataType object const testDataItem = { name: 'String', type: 'string' }; - const dataType = new AbiEncoder.SolString(testDataItem); + const dataType = new AbiEncoder.String(testDataItem); // Construct args to be encoded // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const strLength = 40; -- cgit v1.2.3 From e6ab6f38bacdec90c960ff1db4781d161b1f4103 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 12:58:49 -0800 Subject: Split EVM data types and factory into separate files --- packages/utils/src/abi_encoder/calldata.ts | 1 - packages/utils/src/abi_encoder/data_type.ts | 11 +- .../utils/src/abi_encoder/evm_data_type_factory.ts | 124 +++++ packages/utils/src/abi_encoder/evm_data_types.ts | 584 --------------------- .../src/abi_encoder/evm_data_types/address.ts | 51 ++ .../utils/src/abi_encoder/evm_data_types/array.ts | 55 ++ .../utils/src/abi_encoder/evm_data_types/bool.ts | 50 ++ .../abi_encoder/evm_data_types/dynamic_bytes.ts | 58 ++ .../utils/src/abi_encoder/evm_data_types/index.ts | 11 + .../utils/src/abi_encoder/evm_data_types/int.ts | 33 ++ .../utils/src/abi_encoder/evm_data_types/method.ts | 90 ++++ .../utils/src/abi_encoder/evm_data_types/number.ts | 100 ++++ .../src/abi_encoder/evm_data_types/pointer.ts | 15 + .../src/abi_encoder/evm_data_types/static_bytes.ts | 68 +++ .../utils/src/abi_encoder/evm_data_types/string.ts | 47 ++ .../utils/src/abi_encoder/evm_data_types/tuple.ts | 23 + .../utils/src/abi_encoder/evm_data_types/uint.ts | 33 ++ packages/utils/src/abi_encoder/index.ts | 2 +- 18 files changed, 769 insertions(+), 587 deletions(-) create mode 100644 packages/utils/src/abi_encoder/evm_data_type_factory.ts delete mode 100644 packages/utils/src/abi_encoder/evm_data_types.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/address.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/array.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/bool.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/index.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/int.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/method.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/number.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/pointer.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/string.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/tuple.ts create mode 100644 packages/utils/src/abi_encoder/evm_data_types/uint.ts diff --git a/packages/utils/src/abi_encoder/calldata.ts b/packages/utils/src/abi_encoder/calldata.ts index 994b0fb81..d108ef0a7 100644 --- a/packages/utils/src/abi_encoder/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata.ts @@ -1,4 +1,3 @@ - import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index ce7b11ef6..9890619e5 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -22,6 +22,12 @@ export interface DataTypeFactory { mapDataItemToDataType: (dataItem: DataItem) => DataType; } +export interface DataTypeStaticInterface { + matchType: (type: string) => boolean; + encodeValue: (value: any) => Buffer; + decodeValue: (rawCalldata: RawCalldata) => any; +} + export abstract class DataType { private static readonly _DEFAULT_ENCODING_RULES: EncodingRules = { optimize: false, annotate: false }; private static readonly _DEFAULT_DECODING_RULES: DecodingRules = { structsAsObjects: false }; @@ -250,7 +256,10 @@ export abstract class MemberDataType extends DataType { if (this._isArray && this._arrayLength === undefined) { [members] = this._createMembersWithLength(this.getDataItem(), value.length); - const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(Constants.HEX_BASE)}`), Constants.EVM_WORD_WIDTH_IN_BYTES); + const lenBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(`0x${value.length.toString(Constants.HEX_BASE)}`), + Constants.EVM_WORD_WIDTH_IN_BYTES, + ); methodBlock.setHeader(lenBuf); } diff --git a/packages/utils/src/abi_encoder/evm_data_type_factory.ts b/packages/utils/src/abi_encoder/evm_data_type_factory.ts new file mode 100644 index 000000000..0f8dfb4a3 --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_type_factory.ts @@ -0,0 +1,124 @@ +/* tslint:disable prefer-function-over-method */ +/* tslint:disable max-classes-per-file */ +/* tslint:disable no-construct */ +import { DataItem, MethodAbi } from 'ethereum-types'; + +import { DataType, DataTypeFactory } from './data_type'; +import * as Impl from './evm_data_types'; + +export class Address extends Impl.Address { + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + } +} + +export class Bool extends Impl.Bool { + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + } +} + +export class Int extends Impl.Int { + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + } +} + +export class UInt extends Impl.UInt { + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + } +} + +export class StaticBytes extends Impl.StaticBytes { + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + } +} + +export class DynamicBytes extends Impl.DynamicBytes { + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + } +} + +export class String extends Impl.String { + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + } +} + +export class Pointer extends Impl.Pointer { + public constructor(destDataType: DataType, parentDataType: DataType) { + super(destDataType, parentDataType, EvmDataTypeFactory.getInstance()); + } +} + +export class Tuple extends Impl.Tuple { + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + } +} + +export class Array extends Impl.Array { + public constructor(dataItem: DataItem) { + super(dataItem, EvmDataTypeFactory.getInstance()); + } +} + +export class Method extends Impl.Method { + public constructor(abi: MethodAbi) { + super(abi, EvmDataTypeFactory.getInstance()); + } +} + +export class EvmDataTypeFactory implements DataTypeFactory { + private static _instance: DataTypeFactory; + + public static getInstance(): DataTypeFactory { + if (!EvmDataTypeFactory._instance) { + EvmDataTypeFactory._instance = new EvmDataTypeFactory(); + } + return EvmDataTypeFactory._instance; + } + + public mapDataItemToDataType(dataItem: DataItem): DataType { + if (Array.matchType(dataItem.type)) { + return new Array(dataItem); + } else if (Address.matchType(dataItem.type)) { + return new Address(dataItem); + } else if (Bool.matchType(dataItem.type)) { + return new Bool(dataItem); + } else if (Int.matchType(dataItem.type)) { + return new Int(dataItem); + } else if (UInt.matchType(dataItem.type)) { + return new UInt(dataItem); + } else if (StaticBytes.matchType(dataItem.type)) { + return new StaticBytes(dataItem); + } else if (Tuple.matchType(dataItem.type)) { + return new Tuple(dataItem); + } else if (DynamicBytes.matchType(dataItem.type)) { + return new DynamicBytes(dataItem); + } else if (String.matchType(dataItem.type)) { + return new String(dataItem); + } + // @TODO: Implement Fixed/UFixed types + throw new Error(`Unrecognized data type: '${dataItem.type}'`); + } + + public create(dataItem: DataItem, parentDataType?: DataType): DataType { + const dataType = this.mapDataItemToDataType(dataItem); + if (dataType.isStatic()) { + return dataType; + } + + if (parentDataType === undefined) { + // @Todo -- will this work for return values? + throw new Error(`Trying to create a pointer`); + } + const pointer = new Pointer(dataType, parentDataType); + return pointer; + } + + private constructor() { } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts deleted file mode 100644 index 7ad7f403e..000000000 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ /dev/null @@ -1,584 +0,0 @@ -import { DataItem, MethodAbi } from 'ethereum-types'; -import * as ethUtil from 'ethereumjs-util'; -import * as _ from 'lodash'; - -import { BigNumber } from '../configured_bignumber'; - -import { DecodingRules, EncodingRules, RawCalldata } from './calldata'; -import * as Constants from './constants'; -import { DataType, DataTypeFactory, DependentDataType, MemberDataType, PayloadDataType } from './data_type'; - -export interface DataTypeStaticInterface { - matchType: (type: string) => boolean; - encodeValue: (value: any) => Buffer; - decodeValue: (rawCalldata: RawCalldata) => any; -} - -export class Address extends PayloadDataType { - public static ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X = "Address must start with '0x'"; - public static ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES = 'Address must be 20 bytes'; - private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - private static readonly _ADDRESS_SIZE_IN_BYTES = 20; - private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = Constants.EVM_WORD_WIDTH_IN_BYTES - Address._ADDRESS_SIZE_IN_BYTES; - - public static matchType(type: string): boolean { - return type === 'address'; - } - - public constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Address._SIZE_KNOWN_AT_COMPILE_TIME); - if (!Address.matchType(dataItem.type)) { - throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); - } - } - - public getSignature(): string { - return 'address'; - } - - public encodeValue(value: string): Buffer { - if (!value.startsWith('0x')) { - throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); - } - const valueAsBuffer = ethUtil.toBuffer(value); - if (valueAsBuffer.byteLength !== Address._ADDRESS_SIZE_IN_BYTES) { - throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); - } - const encodedValueBuf = ethUtil.setLengthLeft(valueAsBuffer, Constants.EVM_WORD_WIDTH_IN_BYTES); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(Address._DECODED_ADDRESS_OFFSET_IN_BYTES); - const value = ethUtil.bufferToHex(valueBuf); - return value; - } -} - -export class Bool extends PayloadDataType { - private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - - public static matchType(type: string): boolean { - return type === 'bool'; - } - - public constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), Bool._SIZE_KNOWN_AT_COMPILE_TIME); - if (!Bool.matchType(dataItem.type)) { - throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); - } - } - - public getSignature(): string { - return 'bool'; - } - - public encodeValue(value: boolean): Buffer { - const encodedValue = value ? '0x1' : '0x0'; - const encodedValueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(encodedValue), Constants.EVM_WORD_WIDTH_IN_BYTES); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): boolean { - const valueBuf = calldata.popWord(); - const valueHex = ethUtil.bufferToHex(valueBuf); - const valueNumber = new BigNumber(valueHex, 16); - if (!(valueNumber.equals(0) || valueNumber.equals(1))) { - throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); - } - /* tslint:disable boolean-naming */ - const value: boolean = valueNumber.equals(0) ? false : true; - /* tslint:enable boolean-naming */ - return value; - } -} - -abstract class Number extends PayloadDataType { - private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - private static readonly _MAX_WIDTH: number = 256; - private static readonly _DEFAULT_WIDTH: number = Number._MAX_WIDTH; - protected _width: number; - - constructor(dataItem: DataItem, matcher: RegExp) { - super(dataItem, EvmDataTypeFactory.getInstance(), Number._SIZE_KNOWN_AT_COMPILE_TIME); - const matches = matcher.exec(dataItem.type); - if (matches === null) { - throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); - } - this._width = (matches !== null && matches.length === 2 && matches[1] !== undefined) ? - parseInt(matches[1], Constants.DEC_BASE) : - this._width = Number._DEFAULT_WIDTH; - } - - public encodeValue(value_: BigNumber | string | number): Buffer { - const value = new BigNumber(value_, 10); - if (value.greaterThan(this.getMaxValue())) { - throw new Error(`Tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`); - } else if (value.lessThan(this.getMinValue())) { - throw new Error(`Tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`); - } - - let valueBuf: Buffer; - if (value.greaterThanOrEqualTo(0)) { - valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.toString(Constants.HEX_BASE)}`), Constants.EVM_WORD_WIDTH_IN_BYTES); - } else { - // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. - // Step 1/3: Convert value to positive binary string - const binBase = 2; - const valueBin = value.times(-1).toString(binBase); - - // Step 2/3: Invert binary value - let invertedValueBin = '1'.repeat(Constants.EVM_WORD_WIDTH_IN_BITS - valueBin.length); - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, binBase); - - // Step 3/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const negativeValue = invertedValue.plus(1); - - // Convert the negated value to a hex string - valueBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${negativeValue.toString(Constants.HEX_BASE)}`), Constants.EVM_WORD_WIDTH_IN_BYTES); - } - - return valueBuf; - } - - public decodeValue(calldata: RawCalldata): BigNumber { - const paddedValueBuf = calldata.popWord(); - const paddedValueHex = ethUtil.bufferToHex(paddedValueBuf); - let value = new BigNumber(paddedValueHex, 16); - if (this instanceof Int) { - // Check if we're negative - const valueBin = value.toString(Constants.BIN_BASE); - if (valueBin.length === Constants.EVM_WORD_WIDTH_IN_BITS && valueBin[0].startsWith('1')) { - // Negative - // Step 1/3: Invert binary value - let invertedValueBin = ''; - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, Constants.BIN_BASE); - - // Step 2/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const positiveValue = invertedValue.plus(1); - - // Step 3/3: Invert positive value - const negativeValue = positiveValue.times(-1); - value = negativeValue; - } - } - - return value; - } - - public abstract getMaxValue(): BigNumber; - public abstract getMinValue(): BigNumber; -} - -export class Int extends Number { - private static readonly _matcher = RegExp( - '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - public static matchType(type: string): boolean { - return Int._matcher.test(type); - } - - public constructor(dataItem: DataItem) { - super(dataItem, Int._matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this._width - 1).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(2).toPower(this._width - 1).times(-1); - } - - public getSignature(): string { - return `int${this._width}`; - } -} - -export class UInt extends Number { - private static readonly _matcher = RegExp( - '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', - ); - - public static matchType(type: string): boolean { - return UInt._matcher.test(type); - } - - public constructor(dataItem: DataItem) { - super(dataItem, UInt._matcher); - } - - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this._width).sub(1); - } - - public getMinValue(): BigNumber { - return new BigNumber(0); - } - - public getSignature(): string { - return `uint${this._width}`; - } -} - -export class StaticBytes extends PayloadDataType { - private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - private static readonly _matcher = RegExp( - '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', - ); - - private static readonly _DEFAULT_WIDTH = 1; - private readonly _width: number; - - public static matchType(type: string): boolean { - return StaticBytes._matcher.test(type); - } - - public constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), StaticBytes._SIZE_KNOWN_AT_COMPILE_TIME); - const matches = StaticBytes._matcher.exec(dataItem.type); - if (!StaticBytes.matchType(dataItem.type)) { - throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); - } - this._width = (matches !== null && matches.length === 3 && matches[2] !== undefined) ? parseInt(matches[2], Constants.DEC_BASE) : StaticBytes._DEFAULT_WIDTH; - } - - public getSignature(): string { - // Note that `byte` reduces to `bytes1` - return `bytes${this._width}`; - } - - public encodeValue(value: string | Buffer): Buffer { - // Sanity check if string - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); - } - // Convert value into a buffer and do bounds checking - const valueBuf = ethUtil.toBuffer(value); - if (valueBuf.byteLength > this._width) { - throw new Error( - `Tried to assign ${value} (${ - valueBuf.byteLength - } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, - ); - } else if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - // Store value as hex - const evmWordWidth = 32; - const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); - return paddedValue; - } - - public decodeValue(calldata: RawCalldata): string { - const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(0, this._width); - const value = ethUtil.bufferToHex(valueBuf); - return value; - } -} - -export class DynamicBytes extends PayloadDataType { - private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - - public static matchType(type: string): boolean { - return type === 'bytes'; - } - - public constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), DynamicBytes._SIZE_KNOWN_AT_COMPILE_TIME); - if (!DynamicBytes.matchType(dataItem.type)) { - throw new Error(`Tried to instantiate DynamicBytes with bad input: ${dataItem}`); - } - } - - public encodeValue(value: string | Buffer): Buffer { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '${value}'`); - } - const valueBuf = ethUtil.toBuffer(value); - if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - const wordsForValue = Math.ceil(valueBuf.byteLength / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; - const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedDynamicBytesForValue); - const paddedLengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(valueBuf.byteLength), Constants.EVM_WORD_WIDTH_IN_BYTES); - const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const lengthBuf = calldata.popWord(); - const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, Constants.HEX_BASE); - const wordsForValue = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); - const decodedValue = ethUtil.bufferToHex(valueBuf); - return decodedValue; - } - - public getSignature(): string { - return 'bytes'; - } -} - -export class String extends PayloadDataType { - private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; - - public static matchType(type: string): boolean { - return type === 'string'; - } - - public constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance(), String._SIZE_KNOWN_AT_COMPILE_TIME); - if (!String.matchType(dataItem.type)) { - throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); - } - } - - public encodeValue(value: string): Buffer { - const wordsForValue = Math.ceil(value.length / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; - const valueBuf = ethUtil.setLengthRight(new Buffer(value), paddedDynamicBytesForValue); - const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), Constants.EVM_WORD_WIDTH_IN_BYTES); - const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); - return encodedValueBuf; - } - - public decodeValue(calldata: RawCalldata): string { - const lengthBuf = calldata.popWord(); - const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, Constants.HEX_BASE); - const wordsForValue = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); - const value = valueBuf.toString('ascii'); - return value; - } - - public getSignature(): string { - return 'string'; - } -} - -export class Pointer extends DependentDataType { - constructor(destDataType: DataType, parentDataType: DataType) { - const destDataItem = destDataType.getDataItem(); - const dataItem: DataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` }; - super(dataItem, EvmDataTypeFactory.getInstance(), destDataType, parentDataType); - } - - public getSignature(): string { - return this._dependency.getSignature(); - } -} - -export class Tuple extends MemberDataType { - private readonly _tupleSignature: string; - - public static matchType(type: string): boolean { - return type === 'tuple'; - } - - public constructor(dataItem: DataItem) { - super(dataItem, EvmDataTypeFactory.getInstance()); - if (!Tuple.matchType(dataItem.type)) { - throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); - } - this._tupleSignature = this._computeSignatureOfMembers(); - } - - public getSignature(): string { - return this._tupleSignature; - } -} - -export class Array extends MemberDataType { - private static readonly _matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); - private readonly _arraySignature: string; - private readonly _elementType: string; - - public static matchType(type: string): boolean { - return Array._matcher.test(type); - } - - public constructor(dataItem: DataItem) { - // Sanity check - const matches = Array._matcher.exec(dataItem.type); - if (matches === null || matches.length !== 3) { - throw new Error(`Could not parse array: ${dataItem.type}`); - } else if (matches[1] === undefined) { - throw new Error(`Could not parse array type: ${dataItem.type}`); - } else if (matches[2] === undefined) { - throw new Error(`Could not parse array length: ${dataItem.type}`); - } - - const isArray = true; - const arrayElementType = matches[1]; - const arrayLength = matches[2] === '' ? undefined : parseInt(matches[2], Constants.DEC_BASE); - super(dataItem, EvmDataTypeFactory.getInstance(), isArray, arrayLength, arrayElementType); - this._elementType = arrayElementType; - this._arraySignature = this._computeSignature(); - } - - public getSignature(): string { - return this._arraySignature; - } - - private _computeSignature(): string { - const dataItem: DataItem = { - type: this._elementType, - name: 'N/A', - }; - const components = this.getDataItem().components; - if (components !== undefined) { - dataItem.components = components; - } - const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); - const type = elementDataType.getSignature(); - if (this._arrayLength === undefined) { - return `${type}[]`; - } else { - return `${type}[${this._arrayLength}]`; - } - } -} - -export class Method extends MemberDataType { - // TMP - public selector: string; - - private readonly _methodSignature: string; - private readonly _methodSelector: string; - private readonly _returnDataTypes: DataType[]; - private readonly _returnDataItem: DataItem; - - public constructor(abi: MethodAbi) { - super({ type: 'method', name: abi.name, components: abi.inputs }, EvmDataTypeFactory.getInstance()); - this._methodSignature = this._computeSignature(); - this.selector = this._methodSelector = this._computeSelector(); - this._returnDataTypes = []; - this._returnDataItem = { type: 'tuple', name: abi.name, components: abi.outputs }; - const dummy = new StaticBytes({ type: 'byte', name: 'DUMMY' }); // @TODO TMP - _.each(abi.outputs, (dataItem: DataItem) => { - this._returnDataTypes.push(this.getFactory().create(dataItem, dummy)); - }); - } - - public encode(value: any, rules?: EncodingRules): string { - const calldata = super.encode(value, rules, this.selector); - return calldata; - } - - public decode(calldata: string, rules?: DecodingRules): any[] | object { - if (!calldata.startsWith(this.selector)) { - throw new Error( - `Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`, - ); - } - const hasSelector = true; - const value = super.decode(calldata, rules, hasSelector); - return value; - } - - public encodeReturnValues(value: any, rules?: EncodingRules): string { - const returnDataType = new Tuple(this._returnDataItem); - const returndata = returnDataType.encode(value, rules); - return returndata; - } - - public decodeReturnValues(returndata: string, rules?: DecodingRules): any { - const returnValues: any[] = []; - const rules_: DecodingRules = rules ? rules : { structsAsObjects: false }; - const rawReturnData = new RawCalldata(returndata, false); - _.each(this._returnDataTypes, (dataType: DataType) => { - returnValues.push(dataType.generateValue(rawReturnData, rules_)); - }); - return returnValues; - } - - public getSignature(): string { - return this._methodSignature; - } - - public getSelector(): string { - return this._methodSelector; - } - - private _computeSignature(): string { - const memberSignature = this._computeSignatureOfMembers(); - const methodSignature = `${this.getDataItem().name}${memberSignature}`; - return methodSignature; - } - - private _computeSelector(): string { - const signature = this._computeSignature(); - const selector = ethUtil.bufferToHex(ethUtil.toBuffer(ethUtil.sha3(signature).slice(Constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, Constants.HEX_SELECTOR_LENGTH_IN_BYTES))); - return selector; - } -} - -export class EvmDataTypeFactory implements DataTypeFactory { - private static _instance: DataTypeFactory; - - public static getInstance(): DataTypeFactory { - if (!EvmDataTypeFactory._instance) { - EvmDataTypeFactory._instance = new EvmDataTypeFactory(); - } - return EvmDataTypeFactory._instance; - } - - public mapDataItemToDataType(dataItem: DataItem): DataType { - if (Array.matchType(dataItem.type)) { - return new Array(dataItem); - } else if (Address.matchType(dataItem.type)) { - return new Address(dataItem); - } else if (Bool.matchType(dataItem.type)) { - return new Bool(dataItem); - } else if (Int.matchType(dataItem.type)) { - return new Int(dataItem); - } else if (UInt.matchType(dataItem.type)) { - return new UInt(dataItem); - } else if (StaticBytes.matchType(dataItem.type)) { - return new StaticBytes(dataItem); - } else if (Tuple.matchType(dataItem.type)) { - return new Tuple(dataItem); - } else if (DynamicBytes.matchType(dataItem.type)) { - return new DynamicBytes(dataItem); - } else if (String.matchType(dataItem.type)) { - return new String(dataItem); - } - // @TODO: Implement Fixed/UFixed types - throw new Error(`Unrecognized data type: '${dataItem.type}'`); - } - - public create(dataItem: DataItem, parentDataType?: DataType): DataType { - const dataType = this.mapDataItemToDataType(dataItem); - if (dataType.isStatic()) { - return dataType; - } - - if (parentDataType === undefined) { - // @Todo -- will this work for return values? - throw new Error(`Trying to create a pointer`); - } - const pointer = new Pointer(dataType, parentDataType); - return pointer; - } - - private constructor() { } -} diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts new file mode 100644 index 000000000..4bd992cab --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -0,0 +1,51 @@ +/* tslint:disable prefer-function-over-method */ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { RawCalldata } from '../calldata'; +import * as Constants from '../constants'; +import { DataTypeFactory, PayloadDataType } from '../data_type'; + +export class Address extends PayloadDataType { + public static ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X = "Address must start with '0x'"; + public static ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES = 'Address must be 20 bytes'; + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + private static readonly _ADDRESS_SIZE_IN_BYTES = 20; + private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = Constants.EVM_WORD_WIDTH_IN_BYTES - + Address._ADDRESS_SIZE_IN_BYTES; + + public static matchType(type: string): boolean { + return type === 'address'; + } + + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { + super(dataItem, dataTypeFactory, Address._SIZE_KNOWN_AT_COMPILE_TIME); + if (!Address.matchType(dataItem.type)) { + throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); + } + } + + public getSignature(): string { + return 'address'; + } + + public encodeValue(value: string): Buffer { + if (!value.startsWith('0x')) { + throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); + } + const valueAsBuffer = ethUtil.toBuffer(value); + if (valueAsBuffer.byteLength !== Address._ADDRESS_SIZE_IN_BYTES) { + throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); + } + const encodedValueBuf = ethUtil.setLengthLeft(valueAsBuffer, Constants.EVM_WORD_WIDTH_IN_BYTES); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const paddedValueBuf = calldata.popWord(); + const valueBuf = paddedValueBuf.slice(Address._DECODED_ADDRESS_OFFSET_IN_BYTES); + const value = ethUtil.bufferToHex(valueBuf); + return value; + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts new file mode 100644 index 000000000..707af7c7e --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -0,0 +1,55 @@ +import { DataItem } from 'ethereum-types'; + +import * as Constants from '../constants'; +import { DataTypeFactory, MemberDataType } from '../data_type'; + +export class Array extends MemberDataType { + private static readonly _matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); + private readonly _arraySignature: string; + private readonly _elementType: string; + + public static matchType(type: string): boolean { + return Array._matcher.test(type); + } + + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { + // Sanity check + const matches = Array._matcher.exec(dataItem.type); + if (matches === null || matches.length !== 3) { + throw new Error(`Could not parse array: ${dataItem.type}`); + } else if (matches[1] === undefined) { + throw new Error(`Could not parse array type: ${dataItem.type}`); + } else if (matches[2] === undefined) { + throw new Error(`Could not parse array length: ${dataItem.type}`); + } + + const isArray = true; + const arrayElementType = matches[1]; + const arrayLength = matches[2] === '' ? undefined : parseInt(matches[2], Constants.DEC_BASE); + super(dataItem, dataTypeFactory, isArray, arrayLength, arrayElementType); + this._elementType = arrayElementType; + this._arraySignature = this._computeSignature(); + } + + public getSignature(): string { + return this._arraySignature; + } + + private _computeSignature(): string { + const dataItem: DataItem = { + type: this._elementType, + name: 'N/A', + }; + const components = this.getDataItem().components; + if (components !== undefined) { + dataItem.components = components; + } + const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); + const type = elementDataType.getSignature(); + if (this._arrayLength === undefined) { + return `${type}[]`; + } else { + return `${type}[${this._arrayLength}]`; + } + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts new file mode 100644 index 000000000..aee2727c7 --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -0,0 +1,50 @@ +/* tslint:disable prefer-function-over-method */ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { BigNumber } from '../../configured_bignumber'; +import { RawCalldata } from '../calldata'; +import * as Constants from '../constants'; +import { DataTypeFactory, PayloadDataType } from '../data_type'; + +export class Bool extends PayloadDataType { + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + + public static matchType(type: string): boolean { + return type === 'bool'; + } + + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { + super(dataItem, dataTypeFactory, Bool._SIZE_KNOWN_AT_COMPILE_TIME); + if (!Bool.matchType(dataItem.type)) { + throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); + } + } + + public getSignature(): string { + return 'bool'; + } + + public encodeValue(value: boolean): Buffer { + const encodedValue = value ? '0x1' : '0x0'; + const encodedValueBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(encodedValue), + Constants.EVM_WORD_WIDTH_IN_BYTES, + ); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): boolean { + const valueBuf = calldata.popWord(); + const valueHex = ethUtil.bufferToHex(valueBuf); + const valueNumber = new BigNumber(valueHex, Constants.HEX_BASE); + if (!(valueNumber.equals(0) || valueNumber.equals(1))) { + throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); + } + /* tslint:disable boolean-naming */ + const value: boolean = valueNumber.equals(0) ? false : true; + /* tslint:enable boolean-naming */ + return value; + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts new file mode 100644 index 000000000..51165881a --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -0,0 +1,58 @@ +/* tslint:disable prefer-function-over-method */ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { RawCalldata } from '../calldata'; +import * as Constants from '../constants'; +import { DataTypeFactory, PayloadDataType } from '../data_type'; + +export class DynamicBytes extends PayloadDataType { + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; + + public static matchType(type: string): boolean { + return type === 'bytes'; + } + + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { + super(dataItem, dataTypeFactory, DynamicBytes._SIZE_KNOWN_AT_COMPILE_TIME); + if (!DynamicBytes.matchType(dataItem.type)) { + throw new Error(`Tried to instantiate DynamicBytes with bad input: ${dataItem}`); + } + } + + public encodeValue(value: string | Buffer): Buffer { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '${value}'`); + } + const valueBuf = ethUtil.toBuffer(value); + if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + const wordsForValue = Math.ceil(valueBuf.byteLength / Constants.EVM_WORD_WIDTH_IN_BYTES); + const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; + const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedDynamicBytesForValue); + const paddedLengthBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(valueBuf.byteLength), + Constants.EVM_WORD_WIDTH_IN_BYTES, + ); + const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const lengthBuf = calldata.popWord(); + const lengthHex = ethUtil.bufferToHex(lengthBuf); + const length = parseInt(lengthHex, Constants.HEX_BASE); + const wordsForValue = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const paddedValueBuf = calldata.popWords(wordsForValue); + const valueBuf = paddedValueBuf.slice(0, length); + const decodedValue = ethUtil.bufferToHex(valueBuf); + return decodedValue; + } + + public getSignature(): string { + return 'bytes'; + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/index.ts b/packages/utils/src/abi_encoder/evm_data_types/index.ts new file mode 100644 index 000000000..fc0edabf1 --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/index.ts @@ -0,0 +1,11 @@ +export * from './address'; +export * from './bool'; +export * from './int'; +export * from './uint'; +export * from './static_bytes'; +export * from './dynamic_bytes'; +export * from './string'; +export * from './pointer'; +export * from './tuple'; +export * from './array'; +export * from './method'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts new file mode 100644 index 000000000..ba5b4cac9 --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -0,0 +1,33 @@ +/* tslint:disable prefer-function-over-method */ +import { DataItem } from 'ethereum-types'; + +import { BigNumber } from '../../configured_bignumber'; +import { DataTypeFactory } from '../data_type'; + +import { Number } from './number'; + +export class Int extends Number { + private static readonly _matcher = RegExp( + '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + public static matchType(type: string): boolean { + return Int._matcher.test(type); + } + + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { + super(dataItem, Int._matcher, dataTypeFactory); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this._width - 1).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(2).toPower(this._width - 1).times(-1); + } + + public getSignature(): string { + return `int${this._width}`; + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts new file mode 100644 index 000000000..e8e717bf1 --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts @@ -0,0 +1,90 @@ +import { DataItem, MethodAbi } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { DecodingRules, EncodingRules, RawCalldata } from '../calldata'; +import * as Constants from '../constants'; +import { DataType, DataTypeFactory, MemberDataType } from '../data_type'; + +import { StaticBytes } from './static_bytes'; +import { Tuple } from './tuple'; + +export class Method extends MemberDataType { + // TMP + public selector: string; + + private readonly _methodSignature: string; + private readonly _methodSelector: string; + private readonly _returnDataTypes: DataType[]; + private readonly _returnDataItem: DataItem; + + public constructor(abi: MethodAbi, dataTypeFactory: DataTypeFactory) { + super({ type: 'method', name: abi.name, components: abi.inputs }, dataTypeFactory); + this._methodSignature = this._computeSignature(); + this.selector = this._methodSelector = this._computeSelector(); + this._returnDataTypes = []; + this._returnDataItem = { type: 'tuple', name: abi.name, components: abi.outputs }; + const dummy = new StaticBytes({ type: 'byte', name: 'DUMMY' }, dataTypeFactory); // @TODO TMP + _.each(abi.outputs, (dataItem: DataItem) => { + this._returnDataTypes.push(this.getFactory().create(dataItem, dummy)); + }); + } + + public encode(value: any, rules?: EncodingRules): string { + const calldata = super.encode(value, rules, this.selector); + return calldata; + } + + public decode(calldata: string, rules?: DecodingRules): any[] | object { + if (!calldata.startsWith(this.selector)) { + throw new Error( + `Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`, + ); + } + const hasSelector = true; + const value = super.decode(calldata, rules, hasSelector); + return value; + } + + public encodeReturnValues(value: any, rules?: EncodingRules): string { + const returnDataType = new Tuple(this._returnDataItem, this.getFactory()); + const returndata = returnDataType.encode(value, rules); + return returndata; + } + + public decodeReturnValues(returndata: string, rules?: DecodingRules): any { + const returnValues: any[] = []; + const rules_: DecodingRules = rules ? rules : { structsAsObjects: false }; + const rawReturnData = new RawCalldata(returndata, false); + _.each(this._returnDataTypes, (dataType: DataType) => { + returnValues.push(dataType.generateValue(rawReturnData, rules_)); + }); + return returnValues; + } + + public getSignature(): string { + return this._methodSignature; + } + + public getSelector(): string { + return this._methodSelector; + } + + private _computeSignature(): string { + const memberSignature = this._computeSignatureOfMembers(); + const methodSignature = `${this.getDataItem().name}${memberSignature}`; + return methodSignature; + } + + private _computeSelector(): string { + const signature = this._computeSignature(); + const selector = ethUtil.bufferToHex( + ethUtil.toBuffer( + ethUtil + .sha3(signature) + .slice(Constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, Constants.HEX_SELECTOR_LENGTH_IN_BYTES), + ), + ); + return selector; + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/number.ts b/packages/utils/src/abi_encoder/evm_data_types/number.ts new file mode 100644 index 000000000..17201362e --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/number.ts @@ -0,0 +1,100 @@ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { BigNumber } from '../../configured_bignumber'; +import { RawCalldata } from '../calldata'; +import * as Constants from '../constants'; +import { DataTypeFactory, PayloadDataType } from '../data_type'; + +export abstract class Number extends PayloadDataType { + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + private static readonly _MAX_WIDTH: number = 256; + private static readonly _DEFAULT_WIDTH: number = Number._MAX_WIDTH; + protected _width: number; + + constructor(dataItem: DataItem, matcher: RegExp, dataTypeFactory: DataTypeFactory) { + super(dataItem, dataTypeFactory, Number._SIZE_KNOWN_AT_COMPILE_TIME); + const matches = matcher.exec(dataItem.type); + if (matches === null) { + throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); + } + this._width = + matches !== null && matches.length === 2 && matches[1] !== undefined + ? parseInt(matches[1], Constants.DEC_BASE) + : (this._width = Number._DEFAULT_WIDTH); + } + + public encodeValue(value_: BigNumber | string | number): Buffer { + const value = new BigNumber(value_, 10); + if (value.greaterThan(this.getMaxValue())) { + throw new Error(`Tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`); + } else if (value.lessThan(this.getMinValue())) { + throw new Error(`Tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`); + } + + let valueBuf: Buffer; + if (value.greaterThanOrEqualTo(0)) { + valueBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(`0x${value.toString(Constants.HEX_BASE)}`), + Constants.EVM_WORD_WIDTH_IN_BYTES, + ); + } else { + // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. + // Step 1/3: Convert value to positive binary string + const binBase = 2; + const valueBin = value.times(-1).toString(binBase); + + // Step 2/3: Invert binary value + let invertedValueBin = '1'.repeat(Constants.EVM_WORD_WIDTH_IN_BITS - valueBin.length); + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, binBase); + + // Step 3/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const negativeValue = invertedValue.plus(1); + + // Convert the negated value to a hex string + valueBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(`0x${negativeValue.toString(Constants.HEX_BASE)}`), + Constants.EVM_WORD_WIDTH_IN_BYTES, + ); + } + + return valueBuf; + } + + public decodeValue(calldata: RawCalldata): BigNumber { + const paddedValueBuf = calldata.popWord(); + const paddedValueHex = ethUtil.bufferToHex(paddedValueBuf); + let value = new BigNumber(paddedValueHex, 16); + if (this.getMinValue().lessThan(0)) { + // Check if we're negative + const valueBin = value.toString(Constants.BIN_BASE); + if (valueBin.length === Constants.EVM_WORD_WIDTH_IN_BITS && valueBin[0].startsWith('1')) { + // Negative + // Step 1/3: Invert binary value + let invertedValueBin = ''; + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, Constants.BIN_BASE); + + // Step 2/3: Add 1 to inverted value + // The result is the two's-complement represent of the input value. + const positiveValue = invertedValue.plus(1); + + // Step 3/3: Invert positive value + const negativeValue = positiveValue.times(-1); + value = negativeValue; + } + } + + return value; + } + + public abstract getMaxValue(): BigNumber; + public abstract getMinValue(): BigNumber; +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts new file mode 100644 index 000000000..e0bd3509c --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts @@ -0,0 +1,15 @@ +import { DataItem } from 'ethereum-types'; + +import { DataType, DataTypeFactory, DependentDataType } from '../data_type'; + +export class Pointer extends DependentDataType { + constructor(destDataType: DataType, parentDataType: DataType, dataTypeFactory: DataTypeFactory) { + const destDataItem = destDataType.getDataItem(); + const dataItem: DataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` }; + super(dataItem, dataTypeFactory, destDataType, parentDataType); + } + + public getSignature(): string { + return this._dependency.getSignature(); + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts new file mode 100644 index 000000000..309dca234 --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -0,0 +1,68 @@ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { RawCalldata } from '../calldata'; +import * as Constants from '../constants'; +import { DataTypeFactory, PayloadDataType } from '../data_type'; + +export class StaticBytes extends PayloadDataType { + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + private static readonly _matcher = RegExp( + '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', + ); + + private static readonly _DEFAULT_WIDTH = 1; + private readonly _width: number; + + public static matchType(type: string): boolean { + return StaticBytes._matcher.test(type); + } + + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { + super(dataItem, dataTypeFactory, StaticBytes._SIZE_KNOWN_AT_COMPILE_TIME); + const matches = StaticBytes._matcher.exec(dataItem.type); + if (!StaticBytes.matchType(dataItem.type)) { + throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); + } + this._width = + matches !== null && matches.length === 3 && matches[2] !== undefined + ? parseInt(matches[2], Constants.DEC_BASE) + : StaticBytes._DEFAULT_WIDTH; + } + + public getSignature(): string { + // Note that `byte` reduces to `bytes1` + return `bytes${this._width}`; + } + + public encodeValue(value: string | Buffer): Buffer { + // Sanity check if string + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); + } + // Convert value into a buffer and do bounds checking + const valueBuf = ethUtil.toBuffer(value); + if (valueBuf.byteLength > this._width) { + throw new Error( + `Tried to assign ${value} (${ + valueBuf.byteLength + } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, + ); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + + // Store value as hex + const evmWordWidth = 32; + const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); + return paddedValue; + } + + public decodeValue(calldata: RawCalldata): string { + const paddedValueBuf = calldata.popWord(); + const valueBuf = paddedValueBuf.slice(0, this._width); + const value = ethUtil.bufferToHex(valueBuf); + return value; + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts new file mode 100644 index 000000000..96b36e735 --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -0,0 +1,47 @@ +/* tslint:disable prefer-function-over-method */ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { RawCalldata } from '../calldata'; +import * as Constants from '../constants'; +import { DataTypeFactory, PayloadDataType } from '../data_type'; + +export class String extends PayloadDataType { + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; + + public static matchType(type: string): boolean { + return type === 'string'; + } + + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { + super(dataItem, dataTypeFactory, String._SIZE_KNOWN_AT_COMPILE_TIME); + if (!String.matchType(dataItem.type)) { + throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); + } + } + + public encodeValue(value: string): Buffer { + const wordsForValue = Math.ceil(value.length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; + const valueBuf = ethUtil.setLengthRight(new Buffer(value), paddedDynamicBytesForValue); + const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), Constants.EVM_WORD_WIDTH_IN_BYTES); + const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + return encodedValueBuf; + } + + public decodeValue(calldata: RawCalldata): string { + const lengthBuf = calldata.popWord(); + const lengthHex = ethUtil.bufferToHex(lengthBuf); + const length = parseInt(lengthHex, Constants.HEX_BASE); + const wordsForValue = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const paddedValueBuf = calldata.popWords(wordsForValue); + const valueBuf = paddedValueBuf.slice(0, length); + const value = valueBuf.toString('ascii'); + return value; + } + + public getSignature(): string { + return 'string'; + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts new file mode 100644 index 000000000..0db29c1eb --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts @@ -0,0 +1,23 @@ +import { DataItem } from 'ethereum-types'; + +import { DataTypeFactory, MemberDataType } from '../data_type'; + +export class Tuple extends MemberDataType { + private readonly _tupleSignature: string; + + public static matchType(type: string): boolean { + return type === 'tuple'; + } + + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { + super(dataItem, dataTypeFactory); + if (!Tuple.matchType(dataItem.type)) { + throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); + } + this._tupleSignature = this._computeSignatureOfMembers(); + } + + public getSignature(): string { + return this._tupleSignature; + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts new file mode 100644 index 000000000..86b31ab4c --- /dev/null +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -0,0 +1,33 @@ +/* tslint:disable prefer-function-over-method */ +import { DataItem } from 'ethereum-types'; + +import { BigNumber } from '../../configured_bignumber'; +import { DataTypeFactory } from '../data_type'; + +import { Number } from './number'; + +export class UInt extends Number { + private static readonly _matcher = RegExp( + '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', + ); + + public static matchType(type: string): boolean { + return UInt._matcher.test(type); + } + + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { + super(dataItem, UInt._matcher, dataTypeFactory); + } + + public getMaxValue(): BigNumber { + return new BigNumber(2).toPower(this._width).sub(1); + } + + public getMinValue(): BigNumber { + return new BigNumber(0); + } + + public getSignature(): string { + return `uint${this._width}`; + } +} diff --git a/packages/utils/src/abi_encoder/index.ts b/packages/utils/src/abi_encoder/index.ts index 95ad84ac9..a62569fab 100644 --- a/packages/utils/src/abi_encoder/index.ts +++ b/packages/utils/src/abi_encoder/index.ts @@ -1,2 +1,2 @@ export { EncodingRules, DecodingRules } from './calldata'; -export * from './evm_data_types'; +export * from './evm_data_type_factory'; -- cgit v1.2.3 From aed8b083b587e7b420ac6129b04004dea95c3f3a Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 13:24:45 -0800 Subject: Split Calldata into multiple files - 1 class per file --- packages/utils/src/abi_encoder/calldata.ts | 531 --------------------- .../utils/src/abi_encoder/calldata/calldata.ts | 224 +++++++++ .../src/abi_encoder/calldata/calldata_block.ts | 77 +++ .../src/abi_encoder/calldata/calldata_blocks.ts | 3 + .../calldata/dependent_calldata_block.ts | 59 +++ packages/utils/src/abi_encoder/calldata/index.ts | 6 + .../abi_encoder/calldata/member_calldata_block.ts | 48 ++ .../abi_encoder/calldata/payload_calldata_block.ts | 20 + .../utils/src/abi_encoder/calldata/raw_calldata.ts | 82 ++++ packages/utils/src/abi_encoder/data_type.ts | 4 +- .../utils/src/abi_encoder/evm_data_types/method.ts | 3 +- packages/utils/src/abi_encoder/index.ts | 2 +- packages/utils/src/abi_encoder/utils/queue.ts | 39 ++ packages/utils/src/abi_encoder/utils/rules.ts | 8 + 14 files changed, 571 insertions(+), 535 deletions(-) delete mode 100644 packages/utils/src/abi_encoder/calldata.ts create mode 100644 packages/utils/src/abi_encoder/calldata/calldata.ts create mode 100644 packages/utils/src/abi_encoder/calldata/calldata_block.ts create mode 100644 packages/utils/src/abi_encoder/calldata/calldata_blocks.ts create mode 100644 packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts create mode 100644 packages/utils/src/abi_encoder/calldata/index.ts create mode 100644 packages/utils/src/abi_encoder/calldata/member_calldata_block.ts create mode 100644 packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts create mode 100644 packages/utils/src/abi_encoder/calldata/raw_calldata.ts create mode 100644 packages/utils/src/abi_encoder/utils/queue.ts create mode 100644 packages/utils/src/abi_encoder/utils/rules.ts diff --git a/packages/utils/src/abi_encoder/calldata.ts b/packages/utils/src/abi_encoder/calldata.ts deleted file mode 100644 index d108ef0a7..000000000 --- a/packages/utils/src/abi_encoder/calldata.ts +++ /dev/null @@ -1,531 +0,0 @@ -import * as ethUtil from 'ethereumjs-util'; -import * as _ from 'lodash'; - -import * as Constants from './constants'; - -export interface DecodingRules { - structsAsObjects: boolean; -} - -export interface EncodingRules { - optimize?: boolean; - annotate?: boolean; -} - -export abstract class CalldataBlock { - private readonly _signature: string; - private readonly _parentName: string; - private _name: string; - private _offsetInBytes: number; - private _headerSizeInBytes: number; - private _bodySizeInBytes: number; - - constructor( - name: string, - signature: string, - parentName: string, - headerSizeInBytes: number, - bodySizeInBytes: number, - ) { - this._name = name; - this._signature = signature; - this._parentName = parentName; - this._offsetInBytes = 0; - this._headerSizeInBytes = headerSizeInBytes; - this._bodySizeInBytes = bodySizeInBytes; - } - - protected _setHeaderSize(headerSizeInBytes: number): void { - this._headerSizeInBytes = headerSizeInBytes; - } - - protected _setBodySize(bodySizeInBytes: number): void { - this._bodySizeInBytes = bodySizeInBytes; - } - - protected _setName(name: string): void { - this._name = name; - } - - public getName(): string { - return this._name; - } - - public getParentName(): string { - return this._parentName; - } - - public getSignature(): string { - return this._signature; - } - public getHeaderSizeInBytes(): number { - return this._headerSizeInBytes; - } - - public getBodySizeInBytes(): number { - return this._bodySizeInBytes; - } - - public getSizeInBytes(): number { - return this.getHeaderSizeInBytes() + this.getBodySizeInBytes(); - } - - public getOffsetInBytes(): number { - return this._offsetInBytes; - } - - public setOffset(offsetInBytes: number): void { - this._offsetInBytes = offsetInBytes; - } - - public computeHash(): Buffer { - const rawData = this.getRawData(); - const hash = ethUtil.sha3(rawData); - return hash; - } - - public abstract toBuffer(): Buffer; - public abstract getRawData(): Buffer; -} - -export class PayloadCalldataBlock extends CalldataBlock { - private readonly _payload: Buffer; - - constructor(name: string, signature: string, parentName: string, payload: Buffer) { - const headerSizeInBytes = 0; - const bodySizeInBytes = payload.byteLength; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); - this._payload = payload; - } - - public toBuffer(): Buffer { - return this._payload; - } - - public getRawData(): Buffer { - return this._payload; - } -} - -export class DependentCalldataBlock extends CalldataBlock { - public static readonly RAW_DATA_START = new Buffer('<'); - public static readonly RAW_DATA_END = new Buffer('>'); - private static readonly _DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; - private static readonly _EMPTY_HEADER_SIZE = 0; - private readonly _parent: CalldataBlock; - private readonly _dependency: CalldataBlock; - private _aliasFor: CalldataBlock | undefined; - - constructor(name: string, signature: string, parentName: string, dependency: CalldataBlock, parent: CalldataBlock) { - const headerSizeInBytes = DependentCalldataBlock._EMPTY_HEADER_SIZE; - const bodySizeInBytes = DependentCalldataBlock._DEPENDENT_PAYLOAD_SIZE_IN_BYTES; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); - this._parent = parent; - this._dependency = dependency; - this._aliasFor = undefined; - } - - public toBuffer(): Buffer { - const destinationOffset = - this._aliasFor !== undefined ? this._aliasFor.getOffsetInBytes() : this._dependency.getOffsetInBytes(); - const parentOffset = this._parent.getOffsetInBytes(); - const parentHeaderSize = this._parent.getHeaderSizeInBytes(); - const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); - const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(Constants.HEX_BASE)}`); - const evmWordWidthInBytes = 32; - const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); - return pointerBufPadded; - } - - public getDependency(): CalldataBlock { - return this._dependency; - } - - public setAlias(block: CalldataBlock): void { - this._aliasFor = block; - this._setName(`${this.getName()} (alias for ${block.getName()})`); - } - - public getAlias(): CalldataBlock | undefined { - return this._aliasFor; - } - - public getRawData(): Buffer { - const dependencyRawData = this._dependency.getRawData(); - const rawDataComponents: Buffer[] = []; - rawDataComponents.push(DependentCalldataBlock.RAW_DATA_START); - rawDataComponents.push(dependencyRawData); - rawDataComponents.push(DependentCalldataBlock.RAW_DATA_END); - const rawData = Buffer.concat(rawDataComponents); - return rawData; - } -} - -export class MemberCalldataBlock extends CalldataBlock { - private _header: Buffer | undefined; - private _members: CalldataBlock[]; - - constructor(name: string, signature: string, parentName: string) { - super(name, signature, parentName, 0, 0); - this._members = []; - this._header = undefined; - } - - public getRawData(): Buffer { - const rawDataComponents: Buffer[] = []; - if (this._header !== undefined) { - rawDataComponents.push(this._header); - } - _.each(this._members, (member: CalldataBlock) => { - const memberBuffer = member.getRawData(); - rawDataComponents.push(memberBuffer); - }); - - const rawData = Buffer.concat(rawDataComponents); - return rawData; - } - - public setMembers(members: CalldataBlock[]): void { - this._members = members; - } - - public setHeader(header: Buffer): void { - this._setHeaderSize(header.byteLength); - this._header = header; - } - - public toBuffer(): Buffer { - if (this._header !== undefined) { - return this._header; - } - return new Buffer(''); - } - - public getMembers(): CalldataBlock[] { - return this._members; - } -} - -class Queue { - private _store: T[] = []; - public push(val: T): void { - this._store.push(val); - } - public pushFront(val: T): void { - this._store.unshift(val); - } - public pop(): T | undefined { - return this._store.shift(); - } - public popBack(): T | undefined { - if (this._store.length === 0) { - return undefined; - } - const backElement = this._store.splice(-1, 1)[0]; - return backElement; - } - public merge(q: Queue): void { - this._store = this._store.concat(q._store); - } - public mergeFront(q: Queue): void { - this._store = q._store.concat(this._store); - } - public getStore(): T[] { - return this._store; - } - public peek(): T | undefined { - return this._store.length >= 0 ? this._store[0] : undefined; - } -} - -export class Calldata { - private readonly _rules: EncodingRules; - private _selector: string; - private _sizeInBytes: number; - private _root: CalldataBlock | undefined; - - private static _createQueue(block: CalldataBlock): Queue { - const blockQueue = new Queue(); - - // Base Case - if (!(block instanceof MemberCalldataBlock)) { - blockQueue.push(block); - return blockQueue; - } - - // This is a Member Block - const memberBlock = block; - _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof MemberCalldataBlock) { - blockQueue.mergeFront(Calldata._createQueue(member)); - } else { - blockQueue.pushFront(member); - } - }); - - // Children - _.each(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof DependentCalldataBlock && member.getAlias() === undefined) { - const dependency = member.getDependency(); - if (dependency instanceof MemberCalldataBlock) { - blockQueue.merge(Calldata._createQueue(dependency)); - } else { - blockQueue.push(dependency); - } - } - }); - - blockQueue.pushFront(memberBlock); - return blockQueue; - } - - public constructor(rules: EncodingRules) { - this._rules = rules; - this._selector = ''; - this._sizeInBytes = 0; - this._root = undefined; - } - - public optimize(): void { - if (this._root === undefined) { - throw new Error('expected root'); - } - - const blocksByHash: { [key: string]: CalldataBlock } = {}; - - // 1. Create a queue of subtrees by hash - // Note that they are ordered the same as - const subtreeQueue = Calldata._createQueue(this._root); - let block: CalldataBlock | undefined; - for (block = subtreeQueue.popBack(); block !== undefined; block = subtreeQueue.popBack()) { - if (block instanceof DependentCalldataBlock) { - const dependencyBlockHashBuf = block.getDependency().computeHash(); - const dependencyBlockHash = ethUtil.bufferToHex(dependencyBlockHashBuf); - if (dependencyBlockHash in blocksByHash) { - const blockWithSameHash = blocksByHash[dependencyBlockHash]; - if (blockWithSameHash !== block.getDependency()) { - block.setAlias(blockWithSameHash); - } - } - continue; - } - - const blockHashBuf = block.computeHash(); - const blockHash = ethUtil.bufferToHex(blockHashBuf); - if (!(blockHash in blocksByHash)) { - blocksByHash[blockHash] = block; - } - } - } - - public toHexString(): string { - if (this._root === undefined) { - throw new Error('expected root'); - } - - if (this._rules.optimize) { - this.optimize(); - } - - const offsetQueue = Calldata._createQueue(this._root); - let block: CalldataBlock | undefined; - let offset = 0; - for (block = offsetQueue.pop(); block !== undefined; block = offsetQueue.pop()) { - block.setOffset(offset); - offset += block.getSizeInBytes(); - } - - const hexValue = this._rules.annotate ? this._generateAnnotatedHexString() : this._generateCondensedHexString(); - return hexValue; - } - - public getSelectorHex(): string { - return this._selector; - } - - public getSizeInBytes(): number { - return this._sizeInBytes; - } - - public setRoot(block: CalldataBlock): void { - this._root = block; - this._sizeInBytes += block.getSizeInBytes(); - } - - public setSelector(selector: string): void { - this._selector = selector.startsWith('0x') ? selector : `$0x${selector}`; - if (this._selector.length !== Constants.HEX_SELECTOR_LENGTH_IN_CHARS) { - throw new Error(`Invalid selector '${this._selector}'`); - } - this._sizeInBytes += Constants.HEX_SELECTOR_LENGTH_IN_BYTES; // @TODO: Used to be += 8. Bad? - } - - private _generateAnnotatedHexString(): string { - let hexValue = `${this._selector}`; - if (this._root === undefined) { - throw new Error('expected root'); - } - - const valueQueue = Calldata._createQueue(this._root); - - let block: CalldataBlock | undefined; - let offset = 0; - const functionBlock = valueQueue.peek(); - const functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); - for (block = valueQueue.pop(); block !== undefined; block = valueQueue.pop()) { - // Process each block 1 word at a time - const size = block.getSizeInBytes(); - const name = block.getName(); - const parentName = block.getParentName(); - const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, ''); - - // Current offset - let offsetStr = ''; - - // If this block is empty then it's a newline - const offsetPadding = 10; - const valuePadding = 74; - const namePadding = 80; - const evmWordStartIndex = 0; - const emptySize = 0; - let value = ''; - let nameStr = ''; - let line = ''; - if (size === emptySize) { - offsetStr = ' '.repeat(offsetPadding); - value = ' '.repeat(valuePadding); - nameStr = `### ${prettyName.padEnd(namePadding)}`; - line = `\n${offsetStr}${value}${nameStr}`; - } else { - offsetStr = `0x${offset.toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); - value = ethUtil - .stripHexPrefix( - ethUtil.bufferToHex( - block.toBuffer().slice(evmWordStartIndex, Constants.EVM_WORD_WIDTH_IN_BYTES), - ), - ) - .padEnd(valuePadding); - if (block instanceof MemberCalldataBlock) { - nameStr = `### ${prettyName.padEnd(namePadding)}`; - line = `\n${offsetStr}${value}${nameStr}`; - } else { - nameStr = ` ${prettyName.padEnd(namePadding)}`; - line = `${offsetStr}${value}${nameStr}`; - } - } - - for (let j = Constants.EVM_WORD_WIDTH_IN_BYTES; j < size; j += Constants.EVM_WORD_WIDTH_IN_BYTES) { - offsetStr = `0x${(offset + j).toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); - value = ethUtil - .stripHexPrefix( - ethUtil.bufferToHex(block.toBuffer().slice(j, j + Constants.EVM_WORD_WIDTH_IN_BYTES)), - ) - .padEnd(valuePadding); - nameStr = ' '.repeat(namePadding); - line = `${line}\n${offsetStr}${value}${nameStr}`; - } - - // Append to hex value - hexValue = `${hexValue}\n${line}`; - offset += size; - } - - return hexValue; - } - - private _generateCondensedHexString(): string { - const selectorBuffer = ethUtil.toBuffer(this._selector); - if (this._root === undefined) { - throw new Error('expected root'); - } - - const valueQueue = Calldata._createQueue(this._root); - const valueBufs: Buffer[] = [selectorBuffer]; - let block: CalldataBlock | undefined; - for (block = valueQueue.pop(); block !== undefined; block = valueQueue.pop()) { - valueBufs.push(block.toBuffer()); - } - - const combinedBuffers = Buffer.concat(valueBufs); - const hexValue = ethUtil.bufferToHex(combinedBuffers); - return hexValue; - } -} - -export class RawCalldata { - private static readonly _INITIAL_OFFSET = 0; - private readonly _value: Buffer; - private readonly _selector: string; - private readonly _scopes: Queue; - private _offset: number; // tracks current offset into raw calldata; used for parsing - - constructor(value: string | Buffer, hasSelectorPrefix: boolean = true) { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Expected raw calldata to start with '0x'`); - } - const valueBuf = ethUtil.toBuffer(value); - if (hasSelectorPrefix) { - this._selector = ethUtil.bufferToHex( - valueBuf.slice(Constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, Constants.HEX_SELECTOR_LENGTH_IN_BYTES), - ); - this._value = valueBuf.slice(Constants.HEX_SELECTOR_LENGTH_IN_BYTES); // disregard selector - } else { - this._selector = '0x'; - this._value = valueBuf; - } - - this._scopes = new Queue(); - this._scopes.push(RawCalldata._INITIAL_OFFSET); - this._offset = RawCalldata._INITIAL_OFFSET; - } - - public popBytes(lengthInBytes: number): Buffer { - const value = this._value.slice(this._offset, this._offset + lengthInBytes); - this.setOffset(this._offset + lengthInBytes); - return value; - } - - public popWord(): Buffer { - const wordInBytes = 32; - return this.popBytes(wordInBytes); - } - - public popWords(length: number): Buffer { - const wordInBytes = 32; - return this.popBytes(length * wordInBytes); - } - - public readBytes(from: number, to: number): Buffer { - const value = this._value.slice(from, to); - return value; - } - - public setOffset(offsetInBytes: number): void { - this._offset = offsetInBytes; - } - - public startScope(): void { - this._scopes.pushFront(this._offset); - } - - public endScope(): void { - this._scopes.pop(); - } - - public getOffset(): number { - return this._offset; - } - - public toAbsoluteOffset(relativeOffset: number): number { - const scopeOffset = this._scopes.peek(); - if (scopeOffset === undefined) { - throw new Error(`Tried to access undefined scope.`); - } - const absoluteOffset = relativeOffset + scopeOffset; - return absoluteOffset; - } - - public getSelector(): string { - return this._selector; - } -} diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts new file mode 100644 index 000000000..5ac4c1fe7 --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -0,0 +1,224 @@ + +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import * as Constants from '../constants'; +import { Queue } from '../utils/queue'; +import { EncodingRules } from '../utils/rules'; + +import { CalldataBlock } from './calldata_block'; +import * as CalldataBlocks from './calldata_blocks'; + +export class Calldata { + private readonly _rules: EncodingRules; + private _selector: string; + private _sizeInBytes: number; + private _root: CalldataBlock | undefined; + + private static _createQueue(block: CalldataBlock): Queue { + const blockQueue = new Queue(); + + // Base Case + if (!(block instanceof CalldataBlocks.MemberCalldataBlock)) { + blockQueue.push(block); + return blockQueue; + } + + // This is a Member Block + const memberBlock = block; + _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { + if (member instanceof CalldataBlocks.MemberCalldataBlock) { + blockQueue.mergeFront(Calldata._createQueue(member)); + } else { + blockQueue.pushFront(member); + } + }); + + // Children + _.each(memberBlock.getMembers(), (member: CalldataBlock) => { + if (member instanceof CalldataBlocks.DependentCalldataBlock && member.getAlias() === undefined) { + const dependency = member.getDependency(); + if (dependency instanceof CalldataBlocks.MemberCalldataBlock) { + blockQueue.merge(Calldata._createQueue(dependency)); + } else { + blockQueue.push(dependency); + } + } + }); + + blockQueue.pushFront(memberBlock); + return blockQueue; + } + + public constructor(rules: EncodingRules) { + this._rules = rules; + this._selector = ''; + this._sizeInBytes = 0; + this._root = undefined; + } + + public optimize(): void { + if (this._root === undefined) { + throw new Error('expected root'); + } + + const blocksByHash: { [key: string]: CalldataBlock } = {}; + + // 1. Create a queue of subtrees by hash + // Note that they are ordered the same as + const subtreeQueue = Calldata._createQueue(this._root); + let block: CalldataBlock | undefined; + for (block = subtreeQueue.popBack(); block !== undefined; block = subtreeQueue.popBack()) { + if (block instanceof CalldataBlocks.DependentCalldataBlock) { + const dependencyBlockHashBuf = block.getDependency().computeHash(); + const dependencyBlockHash = ethUtil.bufferToHex(dependencyBlockHashBuf); + if (dependencyBlockHash in blocksByHash) { + const blockWithSameHash = blocksByHash[dependencyBlockHash]; + if (blockWithSameHash !== block.getDependency()) { + block.setAlias(blockWithSameHash); + } + } + continue; + } + + const blockHashBuf = block.computeHash(); + const blockHash = ethUtil.bufferToHex(blockHashBuf); + if (!(blockHash in blocksByHash)) { + blocksByHash[blockHash] = block; + } + } + } + + public toHexString(): string { + if (this._root === undefined) { + throw new Error('expected root'); + } + + if (this._rules.optimize) { + this.optimize(); + } + + const offsetQueue = Calldata._createQueue(this._root); + let block: CalldataBlock | undefined; + let offset = 0; + for (block = offsetQueue.pop(); block !== undefined; block = offsetQueue.pop()) { + block.setOffset(offset); + offset += block.getSizeInBytes(); + } + + const hexValue = this._rules.annotate ? this._generateAnnotatedHexString() : this._generateCondensedHexString(); + return hexValue; + } + + public getSelectorHex(): string { + return this._selector; + } + + public getSizeInBytes(): number { + return this._sizeInBytes; + } + + public setRoot(block: CalldataBlock): void { + this._root = block; + this._sizeInBytes += block.getSizeInBytes(); + } + + public setSelector(selector: string): void { + this._selector = selector.startsWith('0x') ? selector : `$0x${selector}`; + if (this._selector.length !== Constants.HEX_SELECTOR_LENGTH_IN_CHARS) { + throw new Error(`Invalid selector '${this._selector}'`); + } + this._sizeInBytes += Constants.HEX_SELECTOR_LENGTH_IN_BYTES; // @TODO: Used to be += 8. Bad? + } + + private _generateAnnotatedHexString(): string { + let hexValue = `${this._selector}`; + if (this._root === undefined) { + throw new Error('expected root'); + } + + const valueQueue = Calldata._createQueue(this._root); + + let block: CalldataBlock | undefined; + let offset = 0; + const functionBlock = valueQueue.peek(); + const functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); + for (block = valueQueue.pop(); block !== undefined; block = valueQueue.pop()) { + // Process each block 1 word at a time + const size = block.getSizeInBytes(); + const name = block.getName(); + const parentName = block.getParentName(); + const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, ''); + + // Current offset + let offsetStr = ''; + + // If this block is empty then it's a newline + const offsetPadding = 10; + const valuePadding = 74; + const namePadding = 80; + const evmWordStartIndex = 0; + const emptySize = 0; + let value = ''; + let nameStr = ''; + let line = ''; + if (size === emptySize) { + offsetStr = ' '.repeat(offsetPadding); + value = ' '.repeat(valuePadding); + nameStr = `### ${prettyName.padEnd(namePadding)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + offsetStr = `0x${offset.toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); + value = ethUtil + .stripHexPrefix( + ethUtil.bufferToHex( + block.toBuffer().slice(evmWordStartIndex, Constants.EVM_WORD_WIDTH_IN_BYTES), + ), + ) + .padEnd(valuePadding); + if (block instanceof CalldataBlocks.MemberCalldataBlock) { + nameStr = `### ${prettyName.padEnd(namePadding)}`; + line = `\n${offsetStr}${value}${nameStr}`; + } else { + nameStr = ` ${prettyName.padEnd(namePadding)}`; + line = `${offsetStr}${value}${nameStr}`; + } + } + + for (let j = Constants.EVM_WORD_WIDTH_IN_BYTES; j < size; j += Constants.EVM_WORD_WIDTH_IN_BYTES) { + offsetStr = `0x${(offset + j).toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); + value = ethUtil + .stripHexPrefix( + ethUtil.bufferToHex(block.toBuffer().slice(j, j + Constants.EVM_WORD_WIDTH_IN_BYTES)), + ) + .padEnd(valuePadding); + nameStr = ' '.repeat(namePadding); + line = `${line}\n${offsetStr}${value}${nameStr}`; + } + + // Append to hex value + hexValue = `${hexValue}\n${line}`; + offset += size; + } + + return hexValue; + } + + private _generateCondensedHexString(): string { + const selectorBuffer = ethUtil.toBuffer(this._selector); + if (this._root === undefined) { + throw new Error('expected root'); + } + + const valueQueue = Calldata._createQueue(this._root); + const valueBufs: Buffer[] = [selectorBuffer]; + let block: CalldataBlock | undefined; + for (block = valueQueue.pop(); block !== undefined; block = valueQueue.pop()) { + valueBufs.push(block.toBuffer()); + } + + const combinedBuffers = Buffer.concat(valueBufs); + const hexValue = ethUtil.bufferToHex(combinedBuffers); + return hexValue; + } +} diff --git a/packages/utils/src/abi_encoder/calldata/calldata_block.ts b/packages/utils/src/abi_encoder/calldata/calldata_block.ts new file mode 100644 index 000000000..35bd994e5 --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/calldata_block.ts @@ -0,0 +1,77 @@ +import * as ethUtil from 'ethereumjs-util'; + +export abstract class CalldataBlock { + private readonly _signature: string; + private readonly _parentName: string; + private _name: string; + private _offsetInBytes: number; + private _headerSizeInBytes: number; + private _bodySizeInBytes: number; + + constructor( + name: string, + signature: string, + parentName: string, + headerSizeInBytes: number, + bodySizeInBytes: number, + ) { + this._name = name; + this._signature = signature; + this._parentName = parentName; + this._offsetInBytes = 0; + this._headerSizeInBytes = headerSizeInBytes; + this._bodySizeInBytes = bodySizeInBytes; + } + + protected _setHeaderSize(headerSizeInBytes: number): void { + this._headerSizeInBytes = headerSizeInBytes; + } + + protected _setBodySize(bodySizeInBytes: number): void { + this._bodySizeInBytes = bodySizeInBytes; + } + + protected _setName(name: string): void { + this._name = name; + } + + public getName(): string { + return this._name; + } + + public getParentName(): string { + return this._parentName; + } + + public getSignature(): string { + return this._signature; + } + public getHeaderSizeInBytes(): number { + return this._headerSizeInBytes; + } + + public getBodySizeInBytes(): number { + return this._bodySizeInBytes; + } + + public getSizeInBytes(): number { + return this.getHeaderSizeInBytes() + this.getBodySizeInBytes(); + } + + public getOffsetInBytes(): number { + return this._offsetInBytes; + } + + public setOffset(offsetInBytes: number): void { + this._offsetInBytes = offsetInBytes; + } + + public computeHash(): Buffer { + const rawData = this.getRawData(); + const hash = ethUtil.sha3(rawData); + return hash; + } + + public abstract toBuffer(): Buffer; + public abstract getRawData(): Buffer; +} diff --git a/packages/utils/src/abi_encoder/calldata/calldata_blocks.ts b/packages/utils/src/abi_encoder/calldata/calldata_blocks.ts new file mode 100644 index 000000000..37660ef21 --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/calldata_blocks.ts @@ -0,0 +1,3 @@ +export * from './dependent_calldata_block'; +export * from './member_calldata_block'; +export * from './payload_calldata_block'; \ No newline at end of file diff --git a/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts b/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts new file mode 100644 index 000000000..d6870ec0b --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts @@ -0,0 +1,59 @@ +import * as ethUtil from 'ethereumjs-util'; + +import * as Constants from '../constants'; + +import { CalldataBlock } from './calldata_block'; + +export class DependentCalldataBlock extends CalldataBlock { + public static readonly RAW_DATA_START = new Buffer('<'); + public static readonly RAW_DATA_END = new Buffer('>'); + private static readonly _DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + private static readonly _EMPTY_HEADER_SIZE = 0; + private readonly _parent: CalldataBlock; + private readonly _dependency: CalldataBlock; + private _aliasFor: CalldataBlock | undefined; + + constructor(name: string, signature: string, parentName: string, dependency: CalldataBlock, parent: CalldataBlock) { + const headerSizeInBytes = DependentCalldataBlock._EMPTY_HEADER_SIZE; + const bodySizeInBytes = DependentCalldataBlock._DEPENDENT_PAYLOAD_SIZE_IN_BYTES; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); + this._parent = parent; + this._dependency = dependency; + this._aliasFor = undefined; + } + + public toBuffer(): Buffer { + const destinationOffset = + this._aliasFor !== undefined ? this._aliasFor.getOffsetInBytes() : this._dependency.getOffsetInBytes(); + const parentOffset = this._parent.getOffsetInBytes(); + const parentHeaderSize = this._parent.getHeaderSizeInBytes(); + const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); + const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(Constants.HEX_BASE)}`); + const evmWordWidthInBytes = 32; + const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); + return pointerBufPadded; + } + + public getDependency(): CalldataBlock { + return this._dependency; + } + + public setAlias(block: CalldataBlock): void { + this._aliasFor = block; + this._setName(`${this.getName()} (alias for ${block.getName()})`); + } + + public getAlias(): CalldataBlock | undefined { + return this._aliasFor; + } + + public getRawData(): Buffer { + const dependencyRawData = this._dependency.getRawData(); + const rawDataComponents: Buffer[] = []; + rawDataComponents.push(DependentCalldataBlock.RAW_DATA_START); + rawDataComponents.push(dependencyRawData); + rawDataComponents.push(DependentCalldataBlock.RAW_DATA_END); + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } +} \ No newline at end of file diff --git a/packages/utils/src/abi_encoder/calldata/index.ts b/packages/utils/src/abi_encoder/calldata/index.ts new file mode 100644 index 000000000..0e1f57a2d --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/index.ts @@ -0,0 +1,6 @@ +export * from './calldata_block'; +export * from './dependent_calldata_block'; +export * from './payload_calldata_block'; +export * from './member_calldata_block'; +export * from './calldata'; +export * from './raw_calldata'; \ No newline at end of file diff --git a/packages/utils/src/abi_encoder/calldata/member_calldata_block.ts b/packages/utils/src/abi_encoder/calldata/member_calldata_block.ts new file mode 100644 index 000000000..e9bcb2f34 --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/member_calldata_block.ts @@ -0,0 +1,48 @@ +import * as _ from 'lodash'; + +import { CalldataBlock } from './calldata_block'; + +export class MemberCalldataBlock extends CalldataBlock { + private _header: Buffer | undefined; + private _members: CalldataBlock[]; + + constructor(name: string, signature: string, parentName: string) { + super(name, signature, parentName, 0, 0); + this._members = []; + this._header = undefined; + } + + public getRawData(): Buffer { + const rawDataComponents: Buffer[] = []; + if (this._header !== undefined) { + rawDataComponents.push(this._header); + } + _.each(this._members, (member: CalldataBlock) => { + const memberBuffer = member.getRawData(); + rawDataComponents.push(memberBuffer); + }); + + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } + + public setMembers(members: CalldataBlock[]): void { + this._members = members; + } + + public setHeader(header: Buffer): void { + this._setHeaderSize(header.byteLength); + this._header = header; + } + + public toBuffer(): Buffer { + if (this._header !== undefined) { + return this._header; + } + return new Buffer(''); + } + + public getMembers(): CalldataBlock[] { + return this._members; + } +} \ No newline at end of file diff --git a/packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts b/packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts new file mode 100644 index 000000000..81a88bc19 --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts @@ -0,0 +1,20 @@ +import { CalldataBlock } from './calldata_block'; + +export class PayloadCalldataBlock extends CalldataBlock { + private readonly _payload: Buffer; + + constructor(name: string, signature: string, parentName: string, payload: Buffer) { + const headerSizeInBytes = 0; + const bodySizeInBytes = payload.byteLength; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); + this._payload = payload; + } + + public toBuffer(): Buffer { + return this._payload; + } + + public getRawData(): Buffer { + return this._payload; + } +} \ No newline at end of file diff --git a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts new file mode 100644 index 000000000..8b946ee4b --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts @@ -0,0 +1,82 @@ +import * as ethUtil from 'ethereumjs-util'; + +import * as Constants from '../constants'; +import { Queue } from '../utils/queue'; + +export class RawCalldata { + private static readonly _INITIAL_OFFSET = 0; + private readonly _value: Buffer; + private readonly _selector: string; + private readonly _scopes: Queue; + private _offset: number; // tracks current offset into raw calldata; used for parsing + + constructor(value: string | Buffer, hasSelectorPrefix: boolean = true) { + if (typeof value === 'string' && !value.startsWith('0x')) { + throw new Error(`Expected raw calldata to start with '0x'`); + } + const valueBuf = ethUtil.toBuffer(value); + if (hasSelectorPrefix) { + this._selector = ethUtil.bufferToHex( + valueBuf.slice(Constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, Constants.HEX_SELECTOR_LENGTH_IN_BYTES), + ); + this._value = valueBuf.slice(Constants.HEX_SELECTOR_LENGTH_IN_BYTES); // disregard selector + } else { + this._selector = '0x'; + this._value = valueBuf; + } + + this._scopes = new Queue(); + this._scopes.push(RawCalldata._INITIAL_OFFSET); + this._offset = RawCalldata._INITIAL_OFFSET; + } + + public popBytes(lengthInBytes: number): Buffer { + const value = this._value.slice(this._offset, this._offset + lengthInBytes); + this.setOffset(this._offset + lengthInBytes); + return value; + } + + public popWord(): Buffer { + const wordInBytes = 32; + return this.popBytes(wordInBytes); + } + + public popWords(length: number): Buffer { + const wordInBytes = 32; + return this.popBytes(length * wordInBytes); + } + + public readBytes(from: number, to: number): Buffer { + const value = this._value.slice(from, to); + return value; + } + + public setOffset(offsetInBytes: number): void { + this._offset = offsetInBytes; + } + + public startScope(): void { + this._scopes.pushFront(this._offset); + } + + public endScope(): void { + this._scopes.pop(); + } + + public getOffset(): number { + return this._offset; + } + + public toAbsoluteOffset(relativeOffset: number): number { + const scopeOffset = this._scopes.peek(); + if (scopeOffset === undefined) { + throw new Error(`Tried to access undefined scope.`); + } + const absoluteOffset = relativeOffset + scopeOffset; + return absoluteOffset; + } + + public getSelector(): string { + return this._selector; + } +} \ No newline at end of file diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index 9890619e5..0fbb9165a 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -9,14 +9,14 @@ import * as Constants from './constants'; import { Calldata, CalldataBlock, - DecodingRules, DependentCalldataBlock, - EncodingRules, MemberCalldataBlock, PayloadCalldataBlock, RawCalldata, } from './calldata'; +import { DecodingRules, EncodingRules } from './utils/rules'; + export interface DataTypeFactory { create: (dataItem: DataItem, parentDataType?: DataType) => DataType; mapDataItemToDataType: (dataItem: DataItem) => DataType; diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts index e8e717bf1..c4e6ee93a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/method.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts @@ -2,9 +2,10 @@ import { DataItem, MethodAbi } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { DecodingRules, EncodingRules, RawCalldata } from '../calldata'; +import { RawCalldata } from '../calldata'; import * as Constants from '../constants'; import { DataType, DataTypeFactory, MemberDataType } from '../data_type'; +import { DecodingRules, EncodingRules } from '../utils/rules'; import { StaticBytes } from './static_bytes'; import { Tuple } from './tuple'; diff --git a/packages/utils/src/abi_encoder/index.ts b/packages/utils/src/abi_encoder/index.ts index a62569fab..ea037b40a 100644 --- a/packages/utils/src/abi_encoder/index.ts +++ b/packages/utils/src/abi_encoder/index.ts @@ -1,2 +1,2 @@ -export { EncodingRules, DecodingRules } from './calldata'; +export { EncodingRules, DecodingRules } from './utils/rules'; export * from './evm_data_type_factory'; diff --git a/packages/utils/src/abi_encoder/utils/queue.ts b/packages/utils/src/abi_encoder/utils/queue.ts new file mode 100644 index 000000000..2e27afd45 --- /dev/null +++ b/packages/utils/src/abi_encoder/utils/queue.ts @@ -0,0 +1,39 @@ +export class Queue { + private _store: T[] = []; + + public push(val: T): void { + this._store.push(val); + } + + public pushFront(val: T): void { + this._store.unshift(val); + } + + public pop(): T | undefined { + return this._store.shift(); + } + + public popBack(): T | undefined { + if (this._store.length === 0) { + return undefined; + } + const backElement = this._store.splice(-1, 1)[0]; + return backElement; + } + + public merge(q: Queue): void { + this._store = this._store.concat(q._store); + } + + public mergeFront(q: Queue): void { + this._store = q._store.concat(this._store); + } + + public getStore(): T[] { + return this._store; + } + + public peek(): T | undefined { + return this._store.length >= 0 ? this._store[0] : undefined; + } +} \ No newline at end of file diff --git a/packages/utils/src/abi_encoder/utils/rules.ts b/packages/utils/src/abi_encoder/utils/rules.ts new file mode 100644 index 000000000..cf97ef6da --- /dev/null +++ b/packages/utils/src/abi_encoder/utils/rules.ts @@ -0,0 +1,8 @@ +export interface DecodingRules { + structsAsObjects: boolean; +} + +export interface EncodingRules { + optimize?: boolean; + annotate?: boolean; +} \ No newline at end of file -- cgit v1.2.3 From d4d917f350fb6916df50f3aa5cf09ce68b316aa1 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 13:25:49 -0800 Subject: Ran prettier --- packages/utils/src/abi_encoder/calldata/calldata.ts | 1 - packages/utils/src/abi_encoder/calldata/calldata_blocks.ts | 2 +- packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts | 2 +- packages/utils/src/abi_encoder/calldata/index.ts | 2 +- packages/utils/src/abi_encoder/calldata/member_calldata_block.ts | 2 +- packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts | 2 +- packages/utils/src/abi_encoder/calldata/raw_calldata.ts | 2 +- packages/utils/src/abi_encoder/evm_data_type_factory.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/address.ts | 2 +- packages/utils/src/abi_encoder/utils/queue.ts | 2 +- packages/utils/src/abi_encoder/utils/rules.ts | 2 +- 11 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index 5ac4c1fe7..1abf1b681 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -1,4 +1,3 @@ - import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; diff --git a/packages/utils/src/abi_encoder/calldata/calldata_blocks.ts b/packages/utils/src/abi_encoder/calldata/calldata_blocks.ts index 37660ef21..8d4e7d7ca 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata_blocks.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata_blocks.ts @@ -1,3 +1,3 @@ export * from './dependent_calldata_block'; export * from './member_calldata_block'; -export * from './payload_calldata_block'; \ No newline at end of file +export * from './payload_calldata_block'; diff --git a/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts b/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts index d6870ec0b..4aec5eabc 100644 --- a/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts +++ b/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts @@ -56,4 +56,4 @@ export class DependentCalldataBlock extends CalldataBlock { const rawData = Buffer.concat(rawDataComponents); return rawData; } -} \ No newline at end of file +} diff --git a/packages/utils/src/abi_encoder/calldata/index.ts b/packages/utils/src/abi_encoder/calldata/index.ts index 0e1f57a2d..2c786cd8d 100644 --- a/packages/utils/src/abi_encoder/calldata/index.ts +++ b/packages/utils/src/abi_encoder/calldata/index.ts @@ -3,4 +3,4 @@ export * from './dependent_calldata_block'; export * from './payload_calldata_block'; export * from './member_calldata_block'; export * from './calldata'; -export * from './raw_calldata'; \ No newline at end of file +export * from './raw_calldata'; diff --git a/packages/utils/src/abi_encoder/calldata/member_calldata_block.ts b/packages/utils/src/abi_encoder/calldata/member_calldata_block.ts index e9bcb2f34..c35beb8de 100644 --- a/packages/utils/src/abi_encoder/calldata/member_calldata_block.ts +++ b/packages/utils/src/abi_encoder/calldata/member_calldata_block.ts @@ -45,4 +45,4 @@ export class MemberCalldataBlock extends CalldataBlock { public getMembers(): CalldataBlock[] { return this._members; } -} \ No newline at end of file +} diff --git a/packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts b/packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts index 81a88bc19..0420b01d8 100644 --- a/packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts +++ b/packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts @@ -17,4 +17,4 @@ export class PayloadCalldataBlock extends CalldataBlock { public getRawData(): Buffer { return this._payload; } -} \ No newline at end of file +} diff --git a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts index 8b946ee4b..b7bd35737 100644 --- a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts @@ -79,4 +79,4 @@ export class RawCalldata { public getSelector(): string { return this._selector; } -} \ No newline at end of file +} diff --git a/packages/utils/src/abi_encoder/evm_data_type_factory.ts b/packages/utils/src/abi_encoder/evm_data_type_factory.ts index 0f8dfb4a3..a3e1d404e 100644 --- a/packages/utils/src/abi_encoder/evm_data_type_factory.ts +++ b/packages/utils/src/abi_encoder/evm_data_type_factory.ts @@ -120,5 +120,5 @@ export class EvmDataTypeFactory implements DataTypeFactory { return pointer; } - private constructor() { } + private constructor() {} } diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 4bd992cab..3e2c9e78c 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -13,7 +13,7 @@ export class Address extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _ADDRESS_SIZE_IN_BYTES = 20; private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = Constants.EVM_WORD_WIDTH_IN_BYTES - - Address._ADDRESS_SIZE_IN_BYTES; + Address._ADDRESS_SIZE_IN_BYTES; public static matchType(type: string): boolean { return type === 'address'; diff --git a/packages/utils/src/abi_encoder/utils/queue.ts b/packages/utils/src/abi_encoder/utils/queue.ts index 2e27afd45..3309d8ba2 100644 --- a/packages/utils/src/abi_encoder/utils/queue.ts +++ b/packages/utils/src/abi_encoder/utils/queue.ts @@ -36,4 +36,4 @@ export class Queue { public peek(): T | undefined { return this._store.length >= 0 ? this._store[0] : undefined; } -} \ No newline at end of file +} diff --git a/packages/utils/src/abi_encoder/utils/rules.ts b/packages/utils/src/abi_encoder/utils/rules.ts index cf97ef6da..31471e97a 100644 --- a/packages/utils/src/abi_encoder/utils/rules.ts +++ b/packages/utils/src/abi_encoder/utils/rules.ts @@ -5,4 +5,4 @@ export interface DecodingRules { export interface EncodingRules { optimize?: boolean; annotate?: boolean; -} \ No newline at end of file +} -- cgit v1.2.3 From a47901370bc69d6247e941c569bc9fe824516db9 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 13:40:26 -0800 Subject: Ran prettier --- packages/utils/src/abi_encoder/data_type.ts | 361 --------------------- .../utils/src/abi_encoder/evm_data_type_factory.ts | 2 +- .../src/abi_encoder/evm_data_types/address.ts | 4 +- .../utils/src/abi_encoder/evm_data_types/array.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/bool.ts | 2 +- .../abi_encoder/evm_data_types/dynamic_bytes.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/int.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/method.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/number.ts | 2 +- .../src/abi_encoder/evm_data_types/pointer.ts | 2 +- .../src/abi_encoder/evm_data_types/static_bytes.ts | 4 +- .../utils/src/abi_encoder/evm_data_types/string.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/tuple.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/uint.ts | 2 +- 14 files changed, 15 insertions(+), 376 deletions(-) delete mode 100644 packages/utils/src/abi_encoder/data_type.ts diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts deleted file mode 100644 index 0fbb9165a..000000000 --- a/packages/utils/src/abi_encoder/data_type.ts +++ /dev/null @@ -1,361 +0,0 @@ -import { DataItem } from 'ethereum-types'; -import * as ethUtil from 'ethereumjs-util'; -import * as _ from 'lodash'; - -import { BigNumber } from '../configured_bignumber'; - -import * as Constants from './constants'; - -import { - Calldata, - CalldataBlock, - DependentCalldataBlock, - MemberCalldataBlock, - PayloadCalldataBlock, - RawCalldata, -} from './calldata'; - -import { DecodingRules, EncodingRules } from './utils/rules'; - -export interface DataTypeFactory { - create: (dataItem: DataItem, parentDataType?: DataType) => DataType; - mapDataItemToDataType: (dataItem: DataItem) => DataType; -} - -export interface DataTypeStaticInterface { - matchType: (type: string) => boolean; - encodeValue: (value: any) => Buffer; - decodeValue: (rawCalldata: RawCalldata) => any; -} - -export abstract class DataType { - private static readonly _DEFAULT_ENCODING_RULES: EncodingRules = { optimize: false, annotate: false }; - private static readonly _DEFAULT_DECODING_RULES: DecodingRules = { structsAsObjects: false }; - private readonly _dataItem: DataItem; - private readonly _factory: DataTypeFactory; - - constructor(dataItem: DataItem, factory: DataTypeFactory) { - this._dataItem = dataItem; - this._factory = factory; - } - - public getDataItem(): DataItem { - return this._dataItem; - } - - public getFactory(): DataTypeFactory { - return this._factory; - } - - public encode(value: any, rules?: EncodingRules, selector?: string): string { - const rules_ = rules ? rules : DataType._DEFAULT_ENCODING_RULES; - const calldata = new Calldata(rules_); - if (selector) { - calldata.setSelector(selector); - } - const block = this.generateCalldataBlock(value); - calldata.setRoot(block); // @TODO CHANGE - const calldataHex = calldata.toHexString(); - return calldataHex; - } - - public decode(calldata: string, rules?: DecodingRules, hasSelector: boolean = false): any { - const rawCalldata = new RawCalldata(calldata, hasSelector); - const rules_ = rules ? rules : DataType._DEFAULT_DECODING_RULES; - const value = this.generateValue(rawCalldata, rules_); - return value; - } - - public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; - public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any; - public abstract getSignature(): string; - public abstract isStatic(): boolean; -} - -export abstract class PayloadDataType extends DataType { - protected _hasConstantSize: boolean; - - public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { - super(dataItem, factory); - this._hasConstantSize = hasConstantSize; - } - - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { - const encodedValue = this.encodeValue(value); - const name = this.getDataItem().name; - const signature = this.getSignature(); - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const block = new PayloadCalldataBlock(name, signature, parentName, encodedValue); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any { - const value = this.decodeValue(calldata); - return value; - } - - public isStatic(): boolean { - return this._hasConstantSize; - } - - public abstract encodeValue(value: any): Buffer; - public abstract decodeValue(calldata: RawCalldata): any; -} - -export abstract class DependentDataType extends DataType { - protected _dependency: DataType; - protected _parent: DataType; - private readonly _isStatic: boolean; - - public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { - super(dataItem, factory); - this._dependency = dependency; - this._parent = parent; - this._isStatic = true; - } - - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { - if (parentBlock === undefined) { - throw new Error(`DependentDataType requires a parent block to generate its block`); - } - const dependencyBlock = this._dependency.generateCalldataBlock(value, parentBlock); - const name = this.getDataItem().name; - const signature = this.getSignature(); - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const block = new DependentCalldataBlock(name, signature, parentName, dependencyBlock, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any { - const destinationOffsetBuf = calldata.popWord(); - const currentOffset = calldata.getOffset(); - const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), Constants.HEX_BASE); - const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); - calldata.setOffset(destinationOffsetAbsolute); - const value = this._dependency.generateValue(calldata, rules); - calldata.setOffset(currentOffset); - return value; - } - - public isStatic(): boolean { - return this._isStatic; - } -} - -export interface MemberMap { - [key: string]: number; -} - -export abstract class MemberDataType extends DataType { - protected readonly _arrayLength: number | undefined; - protected readonly _arrayElementType: string | undefined; - private readonly _memberMap: MemberMap; - private readonly _members: DataType[]; - private readonly _isArray: boolean; - - public constructor( - dataItem: DataItem, - factory: DataTypeFactory, - isArray: boolean = false, - arrayLength?: number, - arrayElementType?: string, - ) { - super(dataItem, factory); - this._memberMap = {}; - this._members = []; - this._isArray = isArray; - this._arrayLength = arrayLength; - this._arrayElementType = arrayElementType; - if (isArray && arrayLength !== undefined) { - [this._members, this._memberMap] = this._createMembersWithLength(dataItem, arrayLength); - } else if (!isArray) { - [this._members, this._memberMap] = this._createMembersWithKeys(dataItem); - } - } - - public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const block = - value instanceof Array - ? this._generateCalldataBlockFromArray(value, parentBlock) - : this._generateCalldataBlockFromObject(value, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { - let members = this._members; - if (this._isArray && this._arrayLength === undefined) { - const arrayLengthBuf = calldata.popWord(); - const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); - const hexBase = 16; - const arrayLength = new BigNumber(arrayLengthHex, hexBase); - - [members] = this._createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); - } - - calldata.startScope(); - let value: any[] | object; - if (rules.structsAsObjects && !this._isArray) { - value = {}; - _.each(this._memberMap, (idx: number, key: string) => { - const member = this._members[idx]; - const memberValue = member.generateValue(calldata, rules); - (value as { [key: string]: any })[key] = memberValue; - }); - } else { - value = []; - _.each(members, (member: DataType, idx: number) => { - const memberValue = member.generateValue(calldata, rules); - (value as any[]).push(memberValue); - }); - } - calldata.endScope(); - return value; - } - - public isStatic(): boolean { - /* For Tuple: - const isStaticTuple = this.children.length === 0; - return isStaticTuple; // @TODO: True in every case or only when dynamic data? - - For Array: - if isLengthDefined = false then this is false - - Otherwise if the first element is a Pointer then false - */ - - if (this._isArray && this._arrayLength === undefined) { - return false; - } - - // Search for dependent members - const dependentMember = _.find(this._members, (member: DataType) => { - return member instanceof DependentDataType; - }); - const isStatic = dependentMember === undefined; // static if we couldn't find a dependent member - return isStatic; - } - - protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { - // Sanity check length - if (this._arrayLength !== undefined && value.length !== this._arrayLength) { - throw new Error( - `Expected array of ${JSON.stringify( - this._arrayLength, - )} elements, but got array of length ${JSON.stringify(value.length)}`, - ); - } - - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( - this.getDataItem().name, - this.getSignature(), - parentName, - ); - - let members = this._members; - if (this._isArray && this._arrayLength === undefined) { - [members] = this._createMembersWithLength(this.getDataItem(), value.length); - - const lenBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(`0x${value.length.toString(Constants.HEX_BASE)}`), - Constants.EVM_WORD_WIDTH_IN_BYTES, - ); - methodBlock.setHeader(lenBuf); - } - - const memberBlocks: CalldataBlock[] = []; - _.each(members, (member: DataType, idx: number) => { - const block = member.generateCalldataBlock(value[idx], methodBlock); - memberBlocks.push(block); - }); - methodBlock.setMembers(memberBlocks); - return methodBlock; - } - - protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( - this.getDataItem().name, - this.getSignature(), - parentName, - ); - const memberBlocks: CalldataBlock[] = []; - const childMap = _.cloneDeep(this._memberMap); - _.forOwn(obj, (value: any, key: string) => { - if (!(key in childMap)) { - throw new Error( - `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`, - ); - } - const block = this._members[this._memberMap[key]].generateCalldataBlock(value, methodBlock); - memberBlocks.push(block); - delete childMap[key]; - }); - - if (Object.keys(childMap).length !== 0) { - throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); - } - - methodBlock.setMembers(memberBlocks); - return methodBlock; - } - - protected _computeSignatureOfMembers(): string { - // Compute signature of members - let signature = `(`; - _.each(this._members, (member: DataType, i: number) => { - signature += member.getSignature(); - if (i < this._members.length - 1) { - signature += ','; - } - }); - signature += ')'; - return signature; - } - - private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { - // Sanity check - if (dataItem.components === undefined) { - throw new Error(`Expected components`); - } - - const members: DataType[] = []; - const memberMap: MemberMap = {}; - _.each(dataItem.components, (memberItem: DataItem) => { - const childDataItem: DataItem = { - type: memberItem.type, - name: `${dataItem.name}.${memberItem.name}`, - }; - const components = memberItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[memberItem.name] = members.length; - members.push(child); - }); - - return [members, memberMap]; - } - - private _createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { - const members: DataType[] = []; - const memberMap: MemberMap = {}; - const range = _.range(length); - _.each(range, (idx: number) => { - const childDataItem: DataItem = { - type: this._arrayElementType ? this._arrayElementType : '', - name: `${dataItem.name}[${idx.toString(Constants.DEC_BASE)}]`, - }; - const components = dataItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[idx.toString(Constants.DEC_BASE)] = members.length; - members.push(child); - }); - - return [members, memberMap]; - } -} diff --git a/packages/utils/src/abi_encoder/evm_data_type_factory.ts b/packages/utils/src/abi_encoder/evm_data_type_factory.ts index a3e1d404e..5d37acad9 100644 --- a/packages/utils/src/abi_encoder/evm_data_type_factory.ts +++ b/packages/utils/src/abi_encoder/evm_data_type_factory.ts @@ -3,7 +3,7 @@ /* tslint:disable no-construct */ import { DataItem, MethodAbi } from 'ethereum-types'; -import { DataType, DataTypeFactory } from './data_type'; +import { DataType, DataTypeFactory } from './abstract_data_types'; import * as Impl from './evm_data_types'; export class Address extends Impl.Address { diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 3e2c9e78c..707e265f8 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -3,9 +3,9 @@ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; +import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../constants'; -import { DataTypeFactory, PayloadDataType } from '../data_type'; export class Address extends PayloadDataType { public static ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X = "Address must start with '0x'"; @@ -13,7 +13,7 @@ export class Address extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _ADDRESS_SIZE_IN_BYTES = 20; private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = Constants.EVM_WORD_WIDTH_IN_BYTES - - Address._ADDRESS_SIZE_IN_BYTES; + Address._ADDRESS_SIZE_IN_BYTES; public static matchType(type: string): boolean { return type === 'address'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts index 707af7c7e..f334282b8 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/array.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -1,7 +1,7 @@ import { DataItem } from 'ethereum-types'; +import { DataTypeFactory, MemberDataType } from '../abstract_data_types'; import * as Constants from '../constants'; -import { DataTypeFactory, MemberDataType } from '../data_type'; export class Array extends MemberDataType { private static readonly _matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index aee2727c7..4b9dd32b1 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -4,9 +4,9 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; +import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../constants'; -import { DataTypeFactory, PayloadDataType } from '../data_type'; export class Bool extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index 51165881a..5fca668e4 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -3,9 +3,9 @@ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; +import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../constants'; -import { DataTypeFactory, PayloadDataType } from '../data_type'; export class DynamicBytes extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index ba5b4cac9..ec41b9cfc 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -2,7 +2,7 @@ import { DataItem } from 'ethereum-types'; import { BigNumber } from '../../configured_bignumber'; -import { DataTypeFactory } from '../data_type'; +import { DataTypeFactory } from '../abstract_data_types'; import { Number } from './number'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts index c4e6ee93a..ce33755f1 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/method.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts @@ -2,9 +2,9 @@ import { DataItem, MethodAbi } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; +import { DataType, DataTypeFactory, MemberDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../constants'; -import { DataType, DataTypeFactory, MemberDataType } from '../data_type'; import { DecodingRules, EncodingRules } from '../utils/rules'; import { StaticBytes } from './static_bytes'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/number.ts b/packages/utils/src/abi_encoder/evm_data_types/number.ts index 17201362e..8ff5e9a6a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/number.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/number.ts @@ -3,9 +3,9 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; +import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../constants'; -import { DataTypeFactory, PayloadDataType } from '../data_type'; export abstract class Number extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; diff --git a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts index e0bd3509c..d4411df9b 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts @@ -1,6 +1,6 @@ import { DataItem } from 'ethereum-types'; -import { DataType, DataTypeFactory, DependentDataType } from '../data_type'; +import { DataType, DataTypeFactory, DependentDataType } from '../abstract_data_types'; export class Pointer extends DependentDataType { constructor(destDataType: DataType, parentDataType: DataType, dataTypeFactory: DataTypeFactory) { diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 309dca234..90e872c78 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -2,9 +2,9 @@ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; +import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../constants'; -import { DataTypeFactory, PayloadDataType } from '../data_type'; export class StaticBytes extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; @@ -46,7 +46,7 @@ export class StaticBytes extends PayloadDataType { if (valueBuf.byteLength > this._width) { throw new Error( `Tried to assign ${value} (${ - valueBuf.byteLength + valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); } else if (value.length % 2 !== 0) { diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index 96b36e735..c0bde8649 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -3,9 +3,9 @@ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; +import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../constants'; -import { DataTypeFactory, PayloadDataType } from '../data_type'; export class String extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts index 0db29c1eb..4a90e375a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts @@ -1,6 +1,6 @@ import { DataItem } from 'ethereum-types'; -import { DataTypeFactory, MemberDataType } from '../data_type'; +import { DataTypeFactory, MemberDataType } from '../abstract_data_types'; export class Tuple extends MemberDataType { private readonly _tupleSignature: string; diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index 86b31ab4c..ced3ef08b 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -2,7 +2,7 @@ import { DataItem } from 'ethereum-types'; import { BigNumber } from '../../configured_bignumber'; -import { DataTypeFactory } from '../data_type'; +import { DataTypeFactory } from '../abstract_data_types'; import { Number } from './number'; -- cgit v1.2.3 From fee67326adbbf745966803c935d5663c3ca7f52c Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 13:40:46 -0800 Subject: Merge above --- .../abi_encoder/abstract_data_types/data_type.ts | 51 +++++ .../abstract_data_types/dependent_data_type.ts | 50 +++++ .../src/abi_encoder/abstract_data_types/index.ts | 5 + .../abi_encoder/abstract_data_types/interfaces.ts | 16 ++ .../abstract_data_types/member_data_type.ts | 230 +++++++++++++++++++++ .../abstract_data_types/payload_data_type.ts | 38 ++++ 6 files changed, 390 insertions(+) create mode 100644 packages/utils/src/abi_encoder/abstract_data_types/data_type.ts create mode 100644 packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts create mode 100644 packages/utils/src/abi_encoder/abstract_data_types/index.ts create mode 100644 packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts create mode 100644 packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts create mode 100644 packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts new file mode 100644 index 000000000..a6adeb23b --- /dev/null +++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts @@ -0,0 +1,51 @@ +import { DataItem } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { Calldata, CalldataBlock, RawCalldata } from '../calldata'; +import { DecodingRules, EncodingRules } from '../utils/rules'; + +import { DataTypeFactory } from './interfaces'; + +export abstract class DataType { + private static readonly _DEFAULT_ENCODING_RULES: EncodingRules = { optimize: false, annotate: false }; + private static readonly _DEFAULT_DECODING_RULES: DecodingRules = { structsAsObjects: false }; + private readonly _dataItem: DataItem; + private readonly _factory: DataTypeFactory; + + constructor(dataItem: DataItem, factory: DataTypeFactory) { + this._dataItem = dataItem; + this._factory = factory; + } + + public getDataItem(): DataItem { + return this._dataItem; + } + + public getFactory(): DataTypeFactory { + return this._factory; + } + + public encode(value: any, rules?: EncodingRules, selector?: string): string { + const rules_ = rules ? rules : DataType._DEFAULT_ENCODING_RULES; + const calldata = new Calldata(rules_); + if (selector) { + calldata.setSelector(selector); + } + const block = this.generateCalldataBlock(value); + calldata.setRoot(block); // @TODO CHANGE + const calldataHex = calldata.toHexString(); + return calldataHex; + } + + public decode(calldata: string, rules?: DecodingRules, hasSelector: boolean = false): any { + const rawCalldata = new RawCalldata(calldata, hasSelector); + const rules_ = rules ? rules : DataType._DEFAULT_DECODING_RULES; + const value = this.generateValue(rawCalldata, rules_); + return value; + } + + public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock; + public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any; + public abstract getSignature(): string; + public abstract isStatic(): boolean; +} diff --git a/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts new file mode 100644 index 000000000..e9f390b22 --- /dev/null +++ b/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts @@ -0,0 +1,50 @@ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { CalldataBlock, DependentCalldataBlock, RawCalldata } from '../calldata'; +import * as Constants from '../constants'; +import { DecodingRules } from '../utils/rules'; + +import { DataType } from './data_type'; +import { DataTypeFactory } from './interfaces'; + +export abstract class DependentDataType extends DataType { + protected _dependency: DataType; + protected _parent: DataType; + private readonly _isStatic: boolean; + + public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { + super(dataItem, factory); + this._dependency = dependency; + this._parent = parent; + this._isStatic = true; + } + + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { + if (parentBlock === undefined) { + throw new Error(`DependentDataType requires a parent block to generate its block`); + } + const dependencyBlock = this._dependency.generateCalldataBlock(value, parentBlock); + const name = this.getDataItem().name; + const signature = this.getSignature(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const block = new DependentCalldataBlock(name, signature, parentName, dependencyBlock, parentBlock); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { + const destinationOffsetBuf = calldata.popWord(); + const currentOffset = calldata.getOffset(); + const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), Constants.HEX_BASE); + const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); + calldata.setOffset(destinationOffsetAbsolute); + const value = this._dependency.generateValue(calldata, rules); + calldata.setOffset(currentOffset); + return value; + } + + public isStatic(): boolean { + return this._isStatic; + } +} diff --git a/packages/utils/src/abi_encoder/abstract_data_types/index.ts b/packages/utils/src/abi_encoder/abstract_data_types/index.ts new file mode 100644 index 000000000..9ad568134 --- /dev/null +++ b/packages/utils/src/abi_encoder/abstract_data_types/index.ts @@ -0,0 +1,5 @@ +export * from './interfaces'; +export * from './data_type'; +export * from './dependent_data_type'; +export * from './member_data_type'; +export * from './payload_data_type'; diff --git a/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts b/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts new file mode 100644 index 000000000..2ae92659c --- /dev/null +++ b/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts @@ -0,0 +1,16 @@ +import { DataItem } from 'ethereum-types'; + +import { RawCalldata } from '../calldata'; + +import { DataType } from './data_type'; + +export interface DataTypeFactory { + create: (dataItem: DataItem, parentDataType?: DataType) => DataType; + mapDataItemToDataType: (dataItem: DataItem) => DataType; +} + +export interface DataTypeStaticInterface { + matchType: (type: string) => boolean; + encodeValue: (value: any) => Buffer; + decodeValue: (rawCalldata: RawCalldata) => any; +} diff --git a/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts new file mode 100644 index 000000000..f44a6dd30 --- /dev/null +++ b/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts @@ -0,0 +1,230 @@ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { BigNumber } from '../../configured_bignumber'; +import { CalldataBlock, MemberCalldataBlock, RawCalldata } from '../calldata'; +import * as Constants from '../constants'; +import { DecodingRules } from '../utils/rules'; + +import { DataType } from './data_type'; +import { DependentDataType } from './dependent_data_type'; +import { DataTypeFactory } from './interfaces'; + +interface MemberMap { + [key: string]: number; +} + +export abstract class MemberDataType extends DataType { + protected readonly _arrayLength: number | undefined; + protected readonly _arrayElementType: string | undefined; + private readonly _memberMap: MemberMap; + private readonly _members: DataType[]; + private readonly _isArray: boolean; + + public constructor( + dataItem: DataItem, + factory: DataTypeFactory, + isArray: boolean = false, + arrayLength?: number, + arrayElementType?: string, + ) { + super(dataItem, factory); + this._memberMap = {}; + this._members = []; + this._isArray = isArray; + this._arrayLength = arrayLength; + this._arrayElementType = arrayElementType; + if (isArray && arrayLength !== undefined) { + [this._members, this._memberMap] = this._createMembersWithLength(dataItem, arrayLength); + } else if (!isArray) { + [this._members, this._memberMap] = this._createMembersWithKeys(dataItem); + } + } + + public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const block = + value instanceof Array + ? this._generateCalldataBlockFromArray(value, parentBlock) + : this._generateCalldataBlockFromObject(value, parentBlock); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { + let members = this._members; + if (this._isArray && this._arrayLength === undefined) { + const arrayLengthBuf = calldata.popWord(); + const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); + const hexBase = 16; + const arrayLength = new BigNumber(arrayLengthHex, hexBase); + + [members] = this._createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); + } + + calldata.startScope(); + let value: any[] | object; + if (rules.structsAsObjects && !this._isArray) { + value = {}; + _.each(this._memberMap, (idx: number, key: string) => { + const member = this._members[idx]; + const memberValue = member.generateValue(calldata, rules); + (value as { [key: string]: any })[key] = memberValue; + }); + } else { + value = []; + _.each(members, (member: DataType, idx: number) => { + const memberValue = member.generateValue(calldata, rules); + (value as any[]).push(memberValue); + }); + } + calldata.endScope(); + return value; + } + + public isStatic(): boolean { + /* For Tuple: + const isStaticTuple = this.children.length === 0; + return isStaticTuple; // @TODO: True in every case or only when dynamic data? + + For Array: + if isLengthDefined = false then this is false + + Otherwise if the first element is a Pointer then false + */ + + if (this._isArray && this._arrayLength === undefined) { + return false; + } + + // Search for dependent members + const dependentMember = _.find(this._members, (member: DataType) => { + return member instanceof DependentDataType; + }); + const isStatic = dependentMember === undefined; // static if we couldn't find a dependent member + return isStatic; + } + + protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { + // Sanity check length + if (this._arrayLength !== undefined && value.length !== this._arrayLength) { + throw new Error( + `Expected array of ${JSON.stringify( + this._arrayLength, + )} elements, but got array of length ${JSON.stringify(value.length)}`, + ); + } + + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( + this.getDataItem().name, + this.getSignature(), + parentName, + ); + + let members = this._members; + if (this._isArray && this._arrayLength === undefined) { + [members] = this._createMembersWithLength(this.getDataItem(), value.length); + + const lenBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(`0x${value.length.toString(Constants.HEX_BASE)}`), + Constants.EVM_WORD_WIDTH_IN_BYTES, + ); + methodBlock.setHeader(lenBuf); + } + + const memberBlocks: CalldataBlock[] = []; + _.each(members, (member: DataType, idx: number) => { + const block = member.generateCalldataBlock(value[idx], methodBlock); + memberBlocks.push(block); + }); + methodBlock.setMembers(memberBlocks); + return methodBlock; + } + + protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( + this.getDataItem().name, + this.getSignature(), + parentName, + ); + const memberBlocks: CalldataBlock[] = []; + const childMap = _.cloneDeep(this._memberMap); + _.forOwn(obj, (value: any, key: string) => { + if (!(key in childMap)) { + throw new Error( + `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`, + ); + } + const block = this._members[this._memberMap[key]].generateCalldataBlock(value, methodBlock); + memberBlocks.push(block); + delete childMap[key]; + }); + + if (Object.keys(childMap).length !== 0) { + throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); + } + + methodBlock.setMembers(memberBlocks); + return methodBlock; + } + + protected _computeSignatureOfMembers(): string { + // Compute signature of members + let signature = `(`; + _.each(this._members, (member: DataType, i: number) => { + signature += member.getSignature(); + if (i < this._members.length - 1) { + signature += ','; + } + }); + signature += ')'; + return signature; + } + + private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { + // Sanity check + if (dataItem.components === undefined) { + throw new Error(`Expected components`); + } + + const members: DataType[] = []; + const memberMap: MemberMap = {}; + _.each(dataItem.components, (memberItem: DataItem) => { + const childDataItem: DataItem = { + type: memberItem.type, + name: `${dataItem.name}.${memberItem.name}`, + }; + const components = memberItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[memberItem.name] = members.length; + members.push(child); + }); + + return [members, memberMap]; + } + + private _createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { + const members: DataType[] = []; + const memberMap: MemberMap = {}; + const range = _.range(length); + _.each(range, (idx: number) => { + const childDataItem: DataItem = { + type: this._arrayElementType ? this._arrayElementType : '', + name: `${dataItem.name}[${idx.toString(Constants.DEC_BASE)}]`, + }; + const components = dataItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[idx.toString(Constants.DEC_BASE)] = members.length; + members.push(child); + }); + + return [members, memberMap]; + } +} diff --git a/packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts new file mode 100644 index 000000000..767e64f51 --- /dev/null +++ b/packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts @@ -0,0 +1,38 @@ +import { DataItem } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { CalldataBlock, PayloadCalldataBlock, RawCalldata } from '../calldata'; +import { DecodingRules } from '../utils/rules'; + +import { DataType } from './data_type'; +import { DataTypeFactory } from './interfaces'; + +export abstract class PayloadDataType extends DataType { + protected _hasConstantSize: boolean; + + public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { + super(dataItem, factory); + this._hasConstantSize = hasConstantSize; + } + + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { + const encodedValue = this.encodeValue(value); + const name = this.getDataItem().name; + const signature = this.getSignature(); + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const block = new PayloadCalldataBlock(name, signature, parentName, encodedValue); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { + const value = this.decodeValue(calldata); + return value; + } + + public isStatic(): boolean { + return this._hasConstantSize; + } + + public abstract encodeValue(value: any): Buffer; + public abstract decodeValue(calldata: RawCalldata): any; +} -- cgit v1.2.3 From 5a748fb4e5ce9603cf100af5d46c323934ab17ad Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 13:56:37 -0800 Subject: Split ABI Encoder/Decoder tests into separate files --- .../src/abi_encoder/evm_data_types/address.ts | 2 +- .../src/abi_encoder/evm_data_types/static_bytes.ts | 2 +- .../test/abi_encoder/abi_samples/method_abis.ts | 780 +++++++++ .../test/abi_encoder/abi_samples/optimizer_abis.ts | 340 ++++ .../abi_encoder/abi_samples/return_value_abis.ts | 99 ++ .../utils/test/abi_encoder/evm_data_types_test.ts | 1094 ++++++++++++ packages/utils/test/abi_encoder/methods_test.ts | 401 +++++ packages/utils/test/abi_encoder/optimizer_test.ts | 293 ++++ .../utils/test/abi_encoder/return_values_test.ts | 78 + packages/utils/test/abi_encoder_test.ts | 1852 -------------------- packages/utils/test/abi_samples.ts | 780 --------- packages/utils/test/optimizer_abis.ts | 340 ---- packages/utils/test/return_value_abis.ts | 99 -- 13 files changed, 3087 insertions(+), 3073 deletions(-) create mode 100644 packages/utils/test/abi_encoder/abi_samples/method_abis.ts create mode 100644 packages/utils/test/abi_encoder/abi_samples/optimizer_abis.ts create mode 100644 packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts create mode 100644 packages/utils/test/abi_encoder/evm_data_types_test.ts create mode 100644 packages/utils/test/abi_encoder/methods_test.ts create mode 100644 packages/utils/test/abi_encoder/optimizer_test.ts create mode 100644 packages/utils/test/abi_encoder/return_values_test.ts delete mode 100644 packages/utils/test/abi_encoder_test.ts delete mode 100644 packages/utils/test/abi_samples.ts delete mode 100644 packages/utils/test/optimizer_abis.ts delete mode 100644 packages/utils/test/return_value_abis.ts diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 707e265f8..0107fdc50 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -13,7 +13,7 @@ export class Address extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _ADDRESS_SIZE_IN_BYTES = 20; private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = Constants.EVM_WORD_WIDTH_IN_BYTES - - Address._ADDRESS_SIZE_IN_BYTES; + Address._ADDRESS_SIZE_IN_BYTES; public static matchType(type: string): boolean { return type === 'address'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 90e872c78..4e49db609 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -46,7 +46,7 @@ export class StaticBytes extends PayloadDataType { if (valueBuf.byteLength > this._width) { throw new Error( `Tried to assign ${value} (${ - valueBuf.byteLength + valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); } else if (value.length % 2 !== 0) { diff --git a/packages/utils/test/abi_encoder/abi_samples/method_abis.ts b/packages/utils/test/abi_encoder/abi_samples/method_abis.ts new file mode 100644 index 000000000..fc552c127 --- /dev/null +++ b/packages/utils/test/abi_encoder/abi_samples/method_abis.ts @@ -0,0 +1,780 @@ +/* tslint:disable max-file-line-count */ +import { MethodAbi } from 'ethereum-types'; + +export const simpleAbi: MethodAbi = { + constant: false, + inputs: [ + { + name: 'greg', + type: 'uint256', + }, + { + name: 'gregStr', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const stringAbi: MethodAbi = { + constant: false, + inputs: [ + { + name: 'greg', + type: 'string[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const GAbi: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'a', + type: 'uint256', + }, + { + name: 'b', + type: 'string', + }, + { + name: 'e', + type: 'bytes', + }, + { + name: 'f', + type: 'address', + }, + ], + + name: 'f', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const typesWithDefaultWidthsAbi: MethodAbi = { + constant: false, + inputs: [ + { + name: 'someUint', + type: 'uint', + }, + { + name: 'someInt', + type: 'int', + }, + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someUint', + type: 'uint[]', + }, + { + name: 'someInt', + type: 'int[]', + }, + { + name: 'someByte', + type: 'byte[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const multiDimensionalArraysStaticTypeAbi: MethodAbi = { + constant: false, + inputs: [ + { + name: 'a', + type: 'uint8[][][]', + }, + { + name: 'b', + type: 'uint8[][][2]', + }, + { + name: 'c', + type: 'uint8[][2][]', + }, + { + name: 'd', + type: 'uint8[2][][]', + }, + { + name: 'e', + type: 'uint8[][2][2]', + }, + { + name: 'f', + type: 'uint8[2][2][]', + }, + { + name: 'g', + type: 'uint8[2][][2]', + }, + { + name: 'h', + type: 'uint8[2][2][2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const multiDimensionalArraysDynamicTypeAbi: MethodAbi = { + constant: false, + inputs: [ + { + name: 'a', + type: 'string[][][]', + }, + { + name: 'b', + type: 'string[][][2]', + }, + { + name: 'c', + type: 'string[][2][]', + }, + { + name: 'h', + type: 'string[2][2][2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const dynamicTupleAbi: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + ], + name: 'order', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const arrayOfStaticTuplesWithDefinedLengthAbi: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + ], + name: 'order', + type: 'tuple[8]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const arrayOfStaticTuplesWithDynamicLengthAbi: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const arrayOfDynamicTuplesWithDefinedLengthAbi: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[8]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const arrayOfDynamicTuplesWithUndefinedLengthAbi: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const arrayOfDynamicTuplesAbi: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const multidimensionalArrayOfDynamicTuplesAbi: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'order', + type: 'tuple[][2][]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const staticTupleAbi: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'someUint1', + type: 'uint256', + }, + { + name: 'someUint2', + type: 'uint256', + }, + { + name: 'someUint3', + type: 'uint256', + }, + { + name: 'someBool', + type: 'bool', + }, + ], + name: 'order', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const staticArrayAbi: MethodAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[3]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const staticArrayDynamicMembersAbi: MethodAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'string[3]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const dynamicArrayDynamicMembersAbi: MethodAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'string[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const dynamicArrayStaticMembersAbi: MethodAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const largeFlatAbi: MethodAbi = { + constant: false, + inputs: [ + { + name: 'someUInt256', + type: 'uint256', + }, + { + name: 'someInt256', + type: 'int256', + }, + { + name: 'someInt32', + type: 'int32', + }, + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someBytes32', + type: 'bytes32', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someString', + type: 'string', + }, + { + name: 'someAddress', + type: 'address', + }, + { + name: 'someBool', + type: 'bool', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const largeNestedAbi: MethodAbi = { + constant: false, + inputs: [ + { + name: 'someStaticArray', + type: 'uint8[3]', + }, + { + name: 'someStaticArrayWithDynamicMembers', + type: 'string[2]', + }, + { + name: 'someDynamicArrayWithDynamicMembers', + type: 'bytes[]', + }, + { + name: 'some2DArray', + type: 'string[][]', + }, + { + name: 'someTuple', + type: 'tuple', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'someStr', + type: 'string', + }, + ], + }, + { + name: 'someTupleWithDynamicTypes', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + /*{ + name: 'someStrArray', + type: 'string[]', + },*/ + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + { + name: 'someArrayOfTuplesWithDynamicTypes', + type: 'tuple[]', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + /*{ + name: 'someStrArray', + type: 'string[]', + },*/ + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const nestedTuples: MethodAbi = { + constant: false, + inputs: [ + { + name: 'firstTuple', + type: 'tuple[1]', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'nestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + ], + }, + { + name: 'secondTuple', + type: 'tuple[]', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + { + name: 'nestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint32', + type: 'uint32', + }, + { + name: 'secondNestedTuple', + type: 'tuple', + components: [ + { + name: 'someUint', + type: 'uint256', + }, + { + name: 'someStr', + type: 'string', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + ], + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someAddress', + type: 'address', + }, + ], + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const simpleAbi2: MethodAbi = { + constant: false, + inputs: [ + { + name: 'someByte', + type: 'byte', + }, + { + name: 'someBytes32', + type: 'bytes32', + }, + { + name: 'someBytes', + type: 'bytes', + }, + { + name: 'someString', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const fillOrderAbi: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'makerAddress', + type: 'address', + }, + { + name: 'takerAddress', + type: 'address', + }, + { + name: 'feeRecipientAddress', + type: 'address', + }, + { + name: 'senderAddress', + type: 'address', + }, + { + name: 'makerAssetAmount', + type: 'uint256', + }, + { + name: 'takerAssetAmount', + type: 'uint256', + }, + { + name: 'makerFee', + type: 'uint256', + }, + { + name: 'takerFee', + type: 'uint256', + }, + { + name: 'expirationTimeSeconds', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'makerAssetData', + type: 'bytes', + }, + { + name: 'takerAssetData', + type: 'bytes', + }, + ], + name: 'order', + type: 'tuple', + }, + { + name: 'takerAssetFillAmount', + type: 'uint256', + }, + { + name: 'salt', + type: 'uint256', + }, + { + name: 'orderSignature', + type: 'bytes', + }, + { + name: 'takerSignature', + type: 'bytes', + }, + ], + name: 'fillOrder', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; diff --git a/packages/utils/test/abi_encoder/abi_samples/optimizer_abis.ts b/packages/utils/test/abi_encoder/abi_samples/optimizer_abis.ts new file mode 100644 index 000000000..7cfd7a118 --- /dev/null +++ b/packages/utils/test/abi_encoder/abi_samples/optimizer_abis.ts @@ -0,0 +1,340 @@ +/* tslint:disable max-file-line-count */ +import { MethodAbi } from 'ethereum-types'; + +export const duplicateDynamicArraysWithStaticElements: MethodAbi = { + constant: false, + inputs: [ + { + name: 'array1', + type: 'uint[]', + }, + { + name: 'array2', + type: 'uint[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const duplicateDynamicArraysWithDynamicElements: MethodAbi = { + constant: false, + inputs: [ + { + name: 'array1', + type: 'string[]', + }, + { + name: 'array2', + type: 'string[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const duplicateStaticArraysWithStaticElements: MethodAbi = { + constant: false, + inputs: [ + { + name: 'array1', + type: 'uint[2]', + }, + { + name: 'array2', + type: 'uint[2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const duplicateStaticArraysWithDynamicElements: MethodAbi = { + constant: false, + inputs: [ + { + name: 'array1', + type: 'string[2]', + }, + { + name: 'array2', + type: 'string[2]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const duplicateArrayElements: MethodAbi = { + constant: false, + inputs: [ + { + name: 'array', + type: 'string[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const duplicateTupleFields: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'field1', + type: 'string', + }, + { + name: 'field2', + type: 'string', + }, + ], + name: 'Tuple', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const duplicateStrings: MethodAbi = { + constant: false, + inputs: [ + { + name: 'string1', + type: 'string', + }, + { + name: 'string2', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const duplicateBytes: MethodAbi = { + constant: false, + inputs: [ + { + name: 'bytes1', + type: 'bytes', + }, + { + name: 'bytes2', + type: 'bytes', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const duplicateTuples: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'field1', + type: 'string', + }, + { + name: 'field2', + type: 'uint', + }, + ], + name: 'Tuple', + type: 'tuple', + }, + { + components: [ + { + name: 'field1', + type: 'string', + }, + { + name: 'field2', + type: 'uint', + }, + ], + name: 'Tuple', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const duplicateArraysNestedInTuples: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + name: 'field', + type: 'uint[]', + }, + ], + name: 'Tuple1', + type: 'tuple', + }, + { + components: [ + { + name: 'field', + type: 'uint[]', + }, + { + name: 'extraField', + type: 'string', + }, + ], + name: 'Tuple2', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const duplicateTuplesNestedInTuples: MethodAbi = { + constant: false, + inputs: [ + { + components: [ + { + components: [ + { + name: 'nestedField', + type: 'string', + }, + ], + name: 'field', + type: 'tuple', + }, + ], + name: 'Tuple1', + type: 'tuple', + }, + { + components: [ + { + components: [ + { + name: 'nestedField', + type: 'string', + }, + ], + name: 'field', + type: 'tuple', + }, + { + name: 'extraField', + type: 'string', + }, + ], + name: 'Tuple1', + type: 'tuple', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const duplicateTwoDimensionalArrays: MethodAbi = { + constant: false, + inputs: [ + { + name: 'array1', + type: 'string[][]', + }, + { + name: 'array2', + type: 'string[][]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const arrayElementsDuplicatedAsSeparateParameter: MethodAbi = { + constant: false, + inputs: [ + { + name: 'stringArray', + type: 'string[]', + }, + { + name: 'string', + type: 'string', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const arrayElementsDuplicatedAsTupleFields: MethodAbi = { + constant: false, + inputs: [ + { + name: 'uint8Array', + type: 'uint8[]', + }, + { + components: [ + { + name: 'uint', + type: 'uint', + }, + ], + name: 'uintTuple', + type: 'tuple[]', + }, + ], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; diff --git a/packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts b/packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts new file mode 100644 index 000000000..ac2124011 --- /dev/null +++ b/packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts @@ -0,0 +1,99 @@ +/* tslint:disable max-file-line-count */ +import { MethodAbi } from 'ethereum-types'; + +export const noReturnValues: MethodAbi = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const singleStaticReturnValue: MethodAbi = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [ + { + name: 'Bytes4', + type: 'bytes4', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const multipleStaticReturnValues: MethodAbi = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [ + { + name: 'val1', + type: 'bytes4', + }, + { + name: 'val2', + type: 'bytes4', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const singleDynamicReturnValue: MethodAbi = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [ + { + name: 'val', + type: 'bytes', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const multipleDynamicReturnValues: MethodAbi = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [ + { + name: 'val1', + type: 'bytes', + }, + { + name: 'val2', + type: 'bytes', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; + +export const mixedStaticAndDynamicReturnValues: MethodAbi = { + constant: false, + inputs: [], + name: 'simpleFunction', + outputs: [ + { + name: 'val1', + type: 'bytes4', + }, + { + name: 'val2', + type: 'bytes', + }, + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function', +}; diff --git a/packages/utils/test/abi_encoder/evm_data_types_test.ts b/packages/utils/test/abi_encoder/evm_data_types_test.ts new file mode 100644 index 000000000..9c3e3c0f9 --- /dev/null +++ b/packages/utils/test/abi_encoder/evm_data_types_test.ts @@ -0,0 +1,1094 @@ +/* tslint:disable max-file-line-count */ +import * as chai from 'chai'; +import * as ethUtil from 'ethereumjs-util'; +import 'mocha'; + +import { AbiEncoder, BigNumber } from '../../src/'; +import { chaiSetup } from '../utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { + describe('Array', () => { + it('Fixed size; Static elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'int[2]' }; + const dataType = new AbiEncoder.Array(testDataItem); + // Construct args to be encoded + const args = [new BigNumber(5), new BigNumber(6)]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Dynamic size; Static elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'int[]' }; + const dataType = new AbiEncoder.Array(testDataItem); + // Construct args to be encoded + const args = [new BigNumber(5), new BigNumber(6)]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Fixed size; Dynamic elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'string[2]' }; + const dataType = new AbiEncoder.Array(testDataItem); + // Construct args to be encoded + const args = ['Hello', 'world']; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Dynamic size; Dynamic elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'string[]' }; + const dataType = new AbiEncoder.Array(testDataItem); + // Construct args to be encoded + const args = ['Hello', 'world']; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Dynamic Size; Multidimensional; Dynamic Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'bytes[][]' }; + const dataType = new AbiEncoder.Array(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708', '0x09101112']; + const array2 = ['0x10111213', '0x14151617']; + const array3 = ['0x18192021']; + const args = [array1, array2, array3]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000405060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000041011121300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414151617000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000041819202100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Dynamic Size; Multidimensional; Static Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'bytes4[][]' }; + const dataType = new AbiEncoder.Array(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708', '0x09101112']; + const array2 = ['0x10111213', '0x14151617']; + const array3 = ['0x18192021']; + const args = [array1, array2, array3]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000301020304000000000000000000000000000000000000000000000000000000000506070800000000000000000000000000000000000000000000000000000000091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021011121300000000000000000000000000000000000000000000000000000000141516170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011819202100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Static Size; Multidimensional; Static Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'bytes4[3][2]' }; + const dataType = new AbiEncoder.Array(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708', '0x09101112']; + const array2 = ['0x10111213', '0x14151617', '0x18192021']; + const args = [array1, array2]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x010203040000000000000000000000000000000000000000000000000000000005060708000000000000000000000000000000000000000000000000000000000910111200000000000000000000000000000000000000000000000000000000101112130000000000000000000000000000000000000000000000000000000014151617000000000000000000000000000000000000000000000000000000001819202100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Static Size; Multidimensional; Dynamic Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'bytes[3][2]' }; + const dataType = new AbiEncoder.Array(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708', '0x09101112']; + const array2 = ['0x10111213', '0x14151617', '0x18192021']; + const args = [array1, array2]; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000401020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004050607080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040910111200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000410111213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004141516170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041819202100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Static size; Too Few Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'string[3]' }; + const dataType = new AbiEncoder.Array(testDataItem); + // Construct args to be encoded + const args = ['Hello', 'world']; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw('Expected array of 3 elements, but got array of length 2'); + }); + it('Static size; Too Many Elements', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'string[1]' }; + const dataType = new AbiEncoder.Array(testDataItem); + // Construct args to be encoded + const args = ['Hello', 'world']; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw('Expected array of 1 elements, but got array of length 2'); + }); + it('Element Type Mismatch', async () => { + // Create DataType object + const testDataItem = { name: 'testArray', type: 'uint[]' }; + const dataType = new AbiEncoder.Array(testDataItem); + // Construct args to be encoded + const args = [new BigNumber(1), 'Bad Argument']; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(); + }); + }); + + describe('Tuple', () => { + it('Static elements only', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { field_1: new BigNumber(-5), field_2: true }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Dynamic elements only', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field_1', type: 'string' }, { name: 'field_2', type: 'bytes' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { field_1: 'Hello, World!', field_2: '0xabcdef0123456789' }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Nested Static Array', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field', type: 'uint[2]' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { field: [new BigNumber(1), new BigNumber(2)] }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Nested Dynamic Array', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field', type: 'uint[]' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { field: [new BigNumber(1), new BigNumber(2)] }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Nested Static Multidimensional Array', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field', type: 'bytes4[2][2]' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708']; + const array2 = ['0x09101112', '0x13141516']; + const args = { field: [array1, array2] }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x0102030400000000000000000000000000000000000000000000000000000000050607080000000000000000000000000000000000000000000000000000000009101112000000000000000000000000000000000000000000000000000000001314151600000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Nested Dynamic Multidimensional Array', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field', type: 'bytes[2][2]' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const array1 = ['0x01020304', '0x05060708']; + const array2 = ['0x09101112', '0x13141516']; + const args = { field: [array1, array2] }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040506070800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041314151600000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Static and dynamic elements mixed', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [ + { name: 'field_1', type: 'int32' }, + { name: 'field_2', type: 'string' }, + { name: 'field_3', type: 'bool' }, + { name: 'field_4', type: 'bytes' }, + ], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { + field_1: new BigNumber(-5), + field_2: 'Hello, World!', + field_3: true, + field_4: '0xabcdef0123456789', + }; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; + const decodedArgs = dataType.decode(encodedArgs, decodingRules); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Missing Key', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { field_1: new BigNumber(-5) }; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw('Could not assign tuple to object: missing keys field_2'); + }); + it('Bad Key', async () => { + // Create DataType object + const testDataItem = { + name: 'Tuple', + type: 'tuple', + components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }], + }; + const dataType = new AbiEncoder.Tuple(testDataItem); + // Construct args to be encoded + const args = { unknown_field: new BigNumber(-5) }; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw("Could not assign tuple to object: unrecognized key 'unknown_field' in object Tuple"); + }); + }); + + describe('Address', () => { + it('Valid Address', async () => { + // Create DataType object + const testDataItem = { name: 'Address', type: 'address' }; + const dataType = new AbiEncoder.Address(testDataItem); + // Construct args to be encoded + const args = '0xe41d2489571d322189246dafa5ebde1f4699f498'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Invalid Address - input is not valid hex', async () => { + // Create DataType object + const testDataItem = { name: 'Address', type: 'address' }; + const dataType = new AbiEncoder.Address(testDataItem); + // Construct args to be encoded + const args = 'e4'; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); + }); + it('Invalid Address - input is not 20 bytes', async () => { + // Create DataType object + const testDataItem = { name: 'Address', type: 'address' }; + const dataType = new AbiEncoder.Address(testDataItem); + // Construct args to be encoded + const args = '0xe4'; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); + }); + }); + + describe('Bool', () => { + it('True', async () => { + // Create DataType object + const testDataItem = { name: 'Boolean', type: 'bool' }; + const dataType = new AbiEncoder.Bool(testDataItem); + // Construct args to be encoded + const args = true; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('False', async () => { + // Create DataType object + const testDataItem = { name: 'Boolean', type: 'bool' }; + const dataType = new AbiEncoder.Bool(testDataItem); + // Construct args to be encoded + const args = false; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + }); + + describe('Integer', () => { + /* tslint:disable custom-no-magic-numbers */ + const max256BitInteger = new BigNumber(2).pow(255).minus(1); + const min256BitInteger = new BigNumber(2).pow(255).times(-1); + const max32BitInteger = new BigNumber(2).pow(31).minus(1); + const min32BitInteger = new BigNumber(2).pow(31).times(-1); + /* tslint:enable custom-no-magic-numbers */ + + it('Int256 - Positive Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = new BigNumber(1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Int256 - Negative Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = new BigNumber(-1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Int256 - Positive Value', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = max256BitInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Int256 - Negative Value', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = min256BitInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = `0x8000000000000000000000000000000000000000000000000000000000000000`; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Int256 - Value too large', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = max256BitInteger.plus(1); + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(); + }); + it('Int256 - Value too small', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (256)', type: 'int' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = min256BitInteger.minus(1); + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(); + }); + it('Int32 - Positive Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = new BigNumber(1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Int32 - Negative Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = new BigNumber(-1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Int32 - Positive Value', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = max32BitInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000007fffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Int32 - Negative Value', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = min32BitInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000`; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Int32 - Value too large', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = max32BitInteger.plus(1); + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(); + }); + it('Int32 - Value too small', async () => { + // Create DataType object + const testDataItem = { name: 'Integer (32)', type: 'int32' }; + const dataType = new AbiEncoder.Int(testDataItem); + // Construct args to be encoded + const args = min32BitInteger.minus(1); + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(); + }); + }); + + describe('Unsigned Integer', () => { + /* tslint:disable custom-no-magic-numbers */ + const max256BitUnsignedInteger = new BigNumber(2).pow(256).minus(1); + const min256BitUnsignedInteger = new BigNumber(0); + const max32BitUnsignedInteger = new BigNumber(2).pow(32).minus(1); + const min32BitUnsignedInteger = new BigNumber(0); + /* tslint:enable custom-no-magic-numbers */ + + it('UInt256 - Positive Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = new BigNumber(1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('UInt256 - Positive Value', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = max256BitUnsignedInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('UInt256 - Zero Value', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = min256BitUnsignedInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = `0x0000000000000000000000000000000000000000000000000000000000000000`; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('UInt256 - Value too large', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = max256BitUnsignedInteger.plus(1); + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(); + }); + it('UInt256 - Value too small', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = min256BitUnsignedInteger.minus(1); + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(); + }); + it('UInt32 - Positive Base Case', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = new BigNumber(1); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('UInt32 - Positive Value', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = max32BitUnsignedInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000ffffffff'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('UInt32 - Zero Value', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = min32BitUnsignedInteger; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = `0x0000000000000000000000000000000000000000000000000000000000000000`; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('UInt32 - Value too large', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = max32BitUnsignedInteger.plus(1); + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(); + }); + it('UInt32 - Value too small', async () => { + // Create DataType object + const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; + const dataType = new AbiEncoder.UInt(testDataItem); + // Construct args to be encoded + const args = min32BitUnsignedInteger.minus(1); + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw(); + }); + }); + + describe('Static Bytes', () => { + it('Single Byte (byte)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Byte', type: 'byte' }; + const dataType = new AbiEncoder.StaticBytes(testDataItem); + // Construct args to be encoded + const args = '0x05'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Single Byte (bytes1)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes1', type: 'bytes1' }; + const dataType = new AbiEncoder.StaticBytes(testDataItem); + // Construct args to be encoded + const args = '0x05'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0500000000000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('4 Bytes (bytes4)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; + const dataType = new AbiEncoder.StaticBytes(testDataItem); + // Construct args to be encoded + const args = '0x00010203'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0001020300000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('4 Bytes (bytes4); Encoder must pad input', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; + const dataType = new AbiEncoder.StaticBytes(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = '0x1a18'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x1a18000000000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const paddedArgs = '0x1a180000'; + const paddedArgsAsJson = JSON.stringify(paddedArgs); + expect(decodedArgsAsJson).to.be.equal(paddedArgsAsJson); + }); + it('32 Bytes (bytes32)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; + const dataType = new AbiEncoder.StaticBytes(testDataItem); + // Construct args to be encoded + const args = '0x0001020304050607080911121314151617181920212223242526272829303132'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x0001020304050607080911121314151617181920212223242526272829303132'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('32 Bytes (bytes32); Encoder must pad input', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; + const dataType = new AbiEncoder.StaticBytes(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = '0x1a18bf61'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const paddedArgs = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; + const paddedArgsAsJson = JSON.stringify(paddedArgs); + expect(decodedArgsAsJson).to.be.equal(paddedArgsAsJson); + }); + it('Should throw when pass in too many bytes (bytes4)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; + const dataType = new AbiEncoder.StaticBytes(testDataItem); + // Construct args to be encoded + const args = '0x0102030405'; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw( + 'Tried to assign 0x0102030405 (5 bytes), which exceeds max bytes that can be stored in a bytes4', + ); + }); + it('Should throw when pass in too many bytes (bytes32)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; + const dataType = new AbiEncoder.StaticBytes(testDataItem); + // Construct args to be encoded + const args = '0x010203040506070809101112131415161718192021222324252627282930313233'; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw( + 'Tried to assign 0x010203040506070809101112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32', + ); + }); + it('Should throw when pass in bad hex (no 0x prefix)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; + const dataType = new AbiEncoder.StaticBytes(testDataItem); + // Construct args to be encoded + const args = '0102030405060708091011121314151617181920212223242526272829303132'; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix."); + }); + it('Should throw when pass in bad hex (include a half-byte)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; + const dataType = new AbiEncoder.StaticBytes(testDataItem); + // Construct args to be encoded + const args = '0x010'; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); + }); + }); + + describe('Dynamic Bytes', () => { + it('Fits into one EVM word', async () => { + // Create DataType object + const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; + const dataType = new AbiEncoder.DynamicBytes(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = '0x1a18bf61'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Spans multiple EVM words', async () => { + // Create DataType object + const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; + const dataType = new AbiEncoder.DynamicBytes(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const bytesLength = 40; + const args = '0x' + '61'.repeat(bytesLength); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Input as Buffer', async () => { + // Create DataType object + const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; + const dataType = new AbiEncoder.DynamicBytes(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = '0x1a18bf61'; + const argsAsBuffer = ethUtil.toBuffer(args); + // Encode Args and validate result + const encodedArgs = dataType.encode(argsAsBuffer); + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Should throw when pass in bad hex (no 0x prefix)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes', type: 'bytes' }; + const dataType = new AbiEncoder.DynamicBytes(testDataItem); + // Construct args to be encoded + const args = '01'; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '01'"); + }); + it('Should throw when pass in bad hex (include a half-byte)', async () => { + // Create DataType object + const testDataItem = { name: 'Static Bytes', type: 'bytes' }; + const dataType = new AbiEncoder.DynamicBytes(testDataItem); + // Construct args to be encoded + const args = '0x010'; + // Encode Args and validate result + expect(() => { + dataType.encode(args); + }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); + }); + }); + + describe('String', () => { + it('Fits into one EVM word', async () => { + // Create DataType object + const testDataItem = { name: 'String', type: 'string' }; + const dataType = new AbiEncoder.String(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const args = 'five'; + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('Spans multiple EVM words', async () => { + // Create DataType object + const testDataItem = { name: 'String', type: 'string' }; + const dataType = new AbiEncoder.String(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const bytesLength = 40; + const args = 'a'.repeat(bytesLength); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + it('String that begins with 0x prefix', async () => { + // Create DataType object + const testDataItem = { name: 'String', type: 'string' }; + const dataType = new AbiEncoder.String(testDataItem); + // Construct args to be encoded + // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. + const strLength = 40; + const args = '0x' + 'a'.repeat(strLength); + // Encode Args and validate result + const encodedArgs = dataType.encode(args); + const expectedEncodedArgs = + '0x000000000000000000000000000000000000000000000000000000000000002a30786161616161616161616161616161616161616161616161616161616161616161616161616161616100000000000000000000000000000000000000000000'; + expect(encodedArgs).to.be.equal(expectedEncodedArgs); + // Decode Encoded Args and validate result + const decodedArgs = dataType.decode(encodedArgs); + const decodedArgsAsJson = JSON.stringify(decodedArgs); + const argsAsJson = JSON.stringify(args); + expect(decodedArgsAsJson).to.be.equal(argsAsJson); + }); + }); +}); diff --git a/packages/utils/test/abi_encoder/methods_test.ts b/packages/utils/test/abi_encoder/methods_test.ts new file mode 100644 index 000000000..d158b9e5b --- /dev/null +++ b/packages/utils/test/abi_encoder/methods_test.ts @@ -0,0 +1,401 @@ +import * as chai from 'chai'; +import 'mocha'; + +import { AbiEncoder, BigNumber } from '../../src/'; +import { chaiSetup } from '../utils/chai_setup'; + +import * as AbiSamples from './abi_samples/method_abis'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('ABI Encoder: Method Encoding / Decoding', () => { + it('Types with default widths', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); + const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Array of Static Tuples (Array has defined length)', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDefinedLengthAbi); + let value = 0; + const arrayOfTuples = []; + const arrayOfTuplesLength = 8; + for (let i = 0; i < arrayOfTuplesLength; ++i) { + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Array of Static Tuples (Array has dynamic length)', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDynamicLengthAbi); + let value = 0; + const arrayOfTuples = []; + const arrayOfTuplesLength = 8; + for (let i = 0; i < arrayOfTuplesLength; ++i) { + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Array of Dynamic Tuples (Array has defined length)', async () => { + // Generate Calldata + const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithDefinedLengthAbi); + let value = 0; + const arrayOfTuples = []; + const arrayOfTuplesLength = 8; + for (let i = 0; i < arrayOfTuplesLength; ++i) { + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Array of Dynamic Tuples (Array has dynamic length)', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithUndefinedLengthAbi); + let value = 0; + const arrayOfTuples = []; + const arrayOfTuplesLength = 8; + for (let i = 0; i < arrayOfTuplesLength; ++i) { + arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]); + } + const args = [arrayOfTuples]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Multidimensional Arrays / Static Members', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi); + // Eight 3-dimensional arrays of uint8[2][2][2] + let value = 0; + const args = []; + const argsLength = 8; + for (let i = 0; i < argsLength; ++i) { + args.push([ + [[new BigNumber(++value), new BigNumber(++value)], [new BigNumber(++value), new BigNumber(++value)]], + [[new BigNumber(++value), new BigNumber(++value)], [new BigNumber(++value), new BigNumber(++value)]], + ]); + } + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; + expect(calldata).to.be.equal(expectedCalldata); + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Multidimensional Arrays / Dynamic Members', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysDynamicTypeAbi); + // Eight 3-dimensional arrays of string[2][2][2] + let value = 0; + const args = []; + const argsLength = 4; + for (let i = 0; i < argsLength; ++i) { + args.push([ + [ + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + ], + [ + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + [new BigNumber(++value).toString(), new BigNumber(++value).toString()], + ], + ]); + } + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Fixed Length Array / Dynamic Members', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); + const args = [['Brave', 'New', 'World']]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Fixed Length Array / Dynamic Members', async () => { + // Generaet calldata + const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); + const args = [['Brave', 'New', 'World']]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Unfixed Length Array / Dynamic Members ABI', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.dynamicArrayDynamicMembersAbi); + const args = [['Brave', 'New', 'World']]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Unfixed Length Array / Static Members ABI', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.dynamicArrayStaticMembersAbi); + const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Fixed Length Array / Static Members ABI', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); + const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Array ABI', async () => { + // Generate calldata + const method = new AbiEncoder.Method(AbiSamples.stringAbi); + const args = [['five', 'six', 'seven']]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Static Tuple', async () => { + // Generate calldata + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi); + const args = [[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Dynamic Tuple (Array input)', async () => { + // Generate calldata + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); + const args = [[new BigNumber(5), 'five']]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Dynamic Tuple (Object input)', async () => { + // Generate Calldata + // This is dynamic because it has dynamic members + const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); + const args = [[new BigNumber(5), 'five']]; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Large, Flat ABI', async () => { + // Construct calldata + const method = new AbiEncoder.Method(AbiSamples.largeFlatAbi); + const args = [ + new BigNumber(256745454), + new BigNumber(-256745454), + new BigNumber(434244), + '0x43', + '0x0001020304050607080911121314151617181920212223242526272829303132', + '0x0001020304050607080911121314151617181920212223242526272829303132080911121314151617181920212223242526272829303132', + 'Little peter piper piped a piping pepper pot', + '0xe41d2489571d322189246dafa5ebde1f4699f498', + true, + ]; + // Validate calldata + const calldata = method.encode(args); + const expectedCalldata = + '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); + it('Large, Nested ABI', async () => { + // Construct Calldata + const method = new AbiEncoder.Method(AbiSamples.largeNestedAbi); + const someStaticArray = [new BigNumber(127), new BigNumber(14), new BigNumber(54)]; + const someStaticArrayWithDynamicMembers = [ + 'the little piping piper piped a piping pipper papper', + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ]; + const someDynamicArrayWithDynamicMembers = [ + '0x38745637834987324827439287423897238947239847', + '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', + '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', + ]; + const some2DArray = [ + [ + 'some string', + 'some another string', + 'there are just too many stringsup in', + 'here', + 'yall ghonna make me lose my mind', + ], + [ + 'the little piping piper piped a piping pipper papper', + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + ], + [], + ]; + const someTuple = { + someUint32: new BigNumber(4037824789), + someStr: + 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', + }; + const someTupleWithDynamicTypes = { + someUint: new BigNumber(4024789), + someStr: 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', + someBytes: '0x29384723894723843743289742389472398473289472348927489274894738427428947389facdea', + someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', + }; + const someTupleWithDynamicTypes2 = { + someUint: new BigNumber(9024789), + someStr: 'ksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjkakdhjasjkdhasjkldshdjahdkjsahdaj', + someBytes: '0x29384723894398473289472348927489272384374328974238947274894738427428947389facde1', + someAddress: '0x746dafa5ebde1f4699f4981d3221892e41d24895', + }; + const someTupleWithDynamicTypes3 = { + someUint: new BigNumber(1024789), + someStr: 'sdhsajkdhsajkdhadjkashdjakdhjasjkdhasjkldshdjahdkjsahdajkksadhajkdhsajkdhsadjk', + someBytes: '0x38947238437432829384729742389472398473289472348927489274894738427428947389facdef', + someAddress: '0x89571d322189e415ebde1f4699f498d24246dafa', + }; + const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3]; + const args = { + someStaticArray, + someStaticArrayWithDynamicMembers, + someDynamicArrayWithDynamicMembers, + some2DArray, + someTuple, + someTupleWithDynamicTypes, + someArrayOfTuplesWithDynamicTypes, + }; + const calldata = method.encode(args); + // Validate calldata + const expectedCalldata = + '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; + expect(calldata).to.be.equal(expectedCalldata); + // Validate decoding + const expectedDecodedValueJson = JSON.stringify(args); + const decodedValue = method.decode(calldata, { structsAsObjects: true }); + const decodedValueJson = JSON.stringify(decodedValue); + expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + }); +}); diff --git a/packages/utils/test/abi_encoder/optimizer_test.ts b/packages/utils/test/abi_encoder/optimizer_test.ts new file mode 100644 index 000000000..304c9cbc2 --- /dev/null +++ b/packages/utils/test/abi_encoder/optimizer_test.ts @@ -0,0 +1,293 @@ +import * as chai from 'chai'; +import 'mocha'; + +import { AbiEncoder, BigNumber } from '../../src/'; +import { chaiSetup } from '../utils/chai_setup'; + +import * as OptimizedAbis from './abi_samples/optimizer_abis'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { + it('Duplicate Dynamic Arrays with Static Elements', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithStaticElements); + const array1 = [new BigNumber(100), new BigNumber(150)]; + const array2 = array1; + const args = [array1, array2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x7221063300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Dynamic Arrays with Dynamic Elements', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithDynamicElements); + const array1 = ['Hello', 'World']; + const array2 = array1; + const args = [array1, array2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0xbb4f12e300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Static Arrays with Static Elements (should not optimize)', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateStaticArraysWithStaticElements); + const array1 = [new BigNumber(100), new BigNumber(150)]; + const array2 = array1; + const args = [array1, array2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x7f8130430000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + const unoptimizedCalldata = method.encode(args); + expect(optimizedCalldata).to.be.equal(unoptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Static Arrays with Dynamic Elements', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateStaticArraysWithDynamicElements); + const array1 = ['Hello', 'World']; + const array2 = array1; + const args = [array1, array2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x9fe31f8e0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Array Elements (should optimize)', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateArrayElements); + const strings = ['Hello', 'World', 'Hello', 'World']; + const args = [strings]; + // Validate calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Tuple Fields', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTupleFields); + const tuple = ['Hello', 'Hello']; + const args = [tuple]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x16780a5e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Strings', async () => { + // Description: + // Two dynamic arrays with the same values. + // In the optimized calldata, only one set of elements should be included. + // Both arrays should point to this set. + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateStrings); + const args = ['Hello', 'Hello']; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x07370bfa00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Bytes', async () => { + // Description: + // Two dynamic arrays with the same values. + // In the optimized calldata, only one set of elements should be included. + // Both arrays should point to this set. + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateBytes); + const value = '0x01020304050607080910111213141516171819202122232425262728293031323334353637383940'; + const args = [value, value]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x6045e42900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002801020304050607080910111213141516171819202122232425262728293031323334353637383940000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Tuples', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuples); + const tuple1 = ['Hello, World!', new BigNumber(424234)]; + const tuple2 = tuple1; + const args = [tuple1, tuple2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x564f826d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000006792a000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c642100000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Fields Across Two Tuples', async () => { + // Description: + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuples); + const tuple1 = ['Hello, World!', new BigNumber(1)]; + const tuple2 = [tuple1[0], new BigNumber(2)]; + const args = [tuple1, tuple2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x564f826d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c642100000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Arrays, Nested in Separate Tuples', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateArraysNestedInTuples); + const array = [new BigNumber(100), new BigNumber(150), new BigNumber(200)]; + const tuple1 = [array]; + const tuple2 = [array, 'extra argument to prevent exactly matching the tuples']; + const args = [tuple1, tuple2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x18970a9e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000035657874726120617267756d656e7420746f2070726576656e742065786163746c79206d61746368696e6720746865207475706c65730000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Tuples, Nested in Separate Tuples', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuplesNestedInTuples); + const nestedTuple = ['Hello, World!']; + const tuple1 = [nestedTuple]; + const tuple2 = [nestedTuple, 'extra argument to prevent exactly matching the tuples']; + const args = [tuple1, tuple2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x0b4d2e6a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035657874726120617267756d656e7420746f2070726576656e742065786163746c79206d61746368696e6720746865207475706c65730000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Two-Dimensional Arrays', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTwoDimensionalArrays); + const twoDimArray1 = [['Hello', 'World'], ['Foo', 'Bar', 'Zaa']]; + const twoDimArray2 = twoDimArray1; + const args = [twoDimArray1, twoDimArray2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: false }); + const expectedOptimizedCalldata = + '0x0d28c4f9000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Duplicate Array, Nested within Separate Two-Dimensional Arrays', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.duplicateTwoDimensionalArrays); + const twoDimArray1 = [['Hello', 'World'], ['Foo']]; + const twoDimArray2 = [['Hello', 'World'], ['Bar']]; + const args = [twoDimArray1, twoDimArray2]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x0d28c4f900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003466f6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034261720000000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Array Elements Duplicated as Tuple Fields', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.arrayElementsDuplicatedAsTupleFields); + const array = [new BigNumber(100), new BigNumber(150), new BigNumber(200), new BigNumber(225)]; + const tuple = [[array[0]], [array[1]], [array[2]], [array[3]]]; + const args = [array, tuple]; + // Validata calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0x5b5c78fd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000e1'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); + it('Array Elements Duplicated as Separate Parameter', async () => { + // Generate calldata + const method = new AbiEncoder.Method(OptimizedAbis.arrayElementsDuplicatedAsSeparateParameter); + const array = ['Hello', 'Hello', 'Hello', 'World']; + const str = 'Hello'; + const args = [array, str]; + // Validate calldata + const optimizedCalldata = method.encode(args, { optimize: true }); + const expectedOptimizedCalldata = + '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; + expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); + // Validate decoding + const decodedArgs = method.decode(optimizedCalldata); + const decodedArgsJson = JSON.stringify(decodedArgs); + const argsJson = JSON.stringify(args); + expect(decodedArgsJson).to.be.equal(argsJson); + }); +}); diff --git a/packages/utils/test/abi_encoder/return_values_test.ts b/packages/utils/test/abi_encoder/return_values_test.ts new file mode 100644 index 000000000..850cb1746 --- /dev/null +++ b/packages/utils/test/abi_encoder/return_values_test.ts @@ -0,0 +1,78 @@ +import * as chai from 'chai'; +import 'mocha'; + +import { AbiEncoder } from '../../src/'; +import { chaiSetup } from '../utils/chai_setup'; + +import * as ReturnValueAbis from './abi_samples/return_value_abis'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('ABI Encoder: Return Value Encoding/Decoding', () => { + it('No Return Value', async () => { + // Decode return value + const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues); + const returnValue = '0x'; + const decodedReturnValue = method.decodeReturnValues(returnValue); + const expectedDecodedReturnValue: any[] = []; + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(expectedDecodedReturnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Single static return value', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.singleStaticReturnValue); + const returnValue = ['0x01020304']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Multiple static return values', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.multipleStaticReturnValues); + const returnValue = ['0x01020304', '0x05060708']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Single dynamic return value', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.singleDynamicReturnValue); + const returnValue = ['0x01020304']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Multiple dynamic return values', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.multipleDynamicReturnValues); + const returnValue = ['0x01020304', '0x05060708']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); + it('Mixed static/dynamic return values', async () => { + // Generate Return Value + const method = new AbiEncoder.Method(ReturnValueAbis.mixedStaticAndDynamicReturnValues); + const returnValue = ['0x01020304', '0x05060708']; + const encodedReturnValue = method.encodeReturnValues(returnValue); + const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); + // Validate decoded return value + const decodedReturnValueJson = JSON.stringify(decodedReturnValue); + const expectedDecodedReturnValueJson = JSON.stringify(returnValue); + expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + }); +}); diff --git a/packages/utils/test/abi_encoder_test.ts b/packages/utils/test/abi_encoder_test.ts deleted file mode 100644 index 35eb8d0a9..000000000 --- a/packages/utils/test/abi_encoder_test.ts +++ /dev/null @@ -1,1852 +0,0 @@ -import * as chai from 'chai'; -import * as ethUtil from 'ethereumjs-util'; -import 'mocha'; - -import { AbiEncoder, BigNumber } from '../src/'; - -import * as AbiSamples from './abi_samples'; -import * as OptimizedAbis from './optimizer_abis'; -import * as ReturnValueAbis from './return_value_abis'; -import { chaiSetup } from './utils/chai_setup'; - -chaiSetup.configure(); -const expect = chai.expect; - -describe.only('ABI Encoder', () => { - describe('Decode Return Values', () => { - it('No Return Value', async () => { - // Decode return value - const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues); - const returnValue = '0x'; - const decodedReturnValue = method.decodeReturnValues(returnValue); - const expectedDecodedReturnValue: any[] = []; - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(expectedDecodedReturnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); - }); - it('Single static return value', async () => { - // Generate Return Value - const method = new AbiEncoder.Method(ReturnValueAbis.singleStaticReturnValue); - const returnValue = ['0x01020304']; - const encodedReturnValue = method.encodeReturnValues(returnValue); - const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); - // Validate decoded return value - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(returnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); - }); - it('Multiple static return values', async () => { - // Generate Return Value - const method = new AbiEncoder.Method(ReturnValueAbis.multipleStaticReturnValues); - const returnValue = ['0x01020304', '0x05060708']; - const encodedReturnValue = method.encodeReturnValues(returnValue); - const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); - // Validate decoded return value - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(returnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); - }); - it('Single dynamic return value', async () => { - // Generate Return Value - const method = new AbiEncoder.Method(ReturnValueAbis.singleDynamicReturnValue); - const returnValue = ['0x01020304']; - const encodedReturnValue = method.encodeReturnValues(returnValue); - const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); - // Validate decoded return value - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(returnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); - }); - it('Multiple dynamic return values', async () => { - // Generate Return Value - const method = new AbiEncoder.Method(ReturnValueAbis.multipleDynamicReturnValues); - const returnValue = ['0x01020304', '0x05060708']; - const encodedReturnValue = method.encodeReturnValues(returnValue); - const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); - // Validate decoded return value - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(returnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); - }); - it('Mixed static/dynamic return values', async () => { - // Generate Return Value - const method = new AbiEncoder.Method(ReturnValueAbis.mixedStaticAndDynamicReturnValues); - const returnValue = ['0x01020304', '0x05060708']; - const encodedReturnValue = method.encodeReturnValues(returnValue); - const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); - // Validate decoded return value - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(returnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); - }); - }); - - describe('Optimizer', () => { - it('Duplicate Dynamic Arrays with Static Elements', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithStaticElements); - const array1 = [new BigNumber(100), new BigNumber(150)]; - const array2 = array1; - const args = [array1, array2]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x7221063300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Dynamic Arrays with Dynamic Elements', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithDynamicElements); - const array1 = ['Hello', 'World']; - const array2 = array1; - const args = [array1, array2]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0xbb4f12e300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Static Arrays with Static Elements (should not optimize)', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateStaticArraysWithStaticElements); - const array1 = [new BigNumber(100), new BigNumber(150)]; - const array2 = array1; - const args = [array1, array2]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x7f8130430000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - const unoptimizedCalldata = method.encode(args); - expect(optimizedCalldata).to.be.equal(unoptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Static Arrays with Dynamic Elements', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateStaticArraysWithDynamicElements); - const array1 = ['Hello', 'World']; - const array2 = array1; - const args = [array1, array2]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x9fe31f8e0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Array Elements (should optimize)', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateArrayElements); - const strings = ['Hello', 'World', 'Hello', 'World']; - const args = [strings]; - // Validate calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Tuple Fields', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateTupleFields); - const tuple = ['Hello', 'Hello']; - const args = [tuple]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x16780a5e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Strings', async () => { - // Description: - // Two dynamic arrays with the same values. - // In the optimized calldata, only one set of elements should be included. - // Both arrays should point to this set. - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateStrings); - const args = ['Hello', 'Hello']; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x07370bfa00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Bytes', async () => { - // Description: - // Two dynamic arrays with the same values. - // In the optimized calldata, only one set of elements should be included. - // Both arrays should point to this set. - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateBytes); - const value = '0x01020304050607080910111213141516171819202122232425262728293031323334353637383940'; - const args = [value, value]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x6045e42900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002801020304050607080910111213141516171819202122232425262728293031323334353637383940000000000000000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Tuples', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuples); - const tuple1 = ['Hello, World!', new BigNumber(424234)]; - const tuple2 = tuple1; - const args = [tuple1, tuple2]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x564f826d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000006792a000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c642100000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Fields Across Two Tuples', async () => { - // Description: - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuples); - const tuple1 = ['Hello, World!', new BigNumber(1)]; - const tuple2 = [tuple1[0], new BigNumber(2)]; - const args = [tuple1, tuple2]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x564f826d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c642100000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Arrays, Nested in Separate Tuples', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateArraysNestedInTuples); - const array = [new BigNumber(100), new BigNumber(150), new BigNumber(200)]; - const tuple1 = [array]; - const tuple2 = [array, 'extra argument to prevent exactly matching the tuples']; - const args = [tuple1, tuple2]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x18970a9e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000035657874726120617267756d656e7420746f2070726576656e742065786163746c79206d61746368696e6720746865207475706c65730000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Tuples, Nested in Separate Tuples', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateTuplesNestedInTuples); - const nestedTuple = ['Hello, World!']; - const tuple1 = [nestedTuple]; - const tuple2 = [nestedTuple, 'extra argument to prevent exactly matching the tuples']; - const args = [tuple1, tuple2]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x0b4d2e6a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035657874726120617267756d656e7420746f2070726576656e742065786163746c79206d61746368696e6720746865207475706c65730000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Two-Dimensional Arrays', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateTwoDimensionalArrays); - const twoDimArray1 = [['Hello', 'World'], ['Foo', 'Bar', 'Zaa']]; - const twoDimArray2 = twoDimArray1; - const args = [twoDimArray1, twoDimArray2]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: false }); - const expectedOptimizedCalldata = - '0x0d28c4f9000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Duplicate Array, Nested within Separate Two-Dimensional Arrays', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.duplicateTwoDimensionalArrays); - const twoDimArray1 = [['Hello', 'World'], ['Foo']]; - const twoDimArray2 = [['Hello', 'World'], ['Bar']]; - const args = [twoDimArray1, twoDimArray2]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x0d28c4f900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003466f6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034261720000000000000000000000000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Array Elements Duplicated as Tuple Fields', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.arrayElementsDuplicatedAsTupleFields); - const array = [new BigNumber(100), new BigNumber(150), new BigNumber(200), new BigNumber(225)]; - const tuple = [[array[0]], [array[1]], [array[2]], [array[3]]]; - const args = [array, tuple]; - // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0x5b5c78fd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000e1'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - it('Array Elements Duplicated as Separate Parameter', async () => { - // Generate calldata - const method = new AbiEncoder.Method(OptimizedAbis.arrayElementsDuplicatedAsSeparateParameter); - const array = ['Hello', 'Hello', 'Hello', 'World']; - const str = 'Hello'; - const args = [array, str]; - // Validate calldata - const optimizedCalldata = method.encode(args, { optimize: true }); - const expectedOptimizedCalldata = - '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; - expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); - // Validate decoding - const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); - }); - }); - - describe('Method ABIs', () => { - it('Types with default widths', async () => { - // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); - const args = [ - new BigNumber(1), - new BigNumber(-1), - '0x56', - [new BigNumber(1)], - [new BigNumber(-1)], - ['0x56'], - ]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Array of Static Tuples (Array has defined length)', async () => { - // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDefinedLengthAbi); - let value = 0; - const arrayOfTuples = []; - const arrayOfTuplesLength = 8; - for (let i = 0; i < arrayOfTuplesLength; ++i) { - arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Array of Static Tuples (Array has dynamic length)', async () => { - // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.arrayOfStaticTuplesWithDynamicLengthAbi); - let value = 0; - const arrayOfTuples = []; - const arrayOfTuplesLength = 8; - for (let i = 0; i < arrayOfTuplesLength; ++i) { - arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Array of Dynamic Tuples (Array has defined length)', async () => { - // Generate Calldata - const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithDefinedLengthAbi); - let value = 0; - const arrayOfTuples = []; - const arrayOfTuplesLength = 8; - for (let i = 0; i < arrayOfTuplesLength; ++i) { - arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Array of Dynamic Tuples (Array has dynamic length)', async () => { - // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.arrayOfDynamicTuplesWithUndefinedLengthAbi); - let value = 0; - const arrayOfTuples = []; - const arrayOfTuplesLength = 8; - for (let i = 0; i < arrayOfTuplesLength; ++i) { - arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]); - } - const args = [arrayOfTuples]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Multidimensional Arrays / Static Members', async () => { - // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysStaticTypeAbi); - // Eight 3-dimensional arrays of uint8[2][2][2] - let value = 0; - const args = []; - const argsLength = 8; - for (let i = 0; i < argsLength; ++i) { - args.push([ - [ - [new BigNumber(++value), new BigNumber(++value)], - [new BigNumber(++value), new BigNumber(++value)], - ], - [ - [new BigNumber(++value), new BigNumber(++value)], - [new BigNumber(++value), new BigNumber(++value)], - ], - ]); - } - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; - expect(calldata).to.be.equal(expectedCalldata); - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Multidimensional Arrays / Dynamic Members', async () => { - // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.multiDimensionalArraysDynamicTypeAbi); - // Eight 3-dimensional arrays of string[2][2][2] - let value = 0; - const args = []; - const argsLength = 4; - for (let i = 0; i < argsLength; ++i) { - args.push([ - [ - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - ], - [ - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - [new BigNumber(++value).toString(), new BigNumber(++value).toString()], - ], - ]); - } - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Fixed Length Array / Dynamic Members', async () => { - // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); - const args = [['Brave', 'New', 'World']]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Fixed Length Array / Dynamic Members', async () => { - // Generaet calldata - const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); - const args = [['Brave', 'New', 'World']]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Unfixed Length Array / Dynamic Members ABI', async () => { - // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.dynamicArrayDynamicMembersAbi); - const args = [['Brave', 'New', 'World']]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Unfixed Length Array / Static Members ABI', async () => { - // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.dynamicArrayStaticMembersAbi); - const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Fixed Length Array / Static Members ABI', async () => { - // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); - const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Array ABI', async () => { - // Generate calldata - const method = new AbiEncoder.Method(AbiSamples.stringAbi); - const args = [['five', 'six', 'seven']]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Static Tuple', async () => { - // Generate calldata - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi); - const args = [[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Dynamic Tuple (Array input)', async () => { - // Generate calldata - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const args = [[new BigNumber(5), 'five']]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Dynamic Tuple (Object input)', async () => { - // Generate Calldata - // This is dynamic because it has dynamic members - const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); - const args = [[new BigNumber(5), 'five']]; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Large, Flat ABI', async () => { - // Construct calldata - const method = new AbiEncoder.Method(AbiSamples.largeFlatAbi); - const args = [ - new BigNumber(256745454), - new BigNumber(-256745454), - new BigNumber(434244), - '0x43', - '0x0001020304050607080911121314151617181920212223242526272829303132', - '0x0001020304050607080911121314151617181920212223242526272829303132080911121314151617181920212223242526272829303132', - 'Little peter piper piped a piping pepper pot', - '0xe41d2489571d322189246dafa5ebde1f4699f498', - true, - ]; - // Validate calldata - const calldata = method.encode(args); - const expectedCalldata = - '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - it('Large, Nested ABI', async () => { - // Construct Calldata - const method = new AbiEncoder.Method(AbiSamples.largeNestedAbi); - const someStaticArray = [new BigNumber(127), new BigNumber(14), new BigNumber(54)]; - const someStaticArrayWithDynamicMembers = [ - 'the little piping piper piped a piping pipper papper', - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ]; - const someDynamicArrayWithDynamicMembers = [ - '0x38745637834987324827439287423897238947239847', - '0x7283472398237423984723984729847248927498748974284728947239487498749847874329423743492347329847239842374892374892374892347238947289478947489374289472894738942749823743298742389472389473289472389437249823749823742893472398', - '0x283473298473248923749238742398742398472894729843278942374982374892374892743982', - ]; - const some2DArray = [ - [ - 'some string', - 'some another string', - 'there are just too many stringsup in', - 'here', - 'yall ghonna make me lose my mind', - ], - [ - 'the little piping piper piped a piping pipper papper', - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - ], - [], - ]; - const someTuple = { - someUint32: new BigNumber(4037824789), - someStr: - 'the kid knows how to write poems, what can I say -- I guess theres a lot I could say to try to fill this line with a lot of text.', - }; - const someTupleWithDynamicTypes = { - someUint: new BigNumber(4024789), - someStr: 'akdhjasjkdhasjkldshdjahdkjsahdajksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjk', - someBytes: '0x29384723894723843743289742389472398473289472348927489274894738427428947389facdea', - someAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498', - }; - const someTupleWithDynamicTypes2 = { - someUint: new BigNumber(9024789), - someStr: 'ksdhsajkdhsajkdhadjkashdjksadhajkdhsajkdhsadjkakdhjasjkdhasjkldshdjahdkjsahdaj', - someBytes: '0x29384723894398473289472348927489272384374328974238947274894738427428947389facde1', - someAddress: '0x746dafa5ebde1f4699f4981d3221892e41d24895', - }; - const someTupleWithDynamicTypes3 = { - someUint: new BigNumber(1024789), - someStr: 'sdhsajkdhsajkdhadjkashdjakdhjasjkdhasjkldshdjahdkjsahdajkksadhajkdhsajkdhsadjk', - someBytes: '0x38947238437432829384729742389472398473289472348927489274894738427428947389facdef', - someAddress: '0x89571d322189e415ebde1f4699f498d24246dafa', - }; - const someArrayOfTuplesWithDynamicTypes = [someTupleWithDynamicTypes2, someTupleWithDynamicTypes3]; - const args = { - someStaticArray, - someStaticArrayWithDynamicMembers, - someDynamicArrayWithDynamicMembers, - some2DArray, - someTuple, - someTupleWithDynamicTypes, - someArrayOfTuplesWithDynamicTypes, - }; - const calldata = method.encode(args); - // Validate calldata - const expectedCalldata = - '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; - expect(calldata).to.be.equal(expectedCalldata); - // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); - const decodedValue = method.decode(calldata, { structsAsObjects: true }); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); - }); - }); - - describe('Array', () => { - it('Fixed size; Static elements', async () => { - // Create DataType object - const testDataItem = { name: 'testArray', type: 'int[2]' }; - const dataType = new AbiEncoder.Array(testDataItem); - // Construct args to be encoded - const args = [new BigNumber(5), new BigNumber(6)]; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Dynamic size; Static elements', async () => { - // Create DataType object - const testDataItem = { name: 'testArray', type: 'int[]' }; - const dataType = new AbiEncoder.Array(testDataItem); - // Construct args to be encoded - const args = [new BigNumber(5), new BigNumber(6)]; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Fixed size; Dynamic elements', async () => { - // Create DataType object - const testDataItem = { name: 'testArray', type: 'string[2]' }; - const dataType = new AbiEncoder.Array(testDataItem); - // Construct args to be encoded - const args = ['Hello', 'world']; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Dynamic size; Dynamic elements', async () => { - // Create DataType object - const testDataItem = { name: 'testArray', type: 'string[]' }; - const dataType = new AbiEncoder.Array(testDataItem); - // Construct args to be encoded - const args = ['Hello', 'world']; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Dynamic Size; Multidimensional; Dynamic Elements', async () => { - // Create DataType object - const testDataItem = { name: 'testArray', type: 'bytes[][]' }; - const dataType = new AbiEncoder.Array(testDataItem); - // Construct args to be encoded - const array1 = ['0x01020304', '0x05060708', '0x09101112']; - const array2 = ['0x10111213', '0x14151617']; - const array3 = ['0x18192021']; - const args = [array1, array2, array3]; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000405060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000041011121300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414151617000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000041819202100000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Dynamic Size; Multidimensional; Static Elements', async () => { - // Create DataType object - const testDataItem = { name: 'testArray', type: 'bytes4[][]' }; - const dataType = new AbiEncoder.Array(testDataItem); - // Construct args to be encoded - const array1 = ['0x01020304', '0x05060708', '0x09101112']; - const array2 = ['0x10111213', '0x14151617']; - const array3 = ['0x18192021']; - const args = [array1, array2, array3]; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000301020304000000000000000000000000000000000000000000000000000000000506070800000000000000000000000000000000000000000000000000000000091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021011121300000000000000000000000000000000000000000000000000000000141516170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011819202100000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Static Size; Multidimensional; Static Elements', async () => { - // Create DataType object - const testDataItem = { name: 'testArray', type: 'bytes4[3][2]' }; - const dataType = new AbiEncoder.Array(testDataItem); - // Construct args to be encoded - const array1 = ['0x01020304', '0x05060708', '0x09101112']; - const array2 = ['0x10111213', '0x14151617', '0x18192021']; - const args = [array1, array2]; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x010203040000000000000000000000000000000000000000000000000000000005060708000000000000000000000000000000000000000000000000000000000910111200000000000000000000000000000000000000000000000000000000101112130000000000000000000000000000000000000000000000000000000014151617000000000000000000000000000000000000000000000000000000001819202100000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Static Size; Multidimensional; Dynamic Elements', async () => { - // Create DataType object - const testDataItem = { name: 'testArray', type: 'bytes[3][2]' }; - const dataType = new AbiEncoder.Array(testDataItem); - // Construct args to be encoded - const array1 = ['0x01020304', '0x05060708', '0x09101112']; - const array2 = ['0x10111213', '0x14151617', '0x18192021']; - const args = [array1, array2]; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000401020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004050607080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040910111200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000410111213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004141516170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041819202100000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Static size; Too Few Elements', async () => { - // Create DataType object - const testDataItem = { name: 'testArray', type: 'string[3]' }; - const dataType = new AbiEncoder.Array(testDataItem); - // Construct args to be encoded - const args = ['Hello', 'world']; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw('Expected array of 3 elements, but got array of length 2'); - }); - it('Static size; Too Many Elements', async () => { - // Create DataType object - const testDataItem = { name: 'testArray', type: 'string[1]' }; - const dataType = new AbiEncoder.Array(testDataItem); - // Construct args to be encoded - const args = ['Hello', 'world']; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw('Expected array of 1 elements, but got array of length 2'); - }); - it('Element Type Mismatch', async () => { - // Create DataType object - const testDataItem = { name: 'testArray', type: 'uint[]' }; - const dataType = new AbiEncoder.Array(testDataItem); - // Construct args to be encoded - const args = [new BigNumber(1), 'Bad Argument']; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw(); - }); - }); - - describe('Tuple', () => { - it('Static elements only', async () => { - // Create DataType object - const testDataItem = { - name: 'Tuple', - type: 'tuple', - components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }], - }; - const dataType = new AbiEncoder.Tuple(testDataItem); - // Construct args to be encoded - const args = { field_1: new BigNumber(-5), field_2: true }; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000001'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; - const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Dynamic elements only', async () => { - // Create DataType object - const testDataItem = { - name: 'Tuple', - type: 'tuple', - components: [{ name: 'field_1', type: 'string' }, { name: 'field_2', type: 'bytes' }], - }; - const dataType = new AbiEncoder.Tuple(testDataItem); - // Construct args to be encoded - const args = { field_1: 'Hello, World!', field_2: '0xabcdef0123456789' }; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; - const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Nested Static Array', async () => { - // Create DataType object - const testDataItem = { - name: 'Tuple', - type: 'tuple', - components: [{ name: 'field', type: 'uint[2]' }], - }; - const dataType = new AbiEncoder.Tuple(testDataItem); - // Construct args to be encoded - const args = { field: [new BigNumber(1), new BigNumber(2)] }; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; - const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Nested Dynamic Array', async () => { - // Create DataType object - const testDataItem = { - name: 'Tuple', - type: 'tuple', - components: [{ name: 'field', type: 'uint[]' }], - }; - const dataType = new AbiEncoder.Tuple(testDataItem); - // Construct args to be encoded - const args = { field: [new BigNumber(1), new BigNumber(2)] }; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; - const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Nested Static Multidimensional Array', async () => { - // Create DataType object - const testDataItem = { - name: 'Tuple', - type: 'tuple', - components: [{ name: 'field', type: 'bytes4[2][2]' }], - }; - const dataType = new AbiEncoder.Tuple(testDataItem); - // Construct args to be encoded - const array1 = ['0x01020304', '0x05060708']; - const array2 = ['0x09101112', '0x13141516']; - const args = { field: [array1, array2] }; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x0102030400000000000000000000000000000000000000000000000000000000050607080000000000000000000000000000000000000000000000000000000009101112000000000000000000000000000000000000000000000000000000001314151600000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; - const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Nested Dynamic Multidimensional Array', async () => { - // Create DataType object - const testDataItem = { - name: 'Tuple', - type: 'tuple', - components: [{ name: 'field', type: 'bytes[2][2]' }], - }; - const dataType = new AbiEncoder.Tuple(testDataItem); - // Construct args to be encoded - const array1 = ['0x01020304', '0x05060708']; - const array2 = ['0x09101112', '0x13141516']; - const args = { field: [array1, array2] }; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040506070800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041314151600000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; - const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Static and dynamic elements mixed', async () => { - // Create DataType object - const testDataItem = { - name: 'Tuple', - type: 'tuple', - components: [ - { name: 'field_1', type: 'int32' }, - { name: 'field_2', type: 'string' }, - { name: 'field_3', type: 'bool' }, - { name: 'field_4', type: 'bytes' }, - ], - }; - const dataType = new AbiEncoder.Tuple(testDataItem); - // Construct args to be encoded - const args = { - field_1: new BigNumber(-5), - field_2: 'Hello, World!', - field_3: true, - field_4: '0xabcdef0123456789', - }; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; - const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Missing Key', async () => { - // Create DataType object - const testDataItem = { - name: 'Tuple', - type: 'tuple', - components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }], - }; - const dataType = new AbiEncoder.Tuple(testDataItem); - // Construct args to be encoded - const args = { field_1: new BigNumber(-5) }; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw('Could not assign tuple to object: missing keys field_2'); - }); - it('Bad Key', async () => { - // Create DataType object - const testDataItem = { - name: 'Tuple', - type: 'tuple', - components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }], - }; - const dataType = new AbiEncoder.Tuple(testDataItem); - // Construct args to be encoded - const args = { unknown_field: new BigNumber(-5) }; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw("Could not assign tuple to object: unrecognized key 'unknown_field' in object Tuple"); - }); - }); - - describe('Address', () => { - it('Valid Address', async () => { - // Create DataType object - const testDataItem = { name: 'Address', type: 'address' }; - const dataType = new AbiEncoder.Address(testDataItem); - // Construct args to be encoded - const args = '0xe41d2489571d322189246dafa5ebde1f4699f498'; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Invalid Address - input is not valid hex', async () => { - // Create DataType object - const testDataItem = { name: 'Address', type: 'address' }; - const dataType = new AbiEncoder.Address(testDataItem); - // Construct args to be encoded - const args = 'e4'; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); - }); - it('Invalid Address - input is not 20 bytes', async () => { - // Create DataType object - const testDataItem = { name: 'Address', type: 'address' }; - const dataType = new AbiEncoder.Address(testDataItem); - // Construct args to be encoded - const args = '0xe4'; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); - }); - }); - - describe('Bool', () => { - it('True', async () => { - // Create DataType object - const testDataItem = { name: 'Boolean', type: 'bool' }; - const dataType = new AbiEncoder.Bool(testDataItem); - // Construct args to be encoded - const args = true; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('False', async () => { - // Create DataType object - const testDataItem = { name: 'Boolean', type: 'bool' }; - const dataType = new AbiEncoder.Bool(testDataItem); - // Construct args to be encoded - const args = false; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - }); - - describe('Integer', () => { - /* tslint:disable custom-no-magic-numbers */ - const max256BitInteger = new BigNumber(2).pow(255).minus(1); - const min256BitInteger = new BigNumber(2).pow(255).times(-1); - const max32BitInteger = new BigNumber(2).pow(31).minus(1); - const min32BitInteger = new BigNumber(2).pow(31).times(-1); - /* tslint:enable custom-no-magic-numbers */ - - it('Int256 - Positive Base Case', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (256)', type: 'int' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = new BigNumber(1); - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Int256 - Negative Base Case', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (256)', type: 'int' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = new BigNumber(-1); - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Int256 - Positive Value', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (256)', type: 'int' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = max256BitInteger; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Int256 - Negative Value', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (256)', type: 'int' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = min256BitInteger; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = `0x8000000000000000000000000000000000000000000000000000000000000000`; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Int256 - Value too large', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (256)', type: 'int' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = max256BitInteger.plus(1); - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw(); - }); - it('Int256 - Value too small', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (256)', type: 'int' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = min256BitInteger.minus(1); - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw(); - }); - it('Int32 - Positive Base Case', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (32)', type: 'int32' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = new BigNumber(1); - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Int32 - Negative Base Case', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (32)', type: 'int32' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = new BigNumber(-1); - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Int32 - Positive Value', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (32)', type: 'int32' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = max32BitInteger; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000007fffffff'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Int32 - Negative Value', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (32)', type: 'int32' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = min32BitInteger; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000`; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Int32 - Value too large', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (32)', type: 'int32' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = max32BitInteger.plus(1); - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw(); - }); - it('Int32 - Value too small', async () => { - // Create DataType object - const testDataItem = { name: 'Integer (32)', type: 'int32' }; - const dataType = new AbiEncoder.Int(testDataItem); - // Construct args to be encoded - const args = min32BitInteger.minus(1); - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw(); - }); - }); - - describe('Unsigned Integer', () => { - /* tslint:disable custom-no-magic-numbers */ - const max256BitUnsignedInteger = new BigNumber(2).pow(256).minus(1); - const min256BitUnsignedInteger = new BigNumber(0); - const max32BitUnsignedInteger = new BigNumber(2).pow(32).minus(1); - const min32BitUnsignedInteger = new BigNumber(0); - /* tslint:enable custom-no-magic-numbers */ - - it('UInt256 - Positive Base Case', async () => { - // Create DataType object - const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; - const dataType = new AbiEncoder.UInt(testDataItem); - // Construct args to be encoded - const args = new BigNumber(1); - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('UInt256 - Positive Value', async () => { - // Create DataType object - const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; - const dataType = new AbiEncoder.UInt(testDataItem); - // Construct args to be encoded - const args = max256BitUnsignedInteger; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('UInt256 - Zero Value', async () => { - // Create DataType object - const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; - const dataType = new AbiEncoder.UInt(testDataItem); - // Construct args to be encoded - const args = min256BitUnsignedInteger; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = `0x0000000000000000000000000000000000000000000000000000000000000000`; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('UInt256 - Value too large', async () => { - // Create DataType object - const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; - const dataType = new AbiEncoder.UInt(testDataItem); - // Construct args to be encoded - const args = max256BitUnsignedInteger.plus(1); - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw(); - }); - it('UInt256 - Value too small', async () => { - // Create DataType object - const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' }; - const dataType = new AbiEncoder.UInt(testDataItem); - // Construct args to be encoded - const args = min256BitUnsignedInteger.minus(1); - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw(); - }); - it('UInt32 - Positive Base Case', async () => { - // Create DataType object - const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; - const dataType = new AbiEncoder.UInt(testDataItem); - // Construct args to be encoded - const args = new BigNumber(1); - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('UInt32 - Positive Value', async () => { - // Create DataType object - const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; - const dataType = new AbiEncoder.UInt(testDataItem); - // Construct args to be encoded - const args = max32BitUnsignedInteger; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000ffffffff'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('UInt32 - Zero Value', async () => { - // Create DataType object - const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; - const dataType = new AbiEncoder.UInt(testDataItem); - // Construct args to be encoded - const args = min32BitUnsignedInteger; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = `0x0000000000000000000000000000000000000000000000000000000000000000`; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('UInt32 - Value too large', async () => { - // Create DataType object - const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; - const dataType = new AbiEncoder.UInt(testDataItem); - // Construct args to be encoded - const args = max32BitUnsignedInteger.plus(1); - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw(); - }); - it('UInt32 - Value too small', async () => { - // Create DataType object - const testDataItem = { name: 'Unsigned Integer (32)', type: 'uint32' }; - const dataType = new AbiEncoder.UInt(testDataItem); - // Construct args to be encoded - const args = min32BitUnsignedInteger.minus(1); - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw(); - }); - }); - - describe('Static Bytes', () => { - it('Single Byte (byte)', async () => { - // Create DataType object - const testDataItem = { name: 'Static Byte', type: 'byte' }; - const dataType = new AbiEncoder.StaticBytes(testDataItem); - // Construct args to be encoded - const args = '0x05'; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Single Byte (bytes1)', async () => { - // Create DataType object - const testDataItem = { name: 'Static Bytes1', type: 'bytes1' }; - const dataType = new AbiEncoder.StaticBytes(testDataItem); - // Construct args to be encoded - const args = '0x05'; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x0500000000000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('4 Bytes (bytes4)', async () => { - // Create DataType object - const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; - const dataType = new AbiEncoder.StaticBytes(testDataItem); - // Construct args to be encoded - const args = '0x00010203'; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x0001020300000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('4 Bytes (bytes4); Encoder must pad input', async () => { - // Create DataType object - const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; - const dataType = new AbiEncoder.StaticBytes(testDataItem); - // Construct args to be encoded - // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. - const args = '0x1a18'; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x1a18000000000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const paddedArgs = '0x1a180000'; - const paddedArgsAsJson = JSON.stringify(paddedArgs); - expect(decodedArgsAsJson).to.be.equal(paddedArgsAsJson); - }); - it('32 Bytes (bytes32)', async () => { - // Create DataType object - const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; - const dataType = new AbiEncoder.StaticBytes(testDataItem); - // Construct args to be encoded - const args = '0x0001020304050607080911121314151617181920212223242526272829303132'; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x0001020304050607080911121314151617181920212223242526272829303132'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('32 Bytes (bytes32); Encoder must pad input', async () => { - // Create DataType object - const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; - const dataType = new AbiEncoder.StaticBytes(testDataItem); - // Construct args to be encoded - // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. - const args = '0x1a18bf61'; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const paddedArgs = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; - const paddedArgsAsJson = JSON.stringify(paddedArgs); - expect(decodedArgsAsJson).to.be.equal(paddedArgsAsJson); - }); - it('Should throw when pass in too many bytes (bytes4)', async () => { - // Create DataType object - const testDataItem = { name: 'Static Bytes4', type: 'bytes4' }; - const dataType = new AbiEncoder.StaticBytes(testDataItem); - // Construct args to be encoded - const args = '0x0102030405'; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw( - 'Tried to assign 0x0102030405 (5 bytes), which exceeds max bytes that can be stored in a bytes4', - ); - }); - it('Should throw when pass in too many bytes (bytes32)', async () => { - // Create DataType object - const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; - const dataType = new AbiEncoder.StaticBytes(testDataItem); - // Construct args to be encoded - const args = '0x010203040506070809101112131415161718192021222324252627282930313233'; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw( - 'Tried to assign 0x010203040506070809101112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32', - ); - }); - it('Should throw when pass in bad hex (no 0x prefix)', async () => { - // Create DataType object - const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; - const dataType = new AbiEncoder.StaticBytes(testDataItem); - // Construct args to be encoded - const args = '0102030405060708091011121314151617181920212223242526272829303132'; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix."); - }); - it('Should throw when pass in bad hex (include a half-byte)', async () => { - // Create DataType object - const testDataItem = { name: 'Static Bytes32', type: 'bytes32' }; - const dataType = new AbiEncoder.StaticBytes(testDataItem); - // Construct args to be encoded - const args = '0x010'; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); - }); - }); - - describe('Dynamic Bytes', () => { - it('Fits into one EVM word', async () => { - // Create DataType object - const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; - const dataType = new AbiEncoder.DynamicBytes(testDataItem); - // Construct args to be encoded - // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. - const args = '0x1a18bf61'; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Spans multiple EVM words', async () => { - // Create DataType object - const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; - const dataType = new AbiEncoder.DynamicBytes(testDataItem); - // Construct args to be encoded - // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. - const bytesLength = 40; - const args = '0x' + '61'.repeat(bytesLength); - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Input as Buffer', async () => { - // Create DataType object - const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' }; - const dataType = new AbiEncoder.DynamicBytes(testDataItem); - // Construct args to be encoded - // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. - const args = '0x1a18bf61'; - const argsAsBuffer = ethUtil.toBuffer(args); - // Encode Args and validate result - const encodedArgs = dataType.encode(argsAsBuffer); - const expectedEncodedArgs = - '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Should throw when pass in bad hex (no 0x prefix)', async () => { - // Create DataType object - const testDataItem = { name: 'Static Bytes', type: 'bytes' }; - const dataType = new AbiEncoder.DynamicBytes(testDataItem); - // Construct args to be encoded - const args = '01'; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '01'"); - }); - it('Should throw when pass in bad hex (include a half-byte)', async () => { - // Create DataType object - const testDataItem = { name: 'Static Bytes', type: 'bytes' }; - const dataType = new AbiEncoder.DynamicBytes(testDataItem); - // Construct args to be encoded - const args = '0x010'; - // Encode Args and validate result - expect(() => { - dataType.encode(args); - }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); - }); - }); - - describe('String', () => { - it('Fits into one EVM word', async () => { - // Create DataType object - const testDataItem = { name: 'String', type: 'string' }; - const dataType = new AbiEncoder.String(testDataItem); - // Construct args to be encoded - // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. - const args = 'five'; - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('Spans multiple EVM words', async () => { - // Create DataType object - const testDataItem = { name: 'String', type: 'string' }; - const dataType = new AbiEncoder.String(testDataItem); - // Construct args to be encoded - // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. - const bytesLength = 40; - const args = 'a'.repeat(bytesLength); - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - it('String that begins with 0x prefix', async () => { - // Create DataType object - const testDataItem = { name: 'String', type: 'string' }; - const dataType = new AbiEncoder.String(testDataItem); - // Construct args to be encoded - // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. - const strLength = 40; - const args = '0x' + 'a'.repeat(strLength); - // Encode Args and validate result - const encodedArgs = dataType.encode(args); - const expectedEncodedArgs = - '0x000000000000000000000000000000000000000000000000000000000000002a30786161616161616161616161616161616161616161616161616161616161616161616161616161616100000000000000000000000000000000000000000000'; - expect(encodedArgs).to.be.equal(expectedEncodedArgs); - // Decode Encoded Args and validate result - const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); - }); - }); -}); diff --git a/packages/utils/test/abi_samples.ts b/packages/utils/test/abi_samples.ts deleted file mode 100644 index fc552c127..000000000 --- a/packages/utils/test/abi_samples.ts +++ /dev/null @@ -1,780 +0,0 @@ -/* tslint:disable max-file-line-count */ -import { MethodAbi } from 'ethereum-types'; - -export const simpleAbi: MethodAbi = { - constant: false, - inputs: [ - { - name: 'greg', - type: 'uint256', - }, - { - name: 'gregStr', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const stringAbi: MethodAbi = { - constant: false, - inputs: [ - { - name: 'greg', - type: 'string[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const GAbi: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'a', - type: 'uint256', - }, - { - name: 'b', - type: 'string', - }, - { - name: 'e', - type: 'bytes', - }, - { - name: 'f', - type: 'address', - }, - ], - - name: 'f', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const typesWithDefaultWidthsAbi: MethodAbi = { - constant: false, - inputs: [ - { - name: 'someUint', - type: 'uint', - }, - { - name: 'someInt', - type: 'int', - }, - { - name: 'someByte', - type: 'byte', - }, - { - name: 'someUint', - type: 'uint[]', - }, - { - name: 'someInt', - type: 'int[]', - }, - { - name: 'someByte', - type: 'byte[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const multiDimensionalArraysStaticTypeAbi: MethodAbi = { - constant: false, - inputs: [ - { - name: 'a', - type: 'uint8[][][]', - }, - { - name: 'b', - type: 'uint8[][][2]', - }, - { - name: 'c', - type: 'uint8[][2][]', - }, - { - name: 'd', - type: 'uint8[2][][]', - }, - { - name: 'e', - type: 'uint8[][2][2]', - }, - { - name: 'f', - type: 'uint8[2][2][]', - }, - { - name: 'g', - type: 'uint8[2][][2]', - }, - { - name: 'h', - type: 'uint8[2][2][2]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const multiDimensionalArraysDynamicTypeAbi: MethodAbi = { - constant: false, - inputs: [ - { - name: 'a', - type: 'string[][][]', - }, - { - name: 'b', - type: 'string[][][2]', - }, - { - name: 'c', - type: 'string[][2][]', - }, - { - name: 'h', - type: 'string[2][2][2]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const dynamicTupleAbi: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - ], - name: 'order', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const arrayOfStaticTuplesWithDefinedLengthAbi: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someUint2', - type: 'uint256', - }, - ], - name: 'order', - type: 'tuple[8]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const arrayOfStaticTuplesWithDynamicLengthAbi: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someUint2', - type: 'uint256', - }, - ], - name: 'order', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const arrayOfDynamicTuplesWithDefinedLengthAbi: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[8]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const arrayOfDynamicTuplesWithUndefinedLengthAbi: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const arrayOfDynamicTuplesAbi: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const multidimensionalArrayOfDynamicTuplesAbi: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'order', - type: 'tuple[][2][]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const staticTupleAbi: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'someUint1', - type: 'uint256', - }, - { - name: 'someUint2', - type: 'uint256', - }, - { - name: 'someUint3', - type: 'uint256', - }, - { - name: 'someBool', - type: 'bool', - }, - ], - name: 'order', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const staticArrayAbi: MethodAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'uint8[3]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const staticArrayDynamicMembersAbi: MethodAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'string[3]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const dynamicArrayDynamicMembersAbi: MethodAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'string[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const dynamicArrayStaticMembersAbi: MethodAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'uint8[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const largeFlatAbi: MethodAbi = { - constant: false, - inputs: [ - { - name: 'someUInt256', - type: 'uint256', - }, - { - name: 'someInt256', - type: 'int256', - }, - { - name: 'someInt32', - type: 'int32', - }, - { - name: 'someByte', - type: 'byte', - }, - { - name: 'someBytes32', - type: 'bytes32', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someString', - type: 'string', - }, - { - name: 'someAddress', - type: 'address', - }, - { - name: 'someBool', - type: 'bool', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const largeNestedAbi: MethodAbi = { - constant: false, - inputs: [ - { - name: 'someStaticArray', - type: 'uint8[3]', - }, - { - name: 'someStaticArrayWithDynamicMembers', - type: 'string[2]', - }, - { - name: 'someDynamicArrayWithDynamicMembers', - type: 'bytes[]', - }, - { - name: 'some2DArray', - type: 'string[][]', - }, - { - name: 'someTuple', - type: 'tuple', - components: [ - { - name: 'someUint32', - type: 'uint32', - }, - { - name: 'someStr', - type: 'string', - }, - ], - }, - { - name: 'someTupleWithDynamicTypes', - type: 'tuple', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - /*{ - name: 'someStrArray', - type: 'string[]', - },*/ - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - }, - { - name: 'someArrayOfTuplesWithDynamicTypes', - type: 'tuple[]', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - /*{ - name: 'someStrArray', - type: 'string[]', - },*/ - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const nestedTuples: MethodAbi = { - constant: false, - inputs: [ - { - name: 'firstTuple', - type: 'tuple[1]', - components: [ - { - name: 'someUint32', - type: 'uint32', - }, - { - name: 'nestedTuple', - type: 'tuple', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - }, - ], - }, - { - name: 'secondTuple', - type: 'tuple[]', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - { - name: 'nestedTuple', - type: 'tuple', - components: [ - { - name: 'someUint32', - type: 'uint32', - }, - { - name: 'secondNestedTuple', - type: 'tuple', - components: [ - { - name: 'someUint', - type: 'uint256', - }, - { - name: 'someStr', - type: 'string', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - }, - ], - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someAddress', - type: 'address', - }, - ], - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const simpleAbi2: MethodAbi = { - constant: false, - inputs: [ - { - name: 'someByte', - type: 'byte', - }, - { - name: 'someBytes32', - type: 'bytes32', - }, - { - name: 'someBytes', - type: 'bytes', - }, - { - name: 'someString', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const fillOrderAbi: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'makerAddress', - type: 'address', - }, - { - name: 'takerAddress', - type: 'address', - }, - { - name: 'feeRecipientAddress', - type: 'address', - }, - { - name: 'senderAddress', - type: 'address', - }, - { - name: 'makerAssetAmount', - type: 'uint256', - }, - { - name: 'takerAssetAmount', - type: 'uint256', - }, - { - name: 'makerFee', - type: 'uint256', - }, - { - name: 'takerFee', - type: 'uint256', - }, - { - name: 'expirationTimeSeconds', - type: 'uint256', - }, - { - name: 'salt', - type: 'uint256', - }, - { - name: 'makerAssetData', - type: 'bytes', - }, - { - name: 'takerAssetData', - type: 'bytes', - }, - ], - name: 'order', - type: 'tuple', - }, - { - name: 'takerAssetFillAmount', - type: 'uint256', - }, - { - name: 'salt', - type: 'uint256', - }, - { - name: 'orderSignature', - type: 'bytes', - }, - { - name: 'takerSignature', - type: 'bytes', - }, - ], - name: 'fillOrder', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; diff --git a/packages/utils/test/optimizer_abis.ts b/packages/utils/test/optimizer_abis.ts deleted file mode 100644 index 7cfd7a118..000000000 --- a/packages/utils/test/optimizer_abis.ts +++ /dev/null @@ -1,340 +0,0 @@ -/* tslint:disable max-file-line-count */ -import { MethodAbi } from 'ethereum-types'; - -export const duplicateDynamicArraysWithStaticElements: MethodAbi = { - constant: false, - inputs: [ - { - name: 'array1', - type: 'uint[]', - }, - { - name: 'array2', - type: 'uint[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const duplicateDynamicArraysWithDynamicElements: MethodAbi = { - constant: false, - inputs: [ - { - name: 'array1', - type: 'string[]', - }, - { - name: 'array2', - type: 'string[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const duplicateStaticArraysWithStaticElements: MethodAbi = { - constant: false, - inputs: [ - { - name: 'array1', - type: 'uint[2]', - }, - { - name: 'array2', - type: 'uint[2]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const duplicateStaticArraysWithDynamicElements: MethodAbi = { - constant: false, - inputs: [ - { - name: 'array1', - type: 'string[2]', - }, - { - name: 'array2', - type: 'string[2]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const duplicateArrayElements: MethodAbi = { - constant: false, - inputs: [ - { - name: 'array', - type: 'string[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const duplicateTupleFields: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'field1', - type: 'string', - }, - { - name: 'field2', - type: 'string', - }, - ], - name: 'Tuple', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const duplicateStrings: MethodAbi = { - constant: false, - inputs: [ - { - name: 'string1', - type: 'string', - }, - { - name: 'string2', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const duplicateBytes: MethodAbi = { - constant: false, - inputs: [ - { - name: 'bytes1', - type: 'bytes', - }, - { - name: 'bytes2', - type: 'bytes', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const duplicateTuples: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'field1', - type: 'string', - }, - { - name: 'field2', - type: 'uint', - }, - ], - name: 'Tuple', - type: 'tuple', - }, - { - components: [ - { - name: 'field1', - type: 'string', - }, - { - name: 'field2', - type: 'uint', - }, - ], - name: 'Tuple', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const duplicateArraysNestedInTuples: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - name: 'field', - type: 'uint[]', - }, - ], - name: 'Tuple1', - type: 'tuple', - }, - { - components: [ - { - name: 'field', - type: 'uint[]', - }, - { - name: 'extraField', - type: 'string', - }, - ], - name: 'Tuple2', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const duplicateTuplesNestedInTuples: MethodAbi = { - constant: false, - inputs: [ - { - components: [ - { - components: [ - { - name: 'nestedField', - type: 'string', - }, - ], - name: 'field', - type: 'tuple', - }, - ], - name: 'Tuple1', - type: 'tuple', - }, - { - components: [ - { - components: [ - { - name: 'nestedField', - type: 'string', - }, - ], - name: 'field', - type: 'tuple', - }, - { - name: 'extraField', - type: 'string', - }, - ], - name: 'Tuple1', - type: 'tuple', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const duplicateTwoDimensionalArrays: MethodAbi = { - constant: false, - inputs: [ - { - name: 'array1', - type: 'string[][]', - }, - { - name: 'array2', - type: 'string[][]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const arrayElementsDuplicatedAsSeparateParameter: MethodAbi = { - constant: false, - inputs: [ - { - name: 'stringArray', - type: 'string[]', - }, - { - name: 'string', - type: 'string', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const arrayElementsDuplicatedAsTupleFields: MethodAbi = { - constant: false, - inputs: [ - { - name: 'uint8Array', - type: 'uint8[]', - }, - { - components: [ - { - name: 'uint', - type: 'uint', - }, - ], - name: 'uintTuple', - type: 'tuple[]', - }, - ], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; diff --git a/packages/utils/test/return_value_abis.ts b/packages/utils/test/return_value_abis.ts deleted file mode 100644 index ac2124011..000000000 --- a/packages/utils/test/return_value_abis.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* tslint:disable max-file-line-count */ -import { MethodAbi } from 'ethereum-types'; - -export const noReturnValues: MethodAbi = { - constant: false, - inputs: [], - name: 'simpleFunction', - outputs: [], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const singleStaticReturnValue: MethodAbi = { - constant: false, - inputs: [], - name: 'simpleFunction', - outputs: [ - { - name: 'Bytes4', - type: 'bytes4', - }, - ], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const multipleStaticReturnValues: MethodAbi = { - constant: false, - inputs: [], - name: 'simpleFunction', - outputs: [ - { - name: 'val1', - type: 'bytes4', - }, - { - name: 'val2', - type: 'bytes4', - }, - ], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const singleDynamicReturnValue: MethodAbi = { - constant: false, - inputs: [], - name: 'simpleFunction', - outputs: [ - { - name: 'val', - type: 'bytes', - }, - ], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const multipleDynamicReturnValues: MethodAbi = { - constant: false, - inputs: [], - name: 'simpleFunction', - outputs: [ - { - name: 'val1', - type: 'bytes', - }, - { - name: 'val2', - type: 'bytes', - }, - ], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; - -export const mixedStaticAndDynamicReturnValues: MethodAbi = { - constant: false, - inputs: [], - name: 'simpleFunction', - outputs: [ - { - name: 'val1', - type: 'bytes4', - }, - { - name: 'val2', - type: 'bytes', - }, - ], - payable: false, - stateMutability: 'nonpayable', - type: 'function', -}; -- cgit v1.2.3 From a895dacd4e20238087245c274564f694c71f7f6e Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 14:18:01 -0800 Subject: moved abi encoder constants into utils dir --- .../src/abi_encoder/abstract_data_types/dependent_data_type.ts | 2 +- .../utils/src/abi_encoder/abstract_data_types/member_data_type.ts | 2 +- packages/utils/src/abi_encoder/calldata/calldata.ts | 2 +- .../utils/src/abi_encoder/calldata/dependent_calldata_block.ts | 2 +- packages/utils/src/abi_encoder/calldata/raw_calldata.ts | 2 +- packages/utils/src/abi_encoder/constants.ts | 8 -------- packages/utils/src/abi_encoder/evm_data_types/address.ts | 4 ++-- packages/utils/src/abi_encoder/evm_data_types/array.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/bool.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/method.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/number.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts | 4 ++-- packages/utils/src/abi_encoder/evm_data_types/string.ts | 2 +- packages/utils/src/abi_encoder/utils/constants.ts | 8 ++++++++ 15 files changed, 23 insertions(+), 23 deletions(-) delete mode 100644 packages/utils/src/abi_encoder/constants.ts create mode 100644 packages/utils/src/abi_encoder/utils/constants.ts diff --git a/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts index e9f390b22..f4992dd0f 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts @@ -3,7 +3,7 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { CalldataBlock, DependentCalldataBlock, RawCalldata } from '../calldata'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; import { DecodingRules } from '../utils/rules'; import { DataType } from './data_type'; diff --git a/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts index f44a6dd30..318564e21 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { CalldataBlock, MemberCalldataBlock, RawCalldata } from '../calldata'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; import { DecodingRules } from '../utils/rules'; import { DataType } from './data_type'; diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index 1abf1b681..154a81b7f 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -1,7 +1,7 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; import { Queue } from '../utils/queue'; import { EncodingRules } from '../utils/rules'; diff --git a/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts b/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts index 4aec5eabc..16b9a6fe6 100644 --- a/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts +++ b/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts @@ -1,6 +1,6 @@ import * as ethUtil from 'ethereumjs-util'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; import { CalldataBlock } from './calldata_block'; diff --git a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts index b7bd35737..27a59c6a3 100644 --- a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts @@ -1,6 +1,6 @@ import * as ethUtil from 'ethereumjs-util'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; import { Queue } from '../utils/queue'; export class RawCalldata { diff --git a/packages/utils/src/abi_encoder/constants.ts b/packages/utils/src/abi_encoder/constants.ts deleted file mode 100644 index 3d85fbdb8..000000000 --- a/packages/utils/src/abi_encoder/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const EVM_WORD_WIDTH_IN_BYTES = 32; -export const EVM_WORD_WIDTH_IN_BITS = 256; -export const HEX_BASE = 16; -export const DEC_BASE = 10; -export const BIN_BASE = 2; -export const HEX_SELECTOR_LENGTH_IN_CHARS = 10; -export const HEX_SELECTOR_LENGTH_IN_BYTES = 4; -export const HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA = 0; diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 0107fdc50..aab5a0605 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -5,7 +5,7 @@ import * as _ from 'lodash'; import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; export class Address extends PayloadDataType { public static ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X = "Address must start with '0x'"; @@ -13,7 +13,7 @@ export class Address extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _ADDRESS_SIZE_IN_BYTES = 20; private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = Constants.EVM_WORD_WIDTH_IN_BYTES - - Address._ADDRESS_SIZE_IN_BYTES; + Address._ADDRESS_SIZE_IN_BYTES; public static matchType(type: string): boolean { return type === 'address'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts index f334282b8..9963b6f32 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/array.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -1,7 +1,7 @@ import { DataItem } from 'ethereum-types'; import { DataTypeFactory, MemberDataType } from '../abstract_data_types'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; export class Array extends MemberDataType { private static readonly _matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index 4b9dd32b1..6bc299544 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -6,7 +6,7 @@ import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; export class Bool extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index 5fca668e4..626e266c9 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -5,7 +5,7 @@ import * as _ from 'lodash'; import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; export class DynamicBytes extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts index ce33755f1..50d676b4a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/method.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { DataType, DataTypeFactory, MemberDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; import { DecodingRules, EncodingRules } from '../utils/rules'; import { StaticBytes } from './static_bytes'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/number.ts b/packages/utils/src/abi_encoder/evm_data_types/number.ts index 8ff5e9a6a..86acdce07 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/number.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/number.ts @@ -5,7 +5,7 @@ import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; export abstract class Number extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 4e49db609..77dde1333 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; export class StaticBytes extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; @@ -46,7 +46,7 @@ export class StaticBytes extends PayloadDataType { if (valueBuf.byteLength > this._width) { throw new Error( `Tried to assign ${value} (${ - valueBuf.byteLength + valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); } else if (value.length % 2 !== 0) { diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index c0bde8649..47ad7cb97 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -5,7 +5,7 @@ import * as _ from 'lodash'; import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../constants'; +import * as Constants from '../utils/constants'; export class String extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; diff --git a/packages/utils/src/abi_encoder/utils/constants.ts b/packages/utils/src/abi_encoder/utils/constants.ts new file mode 100644 index 000000000..3d85fbdb8 --- /dev/null +++ b/packages/utils/src/abi_encoder/utils/constants.ts @@ -0,0 +1,8 @@ +export const EVM_WORD_WIDTH_IN_BYTES = 32; +export const EVM_WORD_WIDTH_IN_BITS = 256; +export const HEX_BASE = 16; +export const DEC_BASE = 10; +export const BIN_BASE = 2; +export const HEX_SELECTOR_LENGTH_IN_CHARS = 10; +export const HEX_SELECTOR_LENGTH_IN_BYTES = 4; +export const HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA = 0; -- cgit v1.2.3 From dcc439c2e3a756af75889ddf3b22146322d1d97d Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 16:28:37 -0800 Subject: Ran prettier --- packages/utils/src/abi_encoder/evm_data_types/address.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index aab5a0605..9ae22bd9c 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -13,7 +13,7 @@ export class Address extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _ADDRESS_SIZE_IN_BYTES = 20; private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = Constants.EVM_WORD_WIDTH_IN_BYTES - - Address._ADDRESS_SIZE_IN_BYTES; + Address._ADDRESS_SIZE_IN_BYTES; public static matchType(type: string): boolean { return type === 'address'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 77dde1333..9a2a99ec7 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -46,7 +46,7 @@ export class StaticBytes extends PayloadDataType { if (valueBuf.byteLength > this._width) { throw new Error( `Tried to assign ${value} (${ - valueBuf.byteLength + valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); } else if (value.length % 2 !== 0) { -- cgit v1.2.3 From 978a58105cd2d2f5d6ee3bcd870218fd357fb010 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 16:34:41 -0800 Subject: Prepended `front` to function names in Queue --- packages/utils/src/abi_encoder/calldata/calldata.ts | 14 +++++++------- packages/utils/src/abi_encoder/calldata/raw_calldata.ts | 6 +++--- packages/utils/src/abi_encoder/utils/queue.ts | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index 154a81b7f..3b85f821b 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -19,7 +19,7 @@ export class Calldata { // Base Case if (!(block instanceof CalldataBlocks.MemberCalldataBlock)) { - blockQueue.push(block); + blockQueue.pushBack(block); return blockQueue; } @@ -38,9 +38,9 @@ export class Calldata { if (member instanceof CalldataBlocks.DependentCalldataBlock && member.getAlias() === undefined) { const dependency = member.getDependency(); if (dependency instanceof CalldataBlocks.MemberCalldataBlock) { - blockQueue.merge(Calldata._createQueue(dependency)); + blockQueue.mergeBack(Calldata._createQueue(dependency)); } else { - blockQueue.push(dependency); + blockQueue.pushBack(dependency); } } }); @@ -100,7 +100,7 @@ export class Calldata { const offsetQueue = Calldata._createQueue(this._root); let block: CalldataBlock | undefined; let offset = 0; - for (block = offsetQueue.pop(); block !== undefined; block = offsetQueue.pop()) { + for (block = offsetQueue.popFront(); block !== undefined; block = offsetQueue.popFront()) { block.setOffset(offset); offset += block.getSizeInBytes(); } @@ -140,9 +140,9 @@ export class Calldata { let block: CalldataBlock | undefined; let offset = 0; - const functionBlock = valueQueue.peek(); + const functionBlock = valueQueue.peekFront(); const functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); - for (block = valueQueue.pop(); block !== undefined; block = valueQueue.pop()) { + for (block = valueQueue.popFront(); block !== undefined; block = valueQueue.popFront()) { // Process each block 1 word at a time const size = block.getSizeInBytes(); const name = block.getName(); @@ -212,7 +212,7 @@ export class Calldata { const valueQueue = Calldata._createQueue(this._root); const valueBufs: Buffer[] = [selectorBuffer]; let block: CalldataBlock | undefined; - for (block = valueQueue.pop(); block !== undefined; block = valueQueue.pop()) { + for (block = valueQueue.popFront(); block !== undefined; block = valueQueue.popFront()) { valueBufs.push(block.toBuffer()); } diff --git a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts index 27a59c6a3..9e72bbd62 100644 --- a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts @@ -26,7 +26,7 @@ export class RawCalldata { } this._scopes = new Queue(); - this._scopes.push(RawCalldata._INITIAL_OFFSET); + this._scopes.pushBack(RawCalldata._INITIAL_OFFSET); this._offset = RawCalldata._INITIAL_OFFSET; } @@ -60,7 +60,7 @@ export class RawCalldata { } public endScope(): void { - this._scopes.pop(); + this._scopes.popFront(); } public getOffset(): number { @@ -68,7 +68,7 @@ export class RawCalldata { } public toAbsoluteOffset(relativeOffset: number): number { - const scopeOffset = this._scopes.peek(); + const scopeOffset = this._scopes.peekFront(); if (scopeOffset === undefined) { throw new Error(`Tried to access undefined scope.`); } diff --git a/packages/utils/src/abi_encoder/utils/queue.ts b/packages/utils/src/abi_encoder/utils/queue.ts index 3309d8ba2..53afb7e11 100644 --- a/packages/utils/src/abi_encoder/utils/queue.ts +++ b/packages/utils/src/abi_encoder/utils/queue.ts @@ -1,7 +1,7 @@ export class Queue { private _store: T[] = []; - public push(val: T): void { + public pushBack(val: T): void { this._store.push(val); } @@ -9,7 +9,7 @@ export class Queue { this._store.unshift(val); } - public pop(): T | undefined { + public popFront(): T | undefined { return this._store.shift(); } @@ -21,7 +21,7 @@ export class Queue { return backElement; } - public merge(q: Queue): void { + public mergeBack(q: Queue): void { this._store = this._store.concat(q._store); } @@ -33,7 +33,7 @@ export class Queue { return this._store; } - public peek(): T | undefined { + public peekFront(): T | undefined { return this._store.length >= 0 ? this._store[0] : undefined; } } -- cgit v1.2.3 From c638151b73289fc936bb7d4323711d1954cc4fcb Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 20 Nov 2018 16:51:56 -0800 Subject: Some minor cleanup in ABI Encoder --- .../abi_encoder/abstract_data_types/data_type.ts | 2 +- .../abstract_data_types/dependent_data_type.ts | 7 +++-- .../abi_encoder/abstract_data_types/interfaces.ts | 4 +++ .../abstract_data_types/member_data_type.ts | 36 ++++++++++------------ .../abstract_data_types/payload_data_type.ts | 2 +- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts index a6adeb23b..c83f2085e 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts @@ -32,7 +32,7 @@ export abstract class DataType { calldata.setSelector(selector); } const block = this.generateCalldataBlock(value); - calldata.setRoot(block); // @TODO CHANGE + calldata.setRoot(block); const calldataHex = calldata.toHexString(); return calldataHex; } diff --git a/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts index f4992dd0f..7649b1836 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts @@ -28,16 +28,17 @@ export abstract class DependentDataType extends DataType { const dependencyBlock = this._dependency.generateCalldataBlock(value, parentBlock); const name = this.getDataItem().name; const signature = this.getSignature(); - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const parentName = parentBlock ? parentBlock.getName() : ''; const block = new DependentCalldataBlock(name, signature, parentName, dependencyBlock, parentBlock); return block; } public generateValue(calldata: RawCalldata, rules: DecodingRules): any { const destinationOffsetBuf = calldata.popWord(); - const currentOffset = calldata.getOffset(); - const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), Constants.HEX_BASE); + const destinationOffsetHex = ethUtil.bufferToHex(destinationOffsetBuf); + const destinationOffsetRelative = parseInt(destinationOffsetHex, Constants.HEX_BASE); const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); + const currentOffset = calldata.getOffset(); calldata.setOffset(destinationOffsetAbsolute); const value = this._dependency.generateValue(calldata, rules); calldata.setOffset(currentOffset); diff --git a/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts b/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts index 2ae92659c..9e2a94522 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts @@ -14,3 +14,7 @@ export interface DataTypeStaticInterface { encodeValue: (value: any) => Buffer; decodeValue: (rawCalldata: RawCalldata) => any; } + +export interface MemberIndexByName { + [key: string]: number; +} diff --git a/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts index 318564e21..dacdbf8af 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts @@ -9,16 +9,12 @@ import { DecodingRules } from '../utils/rules'; import { DataType } from './data_type'; import { DependentDataType } from './dependent_data_type'; -import { DataTypeFactory } from './interfaces'; - -interface MemberMap { - [key: string]: number; -} +import { DataTypeFactory, MemberIndexByName } from './interfaces'; export abstract class MemberDataType extends DataType { protected readonly _arrayLength: number | undefined; protected readonly _arrayElementType: string | undefined; - private readonly _memberMap: MemberMap; + private readonly _memberIndexByName: MemberIndexByName; private readonly _members: DataType[]; private readonly _isArray: boolean; @@ -30,15 +26,15 @@ export abstract class MemberDataType extends DataType { arrayElementType?: string, ) { super(dataItem, factory); - this._memberMap = {}; + this._memberIndexByName = {}; this._members = []; this._isArray = isArray; this._arrayLength = arrayLength; this._arrayElementType = arrayElementType; if (isArray && arrayLength !== undefined) { - [this._members, this._memberMap] = this._createMembersWithLength(dataItem, arrayLength); + [this._members, this._memberIndexByName] = this._createMembersWithLength(dataItem, arrayLength); } else if (!isArray) { - [this._members, this._memberMap] = this._createMembersWithKeys(dataItem); + [this._members, this._memberIndexByName] = this._createMembersWithKeys(dataItem); } } @@ -65,7 +61,7 @@ export abstract class MemberDataType extends DataType { let value: any[] | object; if (rules.structsAsObjects && !this._isArray) { value = {}; - _.each(this._memberMap, (idx: number, key: string) => { + _.each(this._memberIndexByName, (idx: number, key: string) => { const member = this._members[idx]; const memberValue = member.generateValue(calldata, rules); (value as { [key: string]: any })[key] = memberValue; @@ -149,14 +145,14 @@ export abstract class MemberDataType extends DataType { parentName, ); const memberBlocks: CalldataBlock[] = []; - const childMap = _.cloneDeep(this._memberMap); + const childMap = _.cloneDeep(this._memberIndexByName); _.forOwn(obj, (value: any, key: string) => { if (!(key in childMap)) { throw new Error( `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`, ); } - const block = this._members[this._memberMap[key]].generateCalldataBlock(value, methodBlock); + const block = this._members[this._memberIndexByName[key]].generateCalldataBlock(value, methodBlock); memberBlocks.push(block); delete childMap[key]; }); @@ -182,14 +178,14 @@ export abstract class MemberDataType extends DataType { return signature; } - private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { + private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberIndexByName] { // Sanity check if (dataItem.components === undefined) { throw new Error(`Expected components`); } const members: DataType[] = []; - const memberMap: MemberMap = {}; + const memberIndexByName: MemberIndexByName = {}; _.each(dataItem.components, (memberItem: DataItem) => { const childDataItem: DataItem = { type: memberItem.type, @@ -200,16 +196,16 @@ export abstract class MemberDataType extends DataType { childDataItem.components = components; } const child = this.getFactory().create(childDataItem, this); - memberMap[memberItem.name] = members.length; + memberIndexByName[memberItem.name] = members.length; members.push(child); }); - return [members, memberMap]; + return [members, memberIndexByName]; } - private _createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { + private _createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberIndexByName] { const members: DataType[] = []; - const memberMap: MemberMap = {}; + const memberIndexByName: MemberIndexByName = {}; const range = _.range(length); _.each(range, (idx: number) => { const childDataItem: DataItem = { @@ -221,10 +217,10 @@ export abstract class MemberDataType extends DataType { childDataItem.components = components; } const child = this.getFactory().create(childDataItem, this); - memberMap[idx.toString(Constants.DEC_BASE)] = members.length; + memberIndexByName[idx.toString(Constants.DEC_BASE)] = members.length; members.push(child); }); - return [members, memberMap]; + return [members, memberIndexByName]; } } diff --git a/packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts index 767e64f51..fa420bc74 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts @@ -19,7 +19,7 @@ export abstract class PayloadDataType extends DataType { const encodedValue = this.encodeValue(value); const name = this.getDataItem().name; const signature = this.getSignature(); - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const parentName = parentBlock ? parentBlock.getName() : ''; const block = new PayloadCalldataBlock(name, signature, parentName, encodedValue); return block; } -- cgit v1.2.3 From dc7092e1eb11ff9844efe02e367ef37592f38c55 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 15:32:13 -0800 Subject: Removed mapDataItemToDataType from Factory. Now its just ::create() --- .../abi_encoder/abstract_data_types/interfaces.ts | 1 - .../utils/src/abi_encoder/evm_data_type_factory.ts | 42 ++++++++++------------ .../utils/src/abi_encoder/evm_data_types/array.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/method.ts | 22 ++++-------- 4 files changed, 25 insertions(+), 42 deletions(-) diff --git a/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts b/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts index 9e2a94522..bd4d2effd 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts @@ -6,7 +6,6 @@ import { DataType } from './data_type'; export interface DataTypeFactory { create: (dataItem: DataItem, parentDataType?: DataType) => DataType; - mapDataItemToDataType: (dataItem: DataItem) => DataType; } export interface DataTypeStaticInterface { diff --git a/packages/utils/src/abi_encoder/evm_data_type_factory.ts b/packages/utils/src/abi_encoder/evm_data_type_factory.ts index 5d37acad9..bfe457367 100644 --- a/packages/utils/src/abi_encoder/evm_data_type_factory.ts +++ b/packages/utils/src/abi_encoder/evm_data_type_factory.ts @@ -82,42 +82,36 @@ export class EvmDataTypeFactory implements DataTypeFactory { return EvmDataTypeFactory._instance; } - public mapDataItemToDataType(dataItem: DataItem): DataType { + public create(dataItem: DataItem, parentDataType?: DataType): DataType { + // Create data type + let dataType: undefined | DataType; if (Array.matchType(dataItem.type)) { - return new Array(dataItem); + dataType = new Array(dataItem); } else if (Address.matchType(dataItem.type)) { - return new Address(dataItem); + dataType = new Address(dataItem); } else if (Bool.matchType(dataItem.type)) { - return new Bool(dataItem); + dataType = new Bool(dataItem); } else if (Int.matchType(dataItem.type)) { - return new Int(dataItem); + dataType = new Int(dataItem); } else if (UInt.matchType(dataItem.type)) { - return new UInt(dataItem); + dataType = new UInt(dataItem); } else if (StaticBytes.matchType(dataItem.type)) { - return new StaticBytes(dataItem); + dataType = new StaticBytes(dataItem); } else if (Tuple.matchType(dataItem.type)) { - return new Tuple(dataItem); + dataType = new Tuple(dataItem); } else if (DynamicBytes.matchType(dataItem.type)) { - return new DynamicBytes(dataItem); + dataType = new DynamicBytes(dataItem); } else if (String.matchType(dataItem.type)) { - return new String(dataItem); + dataType = new String(dataItem); } // @TODO: Implement Fixed/UFixed types - throw new Error(`Unrecognized data type: '${dataItem.type}'`); - } - - public create(dataItem: DataItem, parentDataType?: DataType): DataType { - const dataType = this.mapDataItemToDataType(dataItem); - if (dataType.isStatic()) { - return dataType; - } - - if (parentDataType === undefined) { - // @Todo -- will this work for return values? - throw new Error(`Trying to create a pointer`); + if (!dataType) { + throw new Error(`Unrecognized data type: '${dataItem.type}'`); + } else if (parentDataType && !dataType.isStatic()) { + const pointerToDataType = new Pointer(dataType, parentDataType); + return pointerToDataType; } - const pointer = new Pointer(dataType, parentDataType); - return pointer; + return dataType; } private constructor() {} diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts index 9963b6f32..dd8184fd0 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/array.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -44,7 +44,7 @@ export class Array extends MemberDataType { if (components !== undefined) { dataItem.components = components; } - const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); + const elementDataType = this.getFactory().create(dataItem); const type = elementDataType.getSignature(); if (this._arrayLength === undefined) { return `${type}[]`; diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts index 50d676b4a..671b80890 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/method.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts @@ -7,7 +7,6 @@ import { RawCalldata } from '../calldata'; import * as Constants from '../utils/constants'; import { DecodingRules, EncodingRules } from '../utils/rules'; -import { StaticBytes } from './static_bytes'; import { Tuple } from './tuple'; export class Method extends MemberDataType { @@ -16,19 +15,14 @@ export class Method extends MemberDataType { private readonly _methodSignature: string; private readonly _methodSelector: string; - private readonly _returnDataTypes: DataType[]; - private readonly _returnDataItem: DataItem; + private readonly _returnDataType: DataType; public constructor(abi: MethodAbi, dataTypeFactory: DataTypeFactory) { super({ type: 'method', name: abi.name, components: abi.inputs }, dataTypeFactory); this._methodSignature = this._computeSignature(); this.selector = this._methodSelector = this._computeSelector(); - this._returnDataTypes = []; - this._returnDataItem = { type: 'tuple', name: abi.name, components: abi.outputs }; - const dummy = new StaticBytes({ type: 'byte', name: 'DUMMY' }, dataTypeFactory); // @TODO TMP - _.each(abi.outputs, (dataItem: DataItem) => { - this._returnDataTypes.push(this.getFactory().create(dataItem, dummy)); - }); + const returnDataItem: DataItem = { type: 'tuple', name: abi.name, components: abi.outputs }; + this._returnDataType = new Tuple(returnDataItem, this.getFactory()); } public encode(value: any, rules?: EncodingRules): string { @@ -48,18 +42,14 @@ export class Method extends MemberDataType { } public encodeReturnValues(value: any, rules?: EncodingRules): string { - const returnDataType = new Tuple(this._returnDataItem, this.getFactory()); - const returndata = returnDataType.encode(value, rules); - return returndata; + const returnData = this._returnDataType.encode(value, rules); + return returnData; } public decodeReturnValues(returndata: string, rules?: DecodingRules): any { - const returnValues: any[] = []; const rules_: DecodingRules = rules ? rules : { structsAsObjects: false }; const rawReturnData = new RawCalldata(returndata, false); - _.each(this._returnDataTypes, (dataType: DataType) => { - returnValues.push(dataType.generateValue(rawReturnData, rules_)); - }); + const returnValues = this._returnDataType.generateValue(rawReturnData, rules_); return returnValues; } -- cgit v1.2.3 From dd8bb6d08b6e837304a76e9707b79e070f951e4e Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 15:38:29 -0800 Subject: Made default encoding/decoding rules global to all modules in encoder --- .../abi_encoder/abstract_data_types/data_type.ts | 7 +++---- .../utils/src/abi_encoder/evm_data_types/method.ts | 23 +++++++++++----------- packages/utils/src/abi_encoder/utils/constants.ts | 4 ++++ 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts index c83f2085e..dd166b19c 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts @@ -2,13 +2,12 @@ import { DataItem } from 'ethereum-types'; import * as _ from 'lodash'; import { Calldata, CalldataBlock, RawCalldata } from '../calldata'; +import * as Constants from '../utils/constants'; import { DecodingRules, EncodingRules } from '../utils/rules'; import { DataTypeFactory } from './interfaces'; export abstract class DataType { - private static readonly _DEFAULT_ENCODING_RULES: EncodingRules = { optimize: false, annotate: false }; - private static readonly _DEFAULT_DECODING_RULES: DecodingRules = { structsAsObjects: false }; private readonly _dataItem: DataItem; private readonly _factory: DataTypeFactory; @@ -26,7 +25,7 @@ export abstract class DataType { } public encode(value: any, rules?: EncodingRules, selector?: string): string { - const rules_ = rules ? rules : DataType._DEFAULT_ENCODING_RULES; + const rules_ = rules ? rules : Constants.DEFAULT_ENCODING_RULES; const calldata = new Calldata(rules_); if (selector) { calldata.setSelector(selector); @@ -39,7 +38,7 @@ export abstract class DataType { public decode(calldata: string, rules?: DecodingRules, hasSelector: boolean = false): any { const rawCalldata = new RawCalldata(calldata, hasSelector); - const rules_ = rules ? rules : DataType._DEFAULT_DECODING_RULES; + const rules_ = rules ? rules : Constants.DEFAULT_DECODING_RULES; const value = this.generateValue(rawCalldata, rules_); return value; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts index 671b80890..2faffd44e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/method.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts @@ -10,30 +10,28 @@ import { DecodingRules, EncodingRules } from '../utils/rules'; import { Tuple } from './tuple'; export class Method extends MemberDataType { - // TMP - public selector: string; - private readonly _methodSignature: string; private readonly _methodSelector: string; private readonly _returnDataType: DataType; public constructor(abi: MethodAbi, dataTypeFactory: DataTypeFactory) { - super({ type: 'method', name: abi.name, components: abi.inputs }, dataTypeFactory); + const methodDataItem = { type: 'method', name: abi.name, components: abi.inputs }; + super(methodDataItem, dataTypeFactory); this._methodSignature = this._computeSignature(); - this.selector = this._methodSelector = this._computeSelector(); + this._methodSelector = this._computeSelector(); const returnDataItem: DataItem = { type: 'tuple', name: abi.name, components: abi.outputs }; this._returnDataType = new Tuple(returnDataItem, this.getFactory()); } public encode(value: any, rules?: EncodingRules): string { - const calldata = super.encode(value, rules, this.selector); + const calldata = super.encode(value, rules, this._methodSelector); return calldata; } public decode(calldata: string, rules?: DecodingRules): any[] | object { - if (!calldata.startsWith(this.selector)) { + if (!calldata.startsWith(this._methodSelector)) { throw new Error( - `Tried to decode calldata, but it was missing the function selector. Expected '${this.selector}'.`, + `Tried to decode calldata, but it was missing the function selector. Expected '${this._methodSelector}'.`, ); } const hasSelector = true; @@ -46,10 +44,11 @@ export class Method extends MemberDataType { return returnData; } - public decodeReturnValues(returndata: string, rules?: DecodingRules): any { - const rules_: DecodingRules = rules ? rules : { structsAsObjects: false }; - const rawReturnData = new RawCalldata(returndata, false); - const returnValues = this._returnDataType.generateValue(rawReturnData, rules_); + public decodeReturnValues(returndata: string, rules_?: DecodingRules): any { + const rules: DecodingRules = rules_ ? rules_ : Constants.DEFAULT_DECODING_RULES; + const returnDataHasSelector = false; + const rawReturnData = new RawCalldata(returndata, returnDataHasSelector); + const returnValues = this._returnDataType.generateValue(rawReturnData, rules); return returnValues; } diff --git a/packages/utils/src/abi_encoder/utils/constants.ts b/packages/utils/src/abi_encoder/utils/constants.ts index 3d85fbdb8..05c6783e7 100644 --- a/packages/utils/src/abi_encoder/utils/constants.ts +++ b/packages/utils/src/abi_encoder/utils/constants.ts @@ -1,3 +1,5 @@ +import { DecodingRules, EncodingRules } from './rules'; + export const EVM_WORD_WIDTH_IN_BYTES = 32; export const EVM_WORD_WIDTH_IN_BITS = 256; export const HEX_BASE = 16; @@ -6,3 +8,5 @@ export const BIN_BASE = 2; export const HEX_SELECTOR_LENGTH_IN_CHARS = 10; export const HEX_SELECTOR_LENGTH_IN_BYTES = 4; export const HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA = 0; +export const DEFAULT_DECODING_RULES: DecodingRules = { structsAsObjects: false }; +export const DEFAULT_ENCODING_RULES: EncodingRules = { optimize: false, annotate: false }; -- cgit v1.2.3 From 173fc1dcefa266704dd80de6335c03b73b7d8702 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 15:50:49 -0800 Subject: Moved encoder selector check into DataType --- .../src/abi_encoder/abstract_data_types/data_type.ts | 8 +++++++- packages/utils/src/abi_encoder/evm_data_types/method.ts | 16 +++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts index dd166b19c..450080353 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts @@ -36,7 +36,13 @@ export abstract class DataType { return calldataHex; } - public decode(calldata: string, rules?: DecodingRules, hasSelector: boolean = false): any { + public decode(calldata: string, rules?: DecodingRules, selector?: string): any { + if (selector && !calldata.startsWith(selector)) { + throw new Error( + `Tried to decode calldata, but it was missing the function selector. Expected '${selector}'.`, + ); + } + const hasSelector = selector ? true : false; const rawCalldata = new RawCalldata(calldata, hasSelector); const rules_ = rules ? rules : Constants.DEFAULT_DECODING_RULES; const value = this.generateValue(rawCalldata, rules_); diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts index 2faffd44e..f2e417485 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/method.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts @@ -3,7 +3,6 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { DataType, DataTypeFactory, MemberDataType } from '../abstract_data_types'; -import { RawCalldata } from '../calldata'; import * as Constants from '../utils/constants'; import { DecodingRules, EncodingRules } from '../utils/rules'; @@ -29,13 +28,7 @@ export class Method extends MemberDataType { } public decode(calldata: string, rules?: DecodingRules): any[] | object { - if (!calldata.startsWith(this._methodSelector)) { - throw new Error( - `Tried to decode calldata, but it was missing the function selector. Expected '${this._methodSelector}'.`, - ); - } - const hasSelector = true; - const value = super.decode(calldata, rules, hasSelector); + const value = super.decode(calldata, rules, this._methodSelector); return value; } @@ -44,11 +37,8 @@ export class Method extends MemberDataType { return returnData; } - public decodeReturnValues(returndata: string, rules_?: DecodingRules): any { - const rules: DecodingRules = rules_ ? rules_ : Constants.DEFAULT_DECODING_RULES; - const returnDataHasSelector = false; - const rawReturnData = new RawCalldata(returndata, returnDataHasSelector); - const returnValues = this._returnDataType.generateValue(rawReturnData, rules); + public decodeReturnValues(returndata: string, rules?: DecodingRules): any { + const returnValues = this._returnDataType.decode(returndata, rules); return returnValues; } -- cgit v1.2.3 From 58a2dfbc4d191ea21e6a749371e586dcff3b3239 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 17:04:50 -0800 Subject: Final rounds on evm data types --- .../src/abi_encoder/evm_data_types/static_bytes.ts | 58 ++++++++++++---------- .../utils/src/abi_encoder/evm_data_types/string.ts | 33 +++++++----- .../utils/src/abi_encoder/evm_data_types/tuple.ts | 8 +-- packages/utils/src/abi_encoder/utils/queue.ts | 2 +- 4 files changed, 59 insertions(+), 42 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 9a2a99ec7..afa9afdf2 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -11,7 +11,6 @@ export class StaticBytes extends PayloadDataType { private static readonly _matcher = RegExp( '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', ); - private static readonly _DEFAULT_WIDTH = 1; private readonly _width: number; @@ -19,16 +18,20 @@ export class StaticBytes extends PayloadDataType { return StaticBytes._matcher.test(type); } + private static _decodeWidthFromType(type: string): number { + const matches = StaticBytes._matcher.exec(type); + const width = (matches !== null && matches.length === 3 && matches[2] !== undefined) + ? parseInt(matches[2], Constants.DEC_BASE) + : StaticBytes._DEFAULT_WIDTH; + return width; + } + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { super(dataItem, dataTypeFactory, StaticBytes._SIZE_KNOWN_AT_COMPILE_TIME); - const matches = StaticBytes._matcher.exec(dataItem.type); if (!StaticBytes.matchType(dataItem.type)) { - throw new Error(`Tried to instantiate Byte with bad input: ${dataItem}`); + throw new Error(`Tried to instantiate Static Bytes with bad input: ${dataItem}`); } - this._width = - matches !== null && matches.length === 3 && matches[2] !== undefined - ? parseInt(matches[2], Constants.DEC_BASE) - : StaticBytes._DEFAULT_WIDTH; + this._width = StaticBytes._decodeWidthFromType(dataItem.type); } public getSignature(): string { @@ -37,11 +40,30 @@ export class StaticBytes extends PayloadDataType { } public encodeValue(value: string | Buffer): Buffer { - // Sanity check if string - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); + // 1/2 Convert value into a buffer and do bounds checking + this._sanityCheckValue(value); + const valueBuf = ethUtil.toBuffer(value); + // 2/2 Store value as hex + const valuePadded = ethUtil.setLengthRight(valueBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + return valuePadded; + } + + public decodeValue(calldata: RawCalldata): string { + const valueBufPadded = calldata.popWord(); + const valueBuf = valueBufPadded.slice(0, this._width); + const value = ethUtil.bufferToHex(valueBuf); + this._sanityCheckValue(value); + return value; + } + + private _sanityCheckValue(value: string | Buffer): void { + if (typeof value === 'string') { + if (!value.startsWith('0x')) { + throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } } - // Convert value into a buffer and do bounds checking const valueBuf = ethUtil.toBuffer(value); if (valueBuf.byteLength > this._width) { throw new Error( @@ -49,20 +71,6 @@ export class StaticBytes extends PayloadDataType { valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); - } else if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); } - - // Store value as hex - const evmWordWidth = 32; - const paddedValue = ethUtil.setLengthRight(valueBuf, evmWordWidth); - return paddedValue; - } - - public decodeValue(calldata: RawCalldata): string { - const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(0, this._width); - const value = ethUtil.bufferToHex(valueBuf); - return value; } } diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index 47ad7cb97..15b93e447 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -13,7 +13,7 @@ export class String extends PayloadDataType { public static matchType(type: string): boolean { return type === 'string'; } - + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { super(dataItem, dataTypeFactory, String._SIZE_KNOWN_AT_COMPILE_TIME); if (!String.matchType(dataItem.type)) { @@ -22,21 +22,30 @@ export class String extends PayloadDataType { } public encodeValue(value: string): Buffer { - const wordsForValue = Math.ceil(value.length / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; - const valueBuf = ethUtil.setLengthRight(new Buffer(value), paddedDynamicBytesForValue); - const lengthBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(value.length), Constants.EVM_WORD_WIDTH_IN_BYTES); - const encodedValueBuf = Buffer.concat([lengthBuf, valueBuf]); + // Encoded value is of the form: , with each field padded to be word-aligned. + // 1/3 Construct the length + const wordsToStoreValuePadded = Math.ceil(value.length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const bytesToStoreValuePadded = wordsToStoreValuePadded * Constants.EVM_WORD_WIDTH_IN_BYTES; + const lengthBuf = ethUtil.toBuffer(value.length); + const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + // 2/3 Construct the value + const valueBuf = new Buffer(value); + const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded); + // 3/3 Combine length and value + const encodedValueBuf = Buffer.concat([lengthBufPadded, valueBufPadded]); return encodedValueBuf; } public decodeValue(calldata: RawCalldata): string { - const lengthBuf = calldata.popWord(); - const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, Constants.HEX_BASE); - const wordsForValue = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); + // Encoded value is of the form: , with each field padded to be word-aligned. + // 1/2 Decode length + const lengthBufPadded = calldata.popWord(); + const lengthHexPadded = ethUtil.bufferToHex(lengthBufPadded); + const length = parseInt(lengthHexPadded, Constants.HEX_BASE); + // 2/2 Decode value + const wordsToStoreValuePadded = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const valueBufPadded = calldata.popWords(wordsToStoreValuePadded); + const valueBuf = valueBufPadded.slice(0, length); const value = valueBuf.toString('ascii'); return value; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts index 4a90e375a..89dd6604d 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts @@ -3,8 +3,8 @@ import { DataItem } from 'ethereum-types'; import { DataTypeFactory, MemberDataType } from '../abstract_data_types'; export class Tuple extends MemberDataType { - private readonly _tupleSignature: string; - + private readonly _signature: string; + public static matchType(type: string): boolean { return type === 'tuple'; } @@ -14,10 +14,10 @@ export class Tuple extends MemberDataType { if (!Tuple.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); } - this._tupleSignature = this._computeSignatureOfMembers(); + this._signature = this._computeSignatureOfMembers(); } public getSignature(): string { - return this._tupleSignature; + return this._signature; } } diff --git a/packages/utils/src/abi_encoder/utils/queue.ts b/packages/utils/src/abi_encoder/utils/queue.ts index 53afb7e11..506a0b56e 100644 --- a/packages/utils/src/abi_encoder/utils/queue.ts +++ b/packages/utils/src/abi_encoder/utils/queue.ts @@ -32,7 +32,7 @@ export class Queue { public getStore(): T[] { return this._store; } - + public peekFront(): T | undefined { return this._store.length >= 0 ? this._store[0] : undefined; } -- cgit v1.2.3 From d2d89adbddaec435ddb65545a86fc4dc981de521 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 17:12:21 -0800 Subject: Abstracted out encoding/decoding of numeric values into its own utility. Could be useful elsewhere. --- .../utils/src/abi_encoder/evm_data_types/array.ts | 6 +- .../utils/src/abi_encoder/evm_data_types/int.ts | 6 +- .../utils/src/abi_encoder/evm_data_types/number.ts | 71 ++------------ .../src/abi_encoder/evm_data_types/static_bytes.ts | 8 +- .../utils/src/abi_encoder/evm_data_types/string.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/tuple.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/uint.ts | 6 +- packages/utils/src/abi_encoder/utils/math.ts | 103 +++++++++++++++++++++ packages/utils/src/abi_encoder/utils/queue.ts | 2 +- 9 files changed, 125 insertions(+), 81 deletions(-) create mode 100644 packages/utils/src/abi_encoder/utils/math.ts diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts index dd8184fd0..527cdadfe 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/array.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -4,17 +4,17 @@ import { DataTypeFactory, MemberDataType } from '../abstract_data_types'; import * as Constants from '../utils/constants'; export class Array extends MemberDataType { - private static readonly _matcher = RegExp('^(.+)\\[([0-9]*)\\]$'); + private static readonly _MATCHER = RegExp('^(.+)\\[([0-9]*)\\]$'); private readonly _arraySignature: string; private readonly _elementType: string; public static matchType(type: string): boolean { - return Array._matcher.test(type); + return Array._MATCHER.test(type); } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { // Sanity check - const matches = Array._matcher.exec(dataItem.type); + const matches = Array._MATCHER.exec(dataItem.type); if (matches === null || matches.length !== 3) { throw new Error(`Could not parse array: ${dataItem.type}`); } else if (matches[1] === undefined) { diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index ec41b9cfc..457c41b28 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -7,16 +7,16 @@ import { DataTypeFactory } from '../abstract_data_types'; import { Number } from './number'; export class Int extends Number { - private static readonly _matcher = RegExp( + private static readonly _MATCHER = RegExp( '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); public static matchType(type: string): boolean { - return Int._matcher.test(type); + return Int._MATCHER.test(type); } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - super(dataItem, Int._matcher, dataTypeFactory); + super(dataItem, Int._MATCHER, dataTypeFactory); } public getMaxValue(): BigNumber { diff --git a/packages/utils/src/abi_encoder/evm_data_types/number.ts b/packages/utils/src/abi_encoder/evm_data_types/number.ts index 86acdce07..053a574e3 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/number.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/number.ts @@ -1,11 +1,11 @@ import { DataItem } from 'ethereum-types'; -import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../utils/constants'; +import * as EncoderMath from '../utils/math'; export abstract class Number extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; @@ -25,73 +25,14 @@ export abstract class Number extends PayloadDataType { : (this._width = Number._DEFAULT_WIDTH); } - public encodeValue(value_: BigNumber | string | number): Buffer { - const value = new BigNumber(value_, 10); - if (value.greaterThan(this.getMaxValue())) { - throw new Error(`Tried to assign value of ${value}, which exceeds max value of ${this.getMaxValue()}`); - } else if (value.lessThan(this.getMinValue())) { - throw new Error(`Tried to assign value of ${value}, which exceeds min value of ${this.getMinValue()}`); - } - - let valueBuf: Buffer; - if (value.greaterThanOrEqualTo(0)) { - valueBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(`0x${value.toString(Constants.HEX_BASE)}`), - Constants.EVM_WORD_WIDTH_IN_BYTES, - ); - } else { - // BigNumber can't write a negative hex value, so we use twos-complement conversion to do it ourselves. - // Step 1/3: Convert value to positive binary string - const binBase = 2; - const valueBin = value.times(-1).toString(binBase); - - // Step 2/3: Invert binary value - let invertedValueBin = '1'.repeat(Constants.EVM_WORD_WIDTH_IN_BITS - valueBin.length); - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, binBase); - - // Step 3/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const negativeValue = invertedValue.plus(1); - - // Convert the negated value to a hex string - valueBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(`0x${negativeValue.toString(Constants.HEX_BASE)}`), - Constants.EVM_WORD_WIDTH_IN_BYTES, - ); - } - - return valueBuf; + public encodeValue(value: BigNumber | string | number): Buffer { + const encodedValue = EncoderMath.safeEncodeNumericValue(value, this.getMinValue(), this.getMaxValue()); + return encodedValue; } public decodeValue(calldata: RawCalldata): BigNumber { - const paddedValueBuf = calldata.popWord(); - const paddedValueHex = ethUtil.bufferToHex(paddedValueBuf); - let value = new BigNumber(paddedValueHex, 16); - if (this.getMinValue().lessThan(0)) { - // Check if we're negative - const valueBin = value.toString(Constants.BIN_BASE); - if (valueBin.length === Constants.EVM_WORD_WIDTH_IN_BITS && valueBin[0].startsWith('1')) { - // Negative - // Step 1/3: Invert binary value - let invertedValueBin = ''; - _.each(valueBin, (bit: string) => { - invertedValueBin += bit === '1' ? '0' : '1'; - }); - const invertedValue = new BigNumber(invertedValueBin, Constants.BIN_BASE); - - // Step 2/3: Add 1 to inverted value - // The result is the two's-complement represent of the input value. - const positiveValue = invertedValue.plus(1); - - // Step 3/3: Invert positive value - const negativeValue = positiveValue.times(-1); - value = negativeValue; - } - } - + const valueBuf = calldata.popWord(); + const value = EncoderMath.safeDecodeNumericValue(valueBuf, this.getMinValue(), this.getMaxValue()); return value; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index afa9afdf2..0d01e6105 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -8,18 +8,18 @@ import * as Constants from '../utils/constants'; export class StaticBytes extends PayloadDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - private static readonly _matcher = RegExp( + private static readonly _MATCHER = RegExp( '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', ); private static readonly _DEFAULT_WIDTH = 1; private readonly _width: number; public static matchType(type: string): boolean { - return StaticBytes._matcher.test(type); + return StaticBytes._MATCHER.test(type); } private static _decodeWidthFromType(type: string): number { - const matches = StaticBytes._matcher.exec(type); + const matches = StaticBytes._MATCHER.exec(type); const width = (matches !== null && matches.length === 3 && matches[2] !== undefined) ? parseInt(matches[2], Constants.DEC_BASE) : StaticBytes._DEFAULT_WIDTH; @@ -55,7 +55,7 @@ export class StaticBytes extends PayloadDataType { this._sanityCheckValue(value); return value; } - + private _sanityCheckValue(value: string | Buffer): void { if (typeof value === 'string') { if (!value.startsWith('0x')) { diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index 15b93e447..428ea21db 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -13,7 +13,7 @@ export class String extends PayloadDataType { public static matchType(type: string): boolean { return type === 'string'; } - + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { super(dataItem, dataTypeFactory, String._SIZE_KNOWN_AT_COMPILE_TIME); if (!String.matchType(dataItem.type)) { diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts index 89dd6604d..63d9dfa9e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts @@ -4,7 +4,7 @@ import { DataTypeFactory, MemberDataType } from '../abstract_data_types'; export class Tuple extends MemberDataType { private readonly _signature: string; - + public static matchType(type: string): boolean { return type === 'tuple'; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index ced3ef08b..c2b6e214a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -7,16 +7,16 @@ import { DataTypeFactory } from '../abstract_data_types'; import { Number } from './number'; export class UInt extends Number { - private static readonly _matcher = RegExp( + private static readonly _MATCHER = RegExp( '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); public static matchType(type: string): boolean { - return UInt._matcher.test(type); + return UInt._MATCHER.test(type); } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - super(dataItem, UInt._matcher, dataTypeFactory); + super(dataItem, UInt._MATCHER, dataTypeFactory); } public getMaxValue(): BigNumber { diff --git a/packages/utils/src/abi_encoder/utils/math.ts b/packages/utils/src/abi_encoder/utils/math.ts new file mode 100644 index 000000000..8d21ada0a --- /dev/null +++ b/packages/utils/src/abi_encoder/utils/math.ts @@ -0,0 +1,103 @@ +import BigNumber from 'bignumber.js'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import * as Constants from '../utils/constants'; + +function sanityCheckBigNumberRange(value_: BigNumber | string | number, minValue: BigNumber, maxValue: BigNumber): void { + const value = new BigNumber(value_, 10); + if (value.greaterThan(maxValue)) { + throw new Error(`Tried to assign value of ${value}, which exceeds max value of ${maxValue}`); + } else if (value.lessThan(minValue)) { + throw new Error(`Tried to assign value of ${value}, which exceeds min value of ${minValue}`); + } +} +function bigNumberToPaddedBuffer(value: BigNumber): Buffer { + const valueHex = `0x${value.toString(Constants.HEX_BASE)}`; + const valueBuf = ethUtil.toBuffer(valueHex); + const valueBufPadded = ethUtil.setLengthLeft(valueBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + return valueBufPadded; +} +/** + * Takes a numeric value and returns its ABI-encoded value + * @param value_ The value to encode. + * @return ABI Encoded value + */ +export function encodeNumericValue(value_: BigNumber | string | number): Buffer { + const value = new BigNumber(value_, 10); + // Case 1/2: value is non-negative + if (value.greaterThanOrEqualTo(0)) { + const encodedPositiveValue = bigNumberToPaddedBuffer(value); + return encodedPositiveValue; + } + // Case 2/2: Value is negative + // Use two's-complement to encode the value + // Step 1/3: Convert negative value to positive binary string + const valueBin = value.times(-1).toString(Constants.BIN_BASE); + // Step 2/3: Invert binary value + let invertedValueBin = '1'.repeat(Constants.EVM_WORD_WIDTH_IN_BITS - valueBin.length); + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, Constants.BIN_BASE); + // Step 3/3: Add 1 to inverted value + const negativeValue = invertedValue.plus(1); + const encodedValue = bigNumberToPaddedBuffer(negativeValue); + return encodedValue; +} +/** + * Takes a numeric value and returns its ABI-encoded value. + * Performs an additional sanity check, given the min/max allowed value. + * @param value_ The value to encode. + * @return ABI Encoded value + */ +export function safeEncodeNumericValue(value: BigNumber | string | number, minValue: BigNumber, maxValue: BigNumber): Buffer { + sanityCheckBigNumberRange(value, minValue, maxValue); + const encodedValue = encodeNumericValue(value); + return encodedValue; +} +/** + * Takes an ABI-encoded numeric value and returns its decoded value as a BigNumber. + * @param encodedValue The encoded numeric value. + * @param minValue The minimum possible decoded value. + * @return ABI Decoded value + */ +export function decodeNumericValue(encodedValue: Buffer, minValue: BigNumber): BigNumber { + const valueHex = ethUtil.bufferToHex(encodedValue); + // Case 1/3: value is definitely non-negative because of numeric boundaries + const value = new BigNumber(valueHex, Constants.HEX_BASE); + if (!minValue.lessThan(0)) { + return value; + } + // Case 2/3: value is non-negative because there is no leading 1 (encoded as two's-complement) + const valueBin = value.toString(Constants.BIN_BASE); + const valueIsNegative = valueBin.length === Constants.EVM_WORD_WIDTH_IN_BITS && valueBin[0].startsWith('1'); + if (!valueIsNegative) { + return value; + } + // Case 3/3: value is negative + // Step 1/3: Invert b inary value + let invertedValueBin = ''; + _.each(valueBin, (bit: string) => { + invertedValueBin += bit === '1' ? '0' : '1'; + }); + const invertedValue = new BigNumber(invertedValueBin, Constants.BIN_BASE); + // Step 2/3: Add 1 to inverted value + // The result is the two's-complement representation of the input value. + const positiveValue = invertedValue.plus(1); + // Step 3/3: Invert positive value to get the negative value + const negativeValue = positiveValue.times(-1); + return negativeValue; +} +/** + * Takes an ABI-encoded numeric value and returns its decoded value as a BigNumber. + * Performs an additional sanity check, given the min/max allowed value. + * @param encodedValue The encoded numeric value. + * @param minValue The minimum possible decoded value. + * @return ABI Decoded value + */ +export function safeDecodeNumericValue(encodedValue: Buffer, minValue: BigNumber, maxValue: BigNumber): BigNumber { + const value = decodeNumericValue(encodedValue, minValue); + sanityCheckBigNumberRange(value, minValue, maxValue); + return value; +} diff --git a/packages/utils/src/abi_encoder/utils/queue.ts b/packages/utils/src/abi_encoder/utils/queue.ts index 506a0b56e..53afb7e11 100644 --- a/packages/utils/src/abi_encoder/utils/queue.ts +++ b/packages/utils/src/abi_encoder/utils/queue.ts @@ -32,7 +32,7 @@ export class Queue { public getStore(): T[] { return this._store; } - + public peekFront(): T | undefined { return this._store.length >= 0 ? this._store[0] : undefined; } -- cgit v1.2.3 From ebaf9dd275403cdecfb3364876737fcbcd0eab82 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 17:25:04 -0800 Subject: Removed abstract Number class. --- .../utils/src/abi_encoder/evm_data_types/int.ts | 42 ++++++++++++++++----- .../utils/src/abi_encoder/evm_data_types/number.ts | 41 --------------------- .../utils/src/abi_encoder/evm_data_types/uint.ts | 43 +++++++++++++++++----- 3 files changed, 67 insertions(+), 59 deletions(-) delete mode 100644 packages/utils/src/abi_encoder/evm_data_types/number.ts diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index 457c41b28..83aeacd66 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -2,29 +2,53 @@ import { DataItem } from 'ethereum-types'; import { BigNumber } from '../../configured_bignumber'; -import { DataTypeFactory } from '../abstract_data_types'; +import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; +import { RawCalldata } from '../calldata'; +import * as Constants from '../utils/constants'; +import * as EncoderMath from '../utils/math'; -import { Number } from './number'; - -export class Int extends Number { +export class Int extends PayloadDataType { private static readonly _MATCHER = RegExp( '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + private static readonly _MAX_WIDTH: number = 256; + private static readonly _DEFAULT_WIDTH: number = Int._MAX_WIDTH; + private _width: number; + private _minValue: BigNumber; + private _maxValue: BigNumber; public static matchType(type: string): boolean { return Int._MATCHER.test(type); } + private static _decodeWidthFromType(type: string): number { + const matches = Int._MATCHER.exec(type); + const width = (matches !== null && matches.length === 2 && matches[1] !== undefined) + ? parseInt(matches[1], Constants.DEC_BASE) + : Int._DEFAULT_WIDTH; + return width; + } + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - super(dataItem, Int._MATCHER, dataTypeFactory); + super(dataItem, dataTypeFactory, Int._SIZE_KNOWN_AT_COMPILE_TIME); + if (!Int.matchType(dataItem.type)) { + throw new Error(`Tried to instantiate Int with bad input: ${dataItem}`); + } + this._width = Int._decodeWidthFromType(dataItem.type); + this._minValue = new BigNumber(2).toPower(this._width - 1).times(-1); + this._maxValue = new BigNumber(2).toPower(this._width - 1).sub(1); } - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this._width - 1).sub(1); + public encodeValue(value: BigNumber | string | number): Buffer { + const encodedValue = EncoderMath.safeEncodeNumericValue(value, this._minValue, this._maxValue); + return encodedValue; } - public getMinValue(): BigNumber { - return new BigNumber(2).toPower(this._width - 1).times(-1); + public decodeValue(calldata: RawCalldata): BigNumber { + const valueBuf = calldata.popWord(); + const value = EncoderMath.safeDecodeNumericValue(valueBuf, this._minValue, this._maxValue); + return value; } public getSignature(): string { diff --git a/packages/utils/src/abi_encoder/evm_data_types/number.ts b/packages/utils/src/abi_encoder/evm_data_types/number.ts deleted file mode 100644 index 053a574e3..000000000 --- a/packages/utils/src/abi_encoder/evm_data_types/number.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { DataItem } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { BigNumber } from '../../configured_bignumber'; -import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; -import { RawCalldata } from '../calldata'; -import * as Constants from '../utils/constants'; -import * as EncoderMath from '../utils/math'; - -export abstract class Number extends PayloadDataType { - private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; - private static readonly _MAX_WIDTH: number = 256; - private static readonly _DEFAULT_WIDTH: number = Number._MAX_WIDTH; - protected _width: number; - - constructor(dataItem: DataItem, matcher: RegExp, dataTypeFactory: DataTypeFactory) { - super(dataItem, dataTypeFactory, Number._SIZE_KNOWN_AT_COMPILE_TIME); - const matches = matcher.exec(dataItem.type); - if (matches === null) { - throw new Error(`Tried to instantiate Number with bad input: ${dataItem}`); - } - this._width = - matches !== null && matches.length === 2 && matches[1] !== undefined - ? parseInt(matches[1], Constants.DEC_BASE) - : (this._width = Number._DEFAULT_WIDTH); - } - - public encodeValue(value: BigNumber | string | number): Buffer { - const encodedValue = EncoderMath.safeEncodeNumericValue(value, this.getMinValue(), this.getMaxValue()); - return encodedValue; - } - - public decodeValue(calldata: RawCalldata): BigNumber { - const valueBuf = calldata.popWord(); - const value = EncoderMath.safeDecodeNumericValue(valueBuf, this.getMinValue(), this.getMaxValue()); - return value; - } - - public abstract getMaxValue(): BigNumber; - public abstract getMinValue(): BigNumber; -} diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index c2b6e214a..832ab075c 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -2,32 +2,57 @@ import { DataItem } from 'ethereum-types'; import { BigNumber } from '../../configured_bignumber'; -import { DataTypeFactory } from '../abstract_data_types'; +import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; +import { RawCalldata } from '../calldata'; +import * as Constants from '../utils/constants'; +import * as EncoderMath from '../utils/math'; -import { Number } from './number'; - -export class UInt extends Number { +export class UInt extends PayloadDataType { private static readonly _MATCHER = RegExp( '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); + private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; + private static readonly _MAX_WIDTH: number = 256; + private static readonly _DEFAULT_WIDTH: number = UInt._MAX_WIDTH; + private _width: number; + private _minValue: BigNumber; + private _maxValue: BigNumber; public static matchType(type: string): boolean { return UInt._MATCHER.test(type); } + private static _decodeWidthFromType(type: string): number { + const matches = UInt._MATCHER.exec(type); + const width = (matches !== null && matches.length === 2 && matches[1] !== undefined) + ? parseInt(matches[1], Constants.DEC_BASE) + : UInt._DEFAULT_WIDTH; + return width; + } + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - super(dataItem, UInt._MATCHER, dataTypeFactory); + super(dataItem, dataTypeFactory, UInt._SIZE_KNOWN_AT_COMPILE_TIME); + if (!UInt.matchType(dataItem.type)) { + throw new Error(`Tried to instantiate UInt with bad input: ${dataItem}`); + } + this._width = UInt._decodeWidthFromType(dataItem.type); + this._minValue = new BigNumber(0); + this._maxValue = new BigNumber(2).toPower(this._width).sub(1); } - public getMaxValue(): BigNumber { - return new BigNumber(2).toPower(this._width).sub(1); + public encodeValue(value: BigNumber | string | number): Buffer { + const encodedValue = EncoderMath.safeEncodeNumericValue(value, this._minValue, this._maxValue); + return encodedValue; } - public getMinValue(): BigNumber { - return new BigNumber(0); + public decodeValue(calldata: RawCalldata): BigNumber { + const valueBuf = calldata.popWord(); + const value = EncoderMath.safeDecodeNumericValue(valueBuf, this._minValue, this._maxValue); + return value; } public getSignature(): string { return `uint${this._width}`; } } + -- cgit v1.2.3 From acd364b71c8b3ddb6d4d75d8667cc7f50b18694d Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 17:37:14 -0800 Subject: Comments and inline documentation for dynamic bytes --- .../utils/src/abi_encoder/evm_data_types/bool.ts | 8 ++-- .../abi_encoder/evm_data_types/dynamic_bytes.ts | 55 +++++++++++++--------- .../utils/src/abi_encoder/evm_data_types/string.ts | 4 +- .../utils/test/abi_encoder/evm_data_types_test.ts | 2 +- 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index 6bc299544..82a519aae 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -22,10 +22,6 @@ export class Bool extends PayloadDataType { } } - public getSignature(): string { - return 'bool'; - } - public encodeValue(value: boolean): Buffer { const encodedValue = value ? '0x1' : '0x0'; const encodedValueBuf = ethUtil.setLengthLeft( @@ -47,4 +43,8 @@ export class Bool extends PayloadDataType { /* tslint:enable boolean-naming */ return value; } + + public getSignature(): string { + return 'bool'; + } } diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index 626e266c9..ce6ace627 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -17,42 +17,53 @@ export class DynamicBytes extends PayloadDataType { public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { super(dataItem, dataTypeFactory, DynamicBytes._SIZE_KNOWN_AT_COMPILE_TIME); if (!DynamicBytes.matchType(dataItem.type)) { - throw new Error(`Tried to instantiate DynamicBytes with bad input: ${dataItem}`); + throw new Error(`Tried to instantiate Dynamic Bytes with bad input: ${dataItem}`); } } public encodeValue(value: string | Buffer): Buffer { - if (typeof value === 'string' && !value.startsWith('0x')) { - throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '${value}'`); - } + // Encoded value is of the form: , with each field padded to be word-aligned. + // 1/3 Construct the length const valueBuf = ethUtil.toBuffer(value); - if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - - const wordsForValue = Math.ceil(valueBuf.byteLength / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedDynamicBytesForValue = wordsForValue * Constants.EVM_WORD_WIDTH_IN_BYTES; - const paddedValueBuf = ethUtil.setLengthRight(valueBuf, paddedDynamicBytesForValue); - const paddedLengthBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(valueBuf.byteLength), - Constants.EVM_WORD_WIDTH_IN_BYTES, - ); - const encodedValueBuf = Buffer.concat([paddedLengthBuf, paddedValueBuf]); - return encodedValueBuf; + const wordsToStoreValuePadded = Math.ceil(valueBuf.byteLength / Constants.EVM_WORD_WIDTH_IN_BYTES); + const bytesToStoreValuePadded = wordsToStoreValuePadded * Constants.EVM_WORD_WIDTH_IN_BYTES; + const lengthBuf = ethUtil.toBuffer(valueBuf.byteLength); + const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + // 2/3 Construct the value + this._sanityCheckValue(value); + const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded); + // 3/3 Combine length and value + const encodedValue = Buffer.concat([lengthBufPadded, valueBufPadded]); + return encodedValue; } public decodeValue(calldata: RawCalldata): string { + // Encoded value is of the form: , with each field padded to be word-aligned. + // 1/2 Decode length const lengthBuf = calldata.popWord(); const lengthHex = ethUtil.bufferToHex(lengthBuf); const length = parseInt(lengthHex, Constants.HEX_BASE); - const wordsForValue = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); - const paddedValueBuf = calldata.popWords(wordsForValue); - const valueBuf = paddedValueBuf.slice(0, length); - const decodedValue = ethUtil.bufferToHex(valueBuf); - return decodedValue; + // 2/2 Decode value + const wordsToStoreValuePadded = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const valueBufPadded = calldata.popWords(wordsToStoreValuePadded); + const valueBuf = valueBufPadded.slice(0, length); + const value = ethUtil.bufferToHex(valueBuf); + this._sanityCheckValue(value); + return value; } public getSignature(): string { return 'bytes'; } + + private _sanityCheckValue(value: string | Buffer): void { + if (typeof value !== 'string') { + return; + } + if (!value.startsWith('0x')) { + throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + } } diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index 428ea21db..2bb6541a3 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -32,8 +32,8 @@ export class String extends PayloadDataType { const valueBuf = new Buffer(value); const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded); // 3/3 Combine length and value - const encodedValueBuf = Buffer.concat([lengthBufPadded, valueBufPadded]); - return encodedValueBuf; + const encodedValue = Buffer.concat([lengthBufPadded, valueBufPadded]); + return encodedValue; } public decodeValue(calldata: RawCalldata): string { 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 9c3e3c0f9..7cea86529 100644 --- a/packages/utils/test/abi_encoder/evm_data_types_test.ts +++ b/packages/utils/test/abi_encoder/evm_data_types_test.ts @@ -1018,7 +1018,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Encode Args and validate result expect(() => { dataType.encode(args); - }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix. Got '01'"); + }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix."); }); it('Should throw when pass in bad hex (include a half-byte)', async () => { // Create DataType object -- cgit v1.2.3 From 22ce3e2e29fb50d9b9244c9ee567c124586be9ae Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 17:46:45 -0800 Subject: Comments for Array --- .../utils/src/abi_encoder/evm_data_types/array.ts | 39 +++++++++++++--------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts index 527cdadfe..77e38ebd7 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/array.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -12,21 +12,26 @@ export class Array extends MemberDataType { return Array._MATCHER.test(type); } - public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - // Sanity check - const matches = Array._MATCHER.exec(dataItem.type); + private static _decodeElementTypeAndLengthFromType(type: string): [string, undefined|number] { + const matches = Array._MATCHER.exec(type); if (matches === null || matches.length !== 3) { - throw new Error(`Could not parse array: ${dataItem.type}`); + throw new Error(`Could not parse array: ${type}`); } else if (matches[1] === undefined) { - throw new Error(`Could not parse array type: ${dataItem.type}`); + throw new Error(`Could not parse array type: ${type}`); } else if (matches[2] === undefined) { - throw new Error(`Could not parse array length: ${dataItem.type}`); + throw new Error(`Could not parse array length: ${type}`); } - - const isArray = true; const arrayElementType = matches[1]; const arrayLength = matches[2] === '' ? undefined : parseInt(matches[2], Constants.DEC_BASE); + return [arrayElementType, arrayLength]; + } + + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { + // Construct parent + const isArray = true; + const [arrayElementType, arrayLength] = Array._decodeElementTypeAndLengthFromType(dataItem.type); super(dataItem, dataTypeFactory, isArray, arrayLength, arrayElementType); + // Set array properties this._elementType = arrayElementType; this._arraySignature = this._computeSignature(); } @@ -36,20 +41,22 @@ export class Array extends MemberDataType { } private _computeSignature(): string { - const dataItem: DataItem = { + // Compute signature for a single array element + const elementDataItem: DataItem = { type: this._elementType, name: 'N/A', }; - const components = this.getDataItem().components; - if (components !== undefined) { - dataItem.components = components; + const elementComponents = this.getDataItem().components; + if (elementComponents !== undefined) { + elementDataItem.components = elementComponents; } - const elementDataType = this.getFactory().create(dataItem); - const type = elementDataType.getSignature(); + const elementDataType = this.getFactory().create(elementDataItem); + const elementSignature = elementDataType.getSignature(); + // Construct signature for array of type `element` if (this._arrayLength === undefined) { - return `${type}[]`; + return `${elementSignature}[]`; } else { - return `${type}[${this._arrayLength}]`; + return `${elementSignature}[${this._arrayLength}]`; } } } -- cgit v1.2.3 From bb4d02e413119132f283ee17549cf5e1732d75b5 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 17:47:33 -0800 Subject: Comments for Address --- packages/utils/src/abi_encoder/evm_data_types/address.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 9ae22bd9c..84f6665cb 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -34,17 +34,17 @@ export class Address extends PayloadDataType { if (!value.startsWith('0x')) { throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); } - const valueAsBuffer = ethUtil.toBuffer(value); - if (valueAsBuffer.byteLength !== Address._ADDRESS_SIZE_IN_BYTES) { + const valueBuf = ethUtil.toBuffer(value); + if (valueBuf.byteLength !== Address._ADDRESS_SIZE_IN_BYTES) { throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); } - const encodedValueBuf = ethUtil.setLengthLeft(valueAsBuffer, Constants.EVM_WORD_WIDTH_IN_BYTES); + const encodedValueBuf = ethUtil.setLengthLeft(valueBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); return encodedValueBuf; } public decodeValue(calldata: RawCalldata): string { - const paddedValueBuf = calldata.popWord(); - const valueBuf = paddedValueBuf.slice(Address._DECODED_ADDRESS_OFFSET_IN_BYTES); + const valueBufPadded = calldata.popWord(); + const valueBuf = valueBufPadded.slice(Address._DECODED_ADDRESS_OFFSET_IN_BYTES); const value = ethUtil.bufferToHex(valueBuf); return value; } -- cgit v1.2.3 From 9a51af46ee4a35b3d1ce2fcdc6f561aa68307cf0 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 18:24:46 -0800 Subject: Payload -> Blob, Dependent -> Pointer, Member -> Set --- .../abstract_data_types/dependent_data_type.ts | 51 ----- .../src/abi_encoder/abstract_data_types/index.ts | 5 +- .../abstract_data_types/member_data_type.ts | 226 -------------------- .../abstract_data_types/payload_data_type.ts | 38 ---- .../abi_encoder/abstract_data_types/types/blob.ts | 38 ++++ .../abi_encoder/abstract_data_types/types/index.ts | 3 + .../abstract_data_types/types/pointer.ts | 51 +++++ .../abi_encoder/abstract_data_types/types/set.ts | 227 +++++++++++++++++++++ .../utils/src/abi_encoder/calldata/blocks/blob.ts | 20 ++ .../utils/src/abi_encoder/calldata/blocks/index.ts | 3 + .../src/abi_encoder/calldata/blocks/pointer.ts | 59 ++++++ .../utils/src/abi_encoder/calldata/blocks/set.ts | 47 +++++ .../utils/src/abi_encoder/calldata/calldata.ts | 14 +- .../src/abi_encoder/calldata/calldata_blocks.ts | 3 - .../calldata/dependent_calldata_block.ts | 59 ------ packages/utils/src/abi_encoder/calldata/index.ts | 7 +- .../abi_encoder/calldata/member_calldata_block.ts | 48 ----- .../abi_encoder/calldata/payload_calldata_block.ts | 20 -- .../utils/src/abi_encoder/calldata/raw_calldata.ts | 25 ++- .../src/abi_encoder/evm_data_types/address.ts | 4 +- .../utils/src/abi_encoder/evm_data_types/array.ts | 4 +- .../utils/src/abi_encoder/evm_data_types/bool.ts | 4 +- .../abi_encoder/evm_data_types/dynamic_bytes.ts | 4 +- .../utils/src/abi_encoder/evm_data_types/int.ts | 10 +- .../utils/src/abi_encoder/evm_data_types/method.ts | 4 +- .../src/abi_encoder/evm_data_types/pointer.ts | 4 +- .../src/abi_encoder/evm_data_types/static_bytes.ts | 4 +- .../utils/src/abi_encoder/evm_data_types/string.ts | 4 +- .../utils/src/abi_encoder/evm_data_types/tuple.ts | 4 +- .../utils/src/abi_encoder/evm_data_types/uint.ts | 16 +- 30 files changed, 502 insertions(+), 504 deletions(-) delete mode 100644 packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts delete mode 100644 packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts delete mode 100644 packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts create mode 100644 packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts create mode 100644 packages/utils/src/abi_encoder/abstract_data_types/types/index.ts create mode 100644 packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts create mode 100644 packages/utils/src/abi_encoder/abstract_data_types/types/set.ts create mode 100644 packages/utils/src/abi_encoder/calldata/blocks/blob.ts create mode 100644 packages/utils/src/abi_encoder/calldata/blocks/index.ts create mode 100644 packages/utils/src/abi_encoder/calldata/blocks/pointer.ts create mode 100644 packages/utils/src/abi_encoder/calldata/blocks/set.ts delete mode 100644 packages/utils/src/abi_encoder/calldata/calldata_blocks.ts delete mode 100644 packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts delete mode 100644 packages/utils/src/abi_encoder/calldata/member_calldata_block.ts delete mode 100644 packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts diff --git a/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts deleted file mode 100644 index 7649b1836..000000000 --- a/packages/utils/src/abi_encoder/abstract_data_types/dependent_data_type.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { DataItem } from 'ethereum-types'; -import * as ethUtil from 'ethereumjs-util'; -import * as _ from 'lodash'; - -import { CalldataBlock, DependentCalldataBlock, RawCalldata } from '../calldata'; -import * as Constants from '../utils/constants'; -import { DecodingRules } from '../utils/rules'; - -import { DataType } from './data_type'; -import { DataTypeFactory } from './interfaces'; - -export abstract class DependentDataType extends DataType { - protected _dependency: DataType; - protected _parent: DataType; - private readonly _isStatic: boolean; - - public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { - super(dataItem, factory); - this._dependency = dependency; - this._parent = parent; - this._isStatic = true; - } - - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { - if (parentBlock === undefined) { - throw new Error(`DependentDataType requires a parent block to generate its block`); - } - const dependencyBlock = this._dependency.generateCalldataBlock(value, parentBlock); - const name = this.getDataItem().name; - const signature = this.getSignature(); - const parentName = parentBlock ? parentBlock.getName() : ''; - const block = new DependentCalldataBlock(name, signature, parentName, dependencyBlock, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any { - const destinationOffsetBuf = calldata.popWord(); - const destinationOffsetHex = ethUtil.bufferToHex(destinationOffsetBuf); - const destinationOffsetRelative = parseInt(destinationOffsetHex, Constants.HEX_BASE); - const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); - const currentOffset = calldata.getOffset(); - calldata.setOffset(destinationOffsetAbsolute); - const value = this._dependency.generateValue(calldata, rules); - calldata.setOffset(currentOffset); - return value; - } - - public isStatic(): boolean { - return this._isStatic; - } -} diff --git a/packages/utils/src/abi_encoder/abstract_data_types/index.ts b/packages/utils/src/abi_encoder/abstract_data_types/index.ts index 9ad568134..d1c7d93e4 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/index.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/index.ts @@ -1,5 +1,4 @@ export * from './interfaces'; export * from './data_type'; -export * from './dependent_data_type'; -export * from './member_data_type'; -export * from './payload_data_type'; +import * as AbstractDataTypes from './types'; +export { AbstractDataTypes }; diff --git a/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts deleted file mode 100644 index dacdbf8af..000000000 --- a/packages/utils/src/abi_encoder/abstract_data_types/member_data_type.ts +++ /dev/null @@ -1,226 +0,0 @@ -import { DataItem } from 'ethereum-types'; -import * as ethUtil from 'ethereumjs-util'; -import * as _ from 'lodash'; - -import { BigNumber } from '../../configured_bignumber'; -import { CalldataBlock, MemberCalldataBlock, RawCalldata } from '../calldata'; -import * as Constants from '../utils/constants'; -import { DecodingRules } from '../utils/rules'; - -import { DataType } from './data_type'; -import { DependentDataType } from './dependent_data_type'; -import { DataTypeFactory, MemberIndexByName } from './interfaces'; - -export abstract class MemberDataType extends DataType { - protected readonly _arrayLength: number | undefined; - protected readonly _arrayElementType: string | undefined; - private readonly _memberIndexByName: MemberIndexByName; - private readonly _members: DataType[]; - private readonly _isArray: boolean; - - public constructor( - dataItem: DataItem, - factory: DataTypeFactory, - isArray: boolean = false, - arrayLength?: number, - arrayElementType?: string, - ) { - super(dataItem, factory); - this._memberIndexByName = {}; - this._members = []; - this._isArray = isArray; - this._arrayLength = arrayLength; - this._arrayElementType = arrayElementType; - if (isArray && arrayLength !== undefined) { - [this._members, this._memberIndexByName] = this._createMembersWithLength(dataItem, arrayLength); - } else if (!isArray) { - [this._members, this._memberIndexByName] = this._createMembersWithKeys(dataItem); - } - } - - public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const block = - value instanceof Array - ? this._generateCalldataBlockFromArray(value, parentBlock) - : this._generateCalldataBlockFromObject(value, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { - let members = this._members; - if (this._isArray && this._arrayLength === undefined) { - const arrayLengthBuf = calldata.popWord(); - const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); - const hexBase = 16; - const arrayLength = new BigNumber(arrayLengthHex, hexBase); - - [members] = this._createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); - } - - calldata.startScope(); - let value: any[] | object; - if (rules.structsAsObjects && !this._isArray) { - value = {}; - _.each(this._memberIndexByName, (idx: number, key: string) => { - const member = this._members[idx]; - const memberValue = member.generateValue(calldata, rules); - (value as { [key: string]: any })[key] = memberValue; - }); - } else { - value = []; - _.each(members, (member: DataType, idx: number) => { - const memberValue = member.generateValue(calldata, rules); - (value as any[]).push(memberValue); - }); - } - calldata.endScope(); - return value; - } - - public isStatic(): boolean { - /* For Tuple: - const isStaticTuple = this.children.length === 0; - return isStaticTuple; // @TODO: True in every case or only when dynamic data? - - For Array: - if isLengthDefined = false then this is false - - Otherwise if the first element is a Pointer then false - */ - - if (this._isArray && this._arrayLength === undefined) { - return false; - } - - // Search for dependent members - const dependentMember = _.find(this._members, (member: DataType) => { - return member instanceof DependentDataType; - }); - const isStatic = dependentMember === undefined; // static if we couldn't find a dependent member - return isStatic; - } - - protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { - // Sanity check length - if (this._arrayLength !== undefined && value.length !== this._arrayLength) { - throw new Error( - `Expected array of ${JSON.stringify( - this._arrayLength, - )} elements, but got array of length ${JSON.stringify(value.length)}`, - ); - } - - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( - this.getDataItem().name, - this.getSignature(), - parentName, - ); - - let members = this._members; - if (this._isArray && this._arrayLength === undefined) { - [members] = this._createMembersWithLength(this.getDataItem(), value.length); - - const lenBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(`0x${value.length.toString(Constants.HEX_BASE)}`), - Constants.EVM_WORD_WIDTH_IN_BYTES, - ); - methodBlock.setHeader(lenBuf); - } - - const memberBlocks: CalldataBlock[] = []; - _.each(members, (member: DataType, idx: number) => { - const block = member.generateCalldataBlock(value[idx], methodBlock); - memberBlocks.push(block); - }); - methodBlock.setMembers(memberBlocks); - return methodBlock; - } - - protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( - this.getDataItem().name, - this.getSignature(), - parentName, - ); - const memberBlocks: CalldataBlock[] = []; - const childMap = _.cloneDeep(this._memberIndexByName); - _.forOwn(obj, (value: any, key: string) => { - if (!(key in childMap)) { - throw new Error( - `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`, - ); - } - const block = this._members[this._memberIndexByName[key]].generateCalldataBlock(value, methodBlock); - memberBlocks.push(block); - delete childMap[key]; - }); - - if (Object.keys(childMap).length !== 0) { - throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); - } - - methodBlock.setMembers(memberBlocks); - return methodBlock; - } - - protected _computeSignatureOfMembers(): string { - // Compute signature of members - let signature = `(`; - _.each(this._members, (member: DataType, i: number) => { - signature += member.getSignature(); - if (i < this._members.length - 1) { - signature += ','; - } - }); - signature += ')'; - return signature; - } - - private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberIndexByName] { - // Sanity check - if (dataItem.components === undefined) { - throw new Error(`Expected components`); - } - - const members: DataType[] = []; - const memberIndexByName: MemberIndexByName = {}; - _.each(dataItem.components, (memberItem: DataItem) => { - const childDataItem: DataItem = { - type: memberItem.type, - name: `${dataItem.name}.${memberItem.name}`, - }; - const components = memberItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberIndexByName[memberItem.name] = members.length; - members.push(child); - }); - - return [members, memberIndexByName]; - } - - private _createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberIndexByName] { - const members: DataType[] = []; - const memberIndexByName: MemberIndexByName = {}; - const range = _.range(length); - _.each(range, (idx: number) => { - const childDataItem: DataItem = { - type: this._arrayElementType ? this._arrayElementType : '', - name: `${dataItem.name}[${idx.toString(Constants.DEC_BASE)}]`, - }; - const components = dataItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberIndexByName[idx.toString(Constants.DEC_BASE)] = members.length; - members.push(child); - }); - - return [members, memberIndexByName]; - } -} diff --git a/packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts deleted file mode 100644 index fa420bc74..000000000 --- a/packages/utils/src/abi_encoder/abstract_data_types/payload_data_type.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { DataItem } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { CalldataBlock, PayloadCalldataBlock, RawCalldata } from '../calldata'; -import { DecodingRules } from '../utils/rules'; - -import { DataType } from './data_type'; -import { DataTypeFactory } from './interfaces'; - -export abstract class PayloadDataType extends DataType { - protected _hasConstantSize: boolean; - - public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { - super(dataItem, factory); - this._hasConstantSize = hasConstantSize; - } - - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { - const encodedValue = this.encodeValue(value); - const name = this.getDataItem().name; - const signature = this.getSignature(); - const parentName = parentBlock ? parentBlock.getName() : ''; - const block = new PayloadCalldataBlock(name, signature, parentName, encodedValue); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any { - const value = this.decodeValue(calldata); - return value; - } - - public isStatic(): boolean { - return this._hasConstantSize; - } - - public abstract encodeValue(value: any): Buffer; - public abstract decodeValue(calldata: RawCalldata): any; -} diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts new file mode 100644 index 000000000..f4246c893 --- /dev/null +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts @@ -0,0 +1,38 @@ +import { DataItem } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { CalldataBlock, CalldataBlocks, RawCalldata } from '../../calldata'; +import { DecodingRules } from '../../utils/rules'; + +import { DataType } from '../data_type'; +import { DataTypeFactory } from '../interfaces'; + +export abstract class Blob extends DataType { + protected _hasConstantSize: boolean; + + public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { + super(dataItem, factory); + this._hasConstantSize = hasConstantSize; + } + + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlocks.Blob { + const encodedValue = this.encodeValue(value); + const name = this.getDataItem().name; + const signature = this.getSignature(); + const parentName = parentBlock ? parentBlock.getName() : ''; + const block = new CalldataBlocks.Blob(name, signature, parentName, encodedValue); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { + const value = this.decodeValue(calldata); + return value; + } + + public isStatic(): boolean { + return this._hasConstantSize; + } + + public abstract encodeValue(value: any): Buffer; + public abstract decodeValue(calldata: RawCalldata): any; +} diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/index.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/index.ts new file mode 100644 index 000000000..958582dae --- /dev/null +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/index.ts @@ -0,0 +1,3 @@ +export * from './blob'; +export * from './pointer'; +export * from './set'; diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts new file mode 100644 index 000000000..47efac521 --- /dev/null +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts @@ -0,0 +1,51 @@ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { CalldataBlock, CalldataBlocks, RawCalldata } from '../../calldata'; +import * as Constants from '../../utils/constants'; +import { DecodingRules } from '../../utils/rules'; + +import { DataType } from '../data_type'; +import { DataTypeFactory } from '../interfaces'; + +export abstract class Pointer extends DataType { + protected _dependency: DataType; + protected _parent: DataType; + private readonly _isStatic: boolean; + + public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { + super(dataItem, factory); + this._dependency = dependency; + this._parent = parent; + this._isStatic = true; + } + + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlocks.Pointer { + if (parentBlock === undefined) { + throw new Error(`DependentDataType requires a parent block to generate its block`); + } + const dependencyBlock = this._dependency.generateCalldataBlock(value, parentBlock); + const name = this.getDataItem().name; + const signature = this.getSignature(); + const parentName = parentBlock ? parentBlock.getName() : ''; + const block = new CalldataBlocks.Pointer(name, signature, parentName, dependencyBlock, parentBlock); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any { + const destinationOffsetBuf = calldata.popWord(); + const destinationOffsetHex = ethUtil.bufferToHex(destinationOffsetBuf); + const destinationOffsetRelative = parseInt(destinationOffsetHex, Constants.HEX_BASE); + const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); + const currentOffset = calldata.getOffset(); + calldata.setOffset(destinationOffsetAbsolute); + const value = this._dependency.generateValue(calldata, rules); + calldata.setOffset(currentOffset); + return value; + } + + public isStatic(): boolean { + return this._isStatic; + } +} 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 new file mode 100644 index 000000000..77fd7b3ea --- /dev/null +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts @@ -0,0 +1,227 @@ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { BigNumber } from '../../../configured_bignumber'; +import { CalldataBlock, CalldataBlocks, RawCalldata } from '../../calldata'; +import * as Constants from '../../utils/constants'; +import { DecodingRules } from '../../utils/rules'; + +import { DataType } from '../data_type'; +import { DataTypeFactory, MemberIndexByName } from '../interfaces'; + +import { Pointer } from './pointer'; + +export abstract class Set extends DataType { + protected readonly _arrayLength: number | undefined; + protected readonly _arrayElementType: string | undefined; + private readonly _memberIndexByName: MemberIndexByName; + private readonly _members: DataType[]; + private readonly _isArray: boolean; + + public constructor( + dataItem: DataItem, + factory: DataTypeFactory, + isArray: boolean = false, + arrayLength?: number, + arrayElementType?: string, + ) { + super(dataItem, factory); + this._memberIndexByName = {}; + this._members = []; + this._isArray = isArray; + this._arrayLength = arrayLength; + this._arrayElementType = arrayElementType; + if (isArray && arrayLength !== undefined) { + [this._members, this._memberIndexByName] = this._createMembersWithLength(dataItem, arrayLength); + } else if (!isArray) { + [this._members, this._memberIndexByName] = this._createMembersWithKeys(dataItem); + } + } + + public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): CalldataBlocks.Set { + const block = + value instanceof Array + ? this._generateCalldataBlockFromArray(value, parentBlock) + : this._generateCalldataBlockFromObject(value, parentBlock); + return block; + } + + public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { + let members = this._members; + if (this._isArray && this._arrayLength === undefined) { + const arrayLengthBuf = calldata.popWord(); + const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); + const hexBase = 16; + const arrayLength = new BigNumber(arrayLengthHex, hexBase); + + [members] = this._createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); + } + + calldata.startScope(); + let value: any[] | object; + if (rules.structsAsObjects && !this._isArray) { + value = {}; + _.each(this._memberIndexByName, (idx: number, key: string) => { + const member = this._members[idx]; + const memberValue = member.generateValue(calldata, rules); + (value as { [key: string]: any })[key] = memberValue; + }); + } else { + value = []; + _.each(members, (member: DataType, idx: number) => { + const memberValue = member.generateValue(calldata, rules); + (value as any[]).push(memberValue); + }); + } + calldata.endScope(); + return value; + } + + public isStatic(): boolean { + /* For Tuple: + const isStaticTuple = this.children.length === 0; + return isStaticTuple; // @TODO: True in every case or only when dynamic data? + + For Array: + if isLengthDefined = false then this is false + + Otherwise if the first element is a Pointer then false + */ + + if (this._isArray && this._arrayLength === undefined) { + return false; + } + + // Search for dependent members + const dependentMember = _.find(this._members, (member: DataType) => { + return member instanceof Pointer; + }); + const isStatic = dependentMember === undefined; // static if we couldn't find a dependent member + return isStatic; + } + + protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): CalldataBlocks.Set { + // Sanity check length + if (this._arrayLength !== undefined && value.length !== this._arrayLength) { + throw new Error( + `Expected array of ${JSON.stringify( + this._arrayLength, + )} elements, but got array of length ${JSON.stringify(value.length)}`, + ); + } + + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: CalldataBlocks.Set = new CalldataBlocks.Set( + this.getDataItem().name, + this.getSignature(), + parentName, + ); + + let members = this._members; + if (this._isArray && this._arrayLength === undefined) { + [members] = this._createMembersWithLength(this.getDataItem(), value.length); + + const lenBuf = ethUtil.setLengthLeft( + ethUtil.toBuffer(`0x${value.length.toString(Constants.HEX_BASE)}`), + Constants.EVM_WORD_WIDTH_IN_BYTES, + ); + methodBlock.setHeader(lenBuf); + } + + const memberCalldataBlocks: CalldataBlock[] = []; + _.each(members, (member: DataType, idx: number) => { + const block = member.generateCalldataBlock(value[idx], methodBlock); + memberCalldataBlocks.push(block); + }); + methodBlock.setMembers(memberCalldataBlocks); + return methodBlock; + } + + protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): CalldataBlocks.Set { + const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const methodBlock: CalldataBlocks.Set = new CalldataBlocks.Set( + this.getDataItem().name, + this.getSignature(), + parentName, + ); + const memberCalldataBlocks: CalldataBlock[] = []; + const childMap = _.cloneDeep(this._memberIndexByName); + _.forOwn(obj, (value: any, key: string) => { + if (!(key in childMap)) { + throw new Error( + `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`, + ); + } + const block = this._members[this._memberIndexByName[key]].generateCalldataBlock(value, methodBlock); + memberCalldataBlocks.push(block); + delete childMap[key]; + }); + + if (Object.keys(childMap).length !== 0) { + throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); + } + + methodBlock.setMembers(memberCalldataBlocks); + return methodBlock; + } + + protected _computeSignatureOfMembers(): string { + // Compute signature of members + let signature = `(`; + _.each(this._members, (member: DataType, i: number) => { + signature += member.getSignature(); + if (i < this._members.length - 1) { + signature += ','; + } + }); + signature += ')'; + return signature; + } + + private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberIndexByName] { + // Sanity check + if (dataItem.components === undefined) { + throw new Error(`Expected components`); + } + + const members: DataType[] = []; + const memberIndexByName: MemberIndexByName = {}; + _.each(dataItem.components, (memberItem: DataItem) => { + const childDataItem: DataItem = { + type: memberItem.type, + name: `${dataItem.name}.${memberItem.name}`, + }; + const components = memberItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberIndexByName[memberItem.name] = members.length; + members.push(child); + }); + + return [members, memberIndexByName]; + } + + private _createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberIndexByName] { + const members: DataType[] = []; + const memberIndexByName: MemberIndexByName = {}; + const range = _.range(length); + _.each(range, (idx: number) => { + const childDataItem: DataItem = { + type: this._arrayElementType ? this._arrayElementType : '', + name: `${dataItem.name}[${idx.toString(Constants.DEC_BASE)}]`, + }; + const components = dataItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberIndexByName[idx.toString(Constants.DEC_BASE)] = members.length; + members.push(child); + }); + + return [members, memberIndexByName]; + } +} diff --git a/packages/utils/src/abi_encoder/calldata/blocks/blob.ts b/packages/utils/src/abi_encoder/calldata/blocks/blob.ts new file mode 100644 index 000000000..210ef6420 --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/blocks/blob.ts @@ -0,0 +1,20 @@ +import { CalldataBlock } from '../calldata_block'; + +export class Blob extends CalldataBlock { + private readonly _blob: Buffer; + + constructor(name: string, signature: string, parentName: string, blob: Buffer) { + const headerSizeInBytes = 0; + const bodySizeInBytes = blob.byteLength; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); + this._blob = blob; + } + + public toBuffer(): Buffer { + return this._blob; + } + + public getRawData(): Buffer { + return this._blob; + } +} diff --git a/packages/utils/src/abi_encoder/calldata/blocks/index.ts b/packages/utils/src/abi_encoder/calldata/blocks/index.ts new file mode 100644 index 000000000..958582dae --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/blocks/index.ts @@ -0,0 +1,3 @@ +export * from './blob'; +export * from './pointer'; +export * from './set'; diff --git a/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts new file mode 100644 index 000000000..1c49a8c6c --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts @@ -0,0 +1,59 @@ +import * as ethUtil from 'ethereumjs-util'; + +import * as Constants from '../../utils/constants'; + +import { CalldataBlock } from '../calldata_block'; + +export class Pointer extends CalldataBlock { + public static readonly RAW_DATA_START = new Buffer('<'); + public static readonly RAW_DATA_END = new Buffer('>'); + private static readonly _DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; + private static readonly _EMPTY_HEADER_SIZE = 0; + private readonly _parent: CalldataBlock; + private readonly _dependency: CalldataBlock; + private _aliasFor: CalldataBlock | undefined; + + constructor(name: string, signature: string, parentName: string, dependency: CalldataBlock, parent: CalldataBlock) { + const headerSizeInBytes = Pointer._EMPTY_HEADER_SIZE; + const bodySizeInBytes = Pointer._DEPENDENT_PAYLOAD_SIZE_IN_BYTES; + super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); + this._parent = parent; + this._dependency = dependency; + this._aliasFor = undefined; + } + + public toBuffer(): Buffer { + const destinationOffset = + this._aliasFor !== undefined ? this._aliasFor.getOffsetInBytes() : this._dependency.getOffsetInBytes(); + const parentOffset = this._parent.getOffsetInBytes(); + const parentHeaderSize = this._parent.getHeaderSizeInBytes(); + const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); + const pointerHex = `0x${pointer.toString(Constants.HEX_BASE)}`; + const pointerBuf = ethUtil.toBuffer(pointerHex); + const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + return pointerBufPadded; + } + + public getDependency(): CalldataBlock { + return this._dependency; + } + + public setAlias(block: CalldataBlock): void { + this._aliasFor = block; + this._setName(`${this.getName()} (alias for ${block.getName()})`); + } + + public getAlias(): CalldataBlock | undefined { + return this._aliasFor; + } + + public getRawData(): Buffer { + const dependencyRawData = this._dependency.getRawData(); + const rawDataComponents: Buffer[] = []; + rawDataComponents.push(Pointer.RAW_DATA_START); + rawDataComponents.push(dependencyRawData); + rawDataComponents.push(Pointer.RAW_DATA_END); + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } +} diff --git a/packages/utils/src/abi_encoder/calldata/blocks/set.ts b/packages/utils/src/abi_encoder/calldata/blocks/set.ts new file mode 100644 index 000000000..e4de22c5c --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/blocks/set.ts @@ -0,0 +1,47 @@ +import * as _ from 'lodash'; + +import { CalldataBlock } from '../calldata_block'; + +export class Set extends CalldataBlock { + private _header: Buffer | undefined; + private _members: CalldataBlock[]; + + constructor(name: string, signature: string, parentName: string) { + super(name, signature, parentName, 0, 0); + this._members = []; + this._header = undefined; + } + + public getRawData(): Buffer { + const rawDataComponents: Buffer[] = []; + if (this._header) { + rawDataComponents.push(this._header); + } + _.each(this._members, (member: CalldataBlock) => { + const memberBuffer = member.getRawData(); + rawDataComponents.push(memberBuffer); + }); + const rawData = Buffer.concat(rawDataComponents); + return rawData; + } + + public setMembers(members: CalldataBlock[]): void { + this._members = members; + } + + public setHeader(header: Buffer): void { + this._setHeaderSize(header.byteLength); + this._header = header; + } + + public toBuffer(): Buffer { + if (this._header) { + return this._header; + } + return new Buffer(''); + } + + public getMembers(): CalldataBlock[] { + return this._members; + } +} diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index 3b85f821b..b2396ee8f 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -5,8 +5,8 @@ import * as Constants from '../utils/constants'; import { Queue } from '../utils/queue'; import { EncodingRules } from '../utils/rules'; +import * as CalldataBlocks from './blocks'; import { CalldataBlock } from './calldata_block'; -import * as CalldataBlocks from './calldata_blocks'; export class Calldata { private readonly _rules: EncodingRules; @@ -18,7 +18,7 @@ export class Calldata { const blockQueue = new Queue(); // Base Case - if (!(block instanceof CalldataBlocks.MemberCalldataBlock)) { + if (!(block instanceof CalldataBlocks.Set)) { blockQueue.pushBack(block); return blockQueue; } @@ -26,7 +26,7 @@ export class Calldata { // This is a Member Block const memberBlock = block; _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof CalldataBlocks.MemberCalldataBlock) { + if (member instanceof CalldataBlocks.Set) { blockQueue.mergeFront(Calldata._createQueue(member)); } else { blockQueue.pushFront(member); @@ -35,9 +35,9 @@ export class Calldata { // Children _.each(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof CalldataBlocks.DependentCalldataBlock && member.getAlias() === undefined) { + if (member instanceof CalldataBlocks.Pointer && member.getAlias() === undefined) { const dependency = member.getDependency(); - if (dependency instanceof CalldataBlocks.MemberCalldataBlock) { + if (dependency instanceof CalldataBlocks.Set) { blockQueue.mergeBack(Calldata._createQueue(dependency)); } else { blockQueue.pushBack(dependency); @@ -68,7 +68,7 @@ export class Calldata { const subtreeQueue = Calldata._createQueue(this._root); let block: CalldataBlock | undefined; for (block = subtreeQueue.popBack(); block !== undefined; block = subtreeQueue.popBack()) { - if (block instanceof CalldataBlocks.DependentCalldataBlock) { + if (block instanceof CalldataBlocks.Pointer) { const dependencyBlockHashBuf = block.getDependency().computeHash(); const dependencyBlockHash = ethUtil.bufferToHex(dependencyBlockHashBuf); if (dependencyBlockHash in blocksByHash) { @@ -175,7 +175,7 @@ export class Calldata { ), ) .padEnd(valuePadding); - if (block instanceof CalldataBlocks.MemberCalldataBlock) { + if (block instanceof CalldataBlocks.Set) { nameStr = `### ${prettyName.padEnd(namePadding)}`; line = `\n${offsetStr}${value}${nameStr}`; } else { diff --git a/packages/utils/src/abi_encoder/calldata/calldata_blocks.ts b/packages/utils/src/abi_encoder/calldata/calldata_blocks.ts deleted file mode 100644 index 8d4e7d7ca..000000000 --- a/packages/utils/src/abi_encoder/calldata/calldata_blocks.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './dependent_calldata_block'; -export * from './member_calldata_block'; -export * from './payload_calldata_block'; diff --git a/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts b/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts deleted file mode 100644 index 16b9a6fe6..000000000 --- a/packages/utils/src/abi_encoder/calldata/dependent_calldata_block.ts +++ /dev/null @@ -1,59 +0,0 @@ -import * as ethUtil from 'ethereumjs-util'; - -import * as Constants from '../utils/constants'; - -import { CalldataBlock } from './calldata_block'; - -export class DependentCalldataBlock extends CalldataBlock { - public static readonly RAW_DATA_START = new Buffer('<'); - public static readonly RAW_DATA_END = new Buffer('>'); - private static readonly _DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; - private static readonly _EMPTY_HEADER_SIZE = 0; - private readonly _parent: CalldataBlock; - private readonly _dependency: CalldataBlock; - private _aliasFor: CalldataBlock | undefined; - - constructor(name: string, signature: string, parentName: string, dependency: CalldataBlock, parent: CalldataBlock) { - const headerSizeInBytes = DependentCalldataBlock._EMPTY_HEADER_SIZE; - const bodySizeInBytes = DependentCalldataBlock._DEPENDENT_PAYLOAD_SIZE_IN_BYTES; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); - this._parent = parent; - this._dependency = dependency; - this._aliasFor = undefined; - } - - public toBuffer(): Buffer { - const destinationOffset = - this._aliasFor !== undefined ? this._aliasFor.getOffsetInBytes() : this._dependency.getOffsetInBytes(); - const parentOffset = this._parent.getOffsetInBytes(); - const parentHeaderSize = this._parent.getHeaderSizeInBytes(); - const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); - const pointerBuf = ethUtil.toBuffer(`0x${pointer.toString(Constants.HEX_BASE)}`); - const evmWordWidthInBytes = 32; - const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, evmWordWidthInBytes); - return pointerBufPadded; - } - - public getDependency(): CalldataBlock { - return this._dependency; - } - - public setAlias(block: CalldataBlock): void { - this._aliasFor = block; - this._setName(`${this.getName()} (alias for ${block.getName()})`); - } - - public getAlias(): CalldataBlock | undefined { - return this._aliasFor; - } - - public getRawData(): Buffer { - const dependencyRawData = this._dependency.getRawData(); - const rawDataComponents: Buffer[] = []; - rawDataComponents.push(DependentCalldataBlock.RAW_DATA_START); - rawDataComponents.push(dependencyRawData); - rawDataComponents.push(DependentCalldataBlock.RAW_DATA_END); - const rawData = Buffer.concat(rawDataComponents); - return rawData; - } -} diff --git a/packages/utils/src/abi_encoder/calldata/index.ts b/packages/utils/src/abi_encoder/calldata/index.ts index 2c786cd8d..2ef75e8d0 100644 --- a/packages/utils/src/abi_encoder/calldata/index.ts +++ b/packages/utils/src/abi_encoder/calldata/index.ts @@ -1,6 +1,5 @@ -export * from './calldata_block'; -export * from './dependent_calldata_block'; -export * from './payload_calldata_block'; -export * from './member_calldata_block'; export * from './calldata'; +export * from './calldata_block'; export * from './raw_calldata'; +import * as CalldataBlocks from './blocks'; +export { CalldataBlocks }; diff --git a/packages/utils/src/abi_encoder/calldata/member_calldata_block.ts b/packages/utils/src/abi_encoder/calldata/member_calldata_block.ts deleted file mode 100644 index c35beb8de..000000000 --- a/packages/utils/src/abi_encoder/calldata/member_calldata_block.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as _ from 'lodash'; - -import { CalldataBlock } from './calldata_block'; - -export class MemberCalldataBlock extends CalldataBlock { - private _header: Buffer | undefined; - private _members: CalldataBlock[]; - - constructor(name: string, signature: string, parentName: string) { - super(name, signature, parentName, 0, 0); - this._members = []; - this._header = undefined; - } - - public getRawData(): Buffer { - const rawDataComponents: Buffer[] = []; - if (this._header !== undefined) { - rawDataComponents.push(this._header); - } - _.each(this._members, (member: CalldataBlock) => { - const memberBuffer = member.getRawData(); - rawDataComponents.push(memberBuffer); - }); - - const rawData = Buffer.concat(rawDataComponents); - return rawData; - } - - public setMembers(members: CalldataBlock[]): void { - this._members = members; - } - - public setHeader(header: Buffer): void { - this._setHeaderSize(header.byteLength); - this._header = header; - } - - public toBuffer(): Buffer { - if (this._header !== undefined) { - return this._header; - } - return new Buffer(''); - } - - public getMembers(): CalldataBlock[] { - return this._members; - } -} diff --git a/packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts b/packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts deleted file mode 100644 index 0420b01d8..000000000 --- a/packages/utils/src/abi_encoder/calldata/payload_calldata_block.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { CalldataBlock } from './calldata_block'; - -export class PayloadCalldataBlock extends CalldataBlock { - private readonly _payload: Buffer; - - constructor(name: string, signature: string, parentName: string, payload: Buffer) { - const headerSizeInBytes = 0; - const bodySizeInBytes = payload.byteLength; - super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); - this._payload = payload; - } - - public toBuffer(): Buffer { - return this._payload; - } - - public getRawData(): Buffer { - return this._payload; - } -} diff --git a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts index 9e72bbd62..b13cbdfd9 100644 --- a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts @@ -8,26 +8,25 @@ export class RawCalldata { private readonly _value: Buffer; private readonly _selector: string; private readonly _scopes: Queue; - private _offset: number; // tracks current offset into raw calldata; used for parsing + private _offset: number; - constructor(value: string | Buffer, hasSelectorPrefix: boolean = true) { + public constructor(value: string | Buffer, hasSelector: boolean = true) { + // Sanity check if (typeof value === 'string' && !value.startsWith('0x')) { throw new Error(`Expected raw calldata to start with '0x'`); } - const valueBuf = ethUtil.toBuffer(value); - if (hasSelectorPrefix) { - this._selector = ethUtil.bufferToHex( - valueBuf.slice(Constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, Constants.HEX_SELECTOR_LENGTH_IN_BYTES), - ); - this._value = valueBuf.slice(Constants.HEX_SELECTOR_LENGTH_IN_BYTES); // disregard selector - } else { - this._selector = '0x'; - this._value = valueBuf; - } - + // Construct initial values + this._value = ethUtil.toBuffer(value); + this._selector = '0x'; this._scopes = new Queue(); this._scopes.pushBack(RawCalldata._INITIAL_OFFSET); this._offset = RawCalldata._INITIAL_OFFSET; + // If there's a selector then slice it + if (hasSelector) { + const selectorBuf = this._value.slice(Constants.HEX_SELECTOR_LENGTH_IN_BYTES); + this._value = this._value.slice(Constants.HEX_SELECTOR_LENGTH_IN_BYTES); + this._selector = ethUtil.bufferToHex(selectorBuf); + } } public popBytes(lengthInBytes: number): Buffer { diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 84f6665cb..71aa293b5 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -3,11 +3,11 @@ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; +import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../utils/constants'; -export class Address extends PayloadDataType { +export class Address extends AbstractDataTypes.Blob { public static ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X = "Address must start with '0x'"; public static ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES = 'Address must be 20 bytes'; private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts index 77e38ebd7..54f7ba9fa 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/array.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -1,9 +1,9 @@ import { DataItem } from 'ethereum-types'; -import { DataTypeFactory, MemberDataType } from '../abstract_data_types'; +import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import * as Constants from '../utils/constants'; -export class Array extends MemberDataType { +export class Array extends AbstractDataTypes.Set { private static readonly _MATCHER = RegExp('^(.+)\\[([0-9]*)\\]$'); private readonly _arraySignature: string; private readonly _elementType: string; diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index 82a519aae..031acc88a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -4,11 +4,11 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; -import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; +import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../utils/constants'; -export class Bool extends PayloadDataType { +export class Bool extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; public static matchType(type: string): boolean { diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index ce6ace627..01d83d11a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -3,11 +3,11 @@ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; +import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../utils/constants'; -export class DynamicBytes extends PayloadDataType { +export class DynamicBytes extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; public static matchType(type: string): boolean { diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index 83aeacd66..8a82ed4cc 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -2,21 +2,21 @@ import { DataItem } from 'ethereum-types'; import { BigNumber } from '../../configured_bignumber'; -import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; +import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../utils/constants'; import * as EncoderMath from '../utils/math'; -export class Int extends PayloadDataType { +export class Int extends AbstractDataTypes.Blob { private static readonly _MATCHER = RegExp( '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _MAX_WIDTH: number = 256; private static readonly _DEFAULT_WIDTH: number = Int._MAX_WIDTH; - private _width: number; - private _minValue: BigNumber; - private _maxValue: BigNumber; + private readonly _width: number; + private readonly _minValue: BigNumber; + private readonly _maxValue: BigNumber; public static matchType(type: string): boolean { return Int._MATCHER.test(type); diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts index f2e417485..bd4732097 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/method.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts @@ -2,13 +2,13 @@ import { DataItem, MethodAbi } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { DataType, DataTypeFactory, MemberDataType } from '../abstract_data_types'; +import { AbstractDataTypes, DataType, DataTypeFactory } from '../abstract_data_types'; import * as Constants from '../utils/constants'; import { DecodingRules, EncodingRules } from '../utils/rules'; import { Tuple } from './tuple'; -export class Method extends MemberDataType { +export class Method extends AbstractDataTypes.Set { private readonly _methodSignature: string; private readonly _methodSelector: string; private readonly _returnDataType: DataType; diff --git a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts index d4411df9b..00c743d2b 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts @@ -1,8 +1,8 @@ import { DataItem } from 'ethereum-types'; -import { DataType, DataTypeFactory, DependentDataType } from '../abstract_data_types'; +import { AbstractDataTypes, DataType, DataTypeFactory } from '../abstract_data_types'; -export class Pointer extends DependentDataType { +export class Pointer extends AbstractDataTypes.Pointer { constructor(destDataType: DataType, parentDataType: DataType, dataTypeFactory: DataTypeFactory) { const destDataItem = destDataType.getDataItem(); const dataItem: DataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` }; diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 0d01e6105..d0b41194e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -2,11 +2,11 @@ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; +import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../utils/constants'; -export class StaticBytes extends PayloadDataType { +export class StaticBytes extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _MATCHER = RegExp( '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index 2bb6541a3..6ab3513c9 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -3,11 +3,11 @@ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; +import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../utils/constants'; -export class String extends PayloadDataType { +export class String extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; public static matchType(type: string): boolean { diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts index 63d9dfa9e..3802f96c0 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts @@ -1,8 +1,8 @@ import { DataItem } from 'ethereum-types'; -import { DataTypeFactory, MemberDataType } from '../abstract_data_types'; +import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; -export class Tuple extends MemberDataType { +export class Tuple extends AbstractDataTypes.Set { private readonly _signature: string; public static matchType(type: string): boolean { diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index 832ab075c..b1bc690d8 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -2,21 +2,21 @@ import { DataItem } from 'ethereum-types'; import { BigNumber } from '../../configured_bignumber'; -import { DataTypeFactory, PayloadDataType } from '../abstract_data_types'; +import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import * as Constants from '../utils/constants'; import * as EncoderMath from '../utils/math'; -export class UInt extends PayloadDataType { +export class UInt extends AbstractDataTypes.Blob { private static readonly _MATCHER = RegExp( '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _MAX_WIDTH: number = 256; private static readonly _DEFAULT_WIDTH: number = UInt._MAX_WIDTH; - private _width: number; - private _minValue: BigNumber; - private _maxValue: BigNumber; + private static readonly _MIN_VALUE = new BigNumber(0); + private readonly _width: number; + private readonly _maxValue: BigNumber; public static matchType(type: string): boolean { return UInt._MATCHER.test(type); @@ -36,18 +36,17 @@ export class UInt extends PayloadDataType { throw new Error(`Tried to instantiate UInt with bad input: ${dataItem}`); } this._width = UInt._decodeWidthFromType(dataItem.type); - this._minValue = new BigNumber(0); this._maxValue = new BigNumber(2).toPower(this._width).sub(1); } public encodeValue(value: BigNumber | string | number): Buffer { - const encodedValue = EncoderMath.safeEncodeNumericValue(value, this._minValue, this._maxValue); + const encodedValue = EncoderMath.safeEncodeNumericValue(value, UInt._MIN_VALUE, this._maxValue); return encodedValue; } public decodeValue(calldata: RawCalldata): BigNumber { const valueBuf = calldata.popWord(); - const value = EncoderMath.safeDecodeNumericValue(valueBuf, this._minValue, this._maxValue); + const value = EncoderMath.safeDecodeNumericValue(valueBuf, UInt._MIN_VALUE, this._maxValue); return value; } @@ -55,4 +54,3 @@ export class UInt extends PayloadDataType { return `uint${this._width}`; } } - -- cgit v1.2.3 From 8f73f53c95d8ba887558863b8b726a2b3f5b7e2b Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 20:05:41 -0800 Subject: Moved calldata iterator logic into its own iterator clas --- .../abi_encoder/abstract_data_types/types/blob.ts | 8 +- .../abstract_data_types/types/pointer.ts | 21 +++-- .../utils/src/abi_encoder/calldata/calldata.ts | 56 +++---------- .../utils/src/abi_encoder/calldata/iterator.ts | 94 ++++++++++++++++++++++ .../src/abi_encoder/evm_data_types/pointer.ts | 2 +- 5 files changed, 119 insertions(+), 62 deletions(-) create mode 100644 packages/utils/src/abi_encoder/calldata/iterator.ts diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts index f4246c893..35ccc0586 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts @@ -8,11 +8,11 @@ import { DataType } from '../data_type'; import { DataTypeFactory } from '../interfaces'; export abstract class Blob extends DataType { - protected _hasConstantSize: boolean; + protected _sizeKnownAtCompileTime: boolean; - public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { + public constructor(dataItem: DataItem, factory: DataTypeFactory, sizeKnownAtCompileTime: boolean) { super(dataItem, factory); - this._hasConstantSize = hasConstantSize; + this._sizeKnownAtCompileTime = sizeKnownAtCompileTime; } public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlocks.Blob { @@ -30,7 +30,7 @@ export abstract class Blob extends DataType { } public isStatic(): boolean { - return this._hasConstantSize; + return this._sizeKnownAtCompileTime; } public abstract encodeValue(value: any): Buffer; diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts index 47efac521..46e60979a 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts @@ -1,3 +1,4 @@ +/* tslint:disable prefer-function-over-method */ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -10,26 +11,24 @@ import { DataType } from '../data_type'; import { DataTypeFactory } from '../interfaces'; export abstract class Pointer extends DataType { - protected _dependency: DataType; + protected _destination: DataType; protected _parent: DataType; - private readonly _isStatic: boolean; - public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { + public constructor(dataItem: DataItem, factory: DataTypeFactory, destination: DataType, parent: DataType) { super(dataItem, factory); - this._dependency = dependency; + this._destination = destination; this._parent = parent; - this._isStatic = true; } public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlocks.Pointer { - if (parentBlock === undefined) { + if (!parentBlock) { throw new Error(`DependentDataType requires a parent block to generate its block`); } - const dependencyBlock = this._dependency.generateCalldataBlock(value, parentBlock); + const destinationBlock = this._destination.generateCalldataBlock(value, parentBlock); const name = this.getDataItem().name; const signature = this.getSignature(); - const parentName = parentBlock ? parentBlock.getName() : ''; - const block = new CalldataBlocks.Pointer(name, signature, parentName, dependencyBlock, parentBlock); + const parentName = parentBlock.getName(); + const block = new CalldataBlocks.Pointer(name, signature, parentName, destinationBlock, parentBlock); return block; } @@ -40,12 +39,12 @@ export abstract class Pointer extends DataType { const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); const currentOffset = calldata.getOffset(); calldata.setOffset(destinationOffsetAbsolute); - const value = this._dependency.generateValue(calldata, rules); + const value = this._destination.generateValue(calldata, rules); calldata.setOffset(currentOffset); return value; } public isStatic(): boolean { - return this._isStatic; + return true; } } diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index b2396ee8f..dd9d47def 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -2,11 +2,11 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import * as Constants from '../utils/constants'; -import { Queue } from '../utils/queue'; import { EncodingRules } from '../utils/rules'; import * as CalldataBlocks from './blocks'; import { CalldataBlock } from './calldata_block'; +import { CalldataIterator, ReverseCalldataIterator } from './iterator'; export class Calldata { private readonly _rules: EncodingRules; @@ -14,41 +14,6 @@ export class Calldata { private _sizeInBytes: number; private _root: CalldataBlock | undefined; - private static _createQueue(block: CalldataBlock): Queue { - const blockQueue = new Queue(); - - // Base Case - if (!(block instanceof CalldataBlocks.Set)) { - blockQueue.pushBack(block); - return blockQueue; - } - - // This is a Member Block - const memberBlock = block; - _.eachRight(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof CalldataBlocks.Set) { - blockQueue.mergeFront(Calldata._createQueue(member)); - } else { - blockQueue.pushFront(member); - } - }); - - // Children - _.each(memberBlock.getMembers(), (member: CalldataBlock) => { - if (member instanceof CalldataBlocks.Pointer && member.getAlias() === undefined) { - const dependency = member.getDependency(); - if (dependency instanceof CalldataBlocks.Set) { - blockQueue.mergeBack(Calldata._createQueue(dependency)); - } else { - blockQueue.pushBack(dependency); - } - } - }); - - blockQueue.pushFront(memberBlock); - return blockQueue; - } - public constructor(rules: EncodingRules) { this._rules = rules; this._selector = ''; @@ -65,9 +30,9 @@ export class Calldata { // 1. Create a queue of subtrees by hash // Note that they are ordered the same as - const subtreeQueue = Calldata._createQueue(this._root); + const iterator = new ReverseCalldataIterator(this._root); let block: CalldataBlock | undefined; - for (block = subtreeQueue.popBack(); block !== undefined; block = subtreeQueue.popBack()) { + while (block = iterator.next()) { if (block instanceof CalldataBlocks.Pointer) { const dependencyBlockHashBuf = block.getDependency().computeHash(); const dependencyBlockHash = ethUtil.bufferToHex(dependencyBlockHashBuf); @@ -97,10 +62,10 @@ export class Calldata { this.optimize(); } - const offsetQueue = Calldata._createQueue(this._root); + const iterator = new CalldataIterator(this._root); let block: CalldataBlock | undefined; let offset = 0; - for (block = offsetQueue.popFront(); block !== undefined; block = offsetQueue.popFront()) { + while (block = iterator.next()) { block.setOffset(offset); offset += block.getSizeInBytes(); } @@ -136,13 +101,12 @@ export class Calldata { throw new Error('expected root'); } - const valueQueue = Calldata._createQueue(this._root); + const iterator = new CalldataIterator(this._root); let block: CalldataBlock | undefined; let offset = 0; - const functionBlock = valueQueue.peekFront(); - const functionName: string = functionBlock === undefined ? '' : functionBlock.getName(); - for (block = valueQueue.popFront(); block !== undefined; block = valueQueue.popFront()) { + const functionName: string = this._root.getName(); + while (block = iterator.next()) { // Process each block 1 word at a time const size = block.getSizeInBytes(); const name = block.getName(); @@ -209,10 +173,10 @@ export class Calldata { throw new Error('expected root'); } - const valueQueue = Calldata._createQueue(this._root); + const iterator = new CalldataIterator(this._root); const valueBufs: Buffer[] = [selectorBuffer]; let block: CalldataBlock | undefined; - for (block = valueQueue.popFront(); block !== undefined; block = valueQueue.popFront()) { + while (block = iterator.next()) { valueBufs.push(block.toBuffer()); } diff --git a/packages/utils/src/abi_encoder/calldata/iterator.ts b/packages/utils/src/abi_encoder/calldata/iterator.ts new file mode 100644 index 000000000..3e3367e10 --- /dev/null +++ b/packages/utils/src/abi_encoder/calldata/iterator.ts @@ -0,0 +1,94 @@ +/* tslint:disable max-classes-per-file */ +import * as _ from 'lodash'; + +import { Queue } from '../utils/queue'; + +import * as CalldataBlocks from './blocks'; +import { CalldataBlock } from './calldata_block'; + +/** + * Iterator class for Calldata Blocks. Blocks follows the order + * they should be put into calldata that is passed to he EVM. + * + * Example #1: + * Let root = Set { + * Blob{} A, + * Pointer { + * Blob{} a + * } B, + * Blob{} C + * } + * It will iterate as follows: [A, B, C, B.a] + * + * Example #2: + * Let root = Set { + * Blob{} A, + * Pointer { + * Blob{} a + * Pointer { + * Blob{} b + * } + * } B, + * Pointer { + * Blob{} c + * } C + * } + * It will iterate as follows: [A, B, C, B.a, B.b, C.c] + */ +abstract class BaseIterator { + protected readonly _root: CalldataBlock; + protected readonly _queue: Queue; + + private static _createQueue(block: CalldataBlock): Queue { + const queue = new Queue(); + // Base case + if (!(block instanceof CalldataBlocks.Set)) { + queue.pushBack(block); + return queue; + } + // This is a set; add members + const set = block; + _.eachRight(set.getMembers(), (member: CalldataBlock) => { + queue.mergeFront(BaseIterator._createQueue(member)); + }); + // Add children + _.each(set.getMembers(), (member: CalldataBlock) => { + // Traverse child if it is a unique pointer. + // A pointer that is an alias for another pointer is ignored. + if (member instanceof CalldataBlocks.Pointer && member.getAlias() === undefined) { + const dependency = member.getDependency(); + queue.mergeBack(BaseIterator._createQueue(dependency)); + } + }); + // Put set block at the front of the queue + queue.pushFront(set); + return queue; + } + + public constructor(root: CalldataBlock) { + this._root = root; + this._queue = BaseIterator._createQueue(root); + } + + public abstract next(): CalldataBlock | undefined; +} + +export class CalldataIterator extends BaseIterator { + public constructor(root: CalldataBlock) { + super(root); + } + + public next(): CalldataBlock | undefined { + return this._queue.popFront(); + } +} + +export class ReverseCalldataIterator extends BaseIterator { + public constructor(root: CalldataBlock) { + super(root); + } + + public next(): CalldataBlock | undefined { + return this._queue.popBack(); + } +} diff --git a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts index 00c743d2b..e7c172afb 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts @@ -10,6 +10,6 @@ export class Pointer extends AbstractDataTypes.Pointer { } public getSignature(): string { - return this._dependency.getSignature(); + return this._destination.getSignature(); } } -- cgit v1.2.3 From bab1c92c703ee53e77a56a7f7a5e3bba5b4a2306 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 20:11:10 -0800 Subject: Linter for Calldata Block Iterator --- .../utils/src/abi_encoder/calldata/calldata.ts | 13 ++++------- .../utils/src/abi_encoder/calldata/iterator.ts | 26 ++++++++++++++++++---- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index dd9d47def..50f0f0fad 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -31,8 +31,7 @@ export class Calldata { // 1. Create a queue of subtrees by hash // Note that they are ordered the same as const iterator = new ReverseCalldataIterator(this._root); - let block: CalldataBlock | undefined; - while (block = iterator.next()) { + for (const block of iterator) { if (block instanceof CalldataBlocks.Pointer) { const dependencyBlockHashBuf = block.getDependency().computeHash(); const dependencyBlockHash = ethUtil.bufferToHex(dependencyBlockHashBuf); @@ -63,9 +62,8 @@ export class Calldata { } const iterator = new CalldataIterator(this._root); - let block: CalldataBlock | undefined; let offset = 0; - while (block = iterator.next()) { + for (const block of iterator) { block.setOffset(offset); offset += block.getSizeInBytes(); } @@ -102,11 +100,9 @@ export class Calldata { } const iterator = new CalldataIterator(this._root); - - let block: CalldataBlock | undefined; let offset = 0; const functionName: string = this._root.getName(); - while (block = iterator.next()) { + for (const block of iterator) { // Process each block 1 word at a time const size = block.getSizeInBytes(); const name = block.getName(); @@ -175,8 +171,7 @@ export class Calldata { const iterator = new CalldataIterator(this._root); const valueBufs: Buffer[] = [selectorBuffer]; - let block: CalldataBlock | undefined; - while (block = iterator.next()) { + for (const block of iterator) { valueBufs.push(block.toBuffer()); } diff --git a/packages/utils/src/abi_encoder/calldata/iterator.ts b/packages/utils/src/abi_encoder/calldata/iterator.ts index 3e3367e10..8e2b16a5a 100644 --- a/packages/utils/src/abi_encoder/calldata/iterator.ts +++ b/packages/utils/src/abi_encoder/calldata/iterator.ts @@ -35,7 +35,7 @@ import { CalldataBlock } from './calldata_block'; * } * It will iterate as follows: [A, B, C, B.a, B.b, C.c] */ -abstract class BaseIterator { +abstract class BaseIterator implements Iterable { protected readonly _root: CalldataBlock; protected readonly _queue: Queue; @@ -70,7 +70,25 @@ abstract class BaseIterator { this._queue = BaseIterator._createQueue(root); } - public abstract next(): CalldataBlock | undefined; + public [Symbol.iterator](): { next: () => IteratorResult } { + return { + next: () => { + const nextBlock = this.nextBlock(); + if (nextBlock !== undefined) { + return { + value: nextBlock, + done: false, + }; + } + return { + done: true, + value: new CalldataBlocks.Blob('', '', '', new Buffer('')), + }; + }, + }; + } + + public abstract nextBlock(): CalldataBlock | undefined; } export class CalldataIterator extends BaseIterator { @@ -78,7 +96,7 @@ export class CalldataIterator extends BaseIterator { super(root); } - public next(): CalldataBlock | undefined { + public nextBlock(): CalldataBlock | undefined { return this._queue.popFront(); } } @@ -88,7 +106,7 @@ export class ReverseCalldataIterator extends BaseIterator { super(root); } - public next(): CalldataBlock | undefined { + public nextBlock(): CalldataBlock | undefined { return this._queue.popBack(); } } -- cgit v1.2.3 From 50344fa24a4a708ae90030af107afb15366f883f Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 21:19:11 -0800 Subject: Added inline documentation for Calldata class --- .../abi_encoder/abstract_data_types/data_type.ts | 4 +- .../utils/src/abi_encoder/calldata/calldata.ts | 227 +++++++++++++-------- 2 files changed, 147 insertions(+), 84 deletions(-) diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts index 450080353..ab7df6ecc 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts @@ -32,8 +32,8 @@ export abstract class DataType { } const block = this.generateCalldataBlock(value); calldata.setRoot(block); - const calldataHex = calldata.toHexString(); - return calldataHex; + const encodedCalldata = calldata.toString(); + return encodedCalldata; } public decode(calldata: string, rules?: DecodingRules, selector?: string): any { diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index 50f0f0fad..da61b2256 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -11,27 +11,107 @@ import { CalldataIterator, ReverseCalldataIterator } from './iterator'; export class Calldata { private readonly _rules: EncodingRules; private _selector: string; - private _sizeInBytes: number; private _root: CalldataBlock | undefined; public constructor(rules: EncodingRules) { this._rules = rules; this._selector = ''; - this._sizeInBytes = 0; this._root = undefined; } - - public optimize(): void { + /** + * Sets the root calldata block. This block usually corresponds to a Method. + */ + public setRoot(block: CalldataBlock): void { + this._root = block; + } + /** + * Sets the selector to be prepended onto the calldata. + * If the root block was created by a Method then a selector will likely be set. + */ + public setSelector(selector: string): void { + if (!selector.startsWith('0x')) { + throw new Error(`Expected selector to be hex. Missing prefix '0x'`); + } else if (selector.length !== Constants.HEX_SELECTOR_LENGTH_IN_CHARS) { + throw new Error(`Invalid selector '${selector}'`); + } + this._selector = selector; + } + /** + * Iterates through the calldata blocks, starting from the root block, to construct calldata as a hex string. + * If the `optimize` flag is set then this calldata will be condensed, to save gas. + * If the `annotate` flag is set then this will return human-readable calldata. + * If the `annotate` flag is *not* set then this will return EVM-compatible calldata. + */ + public toString(): string { + // Sanity check: root block must be set + if (this._root === undefined) { + throw new Error('expected root'); + } + // Optimize, if flag set + if (this._rules.optimize) { + this._optimize(); + } + // Set offsets + const iterator = new CalldataIterator(this._root); + let offset = 0; + for (const block of iterator) { + block.setOffset(offset); + offset += block.getSizeInBytes(); + } + // Generate hex string + const hexString = this._rules.annotate ? this._toAnnotatedString() : this._toCondensedString(); + return hexString; + } + /** + * There are three types of calldata blocks: Blob, Set and Pointer. + * Scenarios arise where distinct pointers resolve to identical values. + * We optimize by keeping only one such instance of the identical value, and redirecting all pointers here. + * We keep the last such duplicate value because pointers can only be positive (they cannot point backwards). + * + * Example #1: + * function f(string[], string[]) + * f(["foo", "bar", "blitz"], ["foo", "bar", "blitz"]) + * The array ["foo", "bar", "blitz"] will only be included in the calldata once. + * + * Example #2: + * function f(string[], string) + * f(["foo", "bar", "blitz"], "foo") + * The string "foo" will only be included in the calldata once. + * + * Example #3: + * function f((string, uint, bytes), string, uint, bytes) + * f(("foo", 5, "0x05"), "foo", 5, "0x05") + * The string "foo" and bytes "0x05" will only be included in the calldata once. + * The duplicate `uint 5` values cannot be optimized out because they are static values (no pointer points to them). + * + * @TODO #1: + * This optimization strategy handles blocks that are exact duplicates of one another. + * But what if some block is a combination of two other blocks? Or a subset of another block? + * This optimization problem is not much different from the current implemetation. + * Instead of tracking "observed" hashes, at each node we would simply do pattern-matching on the calldata. + * This strategy would be applied after assigning offsets to the tree, rather than before (as in this strategy). + * Note that one consequence of this strategy is pointers may resolve to offsets that are not word-aligned. + * This shouldn't be a problem but further investigation should be done. + * + * @TODO #2: + * To be done as a follow-up to @TODO #1. + * Since we optimize from the bottom-up, we could be affecting the outcome of a later potential optimization. + * For example, what if by removing one duplicate value we miss out on optimizing another block higher in the tree. + * To handle this case, at each node we can store a candidate optimization in a priority queue (sorted by calldata size). + * At the end of traversing the tree, the candidate at the front of the queue will be the most optimal output. + * + */ + private _optimize(): void { + // Step 1/1 Create a reverse iterator (starts from the end of the calldata to the beginning) if (this._root === undefined) { throw new Error('expected root'); } - - const blocksByHash: { [key: string]: CalldataBlock } = {}; - - // 1. Create a queue of subtrees by hash - // Note that they are ordered the same as const iterator = new ReverseCalldataIterator(this._root); + // Step 2/2 Iterate over each block, keeping track of which blocks have been seen and pruning redundant blocks. + const blocksByHash: { [key: string]: CalldataBlock } = {}; for (const block of iterator) { + // If a block is a pointer and its value has already been observed, then update + // the pointer to resolve to the existing value. if (block instanceof CalldataBlocks.Pointer) { const dependencyBlockHashBuf = block.getDependency().computeHash(); const dependencyBlockHash = ethUtil.bufferToHex(dependencyBlockHashBuf); @@ -43,7 +123,7 @@ export class Calldata { } continue; } - + // This block has not been seen. Record its hash. const blockHashBuf = block.computeHash(); const blockHash = ethUtil.bufferToHex(blockHashBuf); if (!(blockHash in blocksByHash)) { @@ -51,84 +131,85 @@ export class Calldata { } } } - - public toHexString(): string { + /** + * Returns EVM-compatible calldata as a Hex string. + */ + private _toCondensedString(): string { + // Sanity check: must have a root block. if (this._root === undefined) { throw new Error('expected root'); } - - if (this._rules.optimize) { - this.optimize(); - } - + // Construct an array of buffers (one buffer for each block). + const selectorBuffer = ethUtil.toBuffer(this._selector); + const valueBufs: Buffer[] = [selectorBuffer]; const iterator = new CalldataIterator(this._root); - let offset = 0; for (const block of iterator) { - block.setOffset(offset); - offset += block.getSizeInBytes(); + valueBufs.push(block.toBuffer()); } - - const hexValue = this._rules.annotate ? this._generateAnnotatedHexString() : this._generateCondensedHexString(); + // Create hex from buffer array. + const combinedBuffers = Buffer.concat(valueBufs); + const hexValue = ethUtil.bufferToHex(combinedBuffers); return hexValue; } - - public getSelectorHex(): string { - return this._selector; - } - - public getSizeInBytes(): number { - return this._sizeInBytes; - } - - public setRoot(block: CalldataBlock): void { - this._root = block; - this._sizeInBytes += block.getSizeInBytes(); - } - - public setSelector(selector: string): void { - this._selector = selector.startsWith('0x') ? selector : `$0x${selector}`; - if (this._selector.length !== Constants.HEX_SELECTOR_LENGTH_IN_CHARS) { - throw new Error(`Invalid selector '${this._selector}'`); - } - this._sizeInBytes += Constants.HEX_SELECTOR_LENGTH_IN_BYTES; // @TODO: Used to be += 8. Bad? - } - - private _generateAnnotatedHexString(): string { - let hexValue = `${this._selector}`; + /** + * Returns human-redable calldata. + * + * Example: + * simpleFunction(string[], string[]) + * strings = ["Hello", "World"] + * simpleFunction(strings, strings) + * + * Output: + * 0xbb4f12e3 + * ### simpleFunction + * 0x0 0000000000000000000000000000000000000000000000000000000000000040 ptr (alias for array2) + * 0x20 0000000000000000000000000000000000000000000000000000000000000040 ptr + * + * 0x40 0000000000000000000000000000000000000000000000000000000000000002 ### array2 + * 0x60 0000000000000000000000000000000000000000000000000000000000000040 ptr + * 0x80 0000000000000000000000000000000000000000000000000000000000000080 ptr + * 0xa0 0000000000000000000000000000000000000000000000000000000000000005 array2[0] + * 0xc0 48656c6c6f000000000000000000000000000000000000000000000000000000 + * 0xe0 0000000000000000000000000000000000000000000000000000000000000005 array2[1] + * 0x100 576f726c64000000000000000000000000000000000000000000000000000000 + */ + private _toAnnotatedString(): string { + // Sanity check: must have a root block. if (this._root === undefined) { throw new Error('expected root'); } - - const iterator = new CalldataIterator(this._root); + // Construct annotated calldata + let hexValue = `${this._selector}`; let offset = 0; const functionName: string = this._root.getName(); + const iterator = new CalldataIterator(this._root); for (const block of iterator) { // Process each block 1 word at a time const size = block.getSizeInBytes(); const name = block.getName(); const parentName = block.getParentName(); const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, ''); - - // Current offset - let offsetStr = ''; - - // If this block is empty then it's a newline const offsetPadding = 10; const valuePadding = 74; const namePadding = 80; const evmWordStartIndex = 0; const emptySize = 0; - let value = ''; + // Resulting line will be + let offsetStr = ''; + let valueStr = ''; let nameStr = ''; - let line = ''; + let lineStr = ''; if (size === emptySize) { + // This is a Set block with no header. + // For example, a tuple or an array with a defined length. offsetStr = ' '.repeat(offsetPadding); - value = ' '.repeat(valuePadding); + valueStr = ' '.repeat(valuePadding); nameStr = `### ${prettyName.padEnd(namePadding)}`; - line = `\n${offsetStr}${value}${nameStr}`; + lineStr = `\n${offsetStr}${valueStr}${nameStr}`; } else { + // This block has at least one word of value. offsetStr = `0x${offset.toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); - value = ethUtil + valueStr = ethUtil .stripHexPrefix( ethUtil.bufferToHex( block.toBuffer().slice(evmWordStartIndex, Constants.EVM_WORD_WIDTH_IN_BYTES), @@ -137,46 +218,28 @@ export class Calldata { .padEnd(valuePadding); if (block instanceof CalldataBlocks.Set) { nameStr = `### ${prettyName.padEnd(namePadding)}`; - line = `\n${offsetStr}${value}${nameStr}`; + lineStr = `\n${offsetStr}${valueStr}${nameStr}`; } else { nameStr = ` ${prettyName.padEnd(namePadding)}`; - line = `${offsetStr}${value}${nameStr}`; + lineStr = `${offsetStr}${valueStr}${nameStr}`; } } - + // This block has a value that is more than 1 word. for (let j = Constants.EVM_WORD_WIDTH_IN_BYTES; j < size; j += Constants.EVM_WORD_WIDTH_IN_BYTES) { offsetStr = `0x${(offset + j).toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); - value = ethUtil + valueStr = ethUtil .stripHexPrefix( ethUtil.bufferToHex(block.toBuffer().slice(j, j + Constants.EVM_WORD_WIDTH_IN_BYTES)), ) .padEnd(valuePadding); nameStr = ' '.repeat(namePadding); - line = `${line}\n${offsetStr}${value}${nameStr}`; + lineStr = `${lineStr}\n${offsetStr}${valueStr}${nameStr}`; } - // Append to hex value - hexValue = `${hexValue}\n${line}`; + hexValue = `${hexValue}\n${lineStr}`; offset += size; } return hexValue; } - - private _generateCondensedHexString(): string { - const selectorBuffer = ethUtil.toBuffer(this._selector); - if (this._root === undefined) { - throw new Error('expected root'); - } - - const iterator = new CalldataIterator(this._root); - const valueBufs: Buffer[] = [selectorBuffer]; - for (const block of iterator) { - valueBufs.push(block.toBuffer()); - } - - const combinedBuffers = Buffer.concat(valueBufs); - const hexValue = ethUtil.bufferToHex(combinedBuffers); - return hexValue; - } } -- cgit v1.2.3 From 3bf5a4e83f57f82090a64fc76fcc7bf3f7c68607 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 21:20:46 -0800 Subject: Moved some consts outside of a loop --- packages/utils/src/abi_encoder/calldata/calldata.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index da61b2256..668b92f06 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -178,6 +178,12 @@ export class Calldata { if (this._root === undefined) { throw new Error('expected root'); } + // Constants for constructing annotated string + const offsetPadding = 10; + const valuePadding = 74; + const namePadding = 80; + const evmWordStartIndex = 0; + const emptySize = 0; // Construct annotated calldata let hexValue = `${this._selector}`; let offset = 0; @@ -189,11 +195,6 @@ export class Calldata { const name = block.getName(); const parentName = block.getParentName(); const prettyName = name.replace(`${parentName}.`, '').replace(`${functionName}.`, ''); - const offsetPadding = 10; - const valuePadding = 74; - const namePadding = 80; - const evmWordStartIndex = 0; - const emptySize = 0; // Resulting line will be let offsetStr = ''; let valueStr = ''; -- cgit v1.2.3 From a8d707b4627b8997b02c53e670bd1f5636eced4c Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 21:23:37 -0800 Subject: Linter on Calldata --- .../utils/src/abi_encoder/calldata/calldata.ts | 39 +++++++++++----------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index 668b92f06..a662f30b9 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -67,23 +67,23 @@ export class Calldata { * Scenarios arise where distinct pointers resolve to identical values. * We optimize by keeping only one such instance of the identical value, and redirecting all pointers here. * We keep the last such duplicate value because pointers can only be positive (they cannot point backwards). - * + * * Example #1: * function f(string[], string[]) * f(["foo", "bar", "blitz"], ["foo", "bar", "blitz"]) * The array ["foo", "bar", "blitz"] will only be included in the calldata once. - * + * * Example #2: * function f(string[], string) * f(["foo", "bar", "blitz"], "foo") * The string "foo" will only be included in the calldata once. - * + * * Example #3: * function f((string, uint, bytes), string, uint, bytes) * f(("foo", 5, "0x05"), "foo", 5, "0x05") * The string "foo" and bytes "0x05" will only be included in the calldata once. * The duplicate `uint 5` values cannot be optimized out because they are static values (no pointer points to them). - * + * * @TODO #1: * This optimization strategy handles blocks that are exact duplicates of one another. * But what if some block is a combination of two other blocks? Or a subset of another block? @@ -92,14 +92,14 @@ export class Calldata { * This strategy would be applied after assigning offsets to the tree, rather than before (as in this strategy). * Note that one consequence of this strategy is pointers may resolve to offsets that are not word-aligned. * This shouldn't be a problem but further investigation should be done. - * + * * @TODO #2: * To be done as a follow-up to @TODO #1. * Since we optimize from the bottom-up, we could be affecting the outcome of a later potential optimization. - * For example, what if by removing one duplicate value we miss out on optimizing another block higher in the tree. + * For example, what if by removing one duplicate value we miss out on optimizing another block higher in the tree. * To handle this case, at each node we can store a candidate optimization in a priority queue (sorted by calldata size). * At the end of traversing the tree, the candidate at the front of the queue will be the most optimal output. - * + * */ private _optimize(): void { // Step 1/1 Create a reverse iterator (starts from the end of the calldata to the beginning) @@ -153,25 +153,25 @@ export class Calldata { } /** * Returns human-redable calldata. - * + * * Example: * simpleFunction(string[], string[]) * strings = ["Hello", "World"] * simpleFunction(strings, strings) - * + * * Output: * 0xbb4f12e3 - * ### simpleFunction - * 0x0 0000000000000000000000000000000000000000000000000000000000000040 ptr (alias for array2) - * 0x20 0000000000000000000000000000000000000000000000000000000000000040 ptr + * ### simpleFunction + * 0x0 0000000000000000000000000000000000000000000000000000000000000040 ptr (alias for array2) + * 0x20 0000000000000000000000000000000000000000000000000000000000000040 ptr * - * 0x40 0000000000000000000000000000000000000000000000000000000000000002 ### array2 - * 0x60 0000000000000000000000000000000000000000000000000000000000000040 ptr - * 0x80 0000000000000000000000000000000000000000000000000000000000000080 ptr - * 0xa0 0000000000000000000000000000000000000000000000000000000000000005 array2[0] - * 0xc0 48656c6c6f000000000000000000000000000000000000000000000000000000 - * 0xe0 0000000000000000000000000000000000000000000000000000000000000005 array2[1] - * 0x100 576f726c64000000000000000000000000000000000000000000000000000000 + * 0x40 0000000000000000000000000000000000000000000000000000000000000002 ### array2 + * 0x60 0000000000000000000000000000000000000000000000000000000000000040 ptr + * 0x80 0000000000000000000000000000000000000000000000000000000000000080 ptr + * 0xa0 0000000000000000000000000000000000000000000000000000000000000005 array2[0] + * 0xc0 48656c6c6f000000000000000000000000000000000000000000000000000000 + * 0xe0 0000000000000000000000000000000000000000000000000000000000000005 array2[1] + * 0x100 576f726c64000000000000000000000000000000000000000000000000000000 */ private _toAnnotatedString(): string { // Sanity check: must have a root block. @@ -240,7 +240,6 @@ export class Calldata { hexValue = `${hexValue}\n${lineStr}`; offset += size; } - return hexValue; } } -- cgit v1.2.3 From feb551a02efe144b0aca4696f01dcdd42383fb36 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 21:39:47 -0800 Subject: Comments to Set datatype --- .../abi_encoder/abstract_data_types/types/set.ts | 77 ++++++++++------------ 1 file changed, 35 insertions(+), 42 deletions(-) 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 77fd7b3ea..427122ad6 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 @@ -49,18 +49,19 @@ export abstract class Set extends DataType { public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { let members = this._members; + // Case 1: This is an array of undefined length, which means that `this._members` was not + // populated in the constructor. So, construct the set of members it now. if (this._isArray && this._arrayLength === undefined) { const arrayLengthBuf = calldata.popWord(); const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); - const hexBase = 16; - const arrayLength = new BigNumber(arrayLengthHex, hexBase); - + const arrayLength = new BigNumber(arrayLengthHex, Constants.HEX_BASE); [members] = this._createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); } - + // 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) { + // Construct an object with values for each member of the set. value = {}; _.each(this._memberIndexByName, (idx: number, key: string) => { const member = this._members[idx]; @@ -68,41 +69,33 @@ export abstract class Set extends DataType { (value as { [key: string]: any })[key] = memberValue; }); } else { + // Construct an array with values for each member of the set. value = []; _.each(members, (member: DataType, idx: number) => { const memberValue = member.generateValue(calldata, rules); (value as any[]).push(memberValue); }); } + // Close this scope and return tetheh value. calldata.endScope(); return value; } public isStatic(): boolean { - /* For Tuple: - const isStaticTuple = this.children.length === 0; - return isStaticTuple; // @TODO: True in every case or only when dynamic data? - - For Array: - if isLengthDefined = false then this is false - - Otherwise if the first element is a Pointer then false - */ - + // An array with an undefined length is never static. if (this._isArray && this._arrayLength === undefined) { return false; } - - // Search for dependent members + // If any member of the set is a pointer then the set is not static. const dependentMember = _.find(this._members, (member: DataType) => { return member instanceof Pointer; }); - const isStatic = dependentMember === undefined; // static if we couldn't find a dependent member + const isStatic = dependentMember === undefined; return isStatic; } protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): CalldataBlocks.Set { - // Sanity check length + // Sanity check: if the set has a defined length then `value` must have the same length. if (this._arrayLength !== undefined && value.length !== this._arrayLength) { throw new Error( `Expected array of ${JSON.stringify( @@ -110,41 +103,42 @@ export abstract class Set extends DataType { )} elements, but got array of length ${JSON.stringify(value.length)}`, ); } - + // Create a new calldata block for this set. const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: CalldataBlocks.Set = new CalldataBlocks.Set( + const block: CalldataBlocks.Set = new CalldataBlocks.Set( this.getDataItem().name, this.getSignature(), parentName, ); - + // If this set has an undefined length then set its header to be the number of elements. let members = this._members; if (this._isArray && this._arrayLength === undefined) { [members] = this._createMembersWithLength(this.getDataItem(), value.length); - const lenBuf = ethUtil.setLengthLeft( ethUtil.toBuffer(`0x${value.length.toString(Constants.HEX_BASE)}`), Constants.EVM_WORD_WIDTH_IN_BYTES, ); - methodBlock.setHeader(lenBuf); + block.setHeader(lenBuf); } - + // Create blocks for members of set. const memberCalldataBlocks: CalldataBlock[] = []; _.each(members, (member: DataType, idx: number) => { - const block = member.generateCalldataBlock(value[idx], methodBlock); - memberCalldataBlocks.push(block); + const memberBlock = member.generateCalldataBlock(value[idx], block); + memberCalldataBlocks.push(memberBlock); }); - methodBlock.setMembers(memberCalldataBlocks); - return methodBlock; + block.setMembers(memberCalldataBlocks); + return block; } protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): CalldataBlocks.Set { + // Create a new calldata block for this set. const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: CalldataBlocks.Set = new CalldataBlocks.Set( + const block: CalldataBlocks.Set = new CalldataBlocks.Set( this.getDataItem().name, this.getSignature(), parentName, ); + // Create blocks for members of set. const memberCalldataBlocks: CalldataBlock[] = []; const childMap = _.cloneDeep(this._memberIndexByName); _.forOwn(obj, (value: any, key: string) => { @@ -153,17 +147,17 @@ export abstract class Set extends DataType { `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`, ); } - const block = this._members[this._memberIndexByName[key]].generateCalldataBlock(value, methodBlock); - memberCalldataBlocks.push(block); + const memberBlock = this._members[this._memberIndexByName[key]].generateCalldataBlock(value, block); + memberCalldataBlocks.push(memberBlock); delete childMap[key]; }); - + // Sanity check that all members have been included. if (Object.keys(childMap).length !== 0) { throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); } - - methodBlock.setMembers(memberCalldataBlocks); - return methodBlock; + // Associate member blocks with Set block. + block.setMembers(memberCalldataBlocks); + return block; } protected _computeSignatureOfMembers(): string { @@ -184,7 +178,7 @@ export abstract class Set extends DataType { if (dataItem.components === undefined) { throw new Error(`Expected components`); } - + // Create one member for each component of `dataItem` const members: DataType[] = []; const memberIndexByName: MemberIndexByName = {}; _.each(dataItem.components, (memberItem: DataItem) => { @@ -200,28 +194,27 @@ export abstract class Set extends DataType { memberIndexByName[memberItem.name] = members.length; members.push(child); }); - return [members, memberIndexByName]; } private _createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberIndexByName] { + // Create `length` members, deriving the type from `dataItem` const members: DataType[] = []; const memberIndexByName: MemberIndexByName = {}; const range = _.range(length); _.each(range, (idx: number) => { - const childDataItem: DataItem = { + const memberDataItem: DataItem = { type: this._arrayElementType ? this._arrayElementType : '', name: `${dataItem.name}[${idx.toString(Constants.DEC_BASE)}]`, }; const components = dataItem.components; if (components !== undefined) { - childDataItem.components = components; + memberDataItem.components = components; } - const child = this.getFactory().create(childDataItem, this); + const memberType = this.getFactory().create(memberDataItem, this); memberIndexByName[idx.toString(Constants.DEC_BASE)] = members.length; - members.push(child); + members.push(memberType); }); - return [members, memberIndexByName]; } } -- cgit v1.2.3 From ad1b5af4e59ba750c019cab1f5ec9584b8645101 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Sun, 25 Nov 2018 21:40:37 -0800 Subject: Ran prettier --- packages/utils/src/abi_encoder/evm_data_types/array.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/int.ts | 7 ++++--- .../utils/src/abi_encoder/evm_data_types/static_bytes.ts | 7 ++++--- packages/utils/src/abi_encoder/evm_data_types/uint.ts | 7 ++++--- packages/utils/src/abi_encoder/utils/math.ts | 12 ++++++++++-- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts index 54f7ba9fa..8cf2cf7cf 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/array.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -12,7 +12,7 @@ export class Array extends AbstractDataTypes.Set { return Array._MATCHER.test(type); } - private static _decodeElementTypeAndLengthFromType(type: string): [string, undefined|number] { + private static _decodeElementTypeAndLengthFromType(type: string): [string, undefined | number] { const matches = Array._MATCHER.exec(type); if (matches === null || matches.length !== 3) { throw new Error(`Could not parse array: ${type}`); diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index 8a82ed4cc..032cd045a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -24,9 +24,10 @@ export class Int extends AbstractDataTypes.Blob { private static _decodeWidthFromType(type: string): number { const matches = Int._MATCHER.exec(type); - const width = (matches !== null && matches.length === 2 && matches[1] !== undefined) - ? parseInt(matches[1], Constants.DEC_BASE) - : Int._DEFAULT_WIDTH; + const width = + matches !== null && matches.length === 2 && matches[1] !== undefined + ? parseInt(matches[1], Constants.DEC_BASE) + : Int._DEFAULT_WIDTH; return width; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index d0b41194e..2c649cb33 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -20,9 +20,10 @@ export class StaticBytes extends AbstractDataTypes.Blob { private static _decodeWidthFromType(type: string): number { const matches = StaticBytes._MATCHER.exec(type); - const width = (matches !== null && matches.length === 3 && matches[2] !== undefined) - ? parseInt(matches[2], Constants.DEC_BASE) - : StaticBytes._DEFAULT_WIDTH; + const width = + matches !== null && matches.length === 3 && matches[2] !== undefined + ? parseInt(matches[2], Constants.DEC_BASE) + : StaticBytes._DEFAULT_WIDTH; return width; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index b1bc690d8..b5b7683a2 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -24,9 +24,10 @@ export class UInt extends AbstractDataTypes.Blob { private static _decodeWidthFromType(type: string): number { const matches = UInt._MATCHER.exec(type); - const width = (matches !== null && matches.length === 2 && matches[1] !== undefined) - ? parseInt(matches[1], Constants.DEC_BASE) - : UInt._DEFAULT_WIDTH; + const width = + matches !== null && matches.length === 2 && matches[1] !== undefined + ? parseInt(matches[1], Constants.DEC_BASE) + : UInt._DEFAULT_WIDTH; return width; } diff --git a/packages/utils/src/abi_encoder/utils/math.ts b/packages/utils/src/abi_encoder/utils/math.ts index 8d21ada0a..c5fa10e73 100644 --- a/packages/utils/src/abi_encoder/utils/math.ts +++ b/packages/utils/src/abi_encoder/utils/math.ts @@ -4,7 +4,11 @@ import * as _ from 'lodash'; import * as Constants from '../utils/constants'; -function sanityCheckBigNumberRange(value_: BigNumber | string | number, minValue: BigNumber, maxValue: BigNumber): void { +function sanityCheckBigNumberRange( + value_: BigNumber | string | number, + minValue: BigNumber, + maxValue: BigNumber, +): void { const value = new BigNumber(value_, 10); if (value.greaterThan(maxValue)) { throw new Error(`Tried to assign value of ${value}, which exceeds max value of ${maxValue}`); @@ -51,7 +55,11 @@ export function encodeNumericValue(value_: BigNumber | string | number): Buffer * @param value_ The value to encode. * @return ABI Encoded value */ -export function safeEncodeNumericValue(value: BigNumber | string | number, minValue: BigNumber, maxValue: BigNumber): Buffer { +export function safeEncodeNumericValue( + value: BigNumber | string | number, + minValue: BigNumber, + maxValue: BigNumber, +): Buffer { sanityCheckBigNumberRange(value, minValue, maxValue); const encodedValue = encodeNumericValue(value); return encodedValue; -- cgit v1.2.3 From 2e79ce26cbeacdeb0ea52b7c2e3da68e8055e7c6 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Mon, 26 Nov 2018 18:00:05 -0800 Subject: Tests use to.be.deep.equal instead of JSON --- .../utils/test/abi_encoder/evm_data_types_test.ts | 176 ++++++--------------- packages/utils/test/abi_encoder/methods_test.ts | 72 +++------ packages/utils/test/abi_encoder/optimizer_test.ts | 64 ++------ .../utils/test/abi_encoder/return_values_test.ts | 24 +-- 4 files changed, 84 insertions(+), 252 deletions(-) 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 7cea86529..b99500265 100644 --- a/packages/utils/test/abi_encoder/evm_data_types_test.ts +++ b/packages/utils/test/abi_encoder/evm_data_types_test.ts @@ -24,9 +24,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Dynamic size; Static elements', async () => { // Create DataType object @@ -41,9 +39,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Fixed size; Dynamic elements', async () => { // Create DataType object @@ -58,9 +54,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Dynamic size; Dynamic elements', async () => { // Create DataType object @@ -75,9 +69,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Dynamic Size; Multidimensional; Dynamic Elements', async () => { // Create DataType object @@ -95,9 +87,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Dynamic Size; Multidimensional; Static Elements', async () => { // Create DataType object @@ -115,9 +105,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Static Size; Multidimensional; Static Elements', async () => { // Create DataType object @@ -134,9 +122,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Static Size; Multidimensional; Dynamic Elements', async () => { // Create DataType object @@ -153,9 +139,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Static size; Too Few Elements', async () => { // Create DataType object @@ -211,9 +195,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Decode Encoded Args and validate result const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Dynamic elements only', async () => { // Create DataType object @@ -233,9 +215,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Decode Encoded Args and validate result const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Nested Static Array', async () => { // Create DataType object @@ -255,9 +235,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Decode Encoded Args and validate result const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Nested Dynamic Array', async () => { // Create DataType object @@ -277,9 +255,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Decode Encoded Args and validate result const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Nested Static Multidimensional Array', async () => { // Create DataType object @@ -301,9 +277,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Decode Encoded Args and validate result const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Nested Dynamic Multidimensional Array', async () => { // Create DataType object @@ -325,9 +299,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Decode Encoded Args and validate result const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Static and dynamic elements mixed', async () => { // Create DataType object @@ -357,9 +329,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Decode Encoded Args and validate result const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true }; const decodedArgs = dataType.decode(encodedArgs, decodingRules); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Missing Key', async () => { // Create DataType object @@ -406,9 +376,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Invalid Address - input is not valid hex', async () => { // Create DataType object @@ -447,9 +415,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('False', async () => { // Create DataType object @@ -463,9 +429,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); }); @@ -489,9 +453,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Int256 - Negative Base Case', async () => { // Create DataType object @@ -505,9 +467,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Int256 - Positive Value', async () => { // Create DataType object @@ -521,9 +481,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Int256 - Negative Value', async () => { // Create DataType object @@ -537,9 +495,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Int256 - Value too large', async () => { // Create DataType object @@ -575,9 +531,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Int32 - Negative Base Case', async () => { // Create DataType object @@ -591,9 +545,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Int32 - Positive Value', async () => { // Create DataType object @@ -607,9 +559,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Int32 - Negative Value', async () => { // Create DataType object @@ -623,9 +573,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Int32 - Value too large', async () => { // Create DataType object @@ -671,9 +619,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('UInt256 - Positive Value', async () => { // Create DataType object @@ -687,9 +633,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('UInt256 - Zero Value', async () => { // Create DataType object @@ -703,9 +647,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('UInt256 - Value too large', async () => { // Create DataType object @@ -741,9 +683,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('UInt32 - Positive Value', async () => { // Create DataType object @@ -757,9 +697,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('UInt32 - Zero Value', async () => { // Create DataType object @@ -773,9 +711,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('UInt32 - Value too large', async () => { // Create DataType object @@ -814,9 +750,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Single Byte (bytes1)', async () => { // Create DataType object @@ -830,9 +764,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('4 Bytes (bytes4)', async () => { // Create DataType object @@ -846,9 +778,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('4 Bytes (bytes4); Encoder must pad input', async () => { // Create DataType object @@ -863,10 +793,8 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); const paddedArgs = '0x1a180000'; - const paddedArgsAsJson = JSON.stringify(paddedArgs); - expect(decodedArgsAsJson).to.be.equal(paddedArgsAsJson); + expect(decodedArgs).to.be.deep.equal(paddedArgs); }); it('32 Bytes (bytes32)', async () => { // Create DataType object @@ -880,9 +808,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('32 Bytes (bytes32); Encoder must pad input', async () => { // Create DataType object @@ -897,10 +823,8 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); const paddedArgs = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; - const paddedArgsAsJson = JSON.stringify(paddedArgs); - expect(decodedArgsAsJson).to.be.equal(paddedArgsAsJson); + expect(decodedArgs).to.be.deep.equal(paddedArgs); }); it('Should throw when pass in too many bytes (bytes4)', async () => { // Create DataType object @@ -967,9 +891,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Spans multiple EVM words', async () => { // Create DataType object @@ -986,9 +908,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Input as Buffer', async () => { // Create DataType object @@ -1005,9 +925,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Should throw when pass in bad hex (no 0x prefix)', async () => { // Create DataType object @@ -1048,9 +966,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Spans multiple EVM words', async () => { // Create DataType object @@ -1067,9 +983,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('String that begins with 0x prefix', async () => { // Create DataType object @@ -1086,9 +1000,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result const decodedArgs = dataType.decode(encodedArgs); - const decodedArgsAsJson = JSON.stringify(decodedArgs); - const argsAsJson = JSON.stringify(args); - expect(decodedArgsAsJson).to.be.equal(argsAsJson); + 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 d158b9e5b..c870f4f04 100644 --- a/packages/utils/test/abi_encoder/methods_test.ts +++ b/packages/utils/test/abi_encoder/methods_test.ts @@ -20,10 +20,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Array of Static Tuples (Array has defined length)', async () => { // Generate calldata @@ -41,10 +39,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Array of Static Tuples (Array has dynamic length)', async () => { // Generate calldata @@ -62,10 +58,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Array of Dynamic Tuples (Array has defined length)', async () => { // Generate Calldata @@ -83,10 +77,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Array of Dynamic Tuples (Array has dynamic length)', async () => { // Generate calldata @@ -104,10 +96,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Multidimensional Arrays / Static Members', async () => { // Generate calldata @@ -129,10 +119,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { expect(calldata).to.be.equal(expectedCalldata); expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Multidimensional Arrays / Dynamic Members', async () => { // Generate calldata @@ -159,10 +147,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Fixed Length Array / Dynamic Members', async () => { // Generate calldata @@ -174,10 +160,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Fixed Length Array / Dynamic Members', async () => { // Generaet calldata @@ -189,10 +173,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Unfixed Length Array / Dynamic Members ABI', async () => { // Generate calldata @@ -204,10 +186,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Unfixed Length Array / Static Members ABI', async () => { // Generate calldata @@ -219,10 +199,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Fixed Length Array / Static Members ABI', async () => { // Generate calldata @@ -234,10 +212,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Array ABI', async () => { // Generate calldata @@ -249,10 +225,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Static Tuple', async () => { // Generate calldata @@ -265,10 +239,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Dynamic Tuple (Array input)', async () => { // Generate calldata @@ -281,10 +253,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Dynamic Tuple (Object input)', async () => { // Generate Calldata @@ -297,10 +267,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Large, Flat ABI', async () => { // Construct calldata @@ -322,10 +290,8 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + expect(decodedValue).to.be.deep.equal(args); }); it('Large, Nested ABI', async () => { // Construct Calldata @@ -393,9 +359,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); // Validate decoding - const expectedDecodedValueJson = JSON.stringify(args); const decodedValue = method.decode(calldata, { structsAsObjects: true }); - const decodedValueJson = JSON.stringify(decodedValue); - expect(decodedValueJson).to.be.equal(expectedDecodedValueJson); + 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 304c9cbc2..27443fe48 100644 --- a/packages/utils/test/abi_encoder/optimizer_test.ts +++ b/packages/utils/test/abi_encoder/optimizer_test.ts @@ -23,9 +23,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Dynamic Arrays with Dynamic Elements', async () => { // Generate calldata @@ -40,9 +38,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Static Arrays with Static Elements (should not optimize)', async () => { // Generate calldata @@ -59,9 +55,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(unoptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Static Arrays with Dynamic Elements', async () => { // Generate calldata @@ -76,9 +70,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Array Elements (should optimize)', async () => { // Generate calldata @@ -92,9 +84,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Tuple Fields', async () => { // Generate calldata @@ -108,9 +98,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Strings', async () => { // Description: @@ -127,9 +115,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Bytes', async () => { // Description: @@ -147,9 +133,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Tuples', async () => { // Generate calldata @@ -164,9 +148,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Fields Across Two Tuples', async () => { // Description: @@ -182,9 +164,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Arrays, Nested in Separate Tuples', async () => { // Generate calldata @@ -200,9 +180,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Tuples, Nested in Separate Tuples', async () => { // Generate calldata @@ -218,9 +196,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Two-Dimensional Arrays', async () => { // Generate calldata @@ -235,9 +211,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Duplicate Array, Nested within Separate Two-Dimensional Arrays', async () => { // Generate calldata @@ -252,9 +226,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Array Elements Duplicated as Tuple Fields', async () => { // Generate calldata @@ -269,9 +241,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); it('Array Elements Duplicated as Separate Parameter', async () => { // Generate calldata @@ -286,8 +256,6 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); // Validate decoding const decodedArgs = method.decode(optimizedCalldata); - const decodedArgsJson = JSON.stringify(decodedArgs); - const argsJson = JSON.stringify(args); - expect(decodedArgsJson).to.be.equal(argsJson); + expect(decodedArgs).to.be.deep.equal(args); }); }); diff --git a/packages/utils/test/abi_encoder/return_values_test.ts b/packages/utils/test/abi_encoder/return_values_test.ts index 850cb1746..3215509f1 100644 --- a/packages/utils/test/abi_encoder/return_values_test.ts +++ b/packages/utils/test/abi_encoder/return_values_test.ts @@ -16,9 +16,7 @@ describe('ABI Encoder: Return Value Encoding/Decoding', () => { const returnValue = '0x'; const decodedReturnValue = method.decodeReturnValues(returnValue); const expectedDecodedReturnValue: any[] = []; - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(expectedDecodedReturnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + expect(decodedReturnValue).to.be.deep.equal(expectedDecodedReturnValue); }); it('Single static return value', async () => { // Generate Return Value @@ -27,9 +25,7 @@ describe('ABI Encoder: Return Value Encoding/Decoding', () => { const encodedReturnValue = method.encodeReturnValues(returnValue); const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); // Validate decoded return value - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(returnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + expect(decodedReturnValue).to.be.deep.equal(returnValue); }); it('Multiple static return values', async () => { // Generate Return Value @@ -38,9 +34,7 @@ describe('ABI Encoder: Return Value Encoding/Decoding', () => { const encodedReturnValue = method.encodeReturnValues(returnValue); const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); // Validate decoded return value - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(returnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + expect(decodedReturnValue).to.be.deep.equal(returnValue); }); it('Single dynamic return value', async () => { // Generate Return Value @@ -49,9 +43,7 @@ describe('ABI Encoder: Return Value Encoding/Decoding', () => { const encodedReturnValue = method.encodeReturnValues(returnValue); const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); // Validate decoded return value - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(returnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + expect(decodedReturnValue).to.be.deep.equal(returnValue); }); it('Multiple dynamic return values', async () => { // Generate Return Value @@ -60,9 +52,7 @@ describe('ABI Encoder: Return Value Encoding/Decoding', () => { const encodedReturnValue = method.encodeReturnValues(returnValue); const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); // Validate decoded return value - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(returnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + expect(decodedReturnValue).to.be.deep.equal(returnValue); }); it('Mixed static/dynamic return values', async () => { // Generate Return Value @@ -71,8 +61,6 @@ describe('ABI Encoder: Return Value Encoding/Decoding', () => { const encodedReturnValue = method.encodeReturnValues(returnValue); const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); // Validate decoded return value - const decodedReturnValueJson = JSON.stringify(decodedReturnValue); - const expectedDecodedReturnValueJson = JSON.stringify(returnValue); - expect(decodedReturnValueJson).to.be.equal(expectedDecodedReturnValueJson); + expect(decodedReturnValue).to.be.deep.equal(returnValue); }); }); -- cgit v1.2.3 From f31d4ddffd8dd97f2b2dc226f4f132d1c3192c76 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 27 Nov 2018 13:10:34 -0800 Subject: Replaced null/undefined checks with lodash --- .../abi_encoder/abstract_data_types/types/set.ts | 22 +++++++++++----------- .../src/abi_encoder/calldata/blocks/pointer.ts | 3 ++- .../utils/src/abi_encoder/calldata/calldata.ts | 8 ++++---- .../utils/src/abi_encoder/calldata/iterator.ts | 4 ++-- .../utils/src/abi_encoder/calldata/raw_calldata.ts | 3 ++- .../utils/src/abi_encoder/evm_data_types/array.ts | 13 +++++++------ .../utils/src/abi_encoder/evm_data_types/int.ts | 3 ++- .../src/abi_encoder/evm_data_types/static_bytes.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/uint.ts | 3 ++- 9 files changed, 33 insertions(+), 28 deletions(-) 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 427122ad6..5a188d6fa 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 @@ -32,7 +32,7 @@ export abstract class Set extends DataType { this._isArray = isArray; this._arrayLength = arrayLength; this._arrayElementType = arrayElementType; - if (isArray && arrayLength !== undefined) { + if (isArray && !_.isUndefined(arrayLength)) { [this._members, this._memberIndexByName] = this._createMembersWithLength(dataItem, arrayLength); } else if (!isArray) { [this._members, this._memberIndexByName] = this._createMembersWithKeys(dataItem); @@ -51,7 +51,7 @@ export abstract class Set extends DataType { let members = this._members; // Case 1: This is an array of undefined length, which means that `this._members` was not // populated in the constructor. So, construct the set of members it now. - if (this._isArray && this._arrayLength === undefined) { + if (this._isArray && _.isUndefined(this._arrayLength)) { const arrayLengthBuf = calldata.popWord(); const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); const arrayLength = new BigNumber(arrayLengthHex, Constants.HEX_BASE); @@ -83,20 +83,20 @@ export abstract class Set extends DataType { public isStatic(): boolean { // An array with an undefined length is never static. - if (this._isArray && this._arrayLength === undefined) { + if (this._isArray && _.isUndefined(this._arrayLength)) { return false; } // If any member of the set is a pointer then the set is not static. const dependentMember = _.find(this._members, (member: DataType) => { return member instanceof Pointer; }); - const isStatic = dependentMember === undefined; + const isStatic = _.isUndefined(dependentMember); return isStatic; } protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): CalldataBlocks.Set { // Sanity check: if the set has a defined length then `value` must have the same length. - if (this._arrayLength !== undefined && value.length !== this._arrayLength) { + if (!_.isUndefined(this._arrayLength) && value.length !== this._arrayLength) { throw new Error( `Expected array of ${JSON.stringify( this._arrayLength, @@ -104,7 +104,7 @@ export abstract class Set extends DataType { ); } // Create a new calldata block for this set. - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const parentName = _.isUndefined(parentBlock) ? '' : parentBlock.getName(); const block: CalldataBlocks.Set = new CalldataBlocks.Set( this.getDataItem().name, this.getSignature(), @@ -112,7 +112,7 @@ export abstract class Set extends DataType { ); // If this set has an undefined length then set its header to be the number of elements. let members = this._members; - if (this._isArray && this._arrayLength === undefined) { + if (this._isArray && _.isUndefined(this._arrayLength)) { [members] = this._createMembersWithLength(this.getDataItem(), value.length); const lenBuf = ethUtil.setLengthLeft( ethUtil.toBuffer(`0x${value.length.toString(Constants.HEX_BASE)}`), @@ -132,7 +132,7 @@ export abstract class Set extends DataType { protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): CalldataBlocks.Set { // Create a new calldata block for this set. - const parentName = parentBlock === undefined ? '' : parentBlock.getName(); + const parentName = _.isUndefined(parentBlock) ? '' : parentBlock.getName(); const block: CalldataBlocks.Set = new CalldataBlocks.Set( this.getDataItem().name, this.getSignature(), @@ -175,7 +175,7 @@ export abstract class Set extends DataType { private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberIndexByName] { // Sanity check - if (dataItem.components === undefined) { + if (_.isUndefined(dataItem.components)) { throw new Error(`Expected components`); } // Create one member for each component of `dataItem` @@ -187,7 +187,7 @@ export abstract class Set extends DataType { name: `${dataItem.name}.${memberItem.name}`, }; const components = memberItem.components; - if (components !== undefined) { + if (!_.isUndefined(components)) { childDataItem.components = components; } const child = this.getFactory().create(childDataItem, this); @@ -208,7 +208,7 @@ export abstract class Set extends DataType { name: `${dataItem.name}[${idx.toString(Constants.DEC_BASE)}]`, }; const components = dataItem.components; - if (components !== undefined) { + if (!_.isUndefined(components)) { memberDataItem.components = components; } const memberType = this.getFactory().create(memberDataItem, this); diff --git a/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts index 1c49a8c6c..654cbe26c 100644 --- a/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts +++ b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts @@ -1,4 +1,5 @@ import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; import * as Constants from '../../utils/constants'; @@ -24,7 +25,7 @@ export class Pointer extends CalldataBlock { public toBuffer(): Buffer { const destinationOffset = - this._aliasFor !== undefined ? this._aliasFor.getOffsetInBytes() : this._dependency.getOffsetInBytes(); + !_.isUndefined(this._aliasFor) ? this._aliasFor.getOffsetInBytes() : this._dependency.getOffsetInBytes(); const parentOffset = this._parent.getOffsetInBytes(); const parentHeaderSize = this._parent.getHeaderSizeInBytes(); const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index a662f30b9..e93d63803 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -44,7 +44,7 @@ export class Calldata { */ public toString(): string { // Sanity check: root block must be set - if (this._root === undefined) { + if (_.isUndefined(this._root)) { throw new Error('expected root'); } // Optimize, if flag set @@ -103,7 +103,7 @@ export class Calldata { */ private _optimize(): void { // Step 1/1 Create a reverse iterator (starts from the end of the calldata to the beginning) - if (this._root === undefined) { + if (_.isUndefined(this._root)) { throw new Error('expected root'); } const iterator = new ReverseCalldataIterator(this._root); @@ -136,7 +136,7 @@ export class Calldata { */ private _toCondensedString(): string { // Sanity check: must have a root block. - if (this._root === undefined) { + if (_.isUndefined(this._root)) { throw new Error('expected root'); } // Construct an array of buffers (one buffer for each block). @@ -175,7 +175,7 @@ export class Calldata { */ private _toAnnotatedString(): string { // Sanity check: must have a root block. - if (this._root === undefined) { + if (_.isUndefined(this._root)) { throw new Error('expected root'); } // Constants for constructing annotated string diff --git a/packages/utils/src/abi_encoder/calldata/iterator.ts b/packages/utils/src/abi_encoder/calldata/iterator.ts index 8e2b16a5a..5307f7944 100644 --- a/packages/utils/src/abi_encoder/calldata/iterator.ts +++ b/packages/utils/src/abi_encoder/calldata/iterator.ts @@ -55,7 +55,7 @@ abstract class BaseIterator implements Iterable { _.each(set.getMembers(), (member: CalldataBlock) => { // Traverse child if it is a unique pointer. // A pointer that is an alias for another pointer is ignored. - if (member instanceof CalldataBlocks.Pointer && member.getAlias() === undefined) { + if (member instanceof CalldataBlocks.Pointer && _.isUndefined(member.getAlias())) { const dependency = member.getDependency(); queue.mergeBack(BaseIterator._createQueue(dependency)); } @@ -74,7 +74,7 @@ abstract class BaseIterator implements Iterable { return { next: () => { const nextBlock = this.nextBlock(); - if (nextBlock !== undefined) { + if (!_.isUndefined(nextBlock)) { return { value: nextBlock, done: false, diff --git a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts index b13cbdfd9..dfd4cfa72 100644 --- a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts @@ -1,4 +1,5 @@ import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; import * as Constants from '../utils/constants'; import { Queue } from '../utils/queue'; @@ -68,7 +69,7 @@ export class RawCalldata { public toAbsoluteOffset(relativeOffset: number): number { const scopeOffset = this._scopes.peekFront(); - if (scopeOffset === undefined) { + if (_.isUndefined(scopeOffset)) { throw new Error(`Tried to access undefined scope.`); } const absoluteOffset = relativeOffset + scopeOffset; diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts index 8cf2cf7cf..a86283c2a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/array.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -1,4 +1,5 @@ import { DataItem } from 'ethereum-types'; +import * as _ from 'lodash'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import * as Constants from '../utils/constants'; @@ -14,15 +15,15 @@ export class Array extends AbstractDataTypes.Set { private static _decodeElementTypeAndLengthFromType(type: string): [string, undefined | number] { const matches = Array._MATCHER.exec(type); - if (matches === null || matches.length !== 3) { + if (_.isNull(matches) || matches.length !== 3) { throw new Error(`Could not parse array: ${type}`); - } else if (matches[1] === undefined) { + } else if (_.isUndefined(matches[1])) { throw new Error(`Could not parse array type: ${type}`); - } else if (matches[2] === undefined) { + } else if (_.isUndefined(matches[2])) { throw new Error(`Could not parse array length: ${type}`); } const arrayElementType = matches[1]; - const arrayLength = matches[2] === '' ? undefined : parseInt(matches[2], Constants.DEC_BASE); + const arrayLength = _.isEmpty(matches[2]) ? undefined : parseInt(matches[2], Constants.DEC_BASE); return [arrayElementType, arrayLength]; } @@ -47,13 +48,13 @@ export class Array extends AbstractDataTypes.Set { name: 'N/A', }; const elementComponents = this.getDataItem().components; - if (elementComponents !== undefined) { + if (!_.isUndefined(elementComponents)) { elementDataItem.components = elementComponents; } const elementDataType = this.getFactory().create(elementDataItem); const elementSignature = elementDataType.getSignature(); // Construct signature for array of type `element` - if (this._arrayLength === undefined) { + if (_.isUndefined(this._arrayLength)) { return `${elementSignature}[]`; } else { return `${elementSignature}[${this._arrayLength}]`; diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index 032cd045a..5c5193644 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -1,5 +1,6 @@ /* tslint:disable prefer-function-over-method */ import { DataItem } from 'ethereum-types'; +import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; @@ -25,7 +26,7 @@ export class Int extends AbstractDataTypes.Blob { private static _decodeWidthFromType(type: string): number { const matches = Int._MATCHER.exec(type); const width = - matches !== null && matches.length === 2 && matches[1] !== undefined + !_.isNull(matches) && matches.length === 2 && !_.isUndefined(matches[1]) ? parseInt(matches[1], Constants.DEC_BASE) : Int._DEFAULT_WIDTH; return width; diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 2c649cb33..3a2ad410f 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -21,7 +21,7 @@ export class StaticBytes extends AbstractDataTypes.Blob { private static _decodeWidthFromType(type: string): number { const matches = StaticBytes._MATCHER.exec(type); const width = - matches !== null && matches.length === 3 && matches[2] !== undefined + !_.isNull(matches) && matches.length === 3 && !_.isUndefined(matches[2]) ? parseInt(matches[2], Constants.DEC_BASE) : StaticBytes._DEFAULT_WIDTH; return width; diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index b5b7683a2..76b944610 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -1,5 +1,6 @@ /* tslint:disable prefer-function-over-method */ import { DataItem } from 'ethereum-types'; +import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; @@ -25,7 +26,7 @@ export class UInt extends AbstractDataTypes.Blob { private static _decodeWidthFromType(type: string): number { const matches = UInt._MATCHER.exec(type); const width = - matches !== null && matches.length === 2 && matches[1] !== undefined + !_.isNull(matches) && matches.length === 2 && !_.isUndefined(matches[1]) ? parseInt(matches[1], Constants.DEC_BASE) : UInt._DEFAULT_WIDTH; return width; -- cgit v1.2.3 From 3f545da9f86856b54cd226c29174ac1ae085e35b Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 27 Nov 2018 13:28:17 -0800 Subject: Switched implicit conversions to explicit lodash calls --- .../utils/src/abi_encoder/abstract_data_types/data_type.ts | 12 ++++++------ .../utils/src/abi_encoder/abstract_data_types/types/blob.ts | 2 +- .../utils/src/abi_encoder/abstract_data_types/types/set.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/bool.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts index ab7df6ecc..61d3ac3a9 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts @@ -25,9 +25,9 @@ export abstract class DataType { } public encode(value: any, rules?: EncodingRules, selector?: string): string { - const rules_ = rules ? rules : Constants.DEFAULT_ENCODING_RULES; + const rules_ = _.isUndefined(rules) ? Constants.DEFAULT_ENCODING_RULES : rules; const calldata = new Calldata(rules_); - if (selector) { + if (!_.isUndefined(selector)) { calldata.setSelector(selector); } const block = this.generateCalldataBlock(value); @@ -37,14 +37,14 @@ export abstract class DataType { } public decode(calldata: string, rules?: DecodingRules, selector?: string): any { - if (selector && !calldata.startsWith(selector)) { + if (!_.isUndefined(selector) && !calldata.startsWith(selector)) { throw new Error( - `Tried to decode calldata, but it was missing the function selector. Expected '${selector}'.`, + `Tried to decode calldata, but it was missing the function selector. Expected prefix '${selector}'. Got '${calldata}'.`, ); } - const hasSelector = selector ? true : false; + const hasSelector = !_.isUndefined(selector); const rawCalldata = new RawCalldata(calldata, hasSelector); - const rules_ = rules ? rules : Constants.DEFAULT_DECODING_RULES; + const rules_ = _.isUndefined(rules) ? Constants.DEFAULT_DECODING_RULES : rules; const value = this.generateValue(rawCalldata, rules_); return value; } diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts index 35ccc0586..965738056 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts @@ -19,7 +19,7 @@ export abstract class Blob extends DataType { const encodedValue = this.encodeValue(value); const name = this.getDataItem().name; const signature = this.getSignature(); - const parentName = parentBlock ? parentBlock.getName() : ''; + const parentName = _.isUndefined(parentBlock) ? '' : parentBlock.getName(); const block = new CalldataBlocks.Blob(name, signature, parentName, encodedValue); return block; } 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 5a188d6fa..637abfb7d 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 @@ -204,7 +204,7 @@ export abstract class Set extends DataType { const range = _.range(length); _.each(range, (idx: number) => { const memberDataItem: DataItem = { - type: this._arrayElementType ? this._arrayElementType : '', + type: _.isUndefined(this._arrayElementType) ? '' : this._arrayElementType, name: `${dataItem.name}[${idx.toString(Constants.DEC_BASE)}]`, }; const components = dataItem.components; diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index 031acc88a..fa115bb87 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -39,7 +39,7 @@ export class Bool extends AbstractDataTypes.Blob { throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); } /* tslint:disable boolean-naming */ - const value: boolean = valueNumber.equals(0) ? false : true; + const value: boolean = !valueNumber.equals(0); /* tslint:enable boolean-naming */ return value; } -- cgit v1.2.3 From 1f693ea12142bc761f4067871d92e7d2662cf256 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 27 Nov 2018 13:31:44 -0800 Subject: Changed from .startsWith to _.startsWith --- packages/utils/src/abi_encoder/abstract_data_types/data_type.ts | 2 +- packages/utils/src/abi_encoder/calldata/calldata.ts | 2 +- packages/utils/src/abi_encoder/calldata/raw_calldata.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/address.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts | 2 +- packages/utils/src/abi_encoder/utils/math.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts index 61d3ac3a9..4cef60172 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts @@ -37,7 +37,7 @@ export abstract class DataType { } public decode(calldata: string, rules?: DecodingRules, selector?: string): any { - if (!_.isUndefined(selector) && !calldata.startsWith(selector)) { + if (!_.isUndefined(selector) && !_.startsWith(calldata, selector)) { throw new Error( `Tried to decode calldata, but it was missing the function selector. Expected prefix '${selector}'. Got '${calldata}'.`, ); diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index e93d63803..c57ff08c2 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -29,7 +29,7 @@ export class Calldata { * If the root block was created by a Method then a selector will likely be set. */ public setSelector(selector: string): void { - if (!selector.startsWith('0x')) { + if (!_.startsWith(selector, '0x')) { throw new Error(`Expected selector to be hex. Missing prefix '0x'`); } else if (selector.length !== Constants.HEX_SELECTOR_LENGTH_IN_CHARS) { throw new Error(`Invalid selector '${selector}'`); diff --git a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts index dfd4cfa72..fbe592fc7 100644 --- a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts @@ -13,7 +13,7 @@ export class RawCalldata { public constructor(value: string | Buffer, hasSelector: boolean = true) { // Sanity check - if (typeof value === 'string' && !value.startsWith('0x')) { + if (typeof value === 'string' && !_.startsWith(value, '0x')) { throw new Error(`Expected raw calldata to start with '0x'`); } // Construct initial values diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 71aa293b5..52fc8e7b9 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -31,7 +31,7 @@ export class Address extends AbstractDataTypes.Blob { } public encodeValue(value: string): Buffer { - if (!value.startsWith('0x')) { + if (!_.startsWith(value, '0x')) { throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); } const valueBuf = ethUtil.toBuffer(value); diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index 01d83d11a..98d90b7e4 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -60,7 +60,7 @@ export class DynamicBytes extends AbstractDataTypes.Blob { if (typeof value !== 'string') { return; } - if (!value.startsWith('0x')) { + if (!_.startsWith(value, '0x')) { throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); } else if (value.length % 2 !== 0) { throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 3a2ad410f..68f212f79 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -59,7 +59,7 @@ export class StaticBytes extends AbstractDataTypes.Blob { private _sanityCheckValue(value: string | Buffer): void { if (typeof value === 'string') { - if (!value.startsWith('0x')) { + if (!_.startsWith(value, '0x')) { throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); } else if (value.length % 2 !== 0) { throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); diff --git a/packages/utils/src/abi_encoder/utils/math.ts b/packages/utils/src/abi_encoder/utils/math.ts index c5fa10e73..bc344c695 100644 --- a/packages/utils/src/abi_encoder/utils/math.ts +++ b/packages/utils/src/abi_encoder/utils/math.ts @@ -79,7 +79,7 @@ export function decodeNumericValue(encodedValue: Buffer, minValue: BigNumber): B } // Case 2/3: value is non-negative because there is no leading 1 (encoded as two's-complement) const valueBin = value.toString(Constants.BIN_BASE); - const valueIsNegative = valueBin.length === Constants.EVM_WORD_WIDTH_IN_BITS && valueBin[0].startsWith('1'); + const valueIsNegative = valueBin.length === Constants.EVM_WORD_WIDTH_IN_BITS && _.startsWith(valueBin[0], '1'); if (!valueIsNegative) { return value; } -- cgit v1.2.3 From ffb8b0a619be3b8fb30a6acc99f590a6b40d49e1 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 27 Nov 2018 13:35:39 -0800 Subject: Changed remaining instances of implicit `bool` casts to explicit lodash calls --- packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts | 2 +- packages/utils/src/abi_encoder/calldata/blocks/set.ts | 4 ++-- packages/utils/src/abi_encoder/evm_data_type_factory.ts | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts index 46e60979a..dc9de8a3d 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts @@ -21,7 +21,7 @@ export abstract class Pointer extends DataType { } public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlocks.Pointer { - if (!parentBlock) { + if (_.isUndefined(parentBlock)) { throw new Error(`DependentDataType requires a parent block to generate its block`); } const destinationBlock = this._destination.generateCalldataBlock(value, parentBlock); diff --git a/packages/utils/src/abi_encoder/calldata/blocks/set.ts b/packages/utils/src/abi_encoder/calldata/blocks/set.ts index e4de22c5c..81455b364 100644 --- a/packages/utils/src/abi_encoder/calldata/blocks/set.ts +++ b/packages/utils/src/abi_encoder/calldata/blocks/set.ts @@ -14,7 +14,7 @@ export class Set extends CalldataBlock { public getRawData(): Buffer { const rawDataComponents: Buffer[] = []; - if (this._header) { + if (!_.isUndefined(this._header)) { rawDataComponents.push(this._header); } _.each(this._members, (member: CalldataBlock) => { @@ -35,7 +35,7 @@ export class Set extends CalldataBlock { } public toBuffer(): Buffer { - if (this._header) { + if (!_.isUndefined(this._header)) { return this._header; } return new Buffer(''); diff --git a/packages/utils/src/abi_encoder/evm_data_type_factory.ts b/packages/utils/src/abi_encoder/evm_data_type_factory.ts index bfe457367..16a0b724d 100644 --- a/packages/utils/src/abi_encoder/evm_data_type_factory.ts +++ b/packages/utils/src/abi_encoder/evm_data_type_factory.ts @@ -2,6 +2,7 @@ /* tslint:disable max-classes-per-file */ /* tslint:disable no-construct */ import { DataItem, MethodAbi } from 'ethereum-types'; +import * as _ from 'lodash'; import { DataType, DataTypeFactory } from './abstract_data_types'; import * as Impl from './evm_data_types'; @@ -105,9 +106,9 @@ export class EvmDataTypeFactory implements DataTypeFactory { dataType = new String(dataItem); } // @TODO: Implement Fixed/UFixed types - if (!dataType) { + if (_.isUndefined(dataType)) { throw new Error(`Unrecognized data type: '${dataItem.type}'`); - } else if (parentDataType && !dataType.isStatic()) { + } else if (!_.isUndefined(parentDataType) && !dataType.isStatic()) { const pointerToDataType = new Pointer(dataType, parentDataType); return pointerToDataType; } -- cgit v1.2.3 From f479212410b238a7673983148f403b3a220083af Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 27 Nov 2018 15:28:26 -0800 Subject: Style cleanup. Improved wording of some error messages. --- .../abstract_data_types/types/pointer.ts | 4 +++- .../abi_encoder/abstract_data_types/types/set.ts | 4 ++-- .../utils/src/abi_encoder/calldata/calldata.ts | 11 ++++----- .../utils/src/abi_encoder/evm_data_type_factory.ts | 6 +++-- .../src/abi_encoder/evm_data_types/address.ts | 12 ++++++---- .../utils/src/abi_encoder/evm_data_types/bool.ts | 4 +++- .../abi_encoder/evm_data_types/dynamic_bytes.ts | 28 ++++++++++++---------- .../utils/src/abi_encoder/evm_data_types/int.ts | 1 - .../utils/src/abi_encoder/evm_data_types/string.ts | 4 +++- .../utils/src/abi_encoder/evm_data_types/uint.ts | 1 - packages/utils/src/abi_encoder/utils/math.ts | 4 ++-- 11 files changed, 43 insertions(+), 36 deletions(-) diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts index dc9de8a3d..8e597636b 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts @@ -1,4 +1,3 @@ -/* tslint:disable prefer-function-over-method */ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -44,7 +43,10 @@ export abstract class Pointer extends DataType { return value; } + // Disable prefer-function-over-method for inherited abstract method. + /* tslint:disable prefer-function-over-method */ public isStatic(): boolean { return true; } + /* tslint:enable prefer-function-over-method */ } 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 637abfb7d..aeea7919f 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 @@ -50,7 +50,7 @@ export abstract class Set extends DataType { public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { let members = this._members; // Case 1: This is an array of undefined length, which means that `this._members` was not - // populated in the constructor. So, construct the set of members it now. + // populated in the constructor. So we must construct the set of members now. if (this._isArray && _.isUndefined(this._arrayLength)) { const arrayLengthBuf = calldata.popWord(); const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); @@ -176,7 +176,7 @@ export abstract class Set extends DataType { private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberIndexByName] { // Sanity check if (_.isUndefined(dataItem.components)) { - throw new Error(`Expected components`); + throw new Error(`Tried to create a set using key/value pairs, but no components were defined by the input DataItem '${dataItem.name}'.`); } // Create one member for each component of `dataItem` const members: DataType[] = []; diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index c57ff08c2..6d8814e06 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -59,7 +59,7 @@ export class Calldata { offset += block.getSizeInBytes(); } // Generate hex string - const hexString = this._rules.annotate ? this._toAnnotatedString() : this._toCondensedString(); + const hexString = this._rules.annotate ? this._toHumanReadableCallData() : this._toEvmCompatibeCallDataHex(); return hexString; } /** @@ -131,10 +131,7 @@ export class Calldata { } } } - /** - * Returns EVM-compatible calldata as a Hex string. - */ - private _toCondensedString(): string { + private _toEvmCompatibeCallDataHex(): string { // Sanity check: must have a root block. if (_.isUndefined(this._root)) { throw new Error('expected root'); @@ -152,7 +149,7 @@ export class Calldata { return hexValue; } /** - * Returns human-redable calldata. + * Returns human-readable calldata. * * Example: * simpleFunction(string[], string[]) @@ -173,7 +170,7 @@ export class Calldata { * 0xe0 0000000000000000000000000000000000000000000000000000000000000005 array2[1] * 0x100 576f726c64000000000000000000000000000000000000000000000000000000 */ - private _toAnnotatedString(): string { + private _toHumanReadableCallData(): string { // Sanity check: must have a root block. if (_.isUndefined(this._root)) { throw new Error('expected root'); diff --git a/packages/utils/src/abi_encoder/evm_data_type_factory.ts b/packages/utils/src/abi_encoder/evm_data_type_factory.ts index 16a0b724d..bc68e05b7 100644 --- a/packages/utils/src/abi_encoder/evm_data_type_factory.ts +++ b/packages/utils/src/abi_encoder/evm_data_type_factory.ts @@ -1,6 +1,4 @@ -/* tslint:disable prefer-function-over-method */ /* tslint:disable max-classes-per-file */ -/* tslint:disable no-construct */ import { DataItem, MethodAbi } from 'ethereum-types'; import * as _ from 'lodash'; @@ -73,6 +71,7 @@ export class Method extends Impl.Method { } } +/* tslint:disable no-construct */ export class EvmDataTypeFactory implements DataTypeFactory { private static _instance: DataTypeFactory; @@ -83,6 +82,7 @@ export class EvmDataTypeFactory implements DataTypeFactory { return EvmDataTypeFactory._instance; } + /* tslint:disable prefer-function-over-method */ public create(dataItem: DataItem, parentDataType?: DataType): DataType { // Create data type let dataType: undefined | DataType; @@ -114,6 +114,8 @@ export class EvmDataTypeFactory implements DataTypeFactory { } return dataType; } + /* tslint:enable prefer-function-over-method */ private constructor() {} } +/* tslint:enable no-construct */ diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 52fc8e7b9..25ff55903 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -1,4 +1,3 @@ -/* tslint:disable prefer-function-over-method */ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -26,10 +25,8 @@ export class Address extends AbstractDataTypes.Blob { } } - public getSignature(): string { - return 'address'; - } - + // Disable prefer-function-over-method for inherited abstract methods. + /* tslint:disable prefer-function-over-method */ public encodeValue(value: string): Buffer { if (!_.startsWith(value, '0x')) { throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); @@ -48,4 +45,9 @@ export class Address extends AbstractDataTypes.Blob { const value = ethUtil.bufferToHex(valueBuf); return value; } + + public getSignature(): string { + return 'address'; + } + /* tslint:enable prefer-function-over-method */ } diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index fa115bb87..7e135aba9 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -1,4 +1,3 @@ -/* tslint:disable prefer-function-over-method */ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -22,6 +21,8 @@ export class Bool extends AbstractDataTypes.Blob { } } + // Disable prefer-function-over-method for inherited abstract methods. + /* tslint:disable prefer-function-over-method */ public encodeValue(value: boolean): Buffer { const encodedValue = value ? '0x1' : '0x0'; const encodedValueBuf = ethUtil.setLengthLeft( @@ -47,4 +48,5 @@ export class Bool extends AbstractDataTypes.Blob { public getSignature(): string { return 'bool'; } + /* tslint:enable prefer-function-over-method */ } diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index 98d90b7e4..ad22c8c6e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -1,4 +1,3 @@ -/* tslint:disable prefer-function-over-method */ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -14,6 +13,17 @@ export class DynamicBytes extends AbstractDataTypes.Blob { return type === 'bytes'; } + private static _sanityCheckValue(value: string | Buffer): void { + if (typeof value !== 'string') { + return; + } + if (!_.startsWith(value, '0x')) { + throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); + } else if (value.length % 2 !== 0) { + throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); + } + } + public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { super(dataItem, dataTypeFactory, DynamicBytes._SIZE_KNOWN_AT_COMPILE_TIME); if (!DynamicBytes.matchType(dataItem.type)) { @@ -21,6 +31,8 @@ export class DynamicBytes extends AbstractDataTypes.Blob { } } + // Disable prefer-function-over-method for inherited abstract methods. + /* tslint:disable prefer-function-over-method */ public encodeValue(value: string | Buffer): Buffer { // Encoded value is of the form: , with each field padded to be word-aligned. // 1/3 Construct the length @@ -48,22 +60,12 @@ export class DynamicBytes extends AbstractDataTypes.Blob { const valueBufPadded = calldata.popWords(wordsToStoreValuePadded); const valueBuf = valueBufPadded.slice(0, length); const value = ethUtil.bufferToHex(valueBuf); - this._sanityCheckValue(value); + DynamicBytes._sanityCheckValue(value); return value; } public getSignature(): string { return 'bytes'; } - - private _sanityCheckValue(value: string | Buffer): void { - if (typeof value !== 'string') { - return; - } - if (!_.startsWith(value, '0x')) { - throw new Error(`Tried to encode non-hex value. Value must inlcude '0x' prefix.`); - } else if (value.length % 2 !== 0) { - throw new Error(`Tried to assign ${value}, which is contains a half-byte. Use full bytes only.`); - } - } + /* tslint:enable prefer-function-over-method */ } diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index 5c5193644..9d328bba9 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -1,4 +1,3 @@ -/* tslint:disable prefer-function-over-method */ import { DataItem } from 'ethereum-types'; import * as _ from 'lodash'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index 6ab3513c9..08f928d8a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -1,4 +1,3 @@ -/* tslint:disable prefer-function-over-method */ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -21,6 +20,8 @@ export class String extends AbstractDataTypes.Blob { } } + // Disable prefer-function-over-method for inherited abstract methods. + /* tslint:disable prefer-function-over-method */ public encodeValue(value: string): Buffer { // Encoded value is of the form: , with each field padded to be word-aligned. // 1/3 Construct the length @@ -53,4 +54,5 @@ export class String extends AbstractDataTypes.Blob { public getSignature(): string { return 'string'; } + /* tslint:enable prefer-function-over-method */ } diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index 76b944610..4357f15d2 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -1,4 +1,3 @@ -/* tslint:disable prefer-function-over-method */ import { DataItem } from 'ethereum-types'; import * as _ from 'lodash'; diff --git a/packages/utils/src/abi_encoder/utils/math.ts b/packages/utils/src/abi_encoder/utils/math.ts index bc344c695..9bcdc3af1 100644 --- a/packages/utils/src/abi_encoder/utils/math.ts +++ b/packages/utils/src/abi_encoder/utils/math.ts @@ -79,8 +79,8 @@ export function decodeNumericValue(encodedValue: Buffer, minValue: BigNumber): B } // Case 2/3: value is non-negative because there is no leading 1 (encoded as two's-complement) const valueBin = value.toString(Constants.BIN_BASE); - const valueIsNegative = valueBin.length === Constants.EVM_WORD_WIDTH_IN_BITS && _.startsWith(valueBin[0], '1'); - if (!valueIsNegative) { + const isValueNegative = valueBin.length === Constants.EVM_WORD_WIDTH_IN_BITS && _.startsWith(valueBin[0], '1'); + if (!isValueNegative) { return value; } // Case 3/3: value is negative -- cgit v1.2.3 From e7bdf4717da9d1fd50cda3dba4e9549dfbc337f7 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 27 Nov 2018 15:32:50 -0800 Subject: Fixed build error: was using `this` instead of class name to reference a static function that used to be an instance method. --- packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index ad22c8c6e..fecd1db6b 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -42,7 +42,7 @@ export class DynamicBytes extends AbstractDataTypes.Blob { const lengthBuf = ethUtil.toBuffer(valueBuf.byteLength); const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); // 2/3 Construct the value - this._sanityCheckValue(value); + DynamicBytes._sanityCheckValue(value); const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded); // 3/3 Combine length and value const encodedValue = Buffer.concat([lengthBufPadded, valueBufPadded]); -- cgit v1.2.3 From f196dc9e35d12aad281142371af4d2c32db1fd60 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 27 Nov 2018 16:20:56 -0800 Subject: Use ethUti.isValidAddress in encoder --- packages/utils/src/abi_encoder/evm_data_types/address.ts | 9 ++------- packages/utils/test/abi_encoder/evm_data_types_test.ts | 4 ++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 25ff55903..950901ea8 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -7,8 +7,6 @@ import { RawCalldata } from '../calldata'; import * as Constants from '../utils/constants'; export class Address extends AbstractDataTypes.Blob { - public static ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X = "Address must start with '0x'"; - public static ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES = 'Address must be 20 bytes'; private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _ADDRESS_SIZE_IN_BYTES = 20; private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = Constants.EVM_WORD_WIDTH_IN_BYTES - @@ -28,13 +26,10 @@ export class Address extends AbstractDataTypes.Blob { // Disable prefer-function-over-method for inherited abstract methods. /* tslint:disable prefer-function-over-method */ public encodeValue(value: string): Buffer { - if (!_.startsWith(value, '0x')) { - throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); + if (!ethUtil.isValidAddress(value)) { + throw new Error(`Invalid address: '${value}'`); } const valueBuf = ethUtil.toBuffer(value); - if (valueBuf.byteLength !== Address._ADDRESS_SIZE_IN_BYTES) { - throw new Error(Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); - } const encodedValueBuf = ethUtil.setLengthLeft(valueBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); return encodedValueBuf; } 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 b99500265..11cebda55 100644 --- a/packages/utils/test/abi_encoder/evm_data_types_test.ts +++ b/packages/utils/test/abi_encoder/evm_data_types_test.ts @@ -387,7 +387,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Encode Args and validate result expect(() => { dataType.encode(args); - }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_START_WITH_0X); + }).to.throw(`Invalid address: '${args}'`); }); it('Invalid Address - input is not 20 bytes', async () => { // Create DataType object @@ -398,7 +398,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Encode Args and validate result expect(() => { dataType.encode(args); - }).to.throw(AbiEncoder.Address.ERROR_MESSAGE_ADDRESS_MUST_BE_20_BYTES); + }).to.throw(`Invalid address: '${args}'`); }); }); -- cgit v1.2.3 From 14c094d050e7b2d0a4b31d02dbe58a54153be7bb Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 27 Nov 2018 16:33:51 -0800 Subject: Use SolidityTypes from `ethereum-types` package. --- packages/ethereum-types/src/index.ts | 5 +++++ packages/utils/src/abi_encoder/evm_data_types/address.ts | 6 +++--- packages/utils/src/abi_encoder/evm_data_types/bool.ts | 6 +++--- packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts | 6 +++--- packages/utils/src/abi_encoder/evm_data_types/int.ts | 4 ++-- packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts | 4 ++-- packages/utils/src/abi_encoder/evm_data_types/string.ts | 6 +++--- packages/utils/src/abi_encoder/evm_data_types/tuple.ts | 4 ++-- packages/utils/src/abi_encoder/evm_data_types/uint.ts | 4 ++-- 9 files changed, 25 insertions(+), 20 deletions(-) diff --git a/packages/ethereum-types/src/index.ts b/packages/ethereum-types/src/index.ts index eff38711a..9430fdc98 100644 --- a/packages/ethereum-types/src/index.ts +++ b/packages/ethereum-types/src/index.ts @@ -283,6 +283,11 @@ export interface RawLogEntry { export enum SolidityTypes { Address = 'address', + Bool = 'bool', + Bytes = 'bytes', + Int = 'int', + String = 'string', + Tuple = 'tuple', Uint256 = 'uint256', Uint8 = 'uint8', Uint = 'uint', diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 950901ea8..07a0bd10c 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -1,4 +1,4 @@ -import { DataItem } from 'ethereum-types'; +import { DataItem, SolidityTypes } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -13,7 +13,7 @@ export class Address extends AbstractDataTypes.Blob { Address._ADDRESS_SIZE_IN_BYTES; public static matchType(type: string): boolean { - return type === 'address'; + return type === SolidityTypes.Address; } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { @@ -42,7 +42,7 @@ export class Address extends AbstractDataTypes.Blob { } public getSignature(): string { - return 'address'; + return SolidityTypes.Address; } /* tslint:enable prefer-function-over-method */ } diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index 7e135aba9..7af13506b 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -1,4 +1,4 @@ -import { DataItem } from 'ethereum-types'; +import { DataItem, SolidityTypes } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -11,7 +11,7 @@ export class Bool extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; public static matchType(type: string): boolean { - return type === 'bool'; + return type === SolidityTypes.Bool; } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { @@ -46,7 +46,7 @@ export class Bool extends AbstractDataTypes.Blob { } public getSignature(): string { - return 'bool'; + return SolidityTypes.Bool; } /* tslint:enable prefer-function-over-method */ } diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index fecd1db6b..ac2a1fb6e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -1,4 +1,4 @@ -import { DataItem } from 'ethereum-types'; +import { DataItem, SolidityTypes } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -10,7 +10,7 @@ export class DynamicBytes extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; public static matchType(type: string): boolean { - return type === 'bytes'; + return type === SolidityTypes.Bytes; } private static _sanityCheckValue(value: string | Buffer): void { @@ -65,7 +65,7 @@ export class DynamicBytes extends AbstractDataTypes.Blob { } public getSignature(): string { - return 'bytes'; + return SolidityTypes.Bytes; } /* tslint:enable prefer-function-over-method */ } diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index 9d328bba9..3e465fc15 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -1,4 +1,4 @@ -import { DataItem } from 'ethereum-types'; +import { DataItem, SolidityTypes } from 'ethereum-types'; import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; @@ -53,6 +53,6 @@ export class Int extends AbstractDataTypes.Blob { } public getSignature(): string { - return `int${this._width}`; + return `${SolidityTypes.Int}${this._width}`; } } diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 68f212f79..ed1f51f7e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -1,4 +1,4 @@ -import { DataItem } from 'ethereum-types'; +import { DataItem, SolidityTypes } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -37,7 +37,7 @@ export class StaticBytes extends AbstractDataTypes.Blob { public getSignature(): string { // Note that `byte` reduces to `bytes1` - return `bytes${this._width}`; + return `${SolidityTypes.Bytes}${this._width}`; } public encodeValue(value: string | Buffer): Buffer { diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index 08f928d8a..e5b2d5f33 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -1,4 +1,4 @@ -import { DataItem } from 'ethereum-types'; +import { DataItem, SolidityTypes } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; @@ -10,7 +10,7 @@ export class String extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; public static matchType(type: string): boolean { - return type === 'string'; + return type === SolidityTypes.String; } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { @@ -52,7 +52,7 @@ export class String extends AbstractDataTypes.Blob { } public getSignature(): string { - return 'string'; + return SolidityTypes.String; } /* tslint:enable prefer-function-over-method */ } diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts index 3802f96c0..40859f62e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts @@ -1,4 +1,4 @@ -import { DataItem } from 'ethereum-types'; +import { DataItem, SolidityTypes } from 'ethereum-types'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; @@ -6,7 +6,7 @@ export class Tuple extends AbstractDataTypes.Set { private readonly _signature: string; public static matchType(type: string): boolean { - return type === 'tuple'; + return type === SolidityTypes.Tuple; } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index 4357f15d2..970400a57 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -1,4 +1,4 @@ -import { DataItem } from 'ethereum-types'; +import { DataItem, SolidityTypes } from 'ethereum-types'; import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; @@ -52,6 +52,6 @@ export class UInt extends AbstractDataTypes.Blob { } public getSignature(): string { - return `uint${this._width}`; + return `${SolidityTypes.Uint}${this._width}`; } } -- cgit v1.2.3 From 029b8d59507df25aa9c7d1b096c8d873eb6ae4da Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 27 Nov 2018 17:11:15 -0800 Subject: Changed constants to an exported enum; this is 0x convention --- .../abi_encoder/abstract_data_types/data_type.ts | 6 +++--- .../abstract_data_types/types/pointer.ts | 4 ++-- .../abi_encoder/abstract_data_types/types/set.ts | 12 ++++++------ .../src/abi_encoder/calldata/blocks/pointer.ts | 6 +++--- .../utils/src/abi_encoder/calldata/calldata.ts | 14 +++++++------- .../utils/src/abi_encoder/calldata/raw_calldata.ts | 6 +++--- .../src/abi_encoder/evm_data_types/address.ts | 6 +++--- .../utils/src/abi_encoder/evm_data_types/array.ts | 4 ++-- .../utils/src/abi_encoder/evm_data_types/bool.ts | 6 +++--- .../abi_encoder/evm_data_types/dynamic_bytes.ts | 12 ++++++------ .../utils/src/abi_encoder/evm_data_types/int.ts | 4 ++-- .../utils/src/abi_encoder/evm_data_types/method.ts | 4 ++-- .../src/abi_encoder/evm_data_types/static_bytes.ts | 6 +++--- .../utils/src/abi_encoder/evm_data_types/string.ts | 12 ++++++------ .../utils/src/abi_encoder/evm_data_types/uint.ts | 4 ++-- packages/utils/src/abi_encoder/utils/constants.ts | 22 ++++++++++++---------- packages/utils/src/abi_encoder/utils/math.ts | 20 ++++++++++---------- 17 files changed, 75 insertions(+), 73 deletions(-) diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts index 4cef60172..10e5c2540 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts @@ -2,7 +2,7 @@ import { DataItem } from 'ethereum-types'; import * as _ from 'lodash'; import { Calldata, CalldataBlock, RawCalldata } from '../calldata'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; import { DecodingRules, EncodingRules } from '../utils/rules'; import { DataTypeFactory } from './interfaces'; @@ -25,7 +25,7 @@ export abstract class DataType { } public encode(value: any, rules?: EncodingRules, selector?: string): string { - const rules_ = _.isUndefined(rules) ? Constants.DEFAULT_ENCODING_RULES : rules; + const rules_ = _.isUndefined(rules) ? constants.DEFAULT_ENCODING_RULES : rules; const calldata = new Calldata(rules_); if (!_.isUndefined(selector)) { calldata.setSelector(selector); @@ -44,7 +44,7 @@ export abstract class DataType { } const hasSelector = !_.isUndefined(selector); const rawCalldata = new RawCalldata(calldata, hasSelector); - const rules_ = _.isUndefined(rules) ? Constants.DEFAULT_DECODING_RULES : rules; + const rules_ = _.isUndefined(rules) ? constants.DEFAULT_DECODING_RULES : rules; const value = this.generateValue(rawCalldata, rules_); return value; } diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts index 8e597636b..3d38deb94 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts @@ -3,7 +3,7 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { CalldataBlock, CalldataBlocks, RawCalldata } from '../../calldata'; -import * as Constants from '../../utils/constants'; +import { constants } from '../../utils/constants'; import { DecodingRules } from '../../utils/rules'; import { DataType } from '../data_type'; @@ -34,7 +34,7 @@ export abstract class Pointer extends DataType { public generateValue(calldata: RawCalldata, rules: DecodingRules): any { const destinationOffsetBuf = calldata.popWord(); const destinationOffsetHex = ethUtil.bufferToHex(destinationOffsetBuf); - const destinationOffsetRelative = parseInt(destinationOffsetHex, Constants.HEX_BASE); + const destinationOffsetRelative = parseInt(destinationOffsetHex, constants.HEX_BASE); const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); const currentOffset = calldata.getOffset(); calldata.setOffset(destinationOffsetAbsolute); 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 aeea7919f..f50ed8298 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 @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { BigNumber } from '../../../configured_bignumber'; import { CalldataBlock, CalldataBlocks, RawCalldata } from '../../calldata'; -import * as Constants from '../../utils/constants'; +import { constants } from '../../utils/constants'; import { DecodingRules } from '../../utils/rules'; import { DataType } from '../data_type'; @@ -54,7 +54,7 @@ export abstract class Set extends DataType { if (this._isArray && _.isUndefined(this._arrayLength)) { const arrayLengthBuf = calldata.popWord(); const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); - const arrayLength = new BigNumber(arrayLengthHex, Constants.HEX_BASE); + const arrayLength = new BigNumber(arrayLengthHex, constants.HEX_BASE); [members] = this._createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); } // Create a new scope in the calldata, before descending into the members of this set. @@ -115,8 +115,8 @@ export abstract class Set extends DataType { if (this._isArray && _.isUndefined(this._arrayLength)) { [members] = this._createMembersWithLength(this.getDataItem(), value.length); const lenBuf = ethUtil.setLengthLeft( - ethUtil.toBuffer(`0x${value.length.toString(Constants.HEX_BASE)}`), - Constants.EVM_WORD_WIDTH_IN_BYTES, + ethUtil.toBuffer(`0x${value.length.toString(constants.HEX_BASE)}`), + constants.EVM_WORD_WIDTH_IN_BYTES, ); block.setHeader(lenBuf); } @@ -205,14 +205,14 @@ export abstract class Set extends DataType { _.each(range, (idx: number) => { const memberDataItem: DataItem = { type: _.isUndefined(this._arrayElementType) ? '' : this._arrayElementType, - name: `${dataItem.name}[${idx.toString(Constants.DEC_BASE)}]`, + name: `${dataItem.name}[${idx.toString(constants.DEC_BASE)}]`, }; const components = dataItem.components; if (!_.isUndefined(components)) { memberDataItem.components = components; } const memberType = this.getFactory().create(memberDataItem, this); - memberIndexByName[idx.toString(Constants.DEC_BASE)] = members.length; + memberIndexByName[idx.toString(constants.DEC_BASE)] = members.length; members.push(memberType); }); return [members, memberIndexByName]; diff --git a/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts index 654cbe26c..1daf33f7e 100644 --- a/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts +++ b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts @@ -1,7 +1,7 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import * as Constants from '../../utils/constants'; +import { constants } from '../../utils/constants'; import { CalldataBlock } from '../calldata_block'; @@ -29,9 +29,9 @@ export class Pointer extends CalldataBlock { const parentOffset = this._parent.getOffsetInBytes(); const parentHeaderSize = this._parent.getHeaderSizeInBytes(); const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); - const pointerHex = `0x${pointer.toString(Constants.HEX_BASE)}`; + const pointerHex = `0x${pointer.toString(constants.HEX_BASE)}`; const pointerBuf = ethUtil.toBuffer(pointerHex); - const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + const pointerBufPadded = ethUtil.setLengthLeft(pointerBuf, constants.EVM_WORD_WIDTH_IN_BYTES); return pointerBufPadded; } diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index 6d8814e06..e5858b524 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -1,7 +1,7 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; import { EncodingRules } from '../utils/rules'; import * as CalldataBlocks from './blocks'; @@ -31,7 +31,7 @@ export class Calldata { public setSelector(selector: string): void { if (!_.startsWith(selector, '0x')) { throw new Error(`Expected selector to be hex. Missing prefix '0x'`); - } else if (selector.length !== Constants.HEX_SELECTOR_LENGTH_IN_CHARS) { + } else if (selector.length !== constants.HEX_SELECTOR_LENGTH_IN_CHARS) { throw new Error(`Invalid selector '${selector}'`); } this._selector = selector; @@ -206,11 +206,11 @@ export class Calldata { lineStr = `\n${offsetStr}${valueStr}${nameStr}`; } else { // This block has at least one word of value. - offsetStr = `0x${offset.toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); + offsetStr = `0x${offset.toString(constants.HEX_BASE)}`.padEnd(offsetPadding); valueStr = ethUtil .stripHexPrefix( ethUtil.bufferToHex( - block.toBuffer().slice(evmWordStartIndex, Constants.EVM_WORD_WIDTH_IN_BYTES), + block.toBuffer().slice(evmWordStartIndex, constants.EVM_WORD_WIDTH_IN_BYTES), ), ) .padEnd(valuePadding); @@ -223,11 +223,11 @@ export class Calldata { } } // This block has a value that is more than 1 word. - for (let j = Constants.EVM_WORD_WIDTH_IN_BYTES; j < size; j += Constants.EVM_WORD_WIDTH_IN_BYTES) { - offsetStr = `0x${(offset + j).toString(Constants.HEX_BASE)}`.padEnd(offsetPadding); + for (let j = constants.EVM_WORD_WIDTH_IN_BYTES; j < size; j += constants.EVM_WORD_WIDTH_IN_BYTES) { + offsetStr = `0x${(offset + j).toString(constants.HEX_BASE)}`.padEnd(offsetPadding); valueStr = ethUtil .stripHexPrefix( - ethUtil.bufferToHex(block.toBuffer().slice(j, j + Constants.EVM_WORD_WIDTH_IN_BYTES)), + ethUtil.bufferToHex(block.toBuffer().slice(j, j + constants.EVM_WORD_WIDTH_IN_BYTES)), ) .padEnd(valuePadding); nameStr = ' '.repeat(namePadding); diff --git a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts index fbe592fc7..189841989 100644 --- a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts @@ -1,7 +1,7 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; import { Queue } from '../utils/queue'; export class RawCalldata { @@ -24,8 +24,8 @@ export class RawCalldata { this._offset = RawCalldata._INITIAL_OFFSET; // If there's a selector then slice it if (hasSelector) { - const selectorBuf = this._value.slice(Constants.HEX_SELECTOR_LENGTH_IN_BYTES); - this._value = this._value.slice(Constants.HEX_SELECTOR_LENGTH_IN_BYTES); + const selectorBuf = this._value.slice(constants.HEX_SELECTOR_LENGTH_IN_BYTES); + this._value = this._value.slice(constants.HEX_SELECTOR_LENGTH_IN_BYTES); this._selector = ethUtil.bufferToHex(selectorBuf); } } diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 07a0bd10c..c45355639 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -4,12 +4,12 @@ import * as _ from 'lodash'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; export class Address extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _ADDRESS_SIZE_IN_BYTES = 20; - private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = Constants.EVM_WORD_WIDTH_IN_BYTES - + private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = constants.EVM_WORD_WIDTH_IN_BYTES - Address._ADDRESS_SIZE_IN_BYTES; public static matchType(type: string): boolean { @@ -30,7 +30,7 @@ export class Address extends AbstractDataTypes.Blob { throw new Error(`Invalid address: '${value}'`); } const valueBuf = ethUtil.toBuffer(value); - const encodedValueBuf = ethUtil.setLengthLeft(valueBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + const encodedValueBuf = ethUtil.setLengthLeft(valueBuf, constants.EVM_WORD_WIDTH_IN_BYTES); return encodedValueBuf; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts index a86283c2a..272cc4132 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/array.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -2,7 +2,7 @@ import { DataItem } from 'ethereum-types'; import * as _ from 'lodash'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; export class Array extends AbstractDataTypes.Set { private static readonly _MATCHER = RegExp('^(.+)\\[([0-9]*)\\]$'); @@ -23,7 +23,7 @@ export class Array extends AbstractDataTypes.Set { throw new Error(`Could not parse array length: ${type}`); } const arrayElementType = matches[1]; - const arrayLength = _.isEmpty(matches[2]) ? undefined : parseInt(matches[2], Constants.DEC_BASE); + const arrayLength = _.isEmpty(matches[2]) ? undefined : parseInt(matches[2], constants.DEC_BASE); return [arrayElementType, arrayLength]; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index 7af13506b..0c29f690a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -5,7 +5,7 @@ import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; export class Bool extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; @@ -27,7 +27,7 @@ export class Bool extends AbstractDataTypes.Blob { const encodedValue = value ? '0x1' : '0x0'; const encodedValueBuf = ethUtil.setLengthLeft( ethUtil.toBuffer(encodedValue), - Constants.EVM_WORD_WIDTH_IN_BYTES, + constants.EVM_WORD_WIDTH_IN_BYTES, ); return encodedValueBuf; } @@ -35,7 +35,7 @@ export class Bool extends AbstractDataTypes.Blob { public decodeValue(calldata: RawCalldata): boolean { const valueBuf = calldata.popWord(); const valueHex = ethUtil.bufferToHex(valueBuf); - const valueNumber = new BigNumber(valueHex, Constants.HEX_BASE); + const valueNumber = new BigNumber(valueHex, constants.HEX_BASE); if (!(valueNumber.equals(0) || valueNumber.equals(1))) { throw new Error(`Failed to decode boolean. Expected 0x0 or 0x1, got ${valueHex}`); } diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index ac2a1fb6e..2c256cfa9 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; export class DynamicBytes extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; @@ -37,10 +37,10 @@ export class DynamicBytes extends AbstractDataTypes.Blob { // Encoded value is of the form: , with each field padded to be word-aligned. // 1/3 Construct the length const valueBuf = ethUtil.toBuffer(value); - const wordsToStoreValuePadded = Math.ceil(valueBuf.byteLength / Constants.EVM_WORD_WIDTH_IN_BYTES); - const bytesToStoreValuePadded = wordsToStoreValuePadded * Constants.EVM_WORD_WIDTH_IN_BYTES; + const wordsToStoreValuePadded = Math.ceil(valueBuf.byteLength / constants.EVM_WORD_WIDTH_IN_BYTES); + const bytesToStoreValuePadded = wordsToStoreValuePadded * constants.EVM_WORD_WIDTH_IN_BYTES; const lengthBuf = ethUtil.toBuffer(valueBuf.byteLength); - const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, constants.EVM_WORD_WIDTH_IN_BYTES); // 2/3 Construct the value DynamicBytes._sanityCheckValue(value); const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded); @@ -54,9 +54,9 @@ export class DynamicBytes extends AbstractDataTypes.Blob { // 1/2 Decode length const lengthBuf = calldata.popWord(); const lengthHex = ethUtil.bufferToHex(lengthBuf); - const length = parseInt(lengthHex, Constants.HEX_BASE); + const length = parseInt(lengthHex, constants.HEX_BASE); // 2/2 Decode value - const wordsToStoreValuePadded = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const wordsToStoreValuePadded = Math.ceil(length / constants.EVM_WORD_WIDTH_IN_BYTES); const valueBufPadded = calldata.popWords(wordsToStoreValuePadded); const valueBuf = valueBufPadded.slice(0, length); const value = ethUtil.bufferToHex(valueBuf); diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index 3e465fc15..244b720e3 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; import * as EncoderMath from '../utils/math'; export class Int extends AbstractDataTypes.Blob { @@ -26,7 +26,7 @@ export class Int extends AbstractDataTypes.Blob { const matches = Int._MATCHER.exec(type); const width = !_.isNull(matches) && matches.length === 2 && !_.isUndefined(matches[1]) - ? parseInt(matches[1], Constants.DEC_BASE) + ? parseInt(matches[1], constants.DEC_BASE) : Int._DEFAULT_WIDTH; return width; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts index bd4732097..7256a93d9 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/method.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts @@ -3,7 +3,7 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { AbstractDataTypes, DataType, DataTypeFactory } from '../abstract_data_types'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; import { DecodingRules, EncodingRules } from '../utils/rules'; import { Tuple } from './tuple'; @@ -62,7 +62,7 @@ export class Method extends AbstractDataTypes.Set { ethUtil.toBuffer( ethUtil .sha3(signature) - .slice(Constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, Constants.HEX_SELECTOR_LENGTH_IN_BYTES), + .slice(constants.HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA, constants.HEX_SELECTOR_LENGTH_IN_BYTES), ), ); return selector; diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index ed1f51f7e..5453d47a0 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; export class StaticBytes extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; @@ -22,7 +22,7 @@ export class StaticBytes extends AbstractDataTypes.Blob { const matches = StaticBytes._MATCHER.exec(type); const width = !_.isNull(matches) && matches.length === 3 && !_.isUndefined(matches[2]) - ? parseInt(matches[2], Constants.DEC_BASE) + ? parseInt(matches[2], constants.DEC_BASE) : StaticBytes._DEFAULT_WIDTH; return width; } @@ -45,7 +45,7 @@ export class StaticBytes extends AbstractDataTypes.Blob { this._sanityCheckValue(value); const valueBuf = ethUtil.toBuffer(value); // 2/2 Store value as hex - const valuePadded = ethUtil.setLengthRight(valueBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + const valuePadded = ethUtil.setLengthRight(valueBuf, constants.EVM_WORD_WIDTH_IN_BYTES); return valuePadded; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index e5b2d5f33..ac62ea264 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; export class String extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; @@ -25,10 +25,10 @@ export class String extends AbstractDataTypes.Blob { public encodeValue(value: string): Buffer { // Encoded value is of the form: , with each field padded to be word-aligned. // 1/3 Construct the length - const wordsToStoreValuePadded = Math.ceil(value.length / Constants.EVM_WORD_WIDTH_IN_BYTES); - const bytesToStoreValuePadded = wordsToStoreValuePadded * Constants.EVM_WORD_WIDTH_IN_BYTES; + const wordsToStoreValuePadded = Math.ceil(value.length / constants.EVM_WORD_WIDTH_IN_BYTES); + const bytesToStoreValuePadded = wordsToStoreValuePadded * constants.EVM_WORD_WIDTH_IN_BYTES; const lengthBuf = ethUtil.toBuffer(value.length); - const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, constants.EVM_WORD_WIDTH_IN_BYTES); // 2/3 Construct the value const valueBuf = new Buffer(value); const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded); @@ -42,9 +42,9 @@ export class String extends AbstractDataTypes.Blob { // 1/2 Decode length const lengthBufPadded = calldata.popWord(); const lengthHexPadded = ethUtil.bufferToHex(lengthBufPadded); - const length = parseInt(lengthHexPadded, Constants.HEX_BASE); + const length = parseInt(lengthHexPadded, constants.HEX_BASE); // 2/2 Decode value - const wordsToStoreValuePadded = Math.ceil(length / Constants.EVM_WORD_WIDTH_IN_BYTES); + const wordsToStoreValuePadded = Math.ceil(length / constants.EVM_WORD_WIDTH_IN_BYTES); const valueBufPadded = calldata.popWords(wordsToStoreValuePadded); const valueBuf = valueBufPadded.slice(0, length); const value = valueBuf.toString('ascii'); diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index 970400a57..df7ea38a4 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; import * as EncoderMath from '../utils/math'; export class UInt extends AbstractDataTypes.Blob { @@ -26,7 +26,7 @@ export class UInt extends AbstractDataTypes.Blob { const matches = UInt._MATCHER.exec(type); const width = !_.isNull(matches) && matches.length === 2 && !_.isUndefined(matches[1]) - ? parseInt(matches[1], Constants.DEC_BASE) + ? parseInt(matches[1], constants.DEC_BASE) : UInt._DEFAULT_WIDTH; return width; } diff --git a/packages/utils/src/abi_encoder/utils/constants.ts b/packages/utils/src/abi_encoder/utils/constants.ts index 05c6783e7..82eeab010 100644 --- a/packages/utils/src/abi_encoder/utils/constants.ts +++ b/packages/utils/src/abi_encoder/utils/constants.ts @@ -1,12 +1,14 @@ import { DecodingRules, EncodingRules } from './rules'; -export const EVM_WORD_WIDTH_IN_BYTES = 32; -export const EVM_WORD_WIDTH_IN_BITS = 256; -export const HEX_BASE = 16; -export const DEC_BASE = 10; -export const BIN_BASE = 2; -export const HEX_SELECTOR_LENGTH_IN_CHARS = 10; -export const HEX_SELECTOR_LENGTH_IN_BYTES = 4; -export const HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA = 0; -export const DEFAULT_DECODING_RULES: DecodingRules = { structsAsObjects: false }; -export const DEFAULT_ENCODING_RULES: EncodingRules = { optimize: false, annotate: false }; +export const constants = { + EVM_WORD_WIDTH_IN_BYTES: 32, + EVM_WORD_WIDTH_IN_BITS: 256, + HEX_BASE: 16, + DEC_BASE: 10, + BIN_BASE: 2, + HEX_SELECTOR_LENGTH_IN_CHARS: 10, + HEX_SELECTOR_LENGTH_IN_BYTES: 4, + HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA: 0, + DEFAULT_DECODING_RULES: { structsAsObjects: false } as DecodingRules, + DEFAULT_ENCODING_RULES: { optimize: false, annotate: false } as EncodingRules, +}; diff --git a/packages/utils/src/abi_encoder/utils/math.ts b/packages/utils/src/abi_encoder/utils/math.ts index 9bcdc3af1..d84983c5b 100644 --- a/packages/utils/src/abi_encoder/utils/math.ts +++ b/packages/utils/src/abi_encoder/utils/math.ts @@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import * as Constants from '../utils/constants'; +import { constants } from '../utils/constants'; function sanityCheckBigNumberRange( value_: BigNumber | string | number, @@ -17,9 +17,9 @@ function sanityCheckBigNumberRange( } } function bigNumberToPaddedBuffer(value: BigNumber): Buffer { - const valueHex = `0x${value.toString(Constants.HEX_BASE)}`; + const valueHex = `0x${value.toString(constants.HEX_BASE)}`; const valueBuf = ethUtil.toBuffer(valueHex); - const valueBufPadded = ethUtil.setLengthLeft(valueBuf, Constants.EVM_WORD_WIDTH_IN_BYTES); + const valueBufPadded = ethUtil.setLengthLeft(valueBuf, constants.EVM_WORD_WIDTH_IN_BYTES); return valueBufPadded; } /** @@ -37,13 +37,13 @@ export function encodeNumericValue(value_: BigNumber | string | number): Buffer // Case 2/2: Value is negative // Use two's-complement to encode the value // Step 1/3: Convert negative value to positive binary string - const valueBin = value.times(-1).toString(Constants.BIN_BASE); + const valueBin = value.times(-1).toString(constants.BIN_BASE); // Step 2/3: Invert binary value - let invertedValueBin = '1'.repeat(Constants.EVM_WORD_WIDTH_IN_BITS - valueBin.length); + let invertedValueBin = '1'.repeat(constants.EVM_WORD_WIDTH_IN_BITS - valueBin.length); _.each(valueBin, (bit: string) => { invertedValueBin += bit === '1' ? '0' : '1'; }); - const invertedValue = new BigNumber(invertedValueBin, Constants.BIN_BASE); + const invertedValue = new BigNumber(invertedValueBin, constants.BIN_BASE); // Step 3/3: Add 1 to inverted value const negativeValue = invertedValue.plus(1); const encodedValue = bigNumberToPaddedBuffer(negativeValue); @@ -73,13 +73,13 @@ export function safeEncodeNumericValue( export function decodeNumericValue(encodedValue: Buffer, minValue: BigNumber): BigNumber { const valueHex = ethUtil.bufferToHex(encodedValue); // Case 1/3: value is definitely non-negative because of numeric boundaries - const value = new BigNumber(valueHex, Constants.HEX_BASE); + const value = new BigNumber(valueHex, constants.HEX_BASE); if (!minValue.lessThan(0)) { return value; } // Case 2/3: value is non-negative because there is no leading 1 (encoded as two's-complement) - const valueBin = value.toString(Constants.BIN_BASE); - const isValueNegative = valueBin.length === Constants.EVM_WORD_WIDTH_IN_BITS && _.startsWith(valueBin[0], '1'); + const valueBin = value.toString(constants.BIN_BASE); + const isValueNegative = valueBin.length === constants.EVM_WORD_WIDTH_IN_BITS && _.startsWith(valueBin[0], '1'); if (!isValueNegative) { return value; } @@ -89,7 +89,7 @@ export function decodeNumericValue(encodedValue: Buffer, minValue: BigNumber): B _.each(valueBin, (bit: string) => { invertedValueBin += bit === '1' ? '0' : '1'; }); - const invertedValue = new BigNumber(invertedValueBin, Constants.BIN_BASE); + const invertedValue = new BigNumber(invertedValueBin, constants.BIN_BASE); // Step 2/3: Add 1 to inverted value // The result is the two's-complement representation of the input value. const positiveValue = invertedValue.plus(1); -- cgit v1.2.3 From d6645b8a9116d92ac15f0b64f46566bf7f5447f1 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 27 Nov 2018 17:23:01 -0800 Subject: Explicit encoding rules in tests. --- .../utils/test/abi_encoder/evm_data_types_test.ts | 129 +++++++++++---------- packages/utils/test/abi_encoder/methods_test.ts | 37 +++--- packages/utils/test/abi_encoder/optimizer_test.ts | 31 ++--- .../utils/test/abi_encoder/return_values_test.ts | 11 +- 4 files changed, 106 insertions(+), 102 deletions(-) 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 11cebda55..9ef80a560 100644 --- a/packages/utils/test/abi_encoder/evm_data_types_test.ts +++ b/packages/utils/test/abi_encoder/evm_data_types_test.ts @@ -10,6 +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. describe('Array', () => { it('Fixed size; Static elements', async () => { // Create DataType object @@ -18,7 +19,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = [new BigNumber(5), new BigNumber(6)]; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -33,7 +34,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = [new BigNumber(5), new BigNumber(6)]; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -48,7 +49,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = ['Hello', 'world']; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -63,7 +64,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = ['Hello', 'world']; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -81,7 +82,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const array3 = ['0x18192021']; const args = [array1, array2, array3]; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000405060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000041011121300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000414151617000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000041819202100000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -99,7 +100,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const array3 = ['0x18192021']; const args = [array1, array2, array3]; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000301020304000000000000000000000000000000000000000000000000000000000506070800000000000000000000000000000000000000000000000000000000091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021011121300000000000000000000000000000000000000000000000000000000141516170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011819202100000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -116,7 +117,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const array2 = ['0x10111213', '0x14151617', '0x18192021']; const args = [array1, array2]; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x010203040000000000000000000000000000000000000000000000000000000005060708000000000000000000000000000000000000000000000000000000000910111200000000000000000000000000000000000000000000000000000000101112130000000000000000000000000000000000000000000000000000000014151617000000000000000000000000000000000000000000000000000000001819202100000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -133,7 +134,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const array2 = ['0x10111213', '0x14151617', '0x18192021']; const args = [array1, array2]; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000401020304000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004050607080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040910111200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000410111213000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004141516170000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041819202100000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -149,7 +150,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = ['Hello', 'world']; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw('Expected array of 3 elements, but got array of length 2'); }); it('Static size; Too Many Elements', async () => { @@ -160,7 +161,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = ['Hello', 'world']; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw('Expected array of 1 elements, but got array of length 2'); }); it('Element Type Mismatch', async () => { @@ -171,7 +172,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = [new BigNumber(1), 'Bad Argument']; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw(); }); }); @@ -188,7 +189,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = { field_1: new BigNumber(-5), field_2: true }; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000001'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -208,7 +209,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = { field_1: 'Hello, World!', field_2: '0xabcdef0123456789' }; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -228,7 +229,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = { field: [new BigNumber(1), new BigNumber(2)] }; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -248,7 +249,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = { field: [new BigNumber(1), new BigNumber(2)] }; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -270,7 +271,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const array2 = ['0x09101112', '0x13141516']; const args = { field: [array1, array2] }; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0102030400000000000000000000000000000000000000000000000000000000050607080000000000000000000000000000000000000000000000000000000009101112000000000000000000000000000000000000000000000000000000001314151600000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -292,7 +293,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const array2 = ['0x09101112', '0x13141516']; const args = { field: [array1, array2] }; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040506070800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041314151600000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -322,7 +323,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { field_4: '0xabcdef0123456789', }; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -343,7 +344,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = { field_1: new BigNumber(-5) }; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw('Could not assign tuple to object: missing keys field_2'); }); it('Bad Key', async () => { @@ -358,7 +359,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = { unknown_field: new BigNumber(-5) }; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw("Could not assign tuple to object: unrecognized key 'unknown_field' in object Tuple"); }); }); @@ -371,7 +372,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = '0xe41d2489571d322189246dafa5ebde1f4699f498'; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -386,7 +387,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = 'e4'; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw(`Invalid address: '${args}'`); }); it('Invalid Address - input is not 20 bytes', async () => { @@ -397,7 +398,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = '0xe4'; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw(`Invalid address: '${args}'`); }); }); @@ -410,7 +411,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = true; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -424,7 +425,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = false; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -448,7 +449,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = new BigNumber(1); // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -462,7 +463,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = new BigNumber(-1); // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -476,7 +477,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = max256BitInteger; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -490,7 +491,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = min256BitInteger; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = `0x8000000000000000000000000000000000000000000000000000000000000000`; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -505,7 +506,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = max256BitInteger.plus(1); // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw(); }); it('Int256 - Value too small', async () => { @@ -516,7 +517,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = min256BitInteger.minus(1); // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw(); }); it('Int32 - Positive Base Case', async () => { @@ -526,7 +527,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = new BigNumber(1); // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -540,7 +541,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = new BigNumber(-1); // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -554,7 +555,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = max32BitInteger; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000007fffffff'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -568,7 +569,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = min32BitInteger; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000`; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -583,7 +584,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = max32BitInteger.plus(1); // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw(); }); it('Int32 - Value too small', async () => { @@ -594,7 +595,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = min32BitInteger.minus(1); // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw(); }); }); @@ -614,7 +615,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = new BigNumber(1); // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -628,7 +629,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = max256BitUnsignedInteger; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -642,7 +643,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = min256BitUnsignedInteger; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = `0x0000000000000000000000000000000000000000000000000000000000000000`; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -657,7 +658,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = max256BitUnsignedInteger.plus(1); // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw(); }); it('UInt256 - Value too small', async () => { @@ -668,7 +669,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = min256BitUnsignedInteger.minus(1); // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw(); }); it('UInt32 - Positive Base Case', async () => { @@ -678,7 +679,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = new BigNumber(1); // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000001'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -692,7 +693,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = max32BitUnsignedInteger; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000ffffffff'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -706,7 +707,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = min32BitUnsignedInteger; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = `0x0000000000000000000000000000000000000000000000000000000000000000`; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -721,7 +722,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = max32BitUnsignedInteger.plus(1); // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw(); }); it('UInt32 - Value too small', async () => { @@ -732,7 +733,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = min32BitUnsignedInteger.minus(1); // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw(); }); }); @@ -745,7 +746,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = '0x05'; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0500000000000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -759,7 +760,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = '0x05'; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0500000000000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -773,7 +774,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = '0x00010203'; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0001020300000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -788,7 +789,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const args = '0x1a18'; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x1a18000000000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -803,7 +804,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Construct args to be encoded const args = '0x0001020304050607080911121314151617181920212223242526272829303132'; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x0001020304050607080911121314151617181920212223242526272829303132'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -818,7 +819,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const args = '0x1a18bf61'; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x1a18bf6100000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); // Decode Encoded Args and validate result @@ -834,7 +835,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = '0x0102030405'; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw( 'Tried to assign 0x0102030405 (5 bytes), which exceeds max bytes that can be stored in a bytes4', ); @@ -847,7 +848,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = '0x010203040506070809101112131415161718192021222324252627282930313233'; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw( 'Tried to assign 0x010203040506070809101112131415161718192021222324252627282930313233 (33 bytes), which exceeds max bytes that can be stored in a bytes32', ); @@ -860,7 +861,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = '0102030405060708091011121314151617181920212223242526272829303132'; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix."); }); it('Should throw when pass in bad hex (include a half-byte)', async () => { @@ -871,7 +872,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = '0x010'; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); }); }); @@ -885,7 +886,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const args = '0x1a18bf61'; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000041a18bf6100000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -902,7 +903,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const bytesLength = 40; const args = '0x' + '61'.repeat(bytesLength); // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -935,7 +936,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = '01'; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw("Tried to encode non-hex value. Value must inlcude '0x' prefix."); }); it('Should throw when pass in bad hex (include a half-byte)', async () => { @@ -946,7 +947,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const args = '0x010'; // Encode Args and validate result expect(() => { - dataType.encode(args); + dataType.encode(args, encodingRules); }).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.'); }); }); @@ -960,7 +961,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { // Note: There will be padding because this is a bytes32 but we are only passing in 4 bytes. const args = 'five'; // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x00000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -977,7 +978,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const bytesLength = 40; const args = 'a'.repeat(bytesLength); // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000002861616161616161616161616161616161616161616161616161616161616161616161616161616161000000000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); @@ -994,7 +995,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => { const strLength = 40; const args = '0x' + 'a'.repeat(strLength); // Encode Args and validate result - const encodedArgs = dataType.encode(args); + const encodedArgs = dataType.encode(args, encodingRules); const expectedEncodedArgs = '0x000000000000000000000000000000000000000000000000000000000000002a30786161616161616161616161616161616161616161616161616161616161616161616161616161616100000000000000000000000000000000000000000000'; expect(encodedArgs).to.be.equal(expectedEncodedArgs); diff --git a/packages/utils/test/abi_encoder/methods_test.ts b/packages/utils/test/abi_encoder/methods_test.ts index c870f4f04..837020883 100644 --- a/packages/utils/test/abi_encoder/methods_test.ts +++ b/packages/utils/test/abi_encoder/methods_test.ts @@ -10,11 +10,12 @@ chaiSetup.configure(); const expect = chai.expect; describe('ABI Encoder: Method Encoding / Decoding', () => { + const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately. it('Types with default widths', async () => { // Generate calldata const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi); const args = [new BigNumber(1), new BigNumber(-1), '0x56', [new BigNumber(1)], [new BigNumber(-1)], ['0x56']]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x09f2b0c30000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000015600000000000000000000000000000000000000000000000000000000000000'; @@ -33,7 +34,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); } const args = [arrayOfTuples]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x9eb20969000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; @@ -52,7 +53,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value)]); } const args = [arrayOfTuples]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x63275d6e00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010'; @@ -71,7 +72,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]); } const args = [arrayOfTuples]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0xdeedb00f00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; @@ -90,7 +91,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { arrayOfTuples.push([new BigNumber(++value), new BigNumber(++value).toString()]); } const args = [arrayOfTuples]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x60c847fb000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000013400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001360000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000138000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023130000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023132000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000023136000000000000000000000000000000000000000000000000000000000000'; @@ -112,7 +113,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { [[new BigNumber(++value), new BigNumber(++value)], [new BigNumber(++value), new BigNumber(++value)]], ]); } - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0xc2f47d6f00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000d400000000000000000000000000000000000000000000000000000000000000e600000000000000000000000000000000000000000000000000000000000000039000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000130000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001500000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001700000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000025000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000027000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000029000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002f0000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000003100000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000033000000000000000000000000000000000000000000000000000000000000003400000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000035000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000038'; @@ -141,7 +142,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { ], ]); } - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x81534ebd0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000013300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023131000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000231320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002313300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023134000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000024000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000231370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002313800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023139000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000023231000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000232320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000232370000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002323800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002323900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002333100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023332000000000000000000000000000000000000000000000000000000000000'; @@ -154,7 +155,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { // Generate calldata const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); const args = [['Brave', 'New', 'World']]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; @@ -167,7 +168,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { // Generaet calldata const method = new AbiEncoder.Method(AbiSamples.staticArrayDynamicMembersAbi); const args = [['Brave', 'New', 'World']]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x243a6e6e0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; @@ -180,7 +181,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { // Generate calldata const method = new AbiEncoder.Method(AbiSamples.dynamicArrayDynamicMembersAbi); const args = [['Brave', 'New', 'World']]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000005427261766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034e657700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; @@ -193,7 +194,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { // Generate calldata const method = new AbiEncoder.Method(AbiSamples.dynamicArrayStaticMembersAbi); const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x4fc8a83300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; @@ -206,7 +207,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { // Generate calldata const method = new AbiEncoder.Method(AbiSamples.staticArrayAbi); const args = [[new BigNumber(127), new BigNumber(14), new BigNumber(54)]]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0xf68ade72000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036'; @@ -219,7 +220,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { // Generate calldata const method = new AbiEncoder.Method(AbiSamples.stringAbi); const args = [['five', 'six', 'seven']]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000373697800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005736576656e000000000000000000000000000000000000000000000000000000'; @@ -233,7 +234,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { // This is dynamic because it has dynamic members const method = new AbiEncoder.Method(AbiSamples.staticTupleAbi); const args = [[new BigNumber(5), new BigNumber(10), new BigNumber(15), false]]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0xa9125e150000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000'; @@ -247,7 +248,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { // This is dynamic because it has dynamic members const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); const args = [[new BigNumber(5), 'five']]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; @@ -261,7 +262,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { // This is dynamic because it has dynamic members const method = new AbiEncoder.Method(AbiSamples.dynamicTupleAbi); const args = [[new BigNumber(5), 'five']]; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x5b998f3500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000046669766500000000000000000000000000000000000000000000000000000000'; @@ -285,7 +286,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { true, ]; // Validate calldata - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); const expectedCalldata = '0x312d4d42000000000000000000000000000000000000000000000000000000000f4d9feefffffffffffffffffffffffffffffffffffffffffffffffffffffffff0b26012000000000000000000000000000000000000000000000000000000000006a0444300000000000000000000000000000000000000000000000000000000000000000102030405060708091112131415161718192021222324252627282930313200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000180000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f4980000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000003800010203040506070809111213141516171819202122232425262728293031320809111213141516171819202122232425262728293031320000000000000000000000000000000000000000000000000000000000000000000000000000002c4c6974746c65207065746572207069706572207069706564206120706970696e672070657070657220706f740000000000000000000000000000000000000000'; expect(calldata).to.be.equal(expectedCalldata); @@ -353,7 +354,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => { someTupleWithDynamicTypes, someArrayOfTuplesWithDynamicTypes, }; - const calldata = method.encode(args); + const calldata = method.encode(args, encodingRules); // Validate calldata const expectedCalldata = '0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000'; diff --git a/packages/utils/test/abi_encoder/optimizer_test.ts b/packages/utils/test/abi_encoder/optimizer_test.ts index 27443fe48..18aa6549a 100644 --- a/packages/utils/test/abi_encoder/optimizer_test.ts +++ b/packages/utils/test/abi_encoder/optimizer_test.ts @@ -10,6 +10,7 @@ chaiSetup.configure(); const expect = chai.expect; describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { + const encodingRules: AbiEncoder.EncodingRules = { optimize: true }; it('Duplicate Dynamic Arrays with Static Elements', async () => { // Generate calldata const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithStaticElements); @@ -17,7 +18,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const array2 = array1; const args = [array1, array2]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x7221063300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -32,7 +33,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const array2 = array1; const args = [array1, array2]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0xbb4f12e300000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -47,7 +48,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const array2 = array1; const args = [array1, array2]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x7f8130430000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000096'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -64,7 +65,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const array2 = array1; const args = [array1, array2]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x9fe31f8e0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -78,7 +79,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const strings = ['Hello', 'World', 'Hello', 'World']; const args = [strings]; // Validate calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x13e751a900000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -92,7 +93,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const tuple = ['Hello', 'Hello']; const args = [tuple]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x16780a5e000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -109,7 +110,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const method = new AbiEncoder.Method(OptimizedAbis.duplicateStrings); const args = ['Hello', 'Hello']; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x07370bfa00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -127,7 +128,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const value = '0x01020304050607080910111213141516171819202122232425262728293031323334353637383940'; const args = [value, value]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x6045e42900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002801020304050607080910111213141516171819202122232425262728293031323334353637383940000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -142,7 +143,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const tuple2 = tuple1; const args = [tuple1, tuple2]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x564f826d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000006792a000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c642100000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -158,7 +159,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const tuple2 = [tuple1[0], new BigNumber(2)]; const args = [tuple1, tuple2]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x564f826d000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c642100000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -174,7 +175,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const tuple2 = [array, 'extra argument to prevent exactly matching the tuples']; const args = [tuple1, tuple2]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x18970a9e000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c80000000000000000000000000000000000000000000000000000000000000035657874726120617267756d656e7420746f2070726576656e742065786163746c79206d61746368696e6720746865207475706c65730000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -190,7 +191,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const tuple2 = [nestedTuple, 'extra argument to prevent exactly matching the tuples']; const args = [tuple1, tuple2]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x0b4d2e6a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035657874726120617267756d656e7420746f2070726576656e742065786163746c79206d61746368696e6720746865207475706c65730000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -220,7 +221,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const twoDimArray2 = [['Hello', 'World'], ['Bar']]; const args = [twoDimArray1, twoDimArray2]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x0d28c4f900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003466f6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000034261720000000000000000000000000000000000000000000000000000000000'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -235,7 +236,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const tuple = [[array[0]], [array[1]], [array[2]], [array[3]]]; const args = [array, tuple]; // Validata calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0x5b5c78fd0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000009600000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000e1'; expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata); @@ -250,7 +251,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => { const str = 'Hello'; const args = [array, str]; // Validate calldata - const optimizedCalldata = method.encode(args, { optimize: true }); + const optimizedCalldata = method.encode(args, encodingRules); const expectedOptimizedCalldata = '0xe0e0d34900000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000'; 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 3215509f1..a8cdd6ca3 100644 --- a/packages/utils/test/abi_encoder/return_values_test.ts +++ b/packages/utils/test/abi_encoder/return_values_test.ts @@ -10,6 +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. it('No Return Value', async () => { // Decode return value const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues); @@ -22,7 +23,7 @@ describe('ABI Encoder: Return Value Encoding/Decoding', () => { // Generate Return Value const method = new AbiEncoder.Method(ReturnValueAbis.singleStaticReturnValue); const returnValue = ['0x01020304']; - const encodedReturnValue = method.encodeReturnValues(returnValue); + const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules); const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); // Validate decoded return value expect(decodedReturnValue).to.be.deep.equal(returnValue); @@ -31,7 +32,7 @@ describe('ABI Encoder: Return Value Encoding/Decoding', () => { // Generate Return Value const method = new AbiEncoder.Method(ReturnValueAbis.multipleStaticReturnValues); const returnValue = ['0x01020304', '0x05060708']; - const encodedReturnValue = method.encodeReturnValues(returnValue); + const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules); const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); // Validate decoded return value expect(decodedReturnValue).to.be.deep.equal(returnValue); @@ -40,7 +41,7 @@ describe('ABI Encoder: Return Value Encoding/Decoding', () => { // Generate Return Value const method = new AbiEncoder.Method(ReturnValueAbis.singleDynamicReturnValue); const returnValue = ['0x01020304']; - const encodedReturnValue = method.encodeReturnValues(returnValue); + const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules); const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); // Validate decoded return value expect(decodedReturnValue).to.be.deep.equal(returnValue); @@ -49,7 +50,7 @@ describe('ABI Encoder: Return Value Encoding/Decoding', () => { // Generate Return Value const method = new AbiEncoder.Method(ReturnValueAbis.multipleDynamicReturnValues); const returnValue = ['0x01020304', '0x05060708']; - const encodedReturnValue = method.encodeReturnValues(returnValue); + const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules); const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); // Validate decoded return value expect(decodedReturnValue).to.be.deep.equal(returnValue); @@ -58,7 +59,7 @@ describe('ABI Encoder: Return Value Encoding/Decoding', () => { // Generate Return Value const method = new AbiEncoder.Method(ReturnValueAbis.mixedStaticAndDynamicReturnValues); const returnValue = ['0x01020304', '0x05060708']; - const encodedReturnValue = method.encodeReturnValues(returnValue); + const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules); const decodedReturnValue = method.decodeReturnValues(encodedReturnValue); // Validate decoded return value expect(decodedReturnValue).to.be.deep.equal(returnValue); -- cgit v1.2.3 From 5c13353fb2512411c0f2c8cba9395235188f5df8 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 27 Nov 2018 17:24:14 -0800 Subject: Optimize calldata by default. --- packages/utils/src/abi_encoder/utils/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/utils/src/abi_encoder/utils/constants.ts b/packages/utils/src/abi_encoder/utils/constants.ts index 82eeab010..acc06329c 100644 --- a/packages/utils/src/abi_encoder/utils/constants.ts +++ b/packages/utils/src/abi_encoder/utils/constants.ts @@ -10,5 +10,5 @@ export const constants = { HEX_SELECTOR_LENGTH_IN_BYTES: 4, HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA: 0, DEFAULT_DECODING_RULES: { structsAsObjects: false } as DecodingRules, - DEFAULT_ENCODING_RULES: { optimize: false, annotate: false } as EncodingRules, + DEFAULT_ENCODING_RULES: { optimize: true, annotate: false } as EncodingRules, }; -- cgit v1.2.3 From a172ab158e2eaca8256ef881c3f2d4098987ec8a Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 28 Nov 2018 13:22:18 -0800 Subject: Explicit imports for EVM Data Types --- .../utils/src/abi_encoder/evm_data_type_factory.ts | 38 ++++++++++++++-------- .../src/abi_encoder/evm_data_types/address.ts | 10 +++--- .../utils/src/abi_encoder/evm_data_types/array.ts | 8 ++--- .../utils/src/abi_encoder/evm_data_types/bool.ts | 6 ++-- .../abi_encoder/evm_data_types/dynamic_bytes.ts | 10 +++--- .../utils/src/abi_encoder/evm_data_types/index.ts | 11 ------- .../utils/src/abi_encoder/evm_data_types/int.ts | 16 ++++----- .../utils/src/abi_encoder/evm_data_types/method.ts | 6 ++-- .../src/abi_encoder/evm_data_types/pointer.ts | 2 +- .../src/abi_encoder/evm_data_types/static_bytes.ts | 14 ++++---- .../utils/src/abi_encoder/evm_data_types/string.ts | 6 ++-- .../utils/src/abi_encoder/evm_data_types/tuple.ts | 4 +-- .../utils/src/abi_encoder/evm_data_types/uint.ts | 20 ++++++------ packages/utils/src/abi_encoder/index.ts | 14 +++++++- 14 files changed, 88 insertions(+), 77 deletions(-) delete mode 100644 packages/utils/src/abi_encoder/evm_data_types/index.ts diff --git a/packages/utils/src/abi_encoder/evm_data_type_factory.ts b/packages/utils/src/abi_encoder/evm_data_type_factory.ts index bc68e05b7..4d04d4ed7 100644 --- a/packages/utils/src/abi_encoder/evm_data_type_factory.ts +++ b/packages/utils/src/abi_encoder/evm_data_type_factory.ts @@ -3,69 +3,79 @@ import { DataItem, MethodAbi } from 'ethereum-types'; import * as _ from 'lodash'; import { DataType, DataTypeFactory } from './abstract_data_types'; -import * as Impl from './evm_data_types'; - -export class Address extends Impl.Address { +import { AddressDataType } from './evm_data_types/address'; +import { ArrayDataType } from './evm_data_types/array'; +import { BoolDataType } from './evm_data_types/bool'; +import { DynamicBytesDataType } from './evm_data_types/dynamic_bytes'; +import { IntDataType } from './evm_data_types/int'; +import { MethodDataType } from './evm_data_types/method'; +import { PointerDataType } from './evm_data_types/pointer'; +import { StaticBytesDataType } from './evm_data_types/static_bytes'; +import { StringDataType } from './evm_data_types/string'; +import { TupleDataType } from './evm_data_types/tuple'; +import { UIntDataType } from './evm_data_types/uint'; + +export class Address extends AddressDataType { public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance()); } } -export class Bool extends Impl.Bool { +export class Bool extends BoolDataType { public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance()); } } -export class Int extends Impl.Int { +export class Int extends IntDataType { public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance()); } } -export class UInt extends Impl.UInt { +export class UInt extends UIntDataType { public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance()); } } -export class StaticBytes extends Impl.StaticBytes { +export class StaticBytes extends StaticBytesDataType { public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance()); } } -export class DynamicBytes extends Impl.DynamicBytes { +export class DynamicBytes extends DynamicBytesDataType { public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance()); } } -export class String extends Impl.String { +export class String extends StringDataType { public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance()); } } -export class Pointer extends Impl.Pointer { +export class Pointer extends PointerDataType { public constructor(destDataType: DataType, parentDataType: DataType) { super(destDataType, parentDataType, EvmDataTypeFactory.getInstance()); } } -export class Tuple extends Impl.Tuple { +export class Tuple extends TupleDataType { public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance()); } } -export class Array extends Impl.Array { +export class Array extends ArrayDataType { public constructor(dataItem: DataItem) { super(dataItem, EvmDataTypeFactory.getInstance()); } } -export class Method extends Impl.Method { +export class Method extends MethodDataType { public constructor(abi: MethodAbi) { super(abi, EvmDataTypeFactory.getInstance()); } @@ -105,7 +115,7 @@ export class EvmDataTypeFactory implements DataTypeFactory { } else if (String.matchType(dataItem.type)) { dataType = new String(dataItem); } - // @TODO: Implement Fixed/UFixed types + // @TODO: DataTypeement Fixed/UFixed types if (_.isUndefined(dataType)) { throw new Error(`Unrecognized data type: '${dataItem.type}'`); } else if (!_.isUndefined(parentDataType) && !dataType.isStatic()) { diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index c45355639..769c5a81c 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -6,19 +6,19 @@ import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; -export class Address extends AbstractDataTypes.Blob { +export class AddressDataType extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _ADDRESS_SIZE_IN_BYTES = 20; private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = constants.EVM_WORD_WIDTH_IN_BYTES - - Address._ADDRESS_SIZE_IN_BYTES; + AddressDataType._ADDRESS_SIZE_IN_BYTES; public static matchType(type: string): boolean { return type === SolidityTypes.Address; } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - super(dataItem, dataTypeFactory, Address._SIZE_KNOWN_AT_COMPILE_TIME); - if (!Address.matchType(dataItem.type)) { + super(dataItem, dataTypeFactory, AddressDataType._SIZE_KNOWN_AT_COMPILE_TIME); + if (!AddressDataType.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Address with bad input: ${dataItem}`); } } @@ -36,7 +36,7 @@ export class Address extends AbstractDataTypes.Blob { public decodeValue(calldata: RawCalldata): string { const valueBufPadded = calldata.popWord(); - const valueBuf = valueBufPadded.slice(Address._DECODED_ADDRESS_OFFSET_IN_BYTES); + const valueBuf = valueBufPadded.slice(AddressDataType._DECODED_ADDRESS_OFFSET_IN_BYTES); const value = ethUtil.bufferToHex(valueBuf); return value; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts index 272cc4132..1736bcef0 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/array.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -4,17 +4,17 @@ import * as _ from 'lodash'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { constants } from '../utils/constants'; -export class Array extends AbstractDataTypes.Set { +export class ArrayDataType extends AbstractDataTypes.Set { private static readonly _MATCHER = RegExp('^(.+)\\[([0-9]*)\\]$'); private readonly _arraySignature: string; private readonly _elementType: string; public static matchType(type: string): boolean { - return Array._MATCHER.test(type); + return ArrayDataType._MATCHER.test(type); } private static _decodeElementTypeAndLengthFromType(type: string): [string, undefined | number] { - const matches = Array._MATCHER.exec(type); + const matches = ArrayDataType._MATCHER.exec(type); if (_.isNull(matches) || matches.length !== 3) { throw new Error(`Could not parse array: ${type}`); } else if (_.isUndefined(matches[1])) { @@ -30,7 +30,7 @@ export class Array extends AbstractDataTypes.Set { public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { // Construct parent const isArray = true; - const [arrayElementType, arrayLength] = Array._decodeElementTypeAndLengthFromType(dataItem.type); + const [arrayElementType, arrayLength] = ArrayDataType._decodeElementTypeAndLengthFromType(dataItem.type); super(dataItem, dataTypeFactory, isArray, arrayLength, arrayElementType); // Set array properties this._elementType = arrayElementType; diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index 0c29f690a..32eda9c39 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -7,7 +7,7 @@ import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; -export class Bool extends AbstractDataTypes.Blob { +export class BoolDataType extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; public static matchType(type: string): boolean { @@ -15,8 +15,8 @@ export class Bool extends AbstractDataTypes.Blob { } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - super(dataItem, dataTypeFactory, Bool._SIZE_KNOWN_AT_COMPILE_TIME); - if (!Bool.matchType(dataItem.type)) { + super(dataItem, dataTypeFactory, BoolDataType._SIZE_KNOWN_AT_COMPILE_TIME); + if (!BoolDataType.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Bool with bad input: ${dataItem}`); } } diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index 2c256cfa9..c8ff47c3d 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -6,7 +6,7 @@ import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; -export class DynamicBytes extends AbstractDataTypes.Blob { +export class DynamicBytesDataType extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; public static matchType(type: string): boolean { @@ -25,8 +25,8 @@ export class DynamicBytes extends AbstractDataTypes.Blob { } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - super(dataItem, dataTypeFactory, DynamicBytes._SIZE_KNOWN_AT_COMPILE_TIME); - if (!DynamicBytes.matchType(dataItem.type)) { + super(dataItem, dataTypeFactory, DynamicBytesDataType._SIZE_KNOWN_AT_COMPILE_TIME); + if (!DynamicBytesDataType.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Dynamic Bytes with bad input: ${dataItem}`); } } @@ -42,7 +42,7 @@ export class DynamicBytes extends AbstractDataTypes.Blob { const lengthBuf = ethUtil.toBuffer(valueBuf.byteLength); const lengthBufPadded = ethUtil.setLengthLeft(lengthBuf, constants.EVM_WORD_WIDTH_IN_BYTES); // 2/3 Construct the value - DynamicBytes._sanityCheckValue(value); + DynamicBytesDataType._sanityCheckValue(value); const valueBufPadded = ethUtil.setLengthRight(valueBuf, bytesToStoreValuePadded); // 3/3 Combine length and value const encodedValue = Buffer.concat([lengthBufPadded, valueBufPadded]); @@ -60,7 +60,7 @@ export class DynamicBytes extends AbstractDataTypes.Blob { const valueBufPadded = calldata.popWords(wordsToStoreValuePadded); const valueBuf = valueBufPadded.slice(0, length); const value = ethUtil.bufferToHex(valueBuf); - DynamicBytes._sanityCheckValue(value); + DynamicBytesDataType._sanityCheckValue(value); return value; } diff --git a/packages/utils/src/abi_encoder/evm_data_types/index.ts b/packages/utils/src/abi_encoder/evm_data_types/index.ts deleted file mode 100644 index fc0edabf1..000000000 --- a/packages/utils/src/abi_encoder/evm_data_types/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export * from './address'; -export * from './bool'; -export * from './int'; -export * from './uint'; -export * from './static_bytes'; -export * from './dynamic_bytes'; -export * from './string'; -export * from './pointer'; -export * from './tuple'; -export * from './array'; -export * from './method'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index 244b720e3..aee8320a6 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -7,36 +7,36 @@ import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; import * as EncoderMath from '../utils/math'; -export class Int extends AbstractDataTypes.Blob { +export class IntDataType extends AbstractDataTypes.Blob { private static readonly _MATCHER = RegExp( '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _MAX_WIDTH: number = 256; - private static readonly _DEFAULT_WIDTH: number = Int._MAX_WIDTH; + private static readonly _DEFAULT_WIDTH: number = IntDataType._MAX_WIDTH; private readonly _width: number; private readonly _minValue: BigNumber; private readonly _maxValue: BigNumber; public static matchType(type: string): boolean { - return Int._MATCHER.test(type); + return IntDataType._MATCHER.test(type); } private static _decodeWidthFromType(type: string): number { - const matches = Int._MATCHER.exec(type); + const matches = IntDataType._MATCHER.exec(type); const width = !_.isNull(matches) && matches.length === 2 && !_.isUndefined(matches[1]) ? parseInt(matches[1], constants.DEC_BASE) - : Int._DEFAULT_WIDTH; + : IntDataType._DEFAULT_WIDTH; return width; } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - super(dataItem, dataTypeFactory, Int._SIZE_KNOWN_AT_COMPILE_TIME); - if (!Int.matchType(dataItem.type)) { + super(dataItem, dataTypeFactory, IntDataType._SIZE_KNOWN_AT_COMPILE_TIME); + if (!IntDataType.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Int with bad input: ${dataItem}`); } - this._width = Int._decodeWidthFromType(dataItem.type); + this._width = IntDataType._decodeWidthFromType(dataItem.type); this._minValue = new BigNumber(2).toPower(this._width - 1).times(-1); this._maxValue = new BigNumber(2).toPower(this._width - 1).sub(1); } diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts index 7256a93d9..de8809edf 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/method.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts @@ -6,9 +6,9 @@ import { AbstractDataTypes, DataType, DataTypeFactory } from '../abstract_data_t import { constants } from '../utils/constants'; import { DecodingRules, EncodingRules } from '../utils/rules'; -import { Tuple } from './tuple'; +import { TupleDataType } from './tuple'; -export class Method extends AbstractDataTypes.Set { +export class MethodDataType extends AbstractDataTypes.Set { private readonly _methodSignature: string; private readonly _methodSelector: string; private readonly _returnDataType: DataType; @@ -19,7 +19,7 @@ export class Method extends AbstractDataTypes.Set { this._methodSignature = this._computeSignature(); this._methodSelector = this._computeSelector(); const returnDataItem: DataItem = { type: 'tuple', name: abi.name, components: abi.outputs }; - this._returnDataType = new Tuple(returnDataItem, this.getFactory()); + this._returnDataType = new TupleDataType(returnDataItem, this.getFactory()); } public encode(value: any, rules?: EncodingRules): string { diff --git a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts index e7c172afb..7ca428760 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts @@ -2,7 +2,7 @@ import { DataItem } from 'ethereum-types'; import { AbstractDataTypes, DataType, DataTypeFactory } from '../abstract_data_types'; -export class Pointer extends AbstractDataTypes.Pointer { +export class PointerDataType extends AbstractDataTypes.Pointer { constructor(destDataType: DataType, parentDataType: DataType, dataTypeFactory: DataTypeFactory) { const destDataItem = destDataType.getDataItem(); const dataItem: DataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` }; diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 5453d47a0..3b270630e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -6,7 +6,7 @@ import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; -export class StaticBytes extends AbstractDataTypes.Blob { +export class StaticBytesDataType extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _MATCHER = RegExp( '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', @@ -15,24 +15,24 @@ export class StaticBytes extends AbstractDataTypes.Blob { private readonly _width: number; public static matchType(type: string): boolean { - return StaticBytes._MATCHER.test(type); + return StaticBytesDataType._MATCHER.test(type); } private static _decodeWidthFromType(type: string): number { - const matches = StaticBytes._MATCHER.exec(type); + const matches = StaticBytesDataType._MATCHER.exec(type); const width = !_.isNull(matches) && matches.length === 3 && !_.isUndefined(matches[2]) ? parseInt(matches[2], constants.DEC_BASE) - : StaticBytes._DEFAULT_WIDTH; + : StaticBytesDataType._DEFAULT_WIDTH; return width; } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - super(dataItem, dataTypeFactory, StaticBytes._SIZE_KNOWN_AT_COMPILE_TIME); - if (!StaticBytes.matchType(dataItem.type)) { + super(dataItem, dataTypeFactory, StaticBytesDataType._SIZE_KNOWN_AT_COMPILE_TIME); + if (!StaticBytesDataType.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Static Bytes with bad input: ${dataItem}`); } - this._width = StaticBytes._decodeWidthFromType(dataItem.type); + this._width = StaticBytesDataType._decodeWidthFromType(dataItem.type); } public getSignature(): string { diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index ac62ea264..d7e9ec7fe 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -6,7 +6,7 @@ import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; -export class String extends AbstractDataTypes.Blob { +export class StringDataType extends AbstractDataTypes.Blob { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; public static matchType(type: string): boolean { @@ -14,8 +14,8 @@ export class String extends AbstractDataTypes.Blob { } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - super(dataItem, dataTypeFactory, String._SIZE_KNOWN_AT_COMPILE_TIME); - if (!String.matchType(dataItem.type)) { + super(dataItem, dataTypeFactory, StringDataType._SIZE_KNOWN_AT_COMPILE_TIME); + if (!StringDataType.matchType(dataItem.type)) { throw new Error(`Tried to instantiate String with bad input: ${dataItem}`); } } diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts index 40859f62e..5ba875b9e 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts @@ -2,7 +2,7 @@ import { DataItem, SolidityTypes } from 'ethereum-types'; import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; -export class Tuple extends AbstractDataTypes.Set { +export class TupleDataType extends AbstractDataTypes.Set { private readonly _signature: string; public static matchType(type: string): boolean { @@ -11,7 +11,7 @@ export class Tuple extends AbstractDataTypes.Set { public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { super(dataItem, dataTypeFactory); - if (!Tuple.matchType(dataItem.type)) { + if (!TupleDataType.matchType(dataItem.type)) { throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); } this._signature = this._computeSignatureOfMembers(); diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index df7ea38a4..a5989ea11 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -7,47 +7,47 @@ import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; import * as EncoderMath from '../utils/math'; -export class UInt extends AbstractDataTypes.Blob { +export class UIntDataType extends AbstractDataTypes.Blob { private static readonly _MATCHER = RegExp( '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _MAX_WIDTH: number = 256; - private static readonly _DEFAULT_WIDTH: number = UInt._MAX_WIDTH; + private static readonly _DEFAULT_WIDTH: number = UIntDataType._MAX_WIDTH; private static readonly _MIN_VALUE = new BigNumber(0); private readonly _width: number; private readonly _maxValue: BigNumber; public static matchType(type: string): boolean { - return UInt._MATCHER.test(type); + return UIntDataType._MATCHER.test(type); } private static _decodeWidthFromType(type: string): number { - const matches = UInt._MATCHER.exec(type); + const matches = UIntDataType._MATCHER.exec(type); const width = !_.isNull(matches) && matches.length === 2 && !_.isUndefined(matches[1]) ? parseInt(matches[1], constants.DEC_BASE) - : UInt._DEFAULT_WIDTH; + : UIntDataType._DEFAULT_WIDTH; return width; } public constructor(dataItem: DataItem, dataTypeFactory: DataTypeFactory) { - super(dataItem, dataTypeFactory, UInt._SIZE_KNOWN_AT_COMPILE_TIME); - if (!UInt.matchType(dataItem.type)) { + super(dataItem, dataTypeFactory, UIntDataType._SIZE_KNOWN_AT_COMPILE_TIME); + if (!UIntDataType.matchType(dataItem.type)) { throw new Error(`Tried to instantiate UInt with bad input: ${dataItem}`); } - this._width = UInt._decodeWidthFromType(dataItem.type); + this._width = UIntDataType._decodeWidthFromType(dataItem.type); this._maxValue = new BigNumber(2).toPower(this._width).sub(1); } public encodeValue(value: BigNumber | string | number): Buffer { - const encodedValue = EncoderMath.safeEncodeNumericValue(value, UInt._MIN_VALUE, this._maxValue); + const encodedValue = EncoderMath.safeEncodeNumericValue(value, UIntDataType._MIN_VALUE, this._maxValue); return encodedValue; } public decodeValue(calldata: RawCalldata): BigNumber { const valueBuf = calldata.popWord(); - const value = EncoderMath.safeDecodeNumericValue(valueBuf, UInt._MIN_VALUE, this._maxValue); + const value = EncoderMath.safeDecodeNumericValue(valueBuf, UIntDataType._MIN_VALUE, this._maxValue); return value; } diff --git a/packages/utils/src/abi_encoder/index.ts b/packages/utils/src/abi_encoder/index.ts index ea037b40a..baf844ac6 100644 --- a/packages/utils/src/abi_encoder/index.ts +++ b/packages/utils/src/abi_encoder/index.ts @@ -1,2 +1,14 @@ export { EncodingRules, DecodingRules } from './utils/rules'; -export * from './evm_data_type_factory'; +export { + Address, + Array, + Bool, + DynamicBytes, + Int, + Method, + Pointer, + StaticBytes, + String, + Tuple, + UInt, +} from './evm_data_type_factory'; -- cgit v1.2.3 From b8ea322541e291b84f261bffcc77baf85dae08c1 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 28 Nov 2018 13:35:53 -0800 Subject: Explicit imports for abstract data types. --- packages/utils/src/abi_encoder/abstract_data_types/index.ts | 4 ---- packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts | 2 +- packages/utils/src/abi_encoder/abstract_data_types/types/index.ts | 3 --- packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts | 2 +- packages/utils/src/abi_encoder/abstract_data_types/types/set.ts | 6 +++--- packages/utils/src/abi_encoder/evm_data_type_factory.ts | 3 ++- packages/utils/src/abi_encoder/evm_data_types/address.ts | 5 +++-- packages/utils/src/abi_encoder/evm_data_types/array.ts | 5 +++-- packages/utils/src/abi_encoder/evm_data_types/bool.ts | 5 +++-- packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts | 5 +++-- packages/utils/src/abi_encoder/evm_data_types/int.ts | 5 +++-- packages/utils/src/abi_encoder/evm_data_types/method.ts | 6 ++++-- packages/utils/src/abi_encoder/evm_data_types/pointer.ts | 6 ++++-- packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts | 5 +++-- packages/utils/src/abi_encoder/evm_data_types/string.ts | 5 +++-- packages/utils/src/abi_encoder/evm_data_types/tuple.ts | 5 +++-- packages/utils/src/abi_encoder/evm_data_types/uint.ts | 5 +++-- 17 files changed, 42 insertions(+), 35 deletions(-) delete mode 100644 packages/utils/src/abi_encoder/abstract_data_types/index.ts delete mode 100644 packages/utils/src/abi_encoder/abstract_data_types/types/index.ts diff --git a/packages/utils/src/abi_encoder/abstract_data_types/index.ts b/packages/utils/src/abi_encoder/abstract_data_types/index.ts deleted file mode 100644 index d1c7d93e4..000000000 --- a/packages/utils/src/abi_encoder/abstract_data_types/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './interfaces'; -export * from './data_type'; -import * as AbstractDataTypes from './types'; -export { AbstractDataTypes }; diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts index 965738056..cd2119673 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts @@ -7,7 +7,7 @@ import { DecodingRules } from '../../utils/rules'; import { DataType } from '../data_type'; import { DataTypeFactory } from '../interfaces'; -export abstract class Blob extends DataType { +export abstract class AbstractBlobDataType extends DataType { protected _sizeKnownAtCompileTime: boolean; public constructor(dataItem: DataItem, factory: DataTypeFactory, sizeKnownAtCompileTime: boolean) { diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/index.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/index.ts deleted file mode 100644 index 958582dae..000000000 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './blob'; -export * from './pointer'; -export * from './set'; diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts index 3d38deb94..b6a6a7613 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts @@ -9,7 +9,7 @@ import { DecodingRules } from '../../utils/rules'; import { DataType } from '../data_type'; import { DataTypeFactory } from '../interfaces'; -export abstract class Pointer extends DataType { +export abstract class AbstractPointerDataType extends DataType { protected _destination: DataType; protected _parent: DataType; 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 f50ed8298..4c8bb7b1a 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 @@ -10,9 +10,9 @@ import { DecodingRules } from '../../utils/rules'; import { DataType } from '../data_type'; import { DataTypeFactory, MemberIndexByName } from '../interfaces'; -import { Pointer } from './pointer'; +import { AbstractPointerDataType } from './pointer'; -export abstract class Set extends DataType { +export abstract class AbstractSetDataType extends DataType { protected readonly _arrayLength: number | undefined; protected readonly _arrayElementType: string | undefined; private readonly _memberIndexByName: MemberIndexByName; @@ -88,7 +88,7 @@ export abstract class Set extends DataType { } // If any member of the set is a pointer then the set is not static. const dependentMember = _.find(this._members, (member: DataType) => { - return member instanceof Pointer; + return member instanceof AbstractPointerDataType; }); const isStatic = _.isUndefined(dependentMember); return isStatic; diff --git a/packages/utils/src/abi_encoder/evm_data_type_factory.ts b/packages/utils/src/abi_encoder/evm_data_type_factory.ts index 4d04d4ed7..4cc124e0a 100644 --- a/packages/utils/src/abi_encoder/evm_data_type_factory.ts +++ b/packages/utils/src/abi_encoder/evm_data_type_factory.ts @@ -2,7 +2,8 @@ import { DataItem, MethodAbi } from 'ethereum-types'; import * as _ from 'lodash'; -import { DataType, DataTypeFactory } from './abstract_data_types'; +import { DataType } from './abstract_data_types/data_type'; +import { DataTypeFactory } from './abstract_data_types/interfaces'; import { AddressDataType } from './evm_data_types/address'; import { ArrayDataType } from './evm_data_types/array'; import { BoolDataType } from './evm_data_types/bool'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 769c5a81c..17363b5f3 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -2,11 +2,12 @@ import { DataItem, SolidityTypes } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; +import { DataTypeFactory } from '../abstract_data_types/interfaces'; +import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; -export class AddressDataType extends AbstractDataTypes.Blob { +export class AddressDataType extends AbstractBlobDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _ADDRESS_SIZE_IN_BYTES = 20; private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES = constants.EVM_WORD_WIDTH_IN_BYTES - diff --git a/packages/utils/src/abi_encoder/evm_data_types/array.ts b/packages/utils/src/abi_encoder/evm_data_types/array.ts index 1736bcef0..7595cb667 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/array.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/array.ts @@ -1,10 +1,11 @@ import { DataItem } from 'ethereum-types'; import * as _ from 'lodash'; -import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; +import { DataTypeFactory } from '../abstract_data_types/interfaces'; +import { AbstractSetDataType } from '../abstract_data_types/types/set'; import { constants } from '../utils/constants'; -export class ArrayDataType extends AbstractDataTypes.Set { +export class ArrayDataType extends AbstractSetDataType { private static readonly _MATCHER = RegExp('^(.+)\\[([0-9]*)\\]$'); private readonly _arraySignature: string; private readonly _elementType: string; diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index 32eda9c39..778a01d8a 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -3,11 +3,12 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; -import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; +import { DataTypeFactory } from '../abstract_data_types/interfaces'; +import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; -export class BoolDataType extends AbstractDataTypes.Blob { +export class BoolDataType extends AbstractBlobDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; public static matchType(type: string): boolean { diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index c8ff47c3d..ac8e2b716 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -2,11 +2,12 @@ import { DataItem, SolidityTypes } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; +import { DataTypeFactory } from '../abstract_data_types/interfaces'; +import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; -export class DynamicBytesDataType extends AbstractDataTypes.Blob { +export class DynamicBytesDataType extends AbstractBlobDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; public static matchType(type: string): boolean { diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index aee8320a6..6b5c3bf78 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -2,12 +2,13 @@ import { DataItem, SolidityTypes } from 'ethereum-types'; import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; -import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; +import { DataTypeFactory } from '../abstract_data_types/interfaces'; +import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; import * as EncoderMath from '../utils/math'; -export class IntDataType extends AbstractDataTypes.Blob { +export class IntDataType extends AbstractBlobDataType { private static readonly _MATCHER = RegExp( '^int(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); diff --git a/packages/utils/src/abi_encoder/evm_data_types/method.ts b/packages/utils/src/abi_encoder/evm_data_types/method.ts index de8809edf..b1cd1377f 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/method.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/method.ts @@ -2,13 +2,15 @@ import { DataItem, MethodAbi } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { AbstractDataTypes, DataType, DataTypeFactory } from '../abstract_data_types'; +import { DataType } from '../abstract_data_types/data_type'; +import { DataTypeFactory } from '../abstract_data_types/interfaces'; +import { AbstractSetDataType } from '../abstract_data_types/types/set'; import { constants } from '../utils/constants'; import { DecodingRules, EncodingRules } from '../utils/rules'; import { TupleDataType } from './tuple'; -export class MethodDataType extends AbstractDataTypes.Set { +export class MethodDataType extends AbstractSetDataType { private readonly _methodSignature: string; private readonly _methodSelector: string; private readonly _returnDataType: DataType; diff --git a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts index 7ca428760..389e75927 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts @@ -1,8 +1,10 @@ import { DataItem } from 'ethereum-types'; -import { AbstractDataTypes, DataType, DataTypeFactory } from '../abstract_data_types'; +import { DataType } from '../abstract_data_types/data_type'; +import { DataTypeFactory } from '../abstract_data_types/interfaces'; +import { AbstractPointerDataType } from '../abstract_data_types/types/pointer'; -export class PointerDataType extends AbstractDataTypes.Pointer { +export class PointerDataType extends AbstractPointerDataType { constructor(destDataType: DataType, parentDataType: DataType, dataTypeFactory: DataTypeFactory) { const destDataItem = destDataType.getDataItem(); const dataItem: DataItem = { name: `ptr<${destDataItem.name}>`, type: `ptr<${destDataItem.type}>` }; diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 3b270630e..28584d445 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -2,11 +2,12 @@ import { DataItem, SolidityTypes } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; +import { DataTypeFactory } from '../abstract_data_types/interfaces'; +import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; -export class StaticBytesDataType extends AbstractDataTypes.Blob { +export class StaticBytesDataType extends AbstractBlobDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true; private static readonly _MATCHER = RegExp( '^(byte|bytes(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32))$', diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index d7e9ec7fe..7b6af747b 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -2,11 +2,12 @@ import { DataItem, SolidityTypes } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; +import { DataTypeFactory } from '../abstract_data_types/interfaces'; +import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; -export class StringDataType extends AbstractDataTypes.Blob { +export class StringDataType extends AbstractBlobDataType { private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false; public static matchType(type: string): boolean { diff --git a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts index 5ba875b9e..31593c882 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/tuple.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/tuple.ts @@ -1,8 +1,9 @@ import { DataItem, SolidityTypes } from 'ethereum-types'; -import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; +import { DataTypeFactory } from '../abstract_data_types/interfaces'; +import { AbstractSetDataType } from '../abstract_data_types/types/set'; -export class TupleDataType extends AbstractDataTypes.Set { +export class TupleDataType extends AbstractSetDataType { private readonly _signature: string; public static matchType(type: string): boolean { diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index a5989ea11..45cb366f7 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -2,12 +2,13 @@ import { DataItem, SolidityTypes } from 'ethereum-types'; import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; -import { AbstractDataTypes, DataTypeFactory } from '../abstract_data_types'; +import { DataTypeFactory } from '../abstract_data_types/interfaces'; +import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; import { RawCalldata } from '../calldata'; import { constants } from '../utils/constants'; import * as EncoderMath from '../utils/math'; -export class UIntDataType extends AbstractDataTypes.Blob { +export class UIntDataType extends AbstractBlobDataType { private static readonly _MATCHER = RegExp( '^uint(8|16|24|32|40|48|56|64|72|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256){0,1}$', ); -- cgit v1.2.3 From 2da7cadefa877ff824da8fbaecd59dbff5028728 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 28 Nov 2018 13:47:01 -0800 Subject: Explicit imports for calldata --- .../utils/src/abi_encoder/abstract_data_types/data_type.ts | 4 +++- .../src/abi_encoder/abstract_data_types/interfaces.ts | 2 +- .../src/abi_encoder/abstract_data_types/types/blob.ts | 8 +++++--- .../src/abi_encoder/abstract_data_types/types/pointer.ts | 8 +++++--- .../utils/src/abi_encoder/abstract_data_types/types/set.ts | 14 ++++++++------ packages/utils/src/abi_encoder/calldata/blocks/blob.ts | 2 +- packages/utils/src/abi_encoder/calldata/blocks/index.ts | 3 --- packages/utils/src/abi_encoder/calldata/blocks/pointer.ts | 10 +++++----- packages/utils/src/abi_encoder/calldata/blocks/set.ts | 2 +- packages/utils/src/abi_encoder/calldata/calldata.ts | 7 ++++--- packages/utils/src/abi_encoder/calldata/index.ts | 5 ----- packages/utils/src/abi_encoder/calldata/iterator.ts | 10 ++++++---- packages/utils/src/abi_encoder/evm_data_types/address.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/bool.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/int.ts | 2 +- .../utils/src/abi_encoder/evm_data_types/static_bytes.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/string.ts | 2 +- packages/utils/src/abi_encoder/evm_data_types/uint.ts | 2 +- 19 files changed, 46 insertions(+), 43 deletions(-) delete mode 100644 packages/utils/src/abi_encoder/calldata/blocks/index.ts delete mode 100644 packages/utils/src/abi_encoder/calldata/index.ts diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts index 10e5c2540..13cc87e2a 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts @@ -1,7 +1,9 @@ import { DataItem } from 'ethereum-types'; import * as _ from 'lodash'; -import { Calldata, CalldataBlock, RawCalldata } from '../calldata'; +import { Calldata } from '../calldata/calldata'; +import { CalldataBlock } from '../calldata/calldata_block'; +import { RawCalldata } from '../calldata/raw_calldata'; import { constants } from '../utils/constants'; import { DecodingRules, EncodingRules } from '../utils/rules'; diff --git a/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts b/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts index bd4d2effd..2f2f60871 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/interfaces.ts @@ -1,6 +1,6 @@ import { DataItem } from 'ethereum-types'; -import { RawCalldata } from '../calldata'; +import { RawCalldata } from '../calldata/raw_calldata'; import { DataType } from './data_type'; diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts index cd2119673..a091e55b9 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/blob.ts @@ -1,7 +1,9 @@ import { DataItem } from 'ethereum-types'; import * as _ from 'lodash'; -import { CalldataBlock, CalldataBlocks, RawCalldata } from '../../calldata'; +import { BlobCalldataBlock } from '../../calldata/blocks/blob'; +import { CalldataBlock } from '../../calldata/calldata_block'; +import { RawCalldata } from '../../calldata/raw_calldata'; import { DecodingRules } from '../../utils/rules'; import { DataType } from '../data_type'; @@ -15,12 +17,12 @@ export abstract class AbstractBlobDataType extends DataType { this._sizeKnownAtCompileTime = sizeKnownAtCompileTime; } - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlocks.Blob { + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): BlobCalldataBlock { const encodedValue = this.encodeValue(value); const name = this.getDataItem().name; const signature = this.getSignature(); const parentName = _.isUndefined(parentBlock) ? '' : parentBlock.getName(); - const block = new CalldataBlocks.Blob(name, signature, parentName, encodedValue); + const block = new BlobCalldataBlock(name, signature, parentName, encodedValue); return block; } diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts index b6a6a7613..0f3c55280 100644 --- a/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts +++ b/packages/utils/src/abi_encoder/abstract_data_types/types/pointer.ts @@ -2,7 +2,9 @@ import { DataItem } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { CalldataBlock, CalldataBlocks, RawCalldata } from '../../calldata'; +import { PointerCalldataBlock } from '../../calldata/blocks/pointer'; +import { CalldataBlock } from '../../calldata/calldata_block'; +import { RawCalldata } from '../../calldata/raw_calldata'; import { constants } from '../../utils/constants'; import { DecodingRules } from '../../utils/rules'; @@ -19,7 +21,7 @@ export abstract class AbstractPointerDataType extends DataType { this._parent = parent; } - public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlocks.Pointer { + public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PointerCalldataBlock { if (_.isUndefined(parentBlock)) { throw new Error(`DependentDataType requires a parent block to generate its block`); } @@ -27,7 +29,7 @@ export abstract class AbstractPointerDataType extends DataType { const name = this.getDataItem().name; const signature = this.getSignature(); const parentName = parentBlock.getName(); - const block = new CalldataBlocks.Pointer(name, signature, parentName, destinationBlock, parentBlock); + const block = new PointerCalldataBlock(name, signature, parentName, destinationBlock, parentBlock); return block; } 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 4c8bb7b1a..bbe29eff0 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 @@ -3,7 +3,9 @@ import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { BigNumber } from '../../../configured_bignumber'; -import { CalldataBlock, CalldataBlocks, RawCalldata } from '../../calldata'; +import { SetCalldataBlock } from '../../calldata/blocks/set'; +import { CalldataBlock } from '../../calldata/calldata_block'; +import { RawCalldata } from '../../calldata/raw_calldata'; import { constants } from '../../utils/constants'; import { DecodingRules } from '../../utils/rules'; @@ -39,7 +41,7 @@ export abstract class AbstractSetDataType extends DataType { } } - public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): CalldataBlocks.Set { + public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): SetCalldataBlock { const block = value instanceof Array ? this._generateCalldataBlockFromArray(value, parentBlock) @@ -94,7 +96,7 @@ export abstract class AbstractSetDataType extends DataType { return isStatic; } - protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): CalldataBlocks.Set { + protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): SetCalldataBlock { // Sanity check: if the set has a defined length then `value` must have the same length. if (!_.isUndefined(this._arrayLength) && value.length !== this._arrayLength) { throw new Error( @@ -105,7 +107,7 @@ export abstract class AbstractSetDataType extends DataType { } // Create a new calldata block for this set. const parentName = _.isUndefined(parentBlock) ? '' : parentBlock.getName(); - const block: CalldataBlocks.Set = new CalldataBlocks.Set( + const block = new SetCalldataBlock( this.getDataItem().name, this.getSignature(), parentName, @@ -130,10 +132,10 @@ export abstract class AbstractSetDataType extends DataType { return block; } - protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): CalldataBlocks.Set { + protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): SetCalldataBlock { // Create a new calldata block for this set. const parentName = _.isUndefined(parentBlock) ? '' : parentBlock.getName(); - const block: CalldataBlocks.Set = new CalldataBlocks.Set( + const block = new SetCalldataBlock( this.getDataItem().name, this.getSignature(), parentName, diff --git a/packages/utils/src/abi_encoder/calldata/blocks/blob.ts b/packages/utils/src/abi_encoder/calldata/blocks/blob.ts index 210ef6420..219ea6c61 100644 --- a/packages/utils/src/abi_encoder/calldata/blocks/blob.ts +++ b/packages/utils/src/abi_encoder/calldata/blocks/blob.ts @@ -1,6 +1,6 @@ import { CalldataBlock } from '../calldata_block'; -export class Blob extends CalldataBlock { +export class BlobCalldataBlock extends CalldataBlock { private readonly _blob: Buffer; constructor(name: string, signature: string, parentName: string, blob: Buffer) { diff --git a/packages/utils/src/abi_encoder/calldata/blocks/index.ts b/packages/utils/src/abi_encoder/calldata/blocks/index.ts deleted file mode 100644 index 958582dae..000000000 --- a/packages/utils/src/abi_encoder/calldata/blocks/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './blob'; -export * from './pointer'; -export * from './set'; diff --git a/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts index 1daf33f7e..c706fe908 100644 --- a/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts +++ b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts @@ -5,7 +5,7 @@ import { constants } from '../../utils/constants'; import { CalldataBlock } from '../calldata_block'; -export class Pointer extends CalldataBlock { +export class PointerCalldataBlock extends CalldataBlock { public static readonly RAW_DATA_START = new Buffer('<'); public static readonly RAW_DATA_END = new Buffer('>'); private static readonly _DEPENDENT_PAYLOAD_SIZE_IN_BYTES = 32; @@ -15,8 +15,8 @@ export class Pointer extends CalldataBlock { private _aliasFor: CalldataBlock | undefined; constructor(name: string, signature: string, parentName: string, dependency: CalldataBlock, parent: CalldataBlock) { - const headerSizeInBytes = Pointer._EMPTY_HEADER_SIZE; - const bodySizeInBytes = Pointer._DEPENDENT_PAYLOAD_SIZE_IN_BYTES; + const headerSizeInBytes = PointerCalldataBlock._EMPTY_HEADER_SIZE; + const bodySizeInBytes = PointerCalldataBlock._DEPENDENT_PAYLOAD_SIZE_IN_BYTES; super(name, signature, parentName, headerSizeInBytes, bodySizeInBytes); this._parent = parent; this._dependency = dependency; @@ -51,9 +51,9 @@ export class Pointer extends CalldataBlock { public getRawData(): Buffer { const dependencyRawData = this._dependency.getRawData(); const rawDataComponents: Buffer[] = []; - rawDataComponents.push(Pointer.RAW_DATA_START); + rawDataComponents.push(PointerCalldataBlock.RAW_DATA_START); rawDataComponents.push(dependencyRawData); - rawDataComponents.push(Pointer.RAW_DATA_END); + rawDataComponents.push(PointerCalldataBlock.RAW_DATA_END); const rawData = Buffer.concat(rawDataComponents); return rawData; } diff --git a/packages/utils/src/abi_encoder/calldata/blocks/set.ts b/packages/utils/src/abi_encoder/calldata/blocks/set.ts index 81455b364..d1abc4986 100644 --- a/packages/utils/src/abi_encoder/calldata/blocks/set.ts +++ b/packages/utils/src/abi_encoder/calldata/blocks/set.ts @@ -2,7 +2,7 @@ import * as _ from 'lodash'; import { CalldataBlock } from '../calldata_block'; -export class Set extends CalldataBlock { +export class SetCalldataBlock extends CalldataBlock { private _header: Buffer | undefined; private _members: CalldataBlock[]; diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts index e5858b524..5f3eee94a 100644 --- a/packages/utils/src/abi_encoder/calldata/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata/calldata.ts @@ -4,7 +4,8 @@ import * as _ from 'lodash'; import { constants } from '../utils/constants'; import { EncodingRules } from '../utils/rules'; -import * as CalldataBlocks from './blocks'; +import { PointerCalldataBlock } from './blocks/pointer'; +import { SetCalldataBlock } from './blocks/set'; import { CalldataBlock } from './calldata_block'; import { CalldataIterator, ReverseCalldataIterator } from './iterator'; @@ -112,7 +113,7 @@ export class Calldata { for (const block of iterator) { // If a block is a pointer and its value has already been observed, then update // the pointer to resolve to the existing value. - if (block instanceof CalldataBlocks.Pointer) { + if (block instanceof PointerCalldataBlock) { const dependencyBlockHashBuf = block.getDependency().computeHash(); const dependencyBlockHash = ethUtil.bufferToHex(dependencyBlockHashBuf); if (dependencyBlockHash in blocksByHash) { @@ -214,7 +215,7 @@ export class Calldata { ), ) .padEnd(valuePadding); - if (block instanceof CalldataBlocks.Set) { + if (block instanceof SetCalldataBlock) { nameStr = `### ${prettyName.padEnd(namePadding)}`; lineStr = `\n${offsetStr}${valueStr}${nameStr}`; } else { diff --git a/packages/utils/src/abi_encoder/calldata/index.ts b/packages/utils/src/abi_encoder/calldata/index.ts deleted file mode 100644 index 2ef75e8d0..000000000 --- a/packages/utils/src/abi_encoder/calldata/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './calldata'; -export * from './calldata_block'; -export * from './raw_calldata'; -import * as CalldataBlocks from './blocks'; -export { CalldataBlocks }; diff --git a/packages/utils/src/abi_encoder/calldata/iterator.ts b/packages/utils/src/abi_encoder/calldata/iterator.ts index 5307f7944..333b32b4f 100644 --- a/packages/utils/src/abi_encoder/calldata/iterator.ts +++ b/packages/utils/src/abi_encoder/calldata/iterator.ts @@ -3,7 +3,9 @@ import * as _ from 'lodash'; import { Queue } from '../utils/queue'; -import * as CalldataBlocks from './blocks'; +import { BlobCalldataBlock } from './blocks/blob'; +import { PointerCalldataBlock } from './blocks/pointer'; +import { SetCalldataBlock } from './blocks/set'; import { CalldataBlock } from './calldata_block'; /** @@ -42,7 +44,7 @@ abstract class BaseIterator implements Iterable { private static _createQueue(block: CalldataBlock): Queue { const queue = new Queue(); // Base case - if (!(block instanceof CalldataBlocks.Set)) { + if (!(block instanceof SetCalldataBlock)) { queue.pushBack(block); return queue; } @@ -55,7 +57,7 @@ abstract class BaseIterator implements Iterable { _.each(set.getMembers(), (member: CalldataBlock) => { // Traverse child if it is a unique pointer. // A pointer that is an alias for another pointer is ignored. - if (member instanceof CalldataBlocks.Pointer && _.isUndefined(member.getAlias())) { + if (member instanceof PointerCalldataBlock && _.isUndefined(member.getAlias())) { const dependency = member.getDependency(); queue.mergeBack(BaseIterator._createQueue(dependency)); } @@ -82,7 +84,7 @@ abstract class BaseIterator implements Iterable { } return { done: true, - value: new CalldataBlocks.Blob('', '', '', new Buffer('')), + value: new BlobCalldataBlock('', '', '', new Buffer('')), }; }, }; diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts index 17363b5f3..88846b1fa 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/address.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { DataTypeFactory } from '../abstract_data_types/interfaces'; import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; -import { RawCalldata } from '../calldata'; +import { RawCalldata } from '../calldata/raw_calldata'; import { constants } from '../utils/constants'; export class AddressDataType extends AbstractBlobDataType { diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts index 778a01d8a..d713d5a94 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts @@ -5,7 +5,7 @@ import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { DataTypeFactory } from '../abstract_data_types/interfaces'; import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; -import { RawCalldata } from '../calldata'; +import { RawCalldata } from '../calldata/raw_calldata'; import { constants } from '../utils/constants'; export class BoolDataType extends AbstractBlobDataType { diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts index ac8e2b716..5277efd6c 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { DataTypeFactory } from '../abstract_data_types/interfaces'; import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; -import { RawCalldata } from '../calldata'; +import { RawCalldata } from '../calldata/raw_calldata'; import { constants } from '../utils/constants'; export class DynamicBytesDataType extends AbstractBlobDataType { diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts index 6b5c3bf78..f1dcf5ea1 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/int.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { DataTypeFactory } from '../abstract_data_types/interfaces'; import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; -import { RawCalldata } from '../calldata'; +import { RawCalldata } from '../calldata/raw_calldata'; import { constants } from '../utils/constants'; import * as EncoderMath from '../utils/math'; diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts index 28584d445..2e371c505 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { DataTypeFactory } from '../abstract_data_types/interfaces'; import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; -import { RawCalldata } from '../calldata'; +import { RawCalldata } from '../calldata/raw_calldata'; import { constants } from '../utils/constants'; export class StaticBytesDataType extends AbstractBlobDataType { diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts index 7b6af747b..91a72ad3f 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/string.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { DataTypeFactory } from '../abstract_data_types/interfaces'; import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; -import { RawCalldata } from '../calldata'; +import { RawCalldata } from '../calldata/raw_calldata'; import { constants } from '../utils/constants'; export class StringDataType extends AbstractBlobDataType { diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts index 45cb366f7..5180f0cf3 100644 --- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts +++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts @@ -4,7 +4,7 @@ import * as _ from 'lodash'; import { BigNumber } from '../../configured_bignumber'; import { DataTypeFactory } from '../abstract_data_types/interfaces'; import { AbstractBlobDataType } from '../abstract_data_types/types/blob'; -import { RawCalldata } from '../calldata'; +import { RawCalldata } from '../calldata/raw_calldata'; import { constants } from '../utils/constants'; import * as EncoderMath from '../utils/math'; -- cgit v1.2.3 From 04503200e5eafda6617039b113ef26cf48184f62 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 28 Nov 2018 13:52:32 -0800 Subject: Linter / Prettier --- .../src/abi_encoder/abstract_data_types/types/set.ts | 18 +++++++----------- .../utils/src/abi_encoder/calldata/blocks/pointer.ts | 5 +++-- packages/utils/src/abi_encoder/utils/constants.ts | 3 +++ 3 files changed, 13 insertions(+), 13 deletions(-) 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 bbe29eff0..089d04659 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 @@ -107,11 +107,7 @@ export abstract class AbstractSetDataType extends DataType { } // Create a new calldata block for this set. const parentName = _.isUndefined(parentBlock) ? '' : parentBlock.getName(); - const block = new SetCalldataBlock( - this.getDataItem().name, - this.getSignature(), - parentName, - ); + const block = new SetCalldataBlock(this.getDataItem().name, this.getSignature(), parentName); // If this set has an undefined length then set its header to be the number of elements. let members = this._members; if (this._isArray && _.isUndefined(this._arrayLength)) { @@ -135,11 +131,7 @@ export abstract class AbstractSetDataType extends DataType { protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): SetCalldataBlock { // Create a new calldata block for this set. const parentName = _.isUndefined(parentBlock) ? '' : parentBlock.getName(); - const block = new SetCalldataBlock( - this.getDataItem().name, - this.getSignature(), - parentName, - ); + const block = new SetCalldataBlock(this.getDataItem().name, this.getSignature(), parentName); // Create blocks for members of set. const memberCalldataBlocks: CalldataBlock[] = []; const childMap = _.cloneDeep(this._memberIndexByName); @@ -178,7 +170,11 @@ export abstract class AbstractSetDataType extends DataType { private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberIndexByName] { // Sanity check if (_.isUndefined(dataItem.components)) { - throw new Error(`Tried to create a set using key/value pairs, but no components were defined by the input DataItem '${dataItem.name}'.`); + throw new Error( + `Tried to create a set using key/value pairs, but no components were defined by the input DataItem '${ + dataItem.name + }'.`, + ); } // Create one member for each component of `dataItem` const members: DataType[] = []; diff --git a/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts index c706fe908..72d6a3173 100644 --- a/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts +++ b/packages/utils/src/abi_encoder/calldata/blocks/pointer.ts @@ -24,8 +24,9 @@ export class PointerCalldataBlock extends CalldataBlock { } public toBuffer(): Buffer { - const destinationOffset = - !_.isUndefined(this._aliasFor) ? this._aliasFor.getOffsetInBytes() : this._dependency.getOffsetInBytes(); + const destinationOffset = !_.isUndefined(this._aliasFor) + ? this._aliasFor.getOffsetInBytes() + : this._dependency.getOffsetInBytes(); const parentOffset = this._parent.getOffsetInBytes(); const parentHeaderSize = this._parent.getHeaderSizeInBytes(); const pointer: number = destinationOffset - (parentOffset + parentHeaderSize); diff --git a/packages/utils/src/abi_encoder/utils/constants.ts b/packages/utils/src/abi_encoder/utils/constants.ts index acc06329c..2f43ba04d 100644 --- a/packages/utils/src/abi_encoder/utils/constants.ts +++ b/packages/utils/src/abi_encoder/utils/constants.ts @@ -9,6 +9,9 @@ export const constants = { HEX_SELECTOR_LENGTH_IN_CHARS: 10, HEX_SELECTOR_LENGTH_IN_BYTES: 4, 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, + /* tslint:enable no-object-literal-type-assertion */ }; -- cgit v1.2.3 From b15531fe683df906b4988e63bbca2b3ebfbfc6b2 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 28 Nov 2018 15:50:36 -0800 Subject: Changelog for ABI Encoder --- packages/utils/CHANGELOG.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index 8c6fb124f..bbaf67062 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1543448882, + "version": "2.0.7", + "changes": [ + { + "note": "Optimized ABI Encoder/Decoder. Generates compressed calldata to save gas. Generates human-readable calldata to aid development." + } + ] + }, { "timestamp": 1542821676, "version": "2.0.6", -- cgit v1.2.3 From bcb2af2861952c91ea4fc02462f52d8bc37bac5d Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Wed, 28 Nov 2018 17:30:02 -0800 Subject: Ran prettier --- packages/utils/CHANGELOG.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json index bbaf67062..08801a891 100644 --- a/packages/utils/CHANGELOG.json +++ b/packages/utils/CHANGELOG.json @@ -4,7 +4,8 @@ "version": "2.0.7", "changes": [ { - "note": "Optimized ABI Encoder/Decoder. Generates compressed calldata to save gas. Generates human-readable calldata to aid development." + "note": + "Optimized ABI Encoder/Decoder. Generates compressed calldata to save gas. Generates human-readable calldata to aid development." } ] }, -- cgit v1.2.3 From 5c66f9117fa0bf6a5be29243dbecbfe016b95345 Mon Sep 17 00:00:00 2001 From: Steve Klebanoff Date: Thu, 29 Nov 2018 08:19:39 -0800 Subject: assetAmount -> assetBuyAmount --- packages/instant/src/util/analytics.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/instant/src/util/analytics.ts b/packages/instant/src/util/analytics.ts index 1468ef4a8..4b8aff4c9 100644 --- a/packages/instant/src/util/analytics.ts +++ b/packages/instant/src/util/analytics.ts @@ -206,10 +206,10 @@ export const analytics = { ...buyQuoteEventProperties(buyQuote), fetchOrigin, }), - trackQuoteError: (errorMessage: string, assetAmount: BigNumber, fetchOrigin: QuoteFetchOrigin) => { + trackQuoteError: (errorMessage: string, assetBuyAmount: BigNumber, fetchOrigin: QuoteFetchOrigin) => { trackingEventFnWithPayload(EventNames.QUOTE_ERROR)({ errorMessage, - assetAmount: assetAmount.toString(), + assetBuyAmount: assetBuyAmount.toString(), fetchOrigin, }); }, -- cgit v1.2.3 From b68273e592dc7736d21608e2543ba6bffdb03db2 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 29 Nov 2018 17:30:21 +0000 Subject: Fix import export so that it works with doc gen --- packages/utils/src/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index f59cbec8c..082aff6bb 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -10,5 +10,4 @@ export { NULL_BYTES } from './constants'; export { errorUtils } from './error_utils'; export { fetchAsync } from './fetch_async'; export { signTypedDataUtils } from './sign_typed_data_utils'; -import * as AbiEncoder from './abi_encoder'; -export { AbiEncoder }; +export import AbiEncoder = require('./abi_encoder'); -- cgit v1.2.3