Merge pull request #1363 from 0xProject/feat/order-utils/abiEncoder
Add MAP support to order-utils, order-watcher, and types
This commit is contained in:
commit
3cdb85606a
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -1,4 +1,14 @@
|
||||
[
|
||||
{
|
||||
"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": [
|
||||
|
@ -79,10 +79,13 @@ export {
|
||||
OrderStateInvalid,
|
||||
OrderState,
|
||||
AssetProxyId,
|
||||
AssetData,
|
||||
SingleAssetData,
|
||||
ERC20AssetData,
|
||||
ERC721AssetData,
|
||||
MultiAssetData,
|
||||
MultiAssetDataWithRecursiveDecoding,
|
||||
SignatureType,
|
||||
ObjectMap,
|
||||
OrderRelevantState,
|
||||
Stats,
|
||||
} from '@0x/types';
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add support for MultiAssetProxy",
|
||||
"pr": 1363
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.0.16",
|
||||
"changes": [
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,14 @@
|
||||
[
|
||||
{
|
||||
"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": [
|
||||
|
@ -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) {
|
||||
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}`,
|
||||
);
|
||||
}
|
||||
const assetProxyId = ethUtil.bufferToHex(data.slice(0, constants.SELECTOR_LENGTH));
|
||||
if (assetProxyId !== AssetProxyId.ERC721) {
|
||||
throw new Error(
|
||||
`Could not decode ERC721 Asset Data. Expected Asset Proxy Id to be ERC721 (${
|
||||
AssetProxyId.ERC721
|
||||
}), but got ${assetProxyId}`,
|
||||
);
|
||||
}
|
||||
const [tokenAddress, tokenId] = ethAbi.rawDecode(['address', 'uint256'], data.slice(constants.SELECTOR_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,
|
||||
tokenAddress: ethUtil.addHexPrefix(tokenAddress),
|
||||
tokenId: new BigNumber(tokenId.toString()),
|
||||
// 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(
|
||||
`Invalid MultiAsset arguments. Expected length of 'amounts' (${
|
||||
amounts.length
|
||||
}) to equal length of 'nestedAssetData' (${nestedAssetData.length})`,
|
||||
);
|
||||
}
|
||||
_.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(
|
||||
`Invalid MultiAsset assetData. Expected length of 'amounts' (${
|
||||
amounts.length
|
||||
}) to equal length of 'nestedAssetData' (${nestedAssetData.length})`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
assetProxyId,
|
||||
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}`);
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -34,11 +34,14 @@ export {
|
||||
OrderRelevantState,
|
||||
OrderState,
|
||||
ECSignature,
|
||||
AssetData,
|
||||
SingleAssetData,
|
||||
ERC20AssetData,
|
||||
ERC721AssetData,
|
||||
MultiAssetData,
|
||||
MultiAssetDataWithRecursiveDecoding,
|
||||
AssetProxyId,
|
||||
SignatureType,
|
||||
ObjectMap,
|
||||
OrderStateValid,
|
||||
OrderStateInvalid,
|
||||
ExchangeContractErrs,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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 &&
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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 = {
|
||||
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "2.4.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add support for `MultiAssetProxy`",
|
||||
"pr": 1363
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.3.0",
|
||||
"changes": [
|
||||
|
@ -7,6 +7,7 @@ export {
|
||||
OrderStateInvalid,
|
||||
OrderState,
|
||||
ExchangeContractErrs,
|
||||
ObjectMap,
|
||||
OrderRelevantState,
|
||||
Stats,
|
||||
} from '@0x/types';
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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' })
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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',
|
||||
}
|
||||
|
@ -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`);
|
||||
}
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
export * from './big_number';
|
||||
export * from './number_to_bigint';
|
||||
export * from './asset_proxy_id_types';
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -18,6 +18,10 @@
|
||||
{
|
||||
"note": "Add RevertReasons for DutchAuction contract",
|
||||
"pr": 1225
|
||||
},
|
||||
{
|
||||
"note": "Add MultiAsset types",
|
||||
"pr": 1363
|
||||
}
|
||||
],
|
||||
"timestamp": 1544570656
|
||||
|
@ -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 {
|
||||
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "2.1.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add `should` prefix to names of properties in EncodingRules and DecodingRules",
|
||||
"pr": 1363
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"changes": [
|
||||
|
@ -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) => {
|
||||
|
@ -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;
|
||||
}
|
||||
/**
|
||||
|
@ -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 */
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
export interface DecodingRules {
|
||||
structsAsObjects: boolean;
|
||||
shouldConvertStructsToObjects: boolean;
|
||||
}
|
||||
|
||||
export interface EncodingRules {
|
||||
optimize?: boolean;
|
||||
annotate?: boolean;
|
||||
shouldOptimize?: boolean;
|
||||
shouldAnnotate?: boolean;
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
|
File diff suppressed because one or more lines are too long
@ -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);
|
||||
|
@ -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);
|
||||
|
43
yarn.lock
43
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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user