aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--contracts/protocol/test/asset_proxy/proxies.ts32
-rw-r--r--contracts/protocol/test/exchange/core.ts31
-rw-r--r--contracts/tokens/src/artifacts/index.ts1
-rw-r--r--packages/0x.js/CHANGELOG.json10
-rw-r--r--packages/0x.js/src/index.ts5
-rw-r--r--packages/contract-wrappers/CHANGELOG.json6
-rw-r--r--packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts74
-rw-r--r--packages/fill-scenarios/CHANGELOG.json9
-rw-r--r--packages/fill-scenarios/src/fill_scenarios.ts63
-rw-r--r--packages/order-utils/CHANGELOG.json10
-rw-r--r--packages/order-utils/src/asset_data_utils.ts293
-rw-r--r--packages/order-utils/src/constants.ts66
-rw-r--r--packages/order-utils/src/exchange_transfer_simulator.ts64
-rw-r--r--packages/order-utils/src/index.ts5
-rw-r--r--packages/order-utils/src/order_state_utils.ts56
-rw-r--r--packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts2
-rw-r--r--packages/order-utils/test/asset_data_utils_test.ts116
-rw-r--r--packages/order-utils/test/utils/test_order_factory.ts4
-rw-r--r--packages/order-watcher/CHANGELOG.json9
-rw-r--r--packages/order-watcher/src/index.ts1
-rw-r--r--packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts62
-rw-r--r--packages/order-watcher/src/order_watcher/order_watcher.ts93
-rw-r--r--packages/order-watcher/test/order_watcher_test.ts208
-rw-r--r--packages/order-watcher/test/order_watcher_web_socket_server_test.ts2
-rw-r--r--packages/pipeline/package.json2
-rw-r--r--packages/pipeline/src/entities/token_order.ts3
-rw-r--r--packages/pipeline/src/parsers/ddex_orders/index.ts8
-rw-r--r--packages/pipeline/src/parsers/events/exchange_events.ts38
-rw-r--r--packages/pipeline/src/parsers/idex_orders/index.ts12
-rw-r--r--packages/pipeline/src/parsers/oasis_orders/index.ts8
-rw-r--r--packages/pipeline/src/parsers/paradex_orders/index.ts4
-rw-r--r--packages/pipeline/src/parsers/sra_orders/index.ts20
-rw-r--r--packages/pipeline/src/types.ts11
-rw-r--r--packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts20
-rw-r--r--packages/pipeline/src/utils/transformers/index.ts1
-rw-r--r--packages/pipeline/test/parsers/ddex_orders/index_test.ts4
-rw-r--r--packages/pipeline/test/parsers/events/exchange_events_test.ts5
-rw-r--r--packages/pipeline/test/parsers/idex_orders/index_test.ts8
-rw-r--r--packages/pipeline/test/parsers/oasis_orders/index_test.ts4
-rw-r--r--packages/pipeline/test/parsers/paradex_orders/index_test.ts4
-rw-r--r--packages/pipeline/test/parsers/sra_orders/index_test.ts5
-rw-r--r--packages/sra-spec/public/index.html4
-rw-r--r--packages/types/CHANGELOG.json4
-rw-r--r--packages/types/src/index.ts19
-rw-r--r--packages/utils/CHANGELOG.json9
-rw-r--r--packages/utils/src/abi_encoder/abstract_data_types/types/set.ts2
-rw-r--r--packages/utils/src/abi_encoder/calldata/calldata.ts6
-rw-r--r--packages/utils/src/abi_encoder/utils/constants.ts4
-rw-r--r--packages/utils/src/abi_encoder/utils/rules.ts6
-rw-r--r--packages/utils/test/abi_encoder/evm_data_types_test.ts16
-rw-r--r--packages/utils/test/abi_encoder/methods_test.ts4
-rw-r--r--packages/utils/test/abi_encoder/optimizer_test.ts4
-rw-r--r--packages/utils/test/abi_encoder/return_values_test.ts2
-rw-r--r--yarn.lock43
54 files changed, 1106 insertions, 396 deletions
diff --git a/contracts/protocol/test/asset_proxy/proxies.ts b/contracts/protocol/test/asset_proxy/proxies.ts
index 89c8b390c..bbb44f402 100644
--- a/contracts/protocol/test/asset_proxy/proxies.ts
+++ b/contracts/protocol/test/asset_proxy/proxies.ts
@@ -715,7 +715,7 @@ describe('Asset Transfer Proxies', () => {
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -778,7 +778,7 @@ describe('Asset Transfer Proxies', () => {
const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -811,7 +811,7 @@ describe('Asset Transfer Proxies', () => {
const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address);
const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -849,7 +849,7 @@ describe('Asset Transfer Proxies', () => {
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const amounts = [erc721Amount];
const nestedAssetData = [erc721AssetData];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -881,7 +881,7 @@ describe('Asset Transfer Proxies', () => {
const erc721Amount = new BigNumber(1);
const amounts = [erc721Amount, erc721Amount];
const nestedAssetData = [erc721AssetData1, erc721AssetData2];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -913,7 +913,7 @@ describe('Asset Transfer Proxies', () => {
const erc721Amount = new BigNumber(1);
const amounts = [erc721Amount, erc721Amount];
const nestedAssetData = [erc721AssetData1, erc721AssetData2];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -946,7 +946,7 @@ describe('Asset Transfer Proxies', () => {
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -984,10 +984,7 @@ describe('Asset Transfer Proxies', () => {
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
const extraData = '0102030405060708';
- const assetData = `${assetDataInterface.MultiAsset.getABIEncodedTransactionData(
- amounts,
- nestedAssetData,
- )}${extraData}`;
+ const assetData = `${assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData)}${extraData}`;
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1024,7 +1021,7 @@ describe('Asset Transfer Proxies', () => {
const erc20AssetData2 = assetDataUtils.encodeERC20AssetData(erc20TokenB.address);
const amounts = [erc20Amount1, erc20Amount2];
const nestedAssetData = [erc20AssetData1, erc20AssetData2];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1085,7 +1082,7 @@ describe('Asset Transfer Proxies', () => {
erc721AssetData3,
erc721AssetData4,
];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1143,7 +1140,7 @@ describe('Asset Transfer Proxies', () => {
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1169,6 +1166,7 @@ describe('Asset Transfer Proxies', () => {
const invalidErc721AssetData = `${invalidProxyId}${erc721AssetData.slice(10)}`;
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, invalidErc721AssetData];
+ // HACK: This is used to get around validation built into assetDataUtils
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
@@ -1192,6 +1190,7 @@ describe('Asset Transfer Proxies', () => {
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
+ // HACK: This is used to get around validation built into assetDataUtils
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
@@ -1214,7 +1213,7 @@ describe('Asset Transfer Proxies', () => {
const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address);
const amounts = [erc20Amount];
const nestedAssetData = [erc20AssetData];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
@@ -1238,6 +1237,7 @@ describe('Asset Transfer Proxies', () => {
const erc721AssetData = '0x123456';
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
+ // HACK: This is used to get around validation built into assetDataUtils
const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
@@ -1262,7 +1262,7 @@ describe('Asset Transfer Proxies', () => {
const erc721AssetData = assetDataUtils.encodeERC721AssetData(erc721TokenA.address, erc721AFromTokenId);
const amounts = [erc20Amount, erc721Amount];
const nestedAssetData = [erc20AssetData, erc721AssetData];
- const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData);
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData(
assetData,
fromAddress,
diff --git a/contracts/protocol/test/exchange/core.ts b/contracts/protocol/test/exchange/core.ts
index 9b948f991..42db8623d 100644
--- a/contracts/protocol/test/exchange/core.ts
+++ b/contracts/protocol/test/exchange/core.ts
@@ -1,4 +1,3 @@
-import { artifacts as interfacesArtifacts, IAssetDataContract } from '@0x/contracts-interfaces';
import {
chaiSetup,
constants,
@@ -43,11 +42,6 @@ import { ExchangeWrapper } from '../utils/exchange_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
-const assetDataInterface = new IAssetDataContract(
- interfacesArtifacts.IAssetData.compilerOutput.abi,
- constants.NULL_ADDRESS,
- provider,
-);
// tslint:disable:no-unnecessary-type-assertion
describe('Exchange core', () => {
let makerAddress: string;
@@ -777,10 +771,7 @@ describe('Exchange core', () => {
assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
];
- const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(
- makerAmounts,
- makerNestedAssetData,
- );
+ const makerAssetData = assetDataUtils.encodeMultiAssetData(makerAmounts, makerNestedAssetData);
const makerAssetAmount = new BigNumber(1);
const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
const takerAssetAmount = new BigNumber(10);
@@ -830,10 +821,7 @@ describe('Exchange core', () => {
assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
];
- const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(
- makerAmounts,
- makerNestedAssetData,
- );
+ const makerAssetData = assetDataUtils.encodeMultiAssetData(makerAmounts, makerNestedAssetData);
const makerAssetAmount = new BigNumber(1);
const takerAmounts = [new BigNumber(10), new BigNumber(1)];
const takerAssetId = erc721TakerAssetIds[0];
@@ -841,10 +829,7 @@ describe('Exchange core', () => {
assetDataUtils.encodeERC20AssetData(zrxToken.address),
assetDataUtils.encodeERC721AssetData(erc721Token.address, takerAssetId),
];
- const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(
- takerAmounts,
- takerNestedAssetData,
- );
+ const takerAssetData = assetDataUtils.encodeMultiAssetData(takerAmounts, takerNestedAssetData);
const takerAssetAmount = new BigNumber(1);
signedOrder = await orderFactory.newSignedOrderAsync({
makerAssetData,
@@ -900,10 +885,7 @@ describe('Exchange core', () => {
assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
];
- const makerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(
- makerAmounts,
- makerNestedAssetData,
- );
+ const makerAssetData = assetDataUtils.encodeMultiAssetData(makerAmounts, makerNestedAssetData);
const makerAssetAmount = new BigNumber(30);
const takerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
const takerAssetAmount = new BigNumber(10);
@@ -980,10 +962,7 @@ describe('Exchange core', () => {
assetDataUtils.encodeERC20AssetData(erc20TokenA.address),
assetDataUtils.encodeERC20AssetData(erc20TokenB.address),
];
- const takerAssetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(
- takerAmounts,
- takerNestedAssetData,
- );
+ const takerAssetData = assetDataUtils.encodeMultiAssetData(takerAmounts, takerNestedAssetData);
const takerAssetAmount = new BigNumber(30);
const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address);
const makerAssetAmount = new BigNumber(10);
diff --git a/contracts/tokens/src/artifacts/index.ts b/contracts/tokens/src/artifacts/index.ts
index 63b808748..977d01c3e 100644
--- a/contracts/tokens/src/artifacts/index.ts
+++ b/contracts/tokens/src/artifacts/index.ts
@@ -22,6 +22,7 @@ import * as UnlimitedAllowanceToken_v1 from '../../generated-artifacts/Unlimited
import * as WETH9 from '../../generated-artifacts/WETH9.json';
import * as ZRXToken from '../../generated-artifacts/ZRXToken.json';
+// tslint:disable:no-unnecessary-type-assertion
export const artifacts = {
DummyERC20Token: DummyERC20Token as ContractArtifact,
DummyMultipleReturnERC20Token: DummyMultipleReturnERC20Token as ContractArtifact,
diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json
index 32351ad82..f73de96aa 100644
--- a/packages/0x.js/CHANGELOG.json
+++ b/packages/0x.js/CHANGELOG.json
@@ -1,5 +1,15 @@
[
{
+ "version": "3.0.0",
+ "changes": [
+ {
+ "note":
+ "Export `MultiAssetData`, `MultiAssetDataWithRecursiveDecoding`, `ObjectMap`, and `SingleAssetData` from types. No longer export `AssetData`.",
+ "pr": 1363
+ }
+ ]
+ },
+ {
"version": "2.0.8",
"changes": [
{
diff --git a/packages/0x.js/src/index.ts b/packages/0x.js/src/index.ts
index 2df360b96..d21df0c01 100644
--- a/packages/0x.js/src/index.ts
+++ b/packages/0x.js/src/index.ts
@@ -79,10 +79,13 @@ export {
OrderStateInvalid,
OrderState,
AssetProxyId,
- AssetData,
+ SingleAssetData,
ERC20AssetData,
ERC721AssetData,
+ MultiAssetData,
+ MultiAssetDataWithRecursiveDecoding,
SignatureType,
+ ObjectMap,
OrderRelevantState,
Stats,
} from '@0x/types';
diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json
index d39027797..581fbdee1 100644
--- a/packages/contract-wrappers/CHANGELOG.json
+++ b/packages/contract-wrappers/CHANGELOG.json
@@ -1,9 +1,13 @@
[
{
- "version": "4.1.4",
+ "version": "4.2.0",
"changes": [
{
"note": "Add support for Trust Wallet signature denial error"
+ },
+ {
+ "note": "Add balance and allowance queries for `MultiAssetProxy`",
+ "pr": 1363
}
]
},
diff --git a/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts b/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts
index d10cffe57..1ff130a48 100644
--- a/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts
+++ b/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts
@@ -1,8 +1,7 @@
-// tslint:disable:no-unnecessary-type-assertion
import { AbstractBalanceAndProxyAllowanceFetcher, assetDataUtils } from '@0x/order-utils';
-import { AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { BlockParamLiteral } from 'ethereum-types';
+import * as _ from 'lodash';
import { ERC20TokenWrapper } from '../contract_wrappers/erc20_token_wrapper';
import { ERC721TokenWrapper } from '../contract_wrappers/erc721_token_wrapper';
@@ -18,42 +17,45 @@ export class AssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndP
}
public async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
- if (decodedAssetData.assetProxyId === AssetProxyId.ERC20) {
- const decodedERC20AssetData = decodedAssetData as ERC20AssetData;
- const balance = await this._erc20Token.getBalanceAsync(decodedERC20AssetData.tokenAddress, userAddress, {
+ let balance: BigNumber | undefined;
+ if (assetDataUtils.isERC20AssetData(decodedAssetData)) {
+ balance = await this._erc20Token.getBalanceAsync(decodedAssetData.tokenAddress, userAddress, {
defaultBlock: this._stateLayer,
});
- return balance;
- } else {
- const decodedERC721AssetData = decodedAssetData as ERC721AssetData;
+ } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) {
const tokenOwner = await this._erc721Token.getOwnerOfAsync(
- decodedERC721AssetData.tokenAddress,
- decodedERC721AssetData.tokenId,
+ decodedAssetData.tokenAddress,
+ decodedAssetData.tokenId,
{
defaultBlock: this._stateLayer,
},
);
- const balance = tokenOwner === userAddress ? new BigNumber(1) : new BigNumber(0);
- return balance;
+ balance = tokenOwner === userAddress ? new BigNumber(1) : new BigNumber(0);
+ } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
+ // The `balance` for MultiAssetData is the total units of the entire `assetData` that are held by the `userAddress`.
+ for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) {
+ const nestedAmountElement = decodedAssetData.amounts[index];
+ const nestedAssetBalance = (await this.getBalanceAsync(
+ nestedAssetDataElement,
+ userAddress,
+ )).dividedToIntegerBy(nestedAmountElement);
+ if (_.isUndefined(balance) || nestedAssetBalance.lessThan(balance)) {
+ balance = nestedAssetBalance;
+ }
+ }
}
+ return balance as BigNumber;
}
public async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
- if (decodedAssetData.assetProxyId === AssetProxyId.ERC20) {
- const decodedERC20AssetData = decodedAssetData as ERC20AssetData;
- const proxyAllowance = await this._erc20Token.getProxyAllowanceAsync(
- decodedERC20AssetData.tokenAddress,
- userAddress,
- {
- defaultBlock: this._stateLayer,
- },
- );
- return proxyAllowance;
- } else {
- const decodedERC721AssetData = decodedAssetData as ERC721AssetData;
-
+ let proxyAllowance: BigNumber | undefined;
+ if (assetDataUtils.isERC20AssetData(decodedAssetData)) {
+ proxyAllowance = await this._erc20Token.getProxyAllowanceAsync(decodedAssetData.tokenAddress, userAddress, {
+ defaultBlock: this._stateLayer,
+ });
+ } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) {
const isApprovedForAll = await this._erc721Token.isProxyApprovedForAllAsync(
- decodedERC721AssetData.tokenAddress,
+ decodedAssetData.tokenAddress,
userAddress,
{
defaultBlock: this._stateLayer,
@@ -63,15 +65,27 @@ export class AssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndP
return new BigNumber(this._erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
} else {
const isApproved = await this._erc721Token.isProxyApprovedAsync(
- decodedERC721AssetData.tokenAddress,
- decodedERC721AssetData.tokenId,
+ decodedAssetData.tokenAddress,
+ decodedAssetData.tokenId,
{
defaultBlock: this._stateLayer,
},
);
- const proxyAllowance = isApproved ? new BigNumber(1) : new BigNumber(0);
- return proxyAllowance;
+ proxyAllowance = isApproved ? new BigNumber(1) : new BigNumber(0);
+ }
+ } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
+ // The `proxyAllowance` for MultiAssetData is the total units of the entire `assetData` that the proxies have been approved to spend by the `userAddress`.
+ for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) {
+ const nestedAmountElement = decodedAssetData.amounts[index];
+ const nestedAssetAllowance = (await this.getProxyAllowanceAsync(
+ nestedAssetDataElement,
+ userAddress,
+ )).dividedToIntegerBy(nestedAmountElement);
+ if (_.isUndefined(proxyAllowance) || nestedAssetAllowance.lessThan(proxyAllowance)) {
+ proxyAllowance = nestedAssetAllowance;
+ }
}
}
+ return proxyAllowance as BigNumber;
}
}
diff --git a/packages/fill-scenarios/CHANGELOG.json b/packages/fill-scenarios/CHANGELOG.json
index ca256399a..2c3e261cb 100644
--- a/packages/fill-scenarios/CHANGELOG.json
+++ b/packages/fill-scenarios/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "version": "1.1.0",
+ "changes": [
+ {
+ "note": "Add support for MultiAssetProxy",
+ "pr": 1363
+ }
+ ]
+ },
+ {
"version": "1.0.16",
"changes": [
{
diff --git a/packages/fill-scenarios/src/fill_scenarios.ts b/packages/fill-scenarios/src/fill_scenarios.ts
index 0154bcd0a..ce1f7f9ff 100644
--- a/packages/fill-scenarios/src/fill_scenarios.ts
+++ b/packages/fill-scenarios/src/fill_scenarios.ts
@@ -2,7 +2,7 @@ import { DummyERC20TokenContract, DummyERC721TokenContract, ExchangeContract } f
import * as artifacts from '@0x/contract-artifacts';
import { assetDataUtils } from '@0x/order-utils';
import { orderFactory } from '@0x/order-utils/lib/src/order_factory';
-import { AssetProxyId, ERC721AssetData, OrderWithoutExchangeAddress, SignedOrder } from '@0x/types';
+import { OrderWithoutExchangeAddress, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { Provider } from 'ethereum-types';
@@ -150,39 +150,8 @@ export class FillScenarios {
feeRecipientAddress: string,
expirationTimeSeconds?: BigNumber,
): Promise<SignedOrder> {
- const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(makerAssetData);
- if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) {
- await this._increaseERC20BalanceAndAllowanceAsync(
- decodedMakerAssetData.tokenAddress,
- makerAddress,
- makerFillableAmount,
- );
- } else {
- if (!makerFillableAmount.equals(1)) {
- throw new Error(`ERC721 makerFillableAmount should be equal 1. Found: ${makerFillableAmount}`);
- }
- await this._increaseERC721BalanceAndAllowanceAsync(
- decodedMakerAssetData.tokenAddress,
- makerAddress,
- // tslint:disable-next-line:no-unnecessary-type-assertion
- (decodedMakerAssetData as ERC721AssetData).tokenId,
- );
- }
- const decodedTakerAssetData = assetDataUtils.decodeAssetDataOrThrow(takerAssetData);
- if (decodedTakerAssetData.assetProxyId === AssetProxyId.ERC20) {
- const takerTokenAddress = decodedTakerAssetData.tokenAddress;
- await this._increaseERC20BalanceAndAllowanceAsync(takerTokenAddress, takerAddress, takerFillableAmount);
- } else {
- if (!takerFillableAmount.equals(1)) {
- throw new Error(`ERC721 takerFillableAmount should be equal 1. Found: ${takerFillableAmount}`);
- }
- await this._increaseERC721BalanceAndAllowanceAsync(
- decodedTakerAssetData.tokenAddress,
- takerAddress,
- // tslint:disable-next-line:no-unnecessary-type-assertion
- (decodedMakerAssetData as ERC721AssetData).tokenId,
- );
- }
+ await this._increaseBalanceAndAllowanceWithAssetDataAsync(makerAssetData, makerAddress, makerFillableAmount);
+ await this._increaseBalanceAndAllowanceWithAssetDataAsync(takerAssetData, takerAddress, takerFillableAmount);
// Fees
await Promise.all([
this._increaseERC20BalanceAndAllowanceAsync(this._zrxTokenAddress, makerAddress, makerFee),
@@ -298,4 +267,30 @@ export class FillScenarios {
});
await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
}
+ private async _increaseBalanceAndAllowanceWithAssetDataAsync(
+ assetData: string,
+ userAddress: string,
+ amount: BigNumber,
+ ): Promise<void> {
+ const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
+ if (assetDataUtils.isERC20AssetData(decodedAssetData)) {
+ await this._increaseERC20BalanceAndAllowanceAsync(decodedAssetData.tokenAddress, userAddress, amount);
+ } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) {
+ await this._increaseERC721BalanceAndAllowanceAsync(
+ decodedAssetData.tokenAddress,
+ userAddress,
+ decodedAssetData.tokenId,
+ );
+ } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
+ for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) {
+ const amountsElement = decodedAssetData.amounts[index];
+ const totalAmount = amount.times(amountsElement);
+ await this._increaseBalanceAndAllowanceWithAssetDataAsync(
+ nestedAssetDataElement,
+ userAddress,
+ totalAmount,
+ );
+ }
+ }
+ }
}
diff --git a/packages/order-utils/CHANGELOG.json b/packages/order-utils/CHANGELOG.json
index 11889b92e..292f7a57b 100644
--- a/packages/order-utils/CHANGELOG.json
+++ b/packages/order-utils/CHANGELOG.json
@@ -1,5 +1,15 @@
[
{
+ "version": "3.1.0",
+ "changes": [
+ {
+ "note":
+ "Use new ABI encoder, add encoding/decoding logic for MultiAsset assetData, and add information to return values in orderStateUtils",
+ "pr": 1363
+ }
+ ]
+ },
+ {
"version": "3.0.7",
"changes": [
{
diff --git a/packages/order-utils/src/asset_data_utils.ts b/packages/order-utils/src/asset_data_utils.ts
index 9bbef3a23..f314891e2 100644
--- a/packages/order-utils/src/asset_data_utils.ts
+++ b/packages/order-utils/src/asset_data_utils.ts
@@ -1,10 +1,19 @@
-import { AssetData, AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0x/types';
-import { BigNumber } from '@0x/utils';
-import ethAbi = require('ethereumjs-abi');
-import ethUtil = require('ethereumjs-util');
+import {
+ AssetProxyId,
+ ERC20AssetData,
+ ERC721AssetData,
+ MultiAssetData,
+ MultiAssetDataWithRecursiveDecoding,
+ SingleAssetData,
+} from '@0x/types';
+import { AbiEncoder, BigNumber } from '@0x/utils';
+import * as _ from 'lodash';
import { constants } from './constants';
+const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: true };
+const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true };
+
export const assetDataUtils = {
/**
* Encodes an ERC20 token address into a hex encoded assetData string, usable in the makerAssetData or
@@ -13,7 +22,10 @@ export const assetDataUtils = {
* @return The hex encoded assetData string
*/
encodeERC20AssetData(tokenAddress: string): string {
- return ethUtil.bufferToHex(ethAbi.simpleEncode('ERC20Token(address)', tokenAddress));
+ const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI);
+ const args = [tokenAddress];
+ const assetData = abiEncoder.encode(args, encodingRules);
+ return assetData;
},
/**
* Decodes an ERC20 assetData hex string into it's corresponding ERC20 tokenAddress & assetProxyId
@@ -21,26 +33,14 @@ export const assetDataUtils = {
* @return An object containing the decoded tokenAddress & assetProxyId
*/
decodeERC20AssetData(assetData: string): ERC20AssetData {
- const data = ethUtil.toBuffer(assetData);
- if (data.byteLength < constants.ERC20_ASSET_DATA_BYTE_LENGTH) {
- throw new Error(
- `Could not decode ERC20 Proxy Data. Expected length of encoded data to be at least ${
- constants.ERC20_ASSET_DATA_BYTE_LENGTH
- }. Got ${data.byteLength}`,
- );
- }
- const assetProxyId = ethUtil.bufferToHex(data.slice(0, constants.SELECTOR_LENGTH));
- if (assetProxyId !== AssetProxyId.ERC20) {
- throw new Error(
- `Could not decode ERC20 Proxy Data. Expected Asset Proxy Id to be ERC20 (${
- AssetProxyId.ERC20
- }), but got ${assetProxyId}`,
- );
- }
- const [tokenAddress] = ethAbi.rawDecode(['address'], data.slice(constants.SELECTOR_LENGTH));
+ assetDataUtils.assertIsERC20AssetData(assetData);
+ const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
+ const abiEncoder = new AbiEncoder.Method(constants.ERC20_METHOD_ABI);
+ const decodedAssetData = abiEncoder.decode(assetData, decodingRules);
return {
assetProxyId,
- tokenAddress: ethUtil.addHexPrefix(tokenAddress),
+ // TODO(abandeali1): fix return types for `AbiEncoder.Method.decode` so that we can remove type assertion
+ tokenAddress: (decodedAssetData as any).tokenContract,
};
},
/**
@@ -51,14 +51,10 @@ export const assetDataUtils = {
* @return The hex encoded assetData string
*/
encodeERC721AssetData(tokenAddress: string, tokenId: BigNumber): string {
- // TODO: Pass `tokendId` as a BigNumber.
- return ethUtil.bufferToHex(
- ethAbi.simpleEncode(
- 'ERC721Token(address,uint256)',
- tokenAddress,
- `0x${tokenId.toString(constants.BASE_16)}`,
- ),
- );
+ const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI);
+ const args = [tokenAddress, tokenId];
+ const assetData = abiEncoder.encode(args, encodingRules);
+ return assetData;
},
/**
* Decodes an ERC721 assetData hex string into it's corresponding ERC721 tokenAddress, tokenId & assetProxyId
@@ -66,27 +62,99 @@ export const assetDataUtils = {
* @return An object containing the decoded tokenAddress, tokenId & assetProxyId
*/
decodeERC721AssetData(assetData: string): ERC721AssetData {
- const data = ethUtil.toBuffer(assetData);
- if (data.byteLength < constants.ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH) {
+ assetDataUtils.assertIsERC721AssetData(assetData);
+ const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
+ const abiEncoder = new AbiEncoder.Method(constants.ERC721_METHOD_ABI);
+ const decodedAssetData = abiEncoder.decode(assetData, decodingRules);
+ return {
+ assetProxyId,
+ // TODO(abandeali1): fix return types for `AbiEncoder.Method.decode` so that we can remove type assertion
+ tokenAddress: (decodedAssetData as any).tokenContract,
+ tokenId: (decodedAssetData as any).tokenId,
+ };
+ },
+ /**
+ * Encodes assetData for multiple AssetProxies into a single hex encoded assetData string, usable in the makerAssetData or
+ * takerAssetData fields in a 0x order.
+ * @param amounts Amounts of each asset that correspond to a single unit within an order.
+ * @param nestedAssetData assetData strings that correspond to a valid assetProxyId.
+ * @return The hex encoded assetData string
+ */
+ encodeMultiAssetData(amounts: BigNumber[], nestedAssetData: string[]): string {
+ if (amounts.length !== nestedAssetData.length) {
throw new Error(
- `Could not decode ERC721 Asset Data. Expected length of encoded data to be at least ${
- constants.ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH
- }. Got ${data.byteLength}`,
+ `Invalid MultiAsset arguments. Expected length of 'amounts' (${
+ amounts.length
+ }) to equal length of 'nestedAssetData' (${nestedAssetData.length})`,
);
}
- const assetProxyId = ethUtil.bufferToHex(data.slice(0, constants.SELECTOR_LENGTH));
- if (assetProxyId !== AssetProxyId.ERC721) {
+ _.forEach(nestedAssetData, assetDataElement => assetDataUtils.validateAssetDataOrThrow(assetDataElement));
+ const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI);
+ const args = [amounts, nestedAssetData];
+ const assetData = abiEncoder.encode(args, encodingRules);
+ return assetData;
+ },
+ /**
+ * Decodes a MultiAsset assetData hex string into it's corresponding amounts and nestedAssetData
+ * @param assetData Hex encoded assetData string to decode
+ * @return An object containing the decoded amounts and nestedAssetData
+ */
+ decodeMultiAssetData(assetData: string): MultiAssetData {
+ assetDataUtils.assertIsMultiAssetData(assetData);
+ const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
+ const abiEncoder = new AbiEncoder.Method(constants.MULTI_ASSET_METHOD_ABI);
+ const decodedAssetData = abiEncoder.decode(assetData, decodingRules);
+ // TODO(abandeali1): fix return types for `AbiEncoder.Method.decode` so that we can remove type assertion
+ const amounts = (decodedAssetData as any).amounts;
+ const nestedAssetData = (decodedAssetData as any).nestedAssetData;
+ if (amounts.length !== nestedAssetData.length) {
throw new Error(
- `Could not decode ERC721 Asset Data. Expected Asset Proxy Id to be ERC721 (${
- AssetProxyId.ERC721
- }), but got ${assetProxyId}`,
+ `Invalid MultiAsset assetData. Expected length of 'amounts' (${
+ amounts.length
+ }) to equal length of 'nestedAssetData' (${nestedAssetData.length})`,
);
}
- const [tokenAddress, tokenId] = ethAbi.rawDecode(['address', 'uint256'], data.slice(constants.SELECTOR_LENGTH));
return {
assetProxyId,
- tokenAddress: ethUtil.addHexPrefix(tokenAddress),
- tokenId: new BigNumber(tokenId.toString()),
+ amounts,
+ nestedAssetData,
+ };
+ },
+ /**
+ * Decodes a MultiAsset assetData hex string into it's corresponding amounts and decoded nestedAssetData elements (all nested elements are flattened)
+ * @param assetData Hex encoded assetData string to decode
+ * @return An object containing the decoded amounts and nestedAssetData
+ */
+ decodeMultiAssetDataRecursively(assetData: string): MultiAssetDataWithRecursiveDecoding {
+ const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData);
+ const amounts: any[] = [];
+ const decodedNestedAssetData = _.map(
+ decodedAssetData.nestedAssetData as string[],
+ (nestedAssetDataElement, index) => {
+ const decodedNestedAssetDataElement = assetDataUtils.decodeAssetDataOrThrow(nestedAssetDataElement);
+ if (decodedNestedAssetDataElement.assetProxyId === AssetProxyId.MultiAsset) {
+ const recursivelyDecodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively(
+ nestedAssetDataElement,
+ );
+ amounts.push(
+ _.map(recursivelyDecodedAssetData.amounts, amountElement =>
+ amountElement.times(decodedAssetData.amounts[index]),
+ ),
+ );
+ return recursivelyDecodedAssetData.nestedAssetData;
+ } else {
+ amounts.push(decodedAssetData.amounts[index]);
+ return decodedNestedAssetDataElement as SingleAssetData;
+ }
+ },
+ );
+ const flattenedAmounts = _.flattenDeep(amounts);
+ const flattenedDecodedNestedAssetData = _.flattenDeep(decodedNestedAssetData);
+ return {
+ assetProxyId: decodedAssetData.assetProxyId,
+ amounts: flattenedAmounts,
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ nestedAssetData: flattenedDecodedNestedAssetData as SingleAssetData[],
};
},
/**
@@ -95,24 +163,133 @@ export const assetDataUtils = {
* @return The assetProxyId
*/
decodeAssetProxyId(assetData: string): AssetProxyId {
- const encodedAssetData = ethUtil.toBuffer(assetData);
- if (encodedAssetData.byteLength < constants.SELECTOR_LENGTH) {
+ if (assetData.length < constants.SELECTOR_CHAR_LENGTH_WITH_PREFIX) {
throw new Error(
- `Could not decode assetData. Expected length of encoded data to be at least 4. Got ${
- encodedAssetData.byteLength
+ `Could not decode assetData. Expected length of encoded data to be at least 10. Got ${
+ assetData.length
}`,
);
}
- const encodedAssetProxyId = encodedAssetData.slice(0, constants.SELECTOR_LENGTH);
- const assetProxyId = decodeAssetProxyId(encodedAssetProxyId);
+ const assetProxyId = assetData.slice(0, constants.SELECTOR_CHAR_LENGTH_WITH_PREFIX);
+ if (
+ assetProxyId !== AssetProxyId.ERC20 &&
+ assetProxyId !== AssetProxyId.ERC721 &&
+ assetProxyId !== AssetProxyId.MultiAsset
+ ) {
+ throw new Error(`Invalid assetProxyId: ${assetProxyId}`);
+ }
return assetProxyId;
},
/**
+ * Checks if the decoded asset data is valid ERC20 data
+ * @param decodedAssetData The decoded asset data to check
+ */
+ isERC20AssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is ERC20AssetData {
+ return decodedAssetData.assetProxyId === AssetProxyId.ERC20;
+ },
+ /**
+ * Checks if the decoded asset data is valid ERC721 data
+ * @param decodedAssetData The decoded asset data to check
+ */
+ isERC721AssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is ERC721AssetData {
+ return decodedAssetData.assetProxyId === AssetProxyId.ERC721;
+ },
+ /**
+ * Checks if the decoded asset data is valid MultiAsset data
+ * @param decodedAssetData The decoded asset data to check
+ */
+ isMultiAssetData(decodedAssetData: SingleAssetData | MultiAssetData): decodedAssetData is MultiAssetData {
+ return decodedAssetData.assetProxyId === AssetProxyId.MultiAsset;
+ },
+ /**
+ * Throws if the length or assetProxyId are invalid for the ERC20Proxy.
+ * @param assetData Hex encoded assetData string
+ */
+ assertIsERC20AssetData(assetData: string): void {
+ if (assetData.length < constants.ERC20_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) {
+ throw new Error(
+ `Could not decode ERC20 Proxy Data. Expected length of encoded data to be at least ${
+ constants.ERC20_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX
+ }. Got ${assetData.length}`,
+ );
+ }
+ const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
+ if (assetProxyId !== AssetProxyId.ERC20) {
+ throw new Error(
+ `Could not decode ERC20 assetData. Expected assetProxyId to be ERC20 (${
+ AssetProxyId.ERC20
+ }), but got ${assetProxyId}`,
+ );
+ }
+ },
+ /**
+ * Throws if the length or assetProxyId are invalid for the ERC721Proxy.
+ * @param assetData Hex encoded assetData string
+ */
+ assertIsERC721AssetData(assetData: string): void {
+ if (assetData.length < constants.ERC721_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) {
+ throw new Error(
+ `Could not decode ERC721 assetData. Expected length of encoded data to be at least ${
+ constants.ERC721_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX
+ }. Got ${assetData.length}`,
+ );
+ }
+ const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
+ if (assetProxyId !== AssetProxyId.ERC721) {
+ throw new Error(
+ `Could not decode ERC721 assetData. Expected assetProxyId to be ERC721 (${
+ AssetProxyId.ERC721
+ }), but got ${assetProxyId}`,
+ );
+ }
+ },
+ /**
+ * Throws if the length or assetProxyId are invalid for the MultiAssetProxy.
+ * @param assetData Hex encoded assetData string
+ */
+ assertIsMultiAssetData(assetData: string): void {
+ if (assetData.length < constants.MULTI_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX) {
+ throw new Error(
+ `Could not decode MultiAsset assetData. Expected length of encoded data to be at least ${
+ constants.MULTI_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX
+ }. Got ${assetData.length}`,
+ );
+ }
+ const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
+ if (assetProxyId !== AssetProxyId.MultiAsset) {
+ throw new Error(
+ `Could not decode MultiAsset assetData. Expected assetProxyId to be MultiAsset (${
+ AssetProxyId.MultiAsset
+ }), but got ${assetProxyId}`,
+ );
+ }
+ },
+ /**
+ * Throws if the length or assetProxyId are invalid for the corresponding AssetProxy.
+ * @param assetData Hex encoded assetData string
+ */
+ validateAssetDataOrThrow(assetData: string): void {
+ const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
+ switch (assetProxyId) {
+ case AssetProxyId.ERC20:
+ assetDataUtils.assertIsERC20AssetData(assetData);
+ break;
+ case AssetProxyId.ERC721:
+ assetDataUtils.assertIsERC721AssetData(assetData);
+ break;
+ case AssetProxyId.MultiAsset:
+ assetDataUtils.assertIsMultiAssetData(assetData);
+ break;
+ default:
+ throw new Error(`Unrecognized asset proxy id: ${assetProxyId}`);
+ }
+ },
+ /**
* Decode any assetData into it's corresponding assetData object
* @param assetData Hex encoded assetData string to decode
* @return Either a ERC20 or ERC721 assetData object
*/
- decodeAssetDataOrThrow(assetData: string): AssetData {
+ decodeAssetDataOrThrow(assetData: string): SingleAssetData | MultiAssetData {
const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
switch (assetProxyId) {
case AssetProxyId.ERC20:
@@ -121,19 +298,11 @@ export const assetDataUtils = {
case AssetProxyId.ERC721:
const erc721AssetData = assetDataUtils.decodeERC721AssetData(assetData);
return erc721AssetData;
+ case AssetProxyId.MultiAsset:
+ const multiAssetData = assetDataUtils.decodeMultiAssetData(assetData);
+ return multiAssetData;
default:
throw new Error(`Unrecognized asset proxy id: ${assetProxyId}`);
}
},
};
-
-function decodeAssetProxyId(encodedAssetProxyId: Buffer): AssetProxyId {
- const hexString = ethUtil.bufferToHex(encodedAssetProxyId);
- if (hexString === AssetProxyId.ERC20) {
- return AssetProxyId.ERC20;
- }
- if (hexString === AssetProxyId.ERC721) {
- return AssetProxyId.ERC721;
- }
- throw new Error(`Invalid ProxyId: ${hexString}`);
-}
diff --git a/packages/order-utils/src/constants.ts b/packages/order-utils/src/constants.ts
index 10029dcc3..a9a687719 100644
--- a/packages/order-utils/src/constants.ts
+++ b/packages/order-utils/src/constants.ts
@@ -1,16 +1,71 @@
import { BigNumber } from '@0x/utils';
+import { MethodAbi } from 'ethereum-types';
+
+const ERC20_METHOD_ABI: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'tokenContract',
+ type: 'address',
+ },
+ ],
+ name: 'ERC20Token',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+const ERC721_METHOD_ABI: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'tokenContract',
+ type: 'address',
+ },
+ {
+ name: 'tokenId',
+ type: 'uint256',
+ },
+ ],
+ name: 'ERC721Token',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
+
+const MULTI_ASSET_METHOD_ABI: MethodAbi = {
+ constant: false,
+ inputs: [
+ {
+ name: 'amounts',
+ type: 'uint256[]',
+ },
+ {
+ name: 'nestedAssetData',
+ type: 'bytes[]',
+ },
+ ],
+ name: 'MultiAsset',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
export const constants = {
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
NULL_BYTES: '0x',
+ NULL_ERC20_ASSET_DATA: '0xf47261b00000000000000000000000000000000000000000000000000000000000000000',
// tslint:disable-next-line:custom-no-magic-numbers
UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1),
TESTRPC_NETWORK_ID: 50,
ADDRESS_LENGTH: 20,
- ERC20_ASSET_DATA_BYTE_LENGTH: 36,
- ERC721_ASSET_DATA_MINIMUM_BYTE_LENGTH: 53,
- SELECTOR_LENGTH: 4,
- BASE_16: 16,
+ ERC20_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX: 74,
+ ERC721_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX: 136,
+ MULTI_ASSET_DATA_MIN_CHAR_LENGTH_WITH_PREFIX: 266,
+ SELECTOR_CHAR_LENGTH_WITH_PREFIX: 10,
INFINITE_TIMESTAMP_SEC: new BigNumber(2524604400), // Close to infinite
ZERO_AMOUNT: new BigNumber(0),
EIP712_DOMAIN_NAME: '0x Protocol',
@@ -48,4 +103,7 @@ export const constants = {
{ name: 'data', type: 'bytes' },
],
},
+ ERC20_METHOD_ABI,
+ ERC721_METHOD_ABI,
+ MULTI_ASSET_METHOD_ABI,
};
diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts
index 7a38b35df..06621fd9e 100644
--- a/packages/order-utils/src/exchange_transfer_simulator.ts
+++ b/packages/order-utils/src/exchange_transfer_simulator.ts
@@ -1,7 +1,8 @@
-import { ExchangeContractErrs } from '@0x/types';
+import { AssetProxyId, ExchangeContractErrs } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store';
+import { assetDataUtils } from './asset_data_utils';
import { constants } from './constants';
import { TradeSide, TransferType } from './types';
@@ -74,24 +75,51 @@ export class ExchangeTransferSimulator {
tradeSide: TradeSide,
transferType: TransferType,
): Promise<void> {
- // HACK: When simulating an open order (e.g taker is NULL_ADDRESS), we don't want to adjust balances/
- // allowances for the taker. We do however, want to increase the balance of the maker since the maker
- // might be relying on those funds to fill subsequent orders or pay the order's fees.
- if (from === constants.NULL_ADDRESS && tradeSide === TradeSide.Taker) {
- await this._increaseBalanceAsync(assetData, to, amountInBaseUnits);
- return;
+ const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
+ switch (assetProxyId) {
+ case AssetProxyId.ERC20:
+ case AssetProxyId.ERC721:
+ // HACK: When simulating an open order (e.g taker is NULL_ADDRESS), we don't want to adjust balances/
+ // allowances for the taker. We do however, want to increase the balance of the maker since the maker
+ // might be relying on those funds to fill subsequent orders or pay the order's fees.
+ if (from === constants.NULL_ADDRESS && tradeSide === TradeSide.Taker) {
+ await this._increaseBalanceAsync(assetData, to, amountInBaseUnits);
+ return;
+ }
+ const balance = await this._store.getBalanceAsync(assetData, from);
+ const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, from);
+ if (proxyAllowance.lessThan(amountInBaseUnits)) {
+ ExchangeTransferSimulator._throwValidationError(
+ FailureReason.ProxyAllowance,
+ tradeSide,
+ transferType,
+ );
+ }
+ if (balance.lessThan(amountInBaseUnits)) {
+ ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType);
+ }
+ await this._decreaseProxyAllowanceAsync(assetData, from, amountInBaseUnits);
+ await this._decreaseBalanceAsync(assetData, from, amountInBaseUnits);
+ await this._increaseBalanceAsync(assetData, to, amountInBaseUnits);
+ break;
+ case AssetProxyId.MultiAsset:
+ const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData);
+ for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) {
+ const amountsElement = decodedAssetData.amounts[index];
+ const totalAmount = amountInBaseUnits.times(amountsElement);
+ await this.transferFromAsync(
+ nestedAssetDataElement,
+ from,
+ to,
+ totalAmount,
+ tradeSide,
+ transferType,
+ );
+ }
+ break;
+ default:
+ break;
}
- const balance = await this._store.getBalanceAsync(assetData, from);
- const proxyAllowance = await this._store.getProxyAllowanceAsync(assetData, from);
- if (proxyAllowance.lessThan(amountInBaseUnits)) {
- ExchangeTransferSimulator._throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType);
- }
- if (balance.lessThan(amountInBaseUnits)) {
- ExchangeTransferSimulator._throwValidationError(FailureReason.Balance, tradeSide, transferType);
- }
- await this._decreaseProxyAllowanceAsync(assetData, from, amountInBaseUnits);
- await this._decreaseBalanceAsync(assetData, from, amountInBaseUnits);
- await this._increaseBalanceAsync(assetData, to, amountInBaseUnits);
}
private async _decreaseProxyAllowanceAsync(
assetData: string,
diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts
index e70d43efb..2150a02e4 100644
--- a/packages/order-utils/src/index.ts
+++ b/packages/order-utils/src/index.ts
@@ -34,11 +34,14 @@ export {
OrderRelevantState,
OrderState,
ECSignature,
- AssetData,
+ SingleAssetData,
ERC20AssetData,
ERC721AssetData,
+ MultiAssetData,
+ MultiAssetDataWithRecursiveDecoding,
AssetProxyId,
SignatureType,
+ ObjectMap,
OrderStateValid,
OrderStateInvalid,
ExchangeContractErrs,
diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts
index fe0d6c773..389419587 100644
--- a/packages/order-utils/src/order_state_utils.ts
+++ b/packages/order-utils/src/order_state_utils.ts
@@ -1,5 +1,6 @@
import {
ExchangeContractErrs,
+ ObjectMap,
OrderRelevantState,
OrderState,
OrderStateInvalid,
@@ -7,9 +8,11 @@ import {
SignedOrder,
} from '@0x/types';
import { BigNumber } from '@0x/utils';
+import * as _ from 'lodash';
import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher';
import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher';
+import { assetDataUtils } from './asset_data_utils';
import { orderHashUtils } from './order_hash';
import { OrderValidationUtils } from './order_validation_utils';
import { RemainingFillableCalculator } from './remaining_fillable_calculator';
@@ -18,7 +21,9 @@ import { utils } from './utils';
interface SidedOrderRelevantState {
isMakerSide: boolean;
traderBalance: BigNumber;
+ traderIndividualBalances: ObjectMap<BigNumber>;
traderProxyAllowance: BigNumber;
+ traderIndividualProxyAllowances: ObjectMap<BigNumber>;
traderFeeBalance: BigNumber;
traderFeeProxyAllowance: BigNumber;
filledTakerAssetAmount: BigNumber;
@@ -121,7 +126,9 @@ export class OrderStateUtils {
const sidedOrderRelevantState = {
isMakerSide: true,
traderBalance: orderRelevantState.makerBalance,
+ traderIndividualBalances: orderRelevantState.makerIndividualBalances,
traderProxyAllowance: orderRelevantState.makerProxyAllowance,
+ traderIndividualProxyAllowances: orderRelevantState.makerIndividualProxyAllowances,
traderFeeBalance: orderRelevantState.makerFeeBalance,
traderFeeProxyAllowance: orderRelevantState.makerFeeProxyAllowance,
filledTakerAssetAmount: orderRelevantState.filledTakerAssetAmount,
@@ -165,7 +172,9 @@ export class OrderStateUtils {
const orderRelevantState = {
makerBalance: sidedOrderRelevantState.traderBalance,
+ makerIndividualBalances: sidedOrderRelevantState.traderIndividualBalances,
makerProxyAllowance: sidedOrderRelevantState.traderProxyAllowance,
+ makerIndividualProxyAllowances: sidedOrderRelevantState.traderIndividualProxyAllowances,
makerFeeBalance: sidedOrderRelevantState.traderFeeBalance,
makerFeeProxyAllowance: sidedOrderRelevantState.traderFeeProxyAllowance,
filledTakerAssetAmount: sidedOrderRelevantState.filledTakerAssetAmount,
@@ -236,10 +245,12 @@ export class OrderStateUtils {
const isAssetZRX = assetData === zrxAssetData;
const traderBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress);
+ const traderIndividualBalances = await this._getAssetBalancesAsync(assetData, traderAddress);
const traderProxyAllowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
assetData,
traderAddress,
);
+ const traderIndividualProxyAllowances = await this._getAssetProxyAllowancesAsync(assetData, traderAddress);
const traderFeeBalance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
zrxAssetData,
traderAddress,
@@ -278,7 +289,9 @@ export class OrderStateUtils {
const sidedOrderRelevantState = {
isMakerSide,
traderBalance,
+ traderIndividualBalances,
traderProxyAllowance,
+ traderIndividualProxyAllowances,
traderFeeBalance,
traderFeeProxyAllowance,
filledTakerAssetAmount,
@@ -287,4 +300,47 @@ export class OrderStateUtils {
};
return sidedOrderRelevantState;
}
+ private async _getAssetBalancesAsync(
+ assetData: string,
+ traderAddress: string,
+ initialBalances: ObjectMap<BigNumber> = {},
+ ): Promise<ObjectMap<BigNumber>> {
+ const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
+ let balances: ObjectMap<BigNumber> = { ...initialBalances };
+ if (assetDataUtils.isERC20AssetData(decodedAssetData) || assetDataUtils.isERC721AssetData(decodedAssetData)) {
+ const balance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, traderAddress);
+ const tokenAddress = decodedAssetData.tokenAddress;
+ balances[tokenAddress] = _.isUndefined(initialBalances[tokenAddress])
+ ? balance
+ : balances[tokenAddress].add(balance);
+ } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
+ for (const assetDataElement of decodedAssetData.nestedAssetData) {
+ balances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, balances);
+ }
+ }
+ return balances;
+ }
+ private async _getAssetProxyAllowancesAsync(
+ assetData: string,
+ traderAddress: string,
+ initialAllowances: ObjectMap<BigNumber> = {},
+ ): Promise<ObjectMap<BigNumber>> {
+ const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
+ let allowances: ObjectMap<BigNumber> = { ...initialAllowances };
+ if (assetDataUtils.isERC20AssetData(decodedAssetData) || assetDataUtils.isERC721AssetData(decodedAssetData)) {
+ const allowance = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
+ assetData,
+ traderAddress,
+ );
+ const tokenAddress = decodedAssetData.tokenAddress;
+ allowances[tokenAddress] = _.isUndefined(initialAllowances[tokenAddress])
+ ? allowance
+ : allowances[tokenAddress].add(allowance);
+ } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
+ for (const assetDataElement of decodedAssetData.nestedAssetData) {
+ allowances = await this._getAssetBalancesAsync(assetDataElement, traderAddress, allowances);
+ }
+ }
+ return allowances;
+ }
}
diff --git a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts
index f42a76d0c..ae3e36238 100644
--- a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts
+++ b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts
@@ -119,7 +119,7 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
public deleteAllERC721ProxyAllowance(tokenAddress: string, userAddress: string): void {
for (const assetData in this._proxyAllowance) {
if (this._proxyAllowance.hasOwnProperty(assetData)) {
- const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
+ const decodedAssetData = assetDataUtils.decodeERC721AssetData(assetData);
if (
decodedAssetData.assetProxyId === AssetProxyId.ERC721 &&
decodedAssetData.tokenAddress === tokenAddress &&
diff --git a/packages/order-utils/test/asset_data_utils_test.ts b/packages/order-utils/test/asset_data_utils_test.ts
index f175b7a38..c498c5a00 100644
--- a/packages/order-utils/test/asset_data_utils_test.ts
+++ b/packages/order-utils/test/asset_data_utils_test.ts
@@ -1,6 +1,6 @@
import * as chai from 'chai';
-import { ERC20AssetData, ERC721AssetData } from '@0x/types';
+import { AssetProxyId, ERC721AssetData } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { assetDataUtils } from '../src/asset_data_utils';
@@ -10,41 +10,101 @@ import { chaiSetup } from './utils/chai_setup';
chaiSetup.configure();
const expect = chai.expect;
-const KNOWN_ENCODINGS = [
- {
- address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
- assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48',
- },
- {
- address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
- tokenId: new BigNumber(1),
- assetData:
- '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001',
- },
-];
-
-const ERC20_ASSET_PROXY_ID = '0xf47261b0';
-const ERC721_ASSET_PROXY_ID = '0x02571792';
+const KNOWN_ERC20_ENCODING = {
+ address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
+ assetData: '0xf47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48',
+};
+const KNOWN_ERC721_ENCODING = {
+ address: '0x1dc4c1cefef38a777b15aa20260a54e584b16c48',
+ tokenId: new BigNumber(1),
+ assetData:
+ '0x025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c480000000000000000000000000000000000000000000000000000000000000001',
+};
+const KNOWN_MULTI_ASSET_ENCODING = {
+ amounts: [new BigNumber(1), new BigNumber(1)],
+ nestedAssetData: [KNOWN_ERC20_ENCODING.assetData, KNOWN_ERC721_ENCODING.assetData],
+ assetData:
+ '0x94cfcdd7000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000024f47261b00000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044025717920000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000',
+};
describe('assetDataUtils', () => {
it('should encode ERC20', () => {
- const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ENCODINGS[0].address);
- expect(assetData).to.equal(KNOWN_ENCODINGS[0].assetData);
+ const assetData = assetDataUtils.encodeERC20AssetData(KNOWN_ERC20_ENCODING.address);
+ expect(assetData).to.equal(KNOWN_ERC20_ENCODING.assetData);
});
it('should decode ERC20', () => {
- const assetData: ERC20AssetData = assetDataUtils.decodeERC20AssetData(KNOWN_ENCODINGS[0].assetData);
- expect(assetData.tokenAddress).to.equal(KNOWN_ENCODINGS[0].address);
- expect(assetData.assetProxyId).to.equal(ERC20_ASSET_PROXY_ID);
+ const decodedAssetData = assetDataUtils.decodeERC20AssetData(KNOWN_ERC20_ENCODING.assetData);
+ expect(decodedAssetData.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address);
+ expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.ERC20);
});
it('should encode ERC721', () => {
- const assetData = assetDataUtils.encodeERC721AssetData(KNOWN_ENCODINGS[1].address, KNOWN_ENCODINGS[1]
- .tokenId as BigNumber);
- expect(assetData).to.equal(KNOWN_ENCODINGS[1].assetData);
+ const assetData = assetDataUtils.encodeERC721AssetData(
+ KNOWN_ERC721_ENCODING.address,
+ KNOWN_ERC721_ENCODING.tokenId,
+ );
+ expect(assetData).to.equal(KNOWN_ERC721_ENCODING.assetData);
});
it('should decode ERC721', () => {
- const assetData: ERC721AssetData = assetDataUtils.decodeERC721AssetData(KNOWN_ENCODINGS[1].assetData);
- expect(assetData.tokenAddress).to.equal(KNOWN_ENCODINGS[1].address);
- expect(assetData.assetProxyId).to.equal(ERC721_ASSET_PROXY_ID);
- expect(assetData.tokenId).to.be.bignumber.equal(KNOWN_ENCODINGS[1].tokenId);
+ const decodedAssetData = assetDataUtils.decodeERC721AssetData(KNOWN_ERC721_ENCODING.assetData);
+ expect(decodedAssetData.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address);
+ expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.ERC721);
+ expect(decodedAssetData.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId);
+ });
+ it('should encode ERC20 and ERC721 multiAssetData', () => {
+ const assetData = assetDataUtils.encodeMultiAssetData(
+ KNOWN_MULTI_ASSET_ENCODING.amounts,
+ KNOWN_MULTI_ASSET_ENCODING.nestedAssetData,
+ );
+ expect(assetData).to.equal(KNOWN_MULTI_ASSET_ENCODING.assetData);
+ });
+ it('should decode ERC20 and ERC721 multiAssetData', () => {
+ const decodedAssetData = assetDataUtils.decodeMultiAssetData(KNOWN_MULTI_ASSET_ENCODING.assetData);
+ expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset);
+ expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts);
+ expect(decodedAssetData.nestedAssetData).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.nestedAssetData);
+ });
+ it('should recursively decode ERC20 and ERC721 multiAssetData', () => {
+ const decodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively(KNOWN_MULTI_ASSET_ENCODING.assetData);
+ expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset);
+ expect(decodedAssetData.amounts).to.deep.equal(KNOWN_MULTI_ASSET_ENCODING.amounts);
+ const decodedErc20AssetData = decodedAssetData.nestedAssetData[0];
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ const decodedErc721AssetData = decodedAssetData.nestedAssetData[1] as ERC721AssetData;
+ expect(decodedErc20AssetData.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address);
+ expect(decodedErc20AssetData.assetProxyId).to.equal(AssetProxyId.ERC20);
+ expect(decodedErc721AssetData.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address);
+ expect(decodedErc721AssetData.assetProxyId).to.equal(AssetProxyId.ERC721);
+ expect(decodedErc721AssetData.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId);
+ });
+ it('should recursively decode nested assetData within multiAssetData', () => {
+ const amounts = [new BigNumber(1), new BigNumber(1), new BigNumber(2)];
+ const nestedAssetData = [
+ KNOWN_ERC20_ENCODING.assetData,
+ KNOWN_ERC721_ENCODING.assetData,
+ KNOWN_MULTI_ASSET_ENCODING.assetData,
+ ];
+ const assetData = assetDataUtils.encodeMultiAssetData(amounts, nestedAssetData);
+ const decodedAssetData = assetDataUtils.decodeMultiAssetDataRecursively(assetData);
+ expect(decodedAssetData.assetProxyId).to.equal(AssetProxyId.MultiAsset);
+ const expectedAmounts = [new BigNumber(1), new BigNumber(1), new BigNumber(2), new BigNumber(2)];
+ expect(decodedAssetData.amounts).to.deep.equal(expectedAmounts);
+ const expectedLength = 4;
+ expect(decodedAssetData.nestedAssetData.length).to.be.equal(expectedLength);
+ const decodedErc20AssetData1 = decodedAssetData.nestedAssetData[0];
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ const decodedErc721AssetData1 = decodedAssetData.nestedAssetData[1] as ERC721AssetData;
+ const decodedErc20AssetData2 = decodedAssetData.nestedAssetData[2];
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ const decodedErc721AssetData2 = decodedAssetData.nestedAssetData[3] as ERC721AssetData;
+ expect(decodedErc20AssetData1.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address);
+ expect(decodedErc20AssetData1.assetProxyId).to.equal(AssetProxyId.ERC20);
+ expect(decodedErc721AssetData1.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address);
+ expect(decodedErc721AssetData1.assetProxyId).to.equal(AssetProxyId.ERC721);
+ expect(decodedErc721AssetData1.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId);
+ expect(decodedErc20AssetData2.tokenAddress).to.equal(KNOWN_ERC20_ENCODING.address);
+ expect(decodedErc20AssetData2.assetProxyId).to.equal(AssetProxyId.ERC20);
+ expect(decodedErc721AssetData2.tokenAddress).to.equal(KNOWN_ERC721_ENCODING.address);
+ expect(decodedErc721AssetData2.assetProxyId).to.equal(AssetProxyId.ERC721);
+ expect(decodedErc721AssetData2.tokenId).to.be.bignumber.equal(KNOWN_ERC721_ENCODING.tokenId);
});
});
diff --git a/packages/order-utils/test/utils/test_order_factory.ts b/packages/order-utils/test/utils/test_order_factory.ts
index 145332674..4efe0b38e 100644
--- a/packages/order-utils/test/utils/test_order_factory.ts
+++ b/packages/order-utils/test/utils/test_order_factory.ts
@@ -7,9 +7,9 @@ import { orderFactory } from '../../src/order_factory';
const BASE_TEST_ORDER: Order = orderFactory.createOrder(
constants.NULL_ADDRESS,
constants.ZERO_AMOUNT,
- constants.NULL_ADDRESS,
+ constants.NULL_ERC20_ASSET_DATA,
constants.ZERO_AMOUNT,
- constants.NULL_ADDRESS,
+ constants.NULL_ERC20_ASSET_DATA,
constants.NULL_ADDRESS,
);
const BASE_TEST_SIGNED_ORDER: SignedOrder = {
diff --git a/packages/order-watcher/CHANGELOG.json b/packages/order-watcher/CHANGELOG.json
index 304dc45fd..4cfecd034 100644
--- a/packages/order-watcher/CHANGELOG.json
+++ b/packages/order-watcher/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "version": "2.4.0",
+ "changes": [
+ {
+ "note": "Add support for `MultiAssetProxy`",
+ "pr": 1363
+ }
+ ]
+ },
+ {
"version": "2.3.0",
"changes": [
{
diff --git a/packages/order-watcher/src/index.ts b/packages/order-watcher/src/index.ts
index e275a0c6a..1f4e5eff1 100644
--- a/packages/order-watcher/src/index.ts
+++ b/packages/order-watcher/src/index.ts
@@ -7,6 +7,7 @@ export {
OrderStateInvalid,
OrderState,
ExchangeContractErrs,
+ ObjectMap,
OrderRelevantState,
Stats,
} from '@0x/types';
diff --git a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts
index a956a94db..d1085014c 100644
--- a/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts
+++ b/packages/order-watcher/src/order_watcher/dependent_order_hashes_tracker.ts
@@ -1,6 +1,5 @@
-// tslint:disable:no-unnecessary-type-assertion
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
-import { AssetProxyId, ERC20AssetData, ERC721AssetData, SignedOrder } from '@0x/types';
+import { AssetProxyId, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
@@ -62,35 +61,18 @@ export class DependentOrderHashesTracker {
return dependentOrderHashes;
}
public addToDependentOrderHashes(signedOrder: SignedOrder): void {
- const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(signedOrder.makerAssetData);
- if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) {
- this._addToERC20DependentOrderHashes(signedOrder, (decodedMakerAssetData as ERC20AssetData).tokenAddress);
- } else {
- this._addToERC721DependentOrderHashes(
- signedOrder,
- (decodedMakerAssetData as ERC721AssetData).tokenAddress,
- (decodedMakerAssetData as ERC721AssetData).tokenId,
- );
- }
+ this._addAssetDataToDependentOrderHashes(signedOrder, signedOrder.makerAssetData);
this._addToERC20DependentOrderHashes(signedOrder, this._zrxTokenAddress);
this._addToMakerDependentOrderHashes(signedOrder);
}
public removeFromDependentOrderHashes(signedOrder: SignedOrder): void {
- const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(signedOrder.makerAssetData);
- if (decodedMakerAssetData.assetProxyId === AssetProxyId.ERC20) {
- this._removeFromERC20DependentOrderhashes(
- signedOrder,
- (decodedMakerAssetData as ERC20AssetData).tokenAddress,
- );
- } else {
- this._removeFromERC721DependentOrderhashes(
- signedOrder,
- (decodedMakerAssetData as ERC721AssetData).tokenAddress,
- (decodedMakerAssetData as ERC721AssetData).tokenId,
- );
- }
+ this._removeAssetDataFromDependentOrderHashes(signedOrder, signedOrder.makerAssetData);
// If makerToken === ZRX then we already removed it and we don't need to remove it again.
- if ((decodedMakerAssetData as ERC20AssetData).tokenAddress !== this._zrxTokenAddress) {
+ const decodedMakerAssetData = assetDataUtils.decodeAssetDataOrThrow(signedOrder.makerAssetData);
+ if (
+ assetDataUtils.isERC20AssetData(decodedMakerAssetData) &&
+ decodedMakerAssetData.tokenAddress !== this._zrxTokenAddress
+ ) {
this._removeFromERC20DependentOrderhashes(signedOrder, this._zrxTokenAddress);
}
this._removeFromMakerDependentOrderhashes(signedOrder);
@@ -167,6 +149,18 @@ export class DependentOrderHashesTracker {
tokenId.toString()
].add(orderHash);
}
+ private _addAssetDataToDependentOrderHashes(signedOrder: SignedOrder, assetData: string): void {
+ const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
+ if (assetDataUtils.isERC20AssetData(decodedAssetData)) {
+ this._addToERC20DependentOrderHashes(signedOrder, decodedAssetData.tokenAddress);
+ } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) {
+ this._addToERC721DependentOrderHashes(signedOrder, decodedAssetData.tokenAddress, decodedAssetData.tokenId);
+ } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
+ _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement =>
+ this._addAssetDataToDependentOrderHashes(signedOrder, nestedAssetDataElement),
+ );
+ }
+ }
private _addToMakerDependentOrderHashes(signedOrder: SignedOrder): void {
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
if (_.isUndefined(this._orderHashesByMakerAddress[signedOrder.makerAddress])) {
@@ -230,4 +224,20 @@ export class DependentOrderHashesTracker {
delete this._orderHashesByMakerAddress[signedOrder.makerAddress];
}
}
+ private _removeAssetDataFromDependentOrderHashes(signedOrder: SignedOrder, assetData: string): void {
+ const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
+ if (assetDataUtils.isERC20AssetData(decodedAssetData)) {
+ this._removeFromERC20DependentOrderhashes(signedOrder, decodedAssetData.tokenAddress);
+ } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) {
+ this._removeFromERC721DependentOrderhashes(
+ signedOrder,
+ decodedAssetData.tokenAddress,
+ decodedAssetData.tokenId,
+ );
+ } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
+ _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement =>
+ this._removeAssetDataFromDependentOrderHashes(signedOrder, nestedAssetDataElement),
+ );
+ }
+ }
}
diff --git a/packages/order-watcher/src/order_watcher/order_watcher.ts b/packages/order-watcher/src/order_watcher/order_watcher.ts
index 96c5ca7b4..a06fd0cfe 100644
--- a/packages/order-watcher/src/order_watcher/order_watcher.ts
+++ b/packages/order-watcher/src/order_watcher/order_watcher.ts
@@ -161,14 +161,7 @@ export class OrderWatcher {
this._dependentOrderHashesTracker.addToDependentOrderHashes(signedOrder);
const orderAssetDatas = [signedOrder.makerAssetData, signedOrder.takerAssetData];
- _.each(orderAssetDatas, assetData => {
- const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
- if (decodedAssetData.assetProxyId === AssetProxyId.ERC20) {
- this._collisionResistantAbiDecoder.addERC20Token(decodedAssetData.tokenAddress);
- } else if (decodedAssetData.assetProxyId === AssetProxyId.ERC721) {
- this._collisionResistantAbiDecoder.addERC721Token(decodedAssetData.tokenAddress);
- }
- });
+ _.each(orderAssetDatas, assetData => this._addAssetDataToAbiDecoder(assetData));
}
/**
* Removes an order from the orderWatcher
@@ -236,31 +229,71 @@ export class OrderWatcher {
await this._emitRevalidateOrdersAsync([orderHash]);
}
}
+ private _addAssetDataToAbiDecoder(assetData: string): void {
+ const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
+ if (assetDataUtils.isERC20AssetData(decodedAssetData)) {
+ this._collisionResistantAbiDecoder.addERC20Token(decodedAssetData.tokenAddress);
+ } else if (assetDataUtils.isERC721AssetData(decodedAssetData)) {
+ this._collisionResistantAbiDecoder.addERC721Token(decodedAssetData.tokenAddress);
+ } else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
+ _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement =>
+ this._addAssetDataToAbiDecoder(nestedAssetDataElement),
+ );
+ }
+ }
+ private _deleteLazyStoreBalance(assetData: string, userAddress: string): void {
+ const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
+ switch (assetProxyId) {
+ case AssetProxyId.ERC20:
+ case AssetProxyId.ERC721:
+ this._balanceAndProxyAllowanceLazyStore.deleteBalance(assetData, userAddress);
+ break;
+ case AssetProxyId.MultiAsset:
+ const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData);
+ _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement =>
+ this._deleteLazyStoreBalance(nestedAssetDataElement, userAddress),
+ );
+ break;
+ default:
+ break;
+ }
+ }
+ private _deleteLazyStoreProxyAllowance(assetData: string, userAddress: string): void {
+ const assetProxyId = assetDataUtils.decodeAssetProxyId(assetData);
+ switch (assetProxyId) {
+ case AssetProxyId.ERC20:
+ case AssetProxyId.ERC721:
+ this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(assetData, userAddress);
+ break;
+ case AssetProxyId.MultiAsset:
+ const decodedAssetData = assetDataUtils.decodeMultiAssetData(assetData);
+ _.each(decodedAssetData.nestedAssetData, nestedAssetDataElement =>
+ this._deleteLazyStoreProxyAllowance(nestedAssetDataElement, userAddress),
+ );
+ break;
+ default:
+ break;
+ }
+ }
private _cleanupOrderRelatedState(orderHash: string): void {
const signedOrder = this._orderByOrderHash[orderHash];
this._orderFilledCancelledLazyStore.deleteFilledTakerAmount(orderHash);
this._orderFilledCancelledLazyStore.deleteIsCancelled(orderHash);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.makerAssetData, signedOrder.makerAddress);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(
- signedOrder.makerAssetData,
- signedOrder.makerAddress,
- );
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(signedOrder.takerAssetData, signedOrder.takerAddress);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(
- signedOrder.takerAssetData,
- signedOrder.takerAddress,
- );
+ this._deleteLazyStoreBalance(signedOrder.makerAssetData, signedOrder.makerAddress);
+ this._deleteLazyStoreProxyAllowance(signedOrder.makerAssetData, signedOrder.makerAddress);
+ this._deleteLazyStoreBalance(signedOrder.takerAssetData, signedOrder.takerAddress);
+ this._deleteLazyStoreProxyAllowance(signedOrder.takerAssetData, signedOrder.takerAddress);
const zrxAssetData = this._orderFilledCancelledLazyStore.getZRXAssetData();
if (!signedOrder.makerFee.isZero()) {
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxAssetData, signedOrder.makerAddress);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxAssetData, signedOrder.makerAddress);
+ this._deleteLazyStoreBalance(zrxAssetData, signedOrder.makerAddress);
+ this._deleteLazyStoreProxyAllowance(zrxAssetData, signedOrder.makerAddress);
}
if (!signedOrder.takerFee.isZero()) {
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(zrxAssetData, signedOrder.takerAddress);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(zrxAssetData, signedOrder.takerAddress);
+ this._deleteLazyStoreBalance(zrxAssetData, signedOrder.takerAddress);
+ this._deleteLazyStoreProxyAllowance(zrxAssetData, signedOrder.takerAddress);
}
}
private _onOrderExpired(orderHash: string): void {
@@ -302,7 +335,7 @@ export class OrderWatcher {
// Invalidate cache
const args = decodedLog.args as ERC20TokenApprovalEventArgs;
const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(tokenAssetData, args._owner);
+ this._deleteLazyStoreProxyAllowance(tokenAssetData, args._owner);
// Revalidate orders
const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
args._owner,
@@ -315,7 +348,7 @@ export class OrderWatcher {
// Invalidate cache
const args = decodedLog.args as ERC721TokenApprovalEventArgs;
const tokenAssetData = assetDataUtils.encodeERC721AssetData(decodedLog.address, args._tokenId);
- this._balanceAndProxyAllowanceLazyStore.deleteProxyAllowance(tokenAssetData, args._owner);
+ this._deleteLazyStoreProxyAllowance(tokenAssetData, args._owner);
// Revalidate orders
const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
args._owner,
@@ -333,8 +366,8 @@ export class OrderWatcher {
// Invalidate cache
const args = decodedLog.args as ERC20TokenTransferEventArgs;
const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._from);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._to);
+ this._deleteLazyStoreBalance(tokenAssetData, args._from);
+ this._deleteLazyStoreBalance(tokenAssetData, args._to);
// Revalidate orders
const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
args._from,
@@ -347,8 +380,8 @@ export class OrderWatcher {
// Invalidate cache
const args = decodedLog.args as ERC721TokenTransferEventArgs;
const tokenAssetData = assetDataUtils.encodeERC721AssetData(decodedLog.address, args._tokenId);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._from);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._to);
+ this._deleteLazyStoreBalance(tokenAssetData, args._from);
+ this._deleteLazyStoreBalance(tokenAssetData, args._to);
// Revalidate orders
const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
args._from,
@@ -375,7 +408,7 @@ export class OrderWatcher {
// Invalidate cache
const args = decodedLog.args as WETH9DepositEventArgs;
const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._owner);
+ this._deleteLazyStoreBalance(tokenAssetData, args._owner);
// Revalidate orders
const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
args._owner,
@@ -388,7 +421,7 @@ export class OrderWatcher {
// Invalidate cache
const args = decodedLog.args as WETH9WithdrawalEventArgs;
const tokenAssetData = assetDataUtils.encodeERC20AssetData(decodedLog.address);
- this._balanceAndProxyAllowanceLazyStore.deleteBalance(tokenAssetData, args._owner);
+ this._deleteLazyStoreBalance(tokenAssetData, args._owner);
// Revalidate orders
const orderHashes = this._dependentOrderHashesTracker.getDependentOrderHashesByAssetDataByMaker(
args._owner,
diff --git a/packages/order-watcher/test/order_watcher_test.ts b/packages/order-watcher/test/order_watcher_test.ts
index 271e5dec5..41dc884d5 100644
--- a/packages/order-watcher/test/order_watcher_test.ts
+++ b/packages/order-watcher/test/order_watcher_test.ts
@@ -675,5 +675,213 @@ describe('OrderWatcher', () => {
})().catch(done);
});
});
+ describe('multiAsset', async () => {
+ const tokenId = new BigNumber(42);
+ const [makerErc721TokenAddress] = tokenUtils.getDummyERC721TokenAddresses();
+ const makerErc721AssetData = assetDataUtils.encodeERC721AssetData(makerErc721TokenAddress, tokenId);
+ const fillableErc721Amount = new BigNumber(1);
+ const [makerErc20TokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
+ const makerErc20AssetData = assetDataUtils.encodeERC20AssetData(makerErc20TokenAddress);
+ const fillableErc20Amount = new BigNumber(2);
+ const multiAssetAmounts = [fillableErc721Amount, fillableErc20Amount];
+ const nestedAssetData = [makerErc721AssetData, makerErc20AssetData];
+ const makerMultiAssetData = assetDataUtils.encodeMultiAssetData(multiAssetAmounts, nestedAssetData);
+ it('should emit orderStateInvalid when maker allowance of ERC721 token set to 0 for watched order', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerMultiAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
+ fillableErc721Amount,
+ );
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ await orderWatcher.addOrderAsync(signedOrder);
+ const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance);
+ });
+ orderWatcher.subscribe(callback);
+ await contractWrappers.erc721Token.setApprovalAsync(
+ makerErc721TokenAddress,
+ constants.NULL_ADDRESS,
+ tokenId,
+ );
+ })().catch(done);
+ });
+ it('should emit orderStateInvalid when maker allowance for all of ERC721 token set to 0 for watched order', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerMultiAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
+ fillableErc721Amount,
+ );
+ await contractWrappers.erc721Token.setApprovalAsync(
+ makerErc721TokenAddress,
+ constants.NULL_ADDRESS,
+ tokenId,
+ );
+ let isApproved = true;
+ await contractWrappers.erc721Token.setProxyApprovalForAllAsync(
+ makerErc721TokenAddress,
+ makerAddress,
+ isApproved,
+ );
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ await orderWatcher.addOrderAsync(signedOrder);
+ const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance);
+ });
+ orderWatcher.subscribe(callback);
+ isApproved = false;
+ await contractWrappers.erc721Token.setProxyApprovalForAllAsync(
+ makerErc721TokenAddress,
+ makerAddress,
+ isApproved,
+ );
+ })().catch(done);
+ });
+ it('should emit orderStateInvalid when maker moves ERC721 backing watched order', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerMultiAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
+ fillableErc721Amount,
+ );
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ await orderWatcher.addOrderAsync(signedOrder);
+ const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance);
+ });
+ orderWatcher.subscribe(callback);
+ await contractWrappers.erc721Token.transferFromAsync(
+ makerErc721TokenAddress,
+ coinbase,
+ makerAddress,
+ tokenId,
+ );
+ })().catch(done);
+ });
+ it('should emit orderStateInvalid when maker allowance of ERC20 token set to 0 for watched order', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerMultiAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
+ fillableErc721Amount,
+ );
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ await orderWatcher.addOrderAsync(signedOrder);
+ const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerAllowance);
+ });
+ orderWatcher.subscribe(callback);
+ await contractWrappers.erc20Token.setProxyAllowanceAsync(
+ makerErc20TokenAddress,
+ makerAddress,
+ new BigNumber(0),
+ );
+ })().catch(done);
+ });
+ it('should not emit an orderState event when irrelevant ERC20 Transfer event received', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerMultiAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ await orderWatcher.addOrderAsync(signedOrder);
+ const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((_orderState: OrderState) => {
+ throw new Error('OrderState callback fired for irrelevant order');
+ });
+ orderWatcher.subscribe(callback);
+ const notTheMaker = userAddresses[0];
+ const anyRecipient = takerAddress;
+ const transferAmount = new BigNumber(2);
+ await contractWrappers.erc20Token.transferAsync(
+ makerTokenAddress,
+ notTheMaker,
+ anyRecipient,
+ transferAmount,
+ );
+ setTimeout(() => {
+ done();
+ }, TIMEOUT_MS);
+ })().catch(done);
+ });
+ it('should emit orderStateInvalid when makerAddress moves ERC20 balance backing watched order', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerMultiAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ await orderWatcher.addOrderAsync(signedOrder);
+ const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.InsufficientMakerBalance);
+ });
+ orderWatcher.subscribe(callback);
+ const anyRecipient = takerAddress;
+ const makerBalance = await contractWrappers.erc20Token.getBalanceAsync(
+ makerTokenAddress,
+ makerAddress,
+ );
+ await contractWrappers.erc20Token.transferAsync(
+ makerTokenAddress,
+ makerAddress,
+ anyRecipient,
+ makerBalance,
+ );
+ })().catch(done);
+ });
+ // TODO(abandeali1): The following test will fail until the MAP has been deployed and activated.
+ it.skip('should emit orderStateInvalid when watched order fully filled', (done: DoneCallback) => {
+ (async () => {
+ signedOrder = await fillScenarios.createFillableSignedOrderAsync(
+ makerMultiAssetData,
+ takerAssetData,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ );
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ await orderWatcher.addOrderAsync(signedOrder);
+
+ const callback = callbackErrorReporter.reportNodeCallbackErrors(done)((orderState: OrderState) => {
+ expect(orderState.isValid).to.be.false();
+ const invalidOrderState = orderState as OrderStateInvalid;
+ expect(invalidOrderState.orderHash).to.be.equal(orderHash);
+ expect(invalidOrderState.error).to.be.equal(ExchangeContractErrs.OrderRemainingFillAmountZero);
+ });
+ orderWatcher.subscribe(callback);
+
+ await contractWrappers.exchange.fillOrderAsync(signedOrder, fillableAmount, takerAddress);
+ })().catch(done);
+ });
+ });
});
}); // tslint:disable:max-file-line-count
diff --git a/packages/order-watcher/test/order_watcher_web_socket_server_test.ts b/packages/order-watcher/test/order_watcher_web_socket_server_test.ts
index 578e0de61..6894f42fb 100644
--- a/packages/order-watcher/test/order_watcher_web_socket_server_test.ts
+++ b/packages/order-watcher/test/order_watcher_web_socket_server_test.ts
@@ -26,7 +26,7 @@ interface WsMessage {
data: string;
}
-describe.only('OrderWatcherWebSocketServer', async () => {
+describe('OrderWatcherWebSocketServer', async () => {
let contractWrappers: ContractWrappers;
let wsServer: OrderWatcherWebSocketServer;
let wsClient: WebSocket.w3cwebsocket;
diff --git a/packages/pipeline/package.json b/packages/pipeline/package.json
index a40f3d21c..ab73642ec 100644
--- a/packages/pipeline/package.json
+++ b/packages/pipeline/package.json
@@ -44,7 +44,7 @@
"@0x/contract-artifacts": "^1.0.1",
"@0x/contract-wrappers": "^3.0.0",
"@0x/dev-utils": "^1.0.21",
- "@0x/order-utils": "^2.0.0",
+ "@0x/order-utils": "^3.0.7",
"@0x/subproviders": "^2.1.8",
"@0x/types": "^1.4.1",
"@0x/utils": "^2.0.8",
diff --git a/packages/pipeline/src/entities/token_order.ts b/packages/pipeline/src/entities/token_order.ts
index 4b8f0abc3..2709747cb 100644
--- a/packages/pipeline/src/entities/token_order.ts
+++ b/packages/pipeline/src/entities/token_order.ts
@@ -1,7 +1,6 @@
import { BigNumber } from '@0x/utils';
import { Column, Entity, PrimaryColumn } from 'typeorm';
-import { OrderType } from '../types';
import { bigNumberTransformer, numberToBigIntTransformer } from '../utils';
@Entity({ name: 'token_orderbook_snapshots', schema: 'raw' })
@@ -11,7 +10,7 @@ export class TokenOrderbookSnapshot {
@PrimaryColumn({ name: 'source' })
public source!: string;
@PrimaryColumn({ name: 'order_type' })
- public orderType!: OrderType;
+ public orderType!: string;
@PrimaryColumn({ name: 'price', type: 'numeric', transformer: bigNumberTransformer })
public price!: BigNumber;
@PrimaryColumn({ name: 'base_asset_symbol' })
diff --git a/packages/pipeline/src/parsers/ddex_orders/index.ts b/packages/pipeline/src/parsers/ddex_orders/index.ts
index d7b97efbe..eeb9c9d5b 100644
--- a/packages/pipeline/src/parsers/ddex_orders/index.ts
+++ b/packages/pipeline/src/parsers/ddex_orders/index.ts
@@ -23,8 +23,12 @@ export function parseDdexOrders(
): TokenOrder[] {
const aggregatedBids = aggregateOrders(ddexOrderbook.bids);
const aggregatedAsks = aggregateOrders(ddexOrderbook.asks);
- const parsedBids = aggregatedBids.map(order => parseDdexOrder(ddexMarket, observedTimestamp, 'bid', source, order));
- const parsedAsks = aggregatedAsks.map(order => parseDdexOrder(ddexMarket, observedTimestamp, 'ask', source, order));
+ const parsedBids = aggregatedBids.map(order =>
+ parseDdexOrder(ddexMarket, observedTimestamp, OrderType.Bid, source, order),
+ );
+ const parsedAsks = aggregatedAsks.map(order =>
+ parseDdexOrder(ddexMarket, observedTimestamp, OrderType.Ask, source, order),
+ );
return parsedBids.concat(parsedAsks);
}
diff --git a/packages/pipeline/src/parsers/events/exchange_events.ts b/packages/pipeline/src/parsers/events/exchange_events.ts
index e18106c75..9c4a5f89a 100644
--- a/packages/pipeline/src/parsers/events/exchange_events.ts
+++ b/packages/pipeline/src/parsers/events/exchange_events.ts
@@ -5,7 +5,7 @@ import { LogWithDecodedArgs } from 'ethereum-types';
import * as R from 'ramda';
import { ExchangeCancelEvent, ExchangeCancelUpToEvent, ExchangeFillEvent } from '../../entities';
-import { bigNumbertoStringOrNull } from '../../utils';
+import { bigNumbertoStringOrNull, convertAssetProxyIdToType } from '../../utils';
/**
* Parses raw event logs for a fill event and returns an array of
@@ -40,9 +40,7 @@ export const parseExchangeCancelUpToEvents: (
*/
export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs<ExchangeFillEventArgs>): ExchangeFillEvent {
const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData);
- const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721';
const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData);
- const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721';
const exchangeFillEvent = new ExchangeFillEvent();
exchangeFillEvent.contractAddress = eventLog.address as string;
exchangeFillEvent.blockNumber = eventLog.blockNumber as number;
@@ -59,16 +57,24 @@ export function _convertToExchangeFillEvent(eventLog: LogWithDecodedArgs<Exchang
exchangeFillEvent.takerFeePaid = eventLog.args.takerFeePaid;
exchangeFillEvent.orderHash = eventLog.args.orderHash;
exchangeFillEvent.rawMakerAssetData = eventLog.args.makerAssetData;
- exchangeFillEvent.makerAssetType = makerAssetType;
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ exchangeFillEvent.makerAssetType = convertAssetProxyIdToType(makerAssetData.assetProxyId as AssetProxyId);
exchangeFillEvent.makerAssetProxyId = makerAssetData.assetProxyId;
- exchangeFillEvent.makerTokenAddress = makerAssetData.tokenAddress;
+ // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData
+ exchangeFillEvent.makerTokenAddress = assetDataUtils.isMultiAssetData(makerAssetData)
+ ? assetDataUtils.decodeMultiAssetDataRecursively(eventLog.args.makerAssetData).nestedAssetData[0].tokenAddress
+ : makerAssetData.tokenAddress;
// tslint has a false positive here. Type assertion is required.
// tslint:disable-next-line:no-unnecessary-type-assertion
exchangeFillEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId);
exchangeFillEvent.rawTakerAssetData = eventLog.args.takerAssetData;
- exchangeFillEvent.takerAssetType = takerAssetType;
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ exchangeFillEvent.takerAssetType = convertAssetProxyIdToType(takerAssetData.assetProxyId as AssetProxyId);
exchangeFillEvent.takerAssetProxyId = takerAssetData.assetProxyId;
- exchangeFillEvent.takerTokenAddress = takerAssetData.tokenAddress;
+ // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData
+ exchangeFillEvent.takerTokenAddress = assetDataUtils.isMultiAssetData(takerAssetData)
+ ? assetDataUtils.decodeMultiAssetDataRecursively(eventLog.args.takerAssetData).nestedAssetData[0].tokenAddress
+ : takerAssetData.tokenAddress;
// tslint:disable-next-line:no-unnecessary-type-assertion
exchangeFillEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId);
return exchangeFillEvent;
@@ -83,9 +89,7 @@ export function _convertToExchangeCancelEvent(
eventLog: LogWithDecodedArgs<ExchangeCancelEventArgs>,
): ExchangeCancelEvent {
const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.makerAssetData);
- const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721';
const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(eventLog.args.takerAssetData);
- const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721';
const exchangeCancelEvent = new ExchangeCancelEvent();
exchangeCancelEvent.contractAddress = eventLog.address as string;
exchangeCancelEvent.blockNumber = eventLog.blockNumber as number;
@@ -98,15 +102,23 @@ export function _convertToExchangeCancelEvent(
exchangeCancelEvent.senderAddress = eventLog.args.senderAddress;
exchangeCancelEvent.orderHash = eventLog.args.orderHash;
exchangeCancelEvent.rawMakerAssetData = eventLog.args.makerAssetData;
- exchangeCancelEvent.makerAssetType = makerAssetType;
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ exchangeCancelEvent.makerAssetType = convertAssetProxyIdToType(makerAssetData.assetProxyId as AssetProxyId);
exchangeCancelEvent.makerAssetProxyId = makerAssetData.assetProxyId;
- exchangeCancelEvent.makerTokenAddress = makerAssetData.tokenAddress;
+ // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData
+ exchangeCancelEvent.makerTokenAddress = assetDataUtils.isMultiAssetData(makerAssetData)
+ ? assetDataUtils.decodeMultiAssetDataRecursively(eventLog.args.makerAssetData).nestedAssetData[0].tokenAddress
+ : makerAssetData.tokenAddress;
// tslint:disable-next-line:no-unnecessary-type-assertion
exchangeCancelEvent.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId);
exchangeCancelEvent.rawTakerAssetData = eventLog.args.takerAssetData;
- exchangeCancelEvent.takerAssetType = takerAssetType;
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ exchangeCancelEvent.takerAssetType = convertAssetProxyIdToType(takerAssetData.assetProxyId as AssetProxyId);
exchangeCancelEvent.takerAssetProxyId = takerAssetData.assetProxyId;
- exchangeCancelEvent.takerTokenAddress = takerAssetData.tokenAddress;
+ // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData
+ exchangeCancelEvent.takerTokenAddress = assetDataUtils.isMultiAssetData(takerAssetData)
+ ? assetDataUtils.decodeMultiAssetDataRecursively(eventLog.args.takerAssetData).nestedAssetData[0].tokenAddress
+ : takerAssetData.tokenAddress;
// tslint:disable-next-line:no-unnecessary-type-assertion
exchangeCancelEvent.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId);
return exchangeCancelEvent;
diff --git a/packages/pipeline/src/parsers/idex_orders/index.ts b/packages/pipeline/src/parsers/idex_orders/index.ts
index dfe27455c..14b871195 100644
--- a/packages/pipeline/src/parsers/idex_orders/index.ts
+++ b/packages/pipeline/src/parsers/idex_orders/index.ts
@@ -2,7 +2,7 @@ import { BigNumber } from '@0x/utils';
import { aggregateOrders } from '../utils';
-import { IdexOrder, IdexOrderbook, IdexOrderParam } from '../../data_sources/idex';
+import { IdexOrderbook, IdexOrderParam } from '../../data_sources/idex';
import { TokenOrderbookSnapshot as TokenOrder } from '../../entities';
import { OrderType } from '../../types';
@@ -21,7 +21,9 @@ export function parseIdexOrders(idexOrderbook: IdexOrderbook, observedTimestamp:
const idexBidOrder = idexOrderbook.bids[0];
const parsedBids =
aggregatedBids.length > 0
- ? aggregatedBids.map(order => parseIdexOrder(idexBidOrder.params, observedTimestamp, 'bid', source, order))
+ ? aggregatedBids.map(order =>
+ parseIdexOrder(idexBidOrder.params, observedTimestamp, OrderType.Bid, source, order),
+ )
: [];
const aggregatedAsks = aggregateOrders(idexOrderbook.asks);
@@ -29,7 +31,9 @@ export function parseIdexOrders(idexOrderbook: IdexOrderbook, observedTimestamp:
const idexAskOrder = idexOrderbook.asks[0];
const parsedAsks =
aggregatedAsks.length > 0
- ? aggregatedAsks.map(order => parseIdexOrder(idexAskOrder.params, observedTimestamp, 'ask', source, order))
+ ? aggregatedAsks.map(order =>
+ parseIdexOrder(idexAskOrder.params, observedTimestamp, OrderType.Ask, source, order),
+ )
: [];
return parsedBids.concat(parsedAsks);
}
@@ -62,7 +66,7 @@ export function parseIdexOrder(
tokenOrder.baseVolume = amount;
tokenOrder.quoteVolume = price.times(amount);
- if (orderType === 'bid') {
+ if (orderType === OrderType.Bid) {
tokenOrder.baseAssetSymbol = idexOrderParam.buySymbol;
tokenOrder.baseAssetAddress = idexOrderParam.tokenBuy;
tokenOrder.quoteAssetSymbol = idexOrderParam.sellSymbol;
diff --git a/packages/pipeline/src/parsers/oasis_orders/index.ts b/packages/pipeline/src/parsers/oasis_orders/index.ts
index 13997f31b..b71fb65b9 100644
--- a/packages/pipeline/src/parsers/oasis_orders/index.ts
+++ b/packages/pipeline/src/parsers/oasis_orders/index.ts
@@ -23,13 +23,13 @@ export function parseOasisOrders(
observedTimestamp: number,
source: string,
): TokenOrder[] {
- const aggregatedBids = aggregateOrders(R.filter(R.propEq('act', 'bid'), oasisOrderbook));
- const aggregatedAsks = aggregateOrders(R.filter(R.propEq('act', 'ask'), oasisOrderbook));
+ const aggregatedBids = aggregateOrders(R.filter(R.propEq('act', OrderType.Bid), oasisOrderbook));
+ const aggregatedAsks = aggregateOrders(R.filter(R.propEq('act', OrderType.Ask), oasisOrderbook));
const parsedBids = aggregatedBids.map(order =>
- parseOasisOrder(oasisMarket, observedTimestamp, 'bid', source, order),
+ parseOasisOrder(oasisMarket, observedTimestamp, OrderType.Bid, source, order),
);
const parsedAsks = aggregatedAsks.map(order =>
- parseOasisOrder(oasisMarket, observedTimestamp, 'ask', source, order),
+ parseOasisOrder(oasisMarket, observedTimestamp, OrderType.Ask, source, order),
);
return parsedBids.concat(parsedAsks);
}
diff --git a/packages/pipeline/src/parsers/paradex_orders/index.ts b/packages/pipeline/src/parsers/paradex_orders/index.ts
index 5ceeb64a4..85990dae4 100644
--- a/packages/pipeline/src/parsers/paradex_orders/index.ts
+++ b/packages/pipeline/src/parsers/paradex_orders/index.ts
@@ -21,10 +21,10 @@ export function parseParadexOrders(
source: string,
): TokenOrder[] {
const parsedBids = paradexOrderbookResponse.bids.map(order =>
- parseParadexOrder(paradexMarket, observedTimestamp, 'bid', source, order),
+ parseParadexOrder(paradexMarket, observedTimestamp, OrderType.Bid, source, order),
);
const parsedAsks = paradexOrderbookResponse.asks.map(order =>
- parseParadexOrder(paradexMarket, observedTimestamp, 'ask', source, order),
+ parseParadexOrder(paradexMarket, observedTimestamp, OrderType.Ask, source, order),
);
return parsedBids.concat(parsedAsks);
}
diff --git a/packages/pipeline/src/parsers/sra_orders/index.ts b/packages/pipeline/src/parsers/sra_orders/index.ts
index ef8901e40..13fe632a4 100644
--- a/packages/pipeline/src/parsers/sra_orders/index.ts
+++ b/packages/pipeline/src/parsers/sra_orders/index.ts
@@ -4,7 +4,7 @@ import { AssetProxyId, ERC721AssetData } from '@0x/types';
import * as R from 'ramda';
import { SraOrder } from '../../entities';
-import { bigNumbertoStringOrNull } from '../../utils';
+import { bigNumbertoStringOrNull, convertAssetProxyIdToType } from '../../utils';
/**
* Parses a raw order response from an SRA endpoint and returns an array of
@@ -22,9 +22,7 @@ export function parseSraOrders(rawOrdersResponse: OrdersResponse): SraOrder[] {
export function _convertToEntity(apiOrder: APIOrder): SraOrder {
// TODO(albrow): refactor out common asset data decoding code.
const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.makerAssetData);
- const makerAssetType = makerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721';
const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(apiOrder.order.takerAssetData);
- const takerAssetType = takerAssetData.assetProxyId === AssetProxyId.ERC20 ? 'erc20' : 'erc721';
const sraOrder = new SraOrder();
sraOrder.exchangeAddress = apiOrder.order.exchangeAddress;
@@ -43,16 +41,24 @@ export function _convertToEntity(apiOrder: APIOrder): SraOrder {
sraOrder.signature = apiOrder.order.signature;
sraOrder.rawMakerAssetData = apiOrder.order.makerAssetData;
- sraOrder.makerAssetType = makerAssetType;
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ sraOrder.makerAssetType = convertAssetProxyIdToType(makerAssetData.assetProxyId as AssetProxyId);
sraOrder.makerAssetProxyId = makerAssetData.assetProxyId;
- sraOrder.makerTokenAddress = makerAssetData.tokenAddress;
+ // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData
+ sraOrder.makerTokenAddress = assetDataUtils.isMultiAssetData(makerAssetData)
+ ? assetDataUtils.decodeMultiAssetDataRecursively(apiOrder.order.makerAssetData).nestedAssetData[0].tokenAddress
+ : makerAssetData.tokenAddress;
// tslint has a false positive here. Type assertion is required.
// tslint:disable-next-line:no-unnecessary-type-assertion
sraOrder.makerTokenId = bigNumbertoStringOrNull((makerAssetData as ERC721AssetData).tokenId);
sraOrder.rawTakerAssetData = apiOrder.order.takerAssetData;
- sraOrder.takerAssetType = takerAssetType;
+ // tslint:disable-next-line:no-unnecessary-type-assertion
+ sraOrder.takerAssetType = convertAssetProxyIdToType(takerAssetData.assetProxyId as AssetProxyId);
sraOrder.takerAssetProxyId = takerAssetData.assetProxyId;
- sraOrder.takerTokenAddress = takerAssetData.tokenAddress;
+ // HACK(abandeali1): this event schema currently does not support multiple maker/taker assets, so we store the first token address from the MultiAssetProxy assetData
+ sraOrder.takerTokenAddress = assetDataUtils.isMultiAssetData(takerAssetData)
+ ? assetDataUtils.decodeMultiAssetDataRecursively(apiOrder.order.takerAssetData).nestedAssetData[0].tokenAddress
+ : takerAssetData.tokenAddress;
// tslint:disable-next-line:no-unnecessary-type-assertion
sraOrder.takerTokenId = bigNumbertoStringOrNull((takerAssetData as ERC721AssetData).tokenId);
diff --git a/packages/pipeline/src/types.ts b/packages/pipeline/src/types.ts
index e02b42a40..5f2121807 100644
--- a/packages/pipeline/src/types.ts
+++ b/packages/pipeline/src/types.ts
@@ -1,2 +1,9 @@
-export type AssetType = 'erc20' | 'erc721';
-export type OrderType = 'bid' | 'ask';
+export enum AssetType {
+ ERC20 = 'erc20',
+ ERC721 = 'erc721',
+ MultiAsset = 'multiAsset',
+}
+export enum OrderType {
+ Bid = 'bid',
+ Ask = 'ask',
+}
diff --git a/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts b/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts
new file mode 100644
index 000000000..2cd05a616
--- /dev/null
+++ b/packages/pipeline/src/utils/transformers/asset_proxy_id_types.ts
@@ -0,0 +1,20 @@
+import { AssetProxyId } from '@0x/types';
+
+import { AssetType } from '../../types';
+
+/**
+ * Converts an assetProxyId to its string equivalent
+ * @param assetProxyId Id of AssetProxy
+ */
+export function convertAssetProxyIdToType(assetProxyId: AssetProxyId): AssetType {
+ switch (assetProxyId) {
+ case AssetProxyId.ERC20:
+ return AssetType.ERC20;
+ case AssetProxyId.ERC721:
+ return AssetType.ERC721;
+ case AssetProxyId.MultiAsset:
+ return AssetType.MultiAsset;
+ default:
+ throw new Error(`${assetProxyId} not a supported assetProxyId`);
+ }
+}
diff --git a/packages/pipeline/src/utils/transformers/index.ts b/packages/pipeline/src/utils/transformers/index.ts
index 232c1c5de..31a4c9223 100644
--- a/packages/pipeline/src/utils/transformers/index.ts
+++ b/packages/pipeline/src/utils/transformers/index.ts
@@ -1,2 +1,3 @@
export * from './big_number';
export * from './number_to_bigint';
+export * from './asset_proxy_id_types';
diff --git a/packages/pipeline/test/parsers/ddex_orders/index_test.ts b/packages/pipeline/test/parsers/ddex_orders/index_test.ts
index 4a4a86bf8..f30e86b02 100644
--- a/packages/pipeline/test/parsers/ddex_orders/index_test.ts
+++ b/packages/pipeline/test/parsers/ddex_orders/index_test.ts
@@ -31,13 +31,13 @@ describe('ddex_orders', () => {
amountDecimals: 0,
};
const observedTimestamp: number = Date.now();
- const orderType: OrderType = 'bid';
+ const orderType: OrderType = OrderType.Bid;
const source: string = 'ddex';
const expected = new TokenOrder();
expected.source = 'ddex';
expected.observedTimestamp = observedTimestamp;
- expected.orderType = 'bid';
+ expected.orderType = OrderType.Bid;
expected.price = new BigNumber(0.5);
// ddex currently confuses base and quote assets.
// Switch them to maintain our internal consistency.
diff --git a/packages/pipeline/test/parsers/events/exchange_events_test.ts b/packages/pipeline/test/parsers/events/exchange_events_test.ts
index 5d4b185a5..956ad9ef8 100644
--- a/packages/pipeline/test/parsers/events/exchange_events_test.ts
+++ b/packages/pipeline/test/parsers/events/exchange_events_test.ts
@@ -6,6 +6,7 @@ import 'mocha';
import { ExchangeFillEvent } from '../../../src/entities';
import { _convertToExchangeFillEvent } from '../../../src/parsers/events/exchange_events';
+import { AssetType } from '../../../src/types';
import { chaiSetup } from '../../utils/chai_setup';
chaiSetup.configure();
@@ -62,12 +63,12 @@ describe('exchange_events', () => {
expected.takerFeePaid = new BigNumber('12345');
expected.orderHash = '0xab12ed2cbaa5615ab690b9da75a46e53ddfcf3f1a68655b5fe0d94c75a1aac4a';
expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
- expected.makerAssetType = 'erc20';
+ expected.makerAssetType = AssetType.ERC20;
expected.makerAssetProxyId = '0xf47261b0';
expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
expected.makerTokenId = null;
expected.rawTakerAssetData = '0xf47261b0000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498';
- expected.takerAssetType = 'erc20';
+ expected.takerAssetType = AssetType.ERC20;
expected.takerAssetProxyId = '0xf47261b0';
expected.takerTokenAddress = '0xe41d2489571d322189246dafa5ebde1f4699f498';
expected.takerTokenId = null;
diff --git a/packages/pipeline/test/parsers/idex_orders/index_test.ts b/packages/pipeline/test/parsers/idex_orders/index_test.ts
index d54ecb9a8..48b019732 100644
--- a/packages/pipeline/test/parsers/idex_orders/index_test.ts
+++ b/packages/pipeline/test/parsers/idex_orders/index_test.ts
@@ -31,13 +31,13 @@ describe('idex_orders', () => {
user: '0x212345667543456435324564345643453453333',
};
const observedTimestamp: number = Date.now();
- const orderType: OrderType = 'bid';
+ const orderType: OrderType = OrderType.Bid;
const source: string = 'idex';
const expected = new TokenOrder();
expected.source = 'idex';
expected.observedTimestamp = observedTimestamp;
- expected.orderType = 'bid';
+ expected.orderType = OrderType.Bid;
expected.price = new BigNumber(0.5);
expected.baseAssetSymbol = 'ABC';
expected.baseAssetAddress = '0x0000000000000000000000000000000000000000';
@@ -65,13 +65,13 @@ describe('idex_orders', () => {
user: '0x212345667543456435324564345643453453333',
};
const observedTimestamp: number = Date.now();
- const orderType: OrderType = 'ask';
+ const orderType: OrderType = OrderType.Ask;
const source: string = 'idex';
const expected = new TokenOrder();
expected.source = 'idex';
expected.observedTimestamp = observedTimestamp;
- expected.orderType = 'ask';
+ expected.orderType = OrderType.Ask;
expected.price = new BigNumber(0.5);
expected.baseAssetSymbol = 'ABC';
expected.baseAssetAddress = '0x0000000000000000000000000000000000000000';
diff --git a/packages/pipeline/test/parsers/oasis_orders/index_test.ts b/packages/pipeline/test/parsers/oasis_orders/index_test.ts
index 433bfb665..401fedff8 100644
--- a/packages/pipeline/test/parsers/oasis_orders/index_test.ts
+++ b/packages/pipeline/test/parsers/oasis_orders/index_test.ts
@@ -27,13 +27,13 @@ describe('oasis_orders', () => {
low: 0,
};
const observedTimestamp: number = Date.now();
- const orderType: OrderType = 'bid';
+ const orderType: OrderType = OrderType.Bid;
const source: string = 'oasis';
const expected = new TokenOrder();
expected.source = 'oasis';
expected.observedTimestamp = observedTimestamp;
- expected.orderType = 'bid';
+ expected.orderType = OrderType.Bid;
expected.price = new BigNumber(0.5);
expected.baseAssetSymbol = 'DEF';
expected.baseAssetAddress = null;
diff --git a/packages/pipeline/test/parsers/paradex_orders/index_test.ts b/packages/pipeline/test/parsers/paradex_orders/index_test.ts
index 6b811b90d..c5dd8751b 100644
--- a/packages/pipeline/test/parsers/paradex_orders/index_test.ts
+++ b/packages/pipeline/test/parsers/paradex_orders/index_test.ts
@@ -32,13 +32,13 @@ describe('paradex_orders', () => {
quoteTokenAddress: '0x0000000000000000000000000000000000000000',
};
const observedTimestamp: number = Date.now();
- const orderType: OrderType = 'bid';
+ const orderType: OrderType = OrderType.Bid;
const source: string = 'paradex';
const expected = new TokenOrder();
expected.source = 'paradex';
expected.observedTimestamp = observedTimestamp;
- expected.orderType = 'bid';
+ expected.orderType = OrderType.Bid;
expected.price = new BigNumber(0.1245);
expected.baseAssetSymbol = 'DEF';
expected.baseAssetAddress = '0xb45df06e38540a675fdb5b598abf2c0dbe9d6b81';
diff --git a/packages/pipeline/test/parsers/sra_orders/index_test.ts b/packages/pipeline/test/parsers/sra_orders/index_test.ts
index ee2842ef3..838171a72 100644
--- a/packages/pipeline/test/parsers/sra_orders/index_test.ts
+++ b/packages/pipeline/test/parsers/sra_orders/index_test.ts
@@ -5,6 +5,7 @@ import 'mocha';
import { SraOrder } from '../../../src/entities';
import { _convertToEntity } from '../../../src/parsers/sra_orders';
+import { AssetType } from '../../../src/types';
import { chaiSetup } from '../../utils/chai_setup';
chaiSetup.configure();
@@ -50,12 +51,12 @@ describe('sra_orders', () => {
expected.signature =
'0x1b5a5d672b0d647b5797387ccbb89d822d5d2e873346b014f4ff816ff0783f2a7a0d2824d2d7042ec8ea375bc7f870963e1cb8248f1db03ddf125e27b5963aa11f03';
expected.rawMakerAssetData = '0xf47261b0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
- expected.makerAssetType = 'erc20';
+ expected.makerAssetType = AssetType.ERC20;
expected.makerAssetProxyId = '0xf47261b0';
expected.makerTokenAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
expected.makerTokenId = null;
expected.rawTakerAssetData = '0xf47261b000000000000000000000000042d6622dece394b54999fbd73d108123806f6a18';
- expected.takerAssetType = 'erc20';
+ expected.takerAssetType = AssetType.ERC20;
expected.takerAssetProxyId = '0xf47261b0';
expected.takerTokenAddress = '0x42d6622dece394b54999fbd73d108123806f6a18';
expected.takerTokenId = null;
diff --git a/packages/sra-spec/public/index.html b/packages/sra-spec/public/index.html
index e75ae7f04..5271b50c6 100644
--- a/packages/sra-spec/public/index.html
+++ b/packages/sra-spec/public/index.html
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
- <title>ReDoc</title>
+ <title>0x Standard Relayer API</title>
<!-- needed for adaptive design -->
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
@@ -21,4 +21,4 @@
<redoc spec-url='http://sra-spec.s3-website-us-east-1.amazonaws.com/api.json'></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/packages/types/CHANGELOG.json b/packages/types/CHANGELOG.json
index f1cd2f18e..fdb421e79 100644
--- a/packages/types/CHANGELOG.json
+++ b/packages/types/CHANGELOG.json
@@ -18,6 +18,10 @@
{
"note": "Add RevertReasons for DutchAuction contract",
"pr": 1225
+ },
+ {
+ "note": "Add MultiAsset types",
+ "pr": 1363
}
],
"timestamp": 1544570656
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 4470dd501..472b56dc2 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -110,7 +110,9 @@ export type DoneCallback = (err?: Error) => void;
export interface OrderRelevantState {
makerBalance: BigNumber;
+ makerIndividualBalances: ObjectMap<BigNumber>;
makerProxyAllowance: BigNumber;
+ makerIndividualProxyAllowances: ObjectMap<BigNumber>;
makerFeeBalance: BigNumber;
makerFeeProxyAllowance: BigNumber;
filledTakerAssetAmount: BigNumber;
@@ -155,6 +157,7 @@ export enum SignatureType {
export enum AssetProxyId {
ERC20 = '0xf47261b0',
ERC721 = '0x02571792',
+ MultiAsset = '0x94cfcdd7',
}
export interface ERC20AssetData {
@@ -168,7 +171,21 @@ export interface ERC721AssetData {
tokenId: BigNumber;
}
-export type AssetData = ERC20AssetData | ERC721AssetData;
+export type SingleAssetData = ERC20AssetData | ERC721AssetData;
+
+export interface MultiAssetData {
+ assetProxyId: string;
+ amounts: BigNumber[];
+ nestedAssetData: string[];
+}
+
+export interface MultiAssetDataWithRecursiveDecoding {
+ assetProxyId: string;
+ amounts: BigNumber[];
+ nestedAssetData: SingleAssetData[];
+}
+
+export type AssetData = SingleAssetData | MultiAssetData | MultiAssetDataWithRecursiveDecoding;
// TODO: DRY. These should be extracted from contract code.
export enum RevertReason {
diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json
index 605151fb6..eb94da8d6 100644
--- a/packages/utils/CHANGELOG.json
+++ b/packages/utils/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "version": "2.1.1",
+ "changes": [
+ {
+ "note": "Add `should` prefix to names of properties in EncodingRules and DecodingRules",
+ "pr": 1363
+ }
+ ]
+ },
+ {
"version": "2.1.0",
"changes": [
{
diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts
index 089d04659..00059a4b6 100644
--- a/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts
+++ b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts
@@ -62,7 +62,7 @@ export abstract class AbstractSetDataType extends DataType {
// Create a new scope in the calldata, before descending into the members of this set.
calldata.startScope();
let value: any[] | object;
- if (rules.structsAsObjects && !this._isArray) {
+ if (rules.shouldConvertStructsToObjects && !this._isArray) {
// Construct an object with values for each member of the set.
value = {};
_.each(this._memberIndexByName, (idx: number, key: string) => {
diff --git a/packages/utils/src/abi_encoder/calldata/calldata.ts b/packages/utils/src/abi_encoder/calldata/calldata.ts
index 5f3eee94a..b08fb71ce 100644
--- a/packages/utils/src/abi_encoder/calldata/calldata.ts
+++ b/packages/utils/src/abi_encoder/calldata/calldata.ts
@@ -49,7 +49,7 @@ export class Calldata {
throw new Error('expected root');
}
// Optimize, if flag set
- if (this._rules.optimize) {
+ if (this._rules.shouldOptimize) {
this._optimize();
}
// Set offsets
@@ -60,7 +60,9 @@ export class Calldata {
offset += block.getSizeInBytes();
}
// Generate hex string
- const hexString = this._rules.annotate ? this._toHumanReadableCallData() : this._toEvmCompatibeCallDataHex();
+ const hexString = this._rules.shouldAnnotate
+ ? this._toHumanReadableCallData()
+ : this._toEvmCompatibeCallDataHex();
return hexString;
}
/**
diff --git a/packages/utils/src/abi_encoder/utils/constants.ts b/packages/utils/src/abi_encoder/utils/constants.ts
index 2f43ba04d..36de2dd4f 100644
--- a/packages/utils/src/abi_encoder/utils/constants.ts
+++ b/packages/utils/src/abi_encoder/utils/constants.ts
@@ -11,7 +11,7 @@ export const constants = {
HEX_SELECTOR_BYTE_OFFSET_IN_CALLDATA: 0,
// Disable no-object-literal-type-assertion so we can enforce cast
/* tslint:disable no-object-literal-type-assertion */
- DEFAULT_DECODING_RULES: { structsAsObjects: false } as DecodingRules,
- DEFAULT_ENCODING_RULES: { optimize: true, annotate: false } as EncodingRules,
+ DEFAULT_DECODING_RULES: { shouldConvertStructsToObjects: false } as DecodingRules,
+ DEFAULT_ENCODING_RULES: { shouldOptimize: true, shouldAnnotate: false } as EncodingRules,
/* tslint:enable no-object-literal-type-assertion */
};
diff --git a/packages/utils/src/abi_encoder/utils/rules.ts b/packages/utils/src/abi_encoder/utils/rules.ts
index 31471e97a..c8d83c3ba 100644
--- a/packages/utils/src/abi_encoder/utils/rules.ts
+++ b/packages/utils/src/abi_encoder/utils/rules.ts
@@ -1,8 +1,8 @@
export interface DecodingRules {
- structsAsObjects: boolean;
+ shouldConvertStructsToObjects: boolean;
}
export interface EncodingRules {
- optimize?: boolean;
- annotate?: boolean;
+ shouldOptimize?: boolean;
+ shouldAnnotate?: boolean;
}
diff --git a/packages/utils/test/abi_encoder/evm_data_types_test.ts b/packages/utils/test/abi_encoder/evm_data_types_test.ts
index 7185851a8..55d582d10 100644
--- a/packages/utils/test/abi_encoder/evm_data_types_test.ts
+++ b/packages/utils/test/abi_encoder/evm_data_types_test.ts
@@ -10,7 +10,7 @@ chaiSetup.configure();
const expect = chai.expect;
describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
- const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately.
+ const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately.
describe('Array', () => {
it('Fixed size; Static elements', async () => {
// Create DataType object
@@ -194,7 +194,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
'0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000001';
expect(encodedArgs).to.be.equal(expectedEncodedArgs);
// Decode Encoded Args and validate result
- const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true };
const decodedArgs = dataType.decode(encodedArgs, decodingRules);
expect(decodedArgs).to.be.deep.equal(args);
});
@@ -214,7 +214,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
'0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000';
expect(encodedArgs).to.be.equal(expectedEncodedArgs);
// Decode Encoded Args and validate result
- const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true };
const decodedArgs = dataType.decode(encodedArgs, decodingRules);
expect(decodedArgs).to.be.deep.equal(args);
});
@@ -234,7 +234,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
'0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002';
expect(encodedArgs).to.be.equal(expectedEncodedArgs);
// Decode Encoded Args and validate result
- const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true };
const decodedArgs = dataType.decode(encodedArgs, decodingRules);
expect(decodedArgs).to.be.deep.equal(args);
});
@@ -254,7 +254,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002';
expect(encodedArgs).to.be.equal(expectedEncodedArgs);
// Decode Encoded Args and validate result
- const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true };
const decodedArgs = dataType.decode(encodedArgs, decodingRules);
expect(decodedArgs).to.be.deep.equal(args);
});
@@ -276,7 +276,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
'0x0102030400000000000000000000000000000000000000000000000000000000050607080000000000000000000000000000000000000000000000000000000009101112000000000000000000000000000000000000000000000000000000001314151600000000000000000000000000000000000000000000000000000000';
expect(encodedArgs).to.be.equal(expectedEncodedArgs);
// Decode Encoded Args and validate result
- const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true };
const decodedArgs = dataType.decode(encodedArgs, decodingRules);
expect(decodedArgs).to.be.deep.equal(args);
});
@@ -298,7 +298,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
'0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040506070800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004091011120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041314151600000000000000000000000000000000000000000000000000000000';
expect(encodedArgs).to.be.equal(expectedEncodedArgs);
// Decode Encoded Args and validate result
- const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true };
const decodedArgs = dataType.decode(encodedArgs, decodingRules);
expect(decodedArgs).to.be.deep.equal(args);
});
@@ -328,7 +328,7 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
'0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c6421000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008abcdef0123456789000000000000000000000000000000000000000000000000';
expect(encodedArgs).to.be.equal(expectedEncodedArgs);
// Decode Encoded Args and validate result
- const decodingRules: AbiEncoder.DecodingRules = { structsAsObjects: true };
+ const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true };
const decodedArgs = dataType.decode(encodedArgs, decodingRules);
expect(decodedArgs).to.be.deep.equal(args);
});
diff --git a/packages/utils/test/abi_encoder/methods_test.ts b/packages/utils/test/abi_encoder/methods_test.ts
index 837020883..a0525967e 100644
--- a/packages/utils/test/abi_encoder/methods_test.ts
+++ b/packages/utils/test/abi_encoder/methods_test.ts
@@ -10,7 +10,7 @@ chaiSetup.configure();
const expect = chai.expect;
describe('ABI Encoder: Method Encoding / Decoding', () => {
- const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately.
+ const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately.
it('Types with default widths', async () => {
// Generate calldata
const method = new AbiEncoder.Method(AbiSamples.typesWithDefaultWidthsAbi);
@@ -360,7 +360,7 @@ describe('ABI Encoder: Method Encoding / Decoding', () => {
'0x4b49031c000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000440000000000000000000000000000000000000000000000000000000000000088000000000000000000000000000000000000000000000000000000000000009800000000000000000000000000000000000000000000000000000000000000ae0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000163874563783498732482743928742389723894723984700000000000000000000000000000000000000000000000000000000000000000000000000000000006e72834723982374239847239847298472489274987489742847289472394874987498478743294237434923473298472398423748923748923748923472389472894789474893742894728947389427498237432987423894723894732894723894372498237498237428934723980000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027283473298473248923749238742398742398472894729843278942374982374892374892743982000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000b736f6d6520737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000013736f6d6520616e6f7468657220737472696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024746865726520617265206a75737420746f6f206d616e7920737472696e6773757020696e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002079616c6c2067686f6e6e61206d616b65206d65206c6f7365206d79206d696e640000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000034746865206c6974746c6520706970696e67207069706572207069706564206120706970696e6720706970706572207061707065720000000000000000000000000000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0ac511500000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000081746865206b6964206b6e6f777320686f7720746f20777269746520706f656d732c20776861742063616e204920736179202d2d2049206775657373207468657265732061206c6f74204920636f756c642073617920746f2074727920746f2066696c6c2074686973206c696e6520776974682061206c6f74206f6620746578742e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003d69d500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000000000000000000000000000000000000000004e616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894723843743289742389472398473289472348927489274894738427428947389facdea0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000089b51500000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000100000000000000000000000000746dafa5ebde1f4699f4981d3221892e41d24895000000000000000000000000000000000000000000000000000000000000004e6b73646873616a6b646873616a6b646861646a6b617368646a6b73616468616a6b646873616a6b64687361646a6b616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002829384723894398473289472348927489272384374328974238947274894738427428947389facde100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fa3150000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000089571d322189e415ebde1f4699f498d24246dafa000000000000000000000000000000000000000000000000000000000000004e73646873616a6b646873616a6b646861646a6b617368646a616b64686a61736a6b646861736a6b6c647368646a6168646b6a73616864616a6b6b73616468616a6b646873616a6b64687361646a6b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002838947238437432829384729742389472398473289472348927489274894738427428947389facdef000000000000000000000000000000000000000000000000';
expect(calldata).to.be.equal(expectedCalldata);
// Validate decoding
- const decodedValue = method.decode(calldata, { structsAsObjects: true });
+ const decodedValue = method.decode(calldata, { shouldConvertStructsToObjects: true });
expect(decodedValue).to.be.deep.equal(args);
});
});
diff --git a/packages/utils/test/abi_encoder/optimizer_test.ts b/packages/utils/test/abi_encoder/optimizer_test.ts
index 18aa6549a..ee0654ec3 100644
--- a/packages/utils/test/abi_encoder/optimizer_test.ts
+++ b/packages/utils/test/abi_encoder/optimizer_test.ts
@@ -10,7 +10,7 @@ chaiSetup.configure();
const expect = chai.expect;
describe('ABI Encoder: Optimized Method Encoding/Decoding', () => {
- const encodingRules: AbiEncoder.EncodingRules = { optimize: true };
+ const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: true };
it('Duplicate Dynamic Arrays with Static Elements', async () => {
// Generate calldata
const method = new AbiEncoder.Method(OptimizedAbis.duplicateDynamicArraysWithStaticElements);
@@ -206,7 +206,7 @@ describe('ABI Encoder: Optimized Method Encoding/Decoding', () => {
const twoDimArray2 = twoDimArray1;
const args = [twoDimArray1, twoDimArray2];
// Validata calldata
- const optimizedCalldata = method.encode(args, { optimize: false });
+ const optimizedCalldata = method.encode(args, { shouldOptimize: false });
const expectedOptimizedCalldata =
'0x0d28c4f9000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000003466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003426172000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a61610000000000000000000000000000000000000000000000000000000000';
expect(optimizedCalldata).to.be.equal(expectedOptimizedCalldata);
diff --git a/packages/utils/test/abi_encoder/return_values_test.ts b/packages/utils/test/abi_encoder/return_values_test.ts
index a8cdd6ca3..104c7f5db 100644
--- a/packages/utils/test/abi_encoder/return_values_test.ts
+++ b/packages/utils/test/abi_encoder/return_values_test.ts
@@ -10,7 +10,7 @@ chaiSetup.configure();
const expect = chai.expect;
describe('ABI Encoder: Return Value Encoding/Decoding', () => {
- const encodingRules: AbiEncoder.EncodingRules = { optimize: false }; // optimizer is tested separately.
+ const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately.
it('No Return Value', async () => {
// Decode return value
const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues);
diff --git a/yarn.lock b/yarn.lock
index 0953cbc74..b86969e3b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -507,7 +507,7 @@
lodash "^4.17.5"
uuid "^3.1.0"
-"@0x/order-utils@^2.0.0", "@0x/order-utils@^2.0.1":
+"@0x/order-utils@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@0x/order-utils/-/order-utils-2.0.1.tgz#8c46d7aeb9e2cce54a0822824c12427cbe5f7eb4"
dependencies:
@@ -2100,10 +2100,6 @@ aes-js@^0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-0.2.4.tgz#94b881ab717286d015fa219e08fb66709dda5a3d"
-aes-js@^3.1.1:
- version "3.1.2"
- resolved "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a"
-
agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
@@ -3669,7 +3665,7 @@ bs-logger@0.x:
dependencies:
fast-json-stable-stringify "^2.0.0"
-bs58@=4.0.1, bs58@^4.0.0:
+bs58@=4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
dependencies:
@@ -3692,14 +3688,6 @@ bs58check@^1.0.8:
bs58 "^3.1.0"
create-hash "^1.1.0"
-bs58check@^2.1.2:
- version "2.1.2"
- resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc"
- dependencies:
- bs58 "^4.0.0"
- create-hash "^1.1.0"
- safe-buffer "^5.1.2"
-
bser@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
@@ -6410,19 +6398,6 @@ ethereumjs-wallet@0.6.0:
utf8 "^2.1.1"
uuid "^2.0.1"
-ethereumjs-wallet@~0.6.0:
- version "0.6.2"
- resolved "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-0.6.2.tgz#67244b6af3e8113b53d709124b25477b64aeccda"
- dependencies:
- aes-js "^3.1.1"
- bs58check "^2.1.2"
- ethereumjs-util "^5.2.0"
- hdkey "^1.0.0"
- safe-buffer "^5.1.2"
- scrypt.js "^0.2.0"
- utf8 "^3.0.0"
- uuid "^3.3.2"
-
ethers@~4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.4.tgz#d3f85e8b27f4b59537e06526439b0fb15b44dc65"
@@ -7339,7 +7314,7 @@ ganache-core@0xProject/ganache-core#monorepo-dep:
ethereumjs-tx "0xProject/ethereumjs-tx#fake-tx-include-signature-by-default"
ethereumjs-util "^5.2.0"
ethereumjs-vm "2.3.5"
- ethereumjs-wallet "~0.6.0"
+ ethereumjs-wallet "0.6.0"
fake-merkle-patricia-tree "~1.0.1"
heap "~0.2.6"
js-scrypt "^0.2.0"
@@ -8077,14 +8052,6 @@ hdkey@^0.7.0, hdkey@^0.7.1:
coinstring "^2.0.0"
secp256k1 "^3.0.1"
-hdkey@^1.0.0:
- version "1.1.0"
- resolved "https://registry.npmjs.org/hdkey/-/hdkey-1.1.0.tgz#e74e7b01d2c47f797fa65d1d839adb7a44639f29"
- dependencies:
- coinstring "^2.0.0"
- safe-buffer "^5.1.1"
- secp256k1 "^3.0.1"
-
he@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
@@ -16679,10 +16646,6 @@ utf8@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.2.tgz#1fa0d9270e9be850d9b05027f63519bf46457d96"
-utf8@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1"
-
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"