Merge pull request #2269 from 0xProject/refactor/balance-stores
BalanceStore++
This commit is contained in:
commit
59a38a8db0
@ -63,6 +63,7 @@
|
|||||||
"chai-as-promised": "^7.1.0",
|
"chai-as-promised": "^7.1.0",
|
||||||
"chai-bignumber": "^3.0.0",
|
"chai-bignumber": "^3.0.0",
|
||||||
"dirty-chai": "^2.0.1",
|
"dirty-chai": "^2.0.1",
|
||||||
|
"js-combinatorics": "^0.5.3",
|
||||||
"make-promises-safe": "^1.1.0",
|
"make-promises-safe": "^1.1.0",
|
||||||
"mocha": "^6.2.0",
|
"mocha": "^6.2.0",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export * from './artifacts';
|
export * from './artifacts';
|
||||||
export * from './wrappers';
|
export * from './wrappers';
|
||||||
|
export * from '../test/balance_stores';
|
||||||
export * from '../test/utils';
|
export * from '../test/utils';
|
||||||
export * from './wrapper_interfaces';
|
export * from './wrapper_interfaces';
|
||||||
|
@ -1,53 +1,47 @@
|
|||||||
import {
|
|
||||||
artifacts as proxyArtifacts,
|
|
||||||
ERC1155ProxyWrapper,
|
|
||||||
ERC20Wrapper,
|
|
||||||
ERC721Wrapper,
|
|
||||||
} from '@0x/contracts-asset-proxy';
|
|
||||||
import { artifacts as erc20Artifacts } from '@0x/contracts-erc20';
|
|
||||||
import { artifacts as erc721Artifacts } from '@0x/contracts-erc721';
|
|
||||||
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
|
import { ReferenceFunctions as LibReferenceFunctions } from '@0x/contracts-exchange-libs';
|
||||||
import {
|
import {
|
||||||
constants,
|
constants,
|
||||||
expect,
|
expect,
|
||||||
FillEventArgs,
|
FillEventArgs,
|
||||||
filterLogsToArguments,
|
filterLogsToArguments,
|
||||||
LogDecoder,
|
|
||||||
OrderStatus,
|
OrderStatus,
|
||||||
orderUtils,
|
orderUtils,
|
||||||
Web3ProviderEngine,
|
|
||||||
} from '@0x/contracts-test-utils';
|
} from '@0x/contracts-test-utils';
|
||||||
import { orderHashUtils } from '@0x/order-utils';
|
import { orderHashUtils } from '@0x/order-utils';
|
||||||
import { FillResults, SignedOrder } from '@0x/types';
|
import { FillResults, SignedOrder } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||||
import { TransactionReceiptWithDecodedLogs, ZeroExProvider } from 'ethereum-types';
|
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { artifacts, ExchangeContract } from '../../src';
|
import {
|
||||||
import { BalanceStore } from '../balance_stores/balance_store';
|
BalanceStore,
|
||||||
import { BlockchainBalanceStore } from '../balance_stores/blockchain_balance_store';
|
BlockchainBalanceStore,
|
||||||
import { LocalBalanceStore } from '../balance_stores/local_balance_store';
|
ExchangeContract,
|
||||||
|
LocalBalanceStore,
|
||||||
|
TokenContractsByName,
|
||||||
|
TokenIds,
|
||||||
|
TokenOwnersByName,
|
||||||
|
} from '../../src';
|
||||||
|
|
||||||
export class FillOrderWrapper {
|
export class FillOrderWrapper {
|
||||||
private readonly _exchange: ExchangeContract;
|
private readonly _exchange: ExchangeContract;
|
||||||
private readonly _blockchainBalanceStore: BlockchainBalanceStore;
|
private readonly _blockchainBalanceStore: BlockchainBalanceStore;
|
||||||
private readonly _web3Wrapper: Web3Wrapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simulates matching two orders by transferring amounts defined in
|
* Locally simulates filling an order.
|
||||||
* `transferAmounts` and returns the results.
|
* @param txReceipt Transaction receipt from the actual fill, needed to update eth balance
|
||||||
* @param orders The orders being matched and their filled states.
|
* @param signedOrder The order being filled.
|
||||||
* @param takerAddress Address of taker (the address who matched the two orders)
|
* @param takerAddress Address of taker (the address who matched the two orders)
|
||||||
* @param tokenBalances Current token balances.
|
* @param opts Optionally specifies the amount to fill.
|
||||||
* @param transferAmounts Amounts to transfer during the simulation.
|
* @param initBalanceStore Account balances prior to the fill.
|
||||||
* @return The new account balances and fill events that occurred during the match.
|
* @return The expected account balances, fill results, and fill events.
|
||||||
*/
|
*/
|
||||||
public static simulateFillOrder(
|
public static simulateFillOrder(
|
||||||
|
txReceipt: TransactionReceiptWithDecodedLogs,
|
||||||
signedOrder: SignedOrder,
|
signedOrder: SignedOrder,
|
||||||
takerAddress: string,
|
takerAddress: string,
|
||||||
opts: { takerAssetFillAmount?: BigNumber } = {},
|
|
||||||
initBalanceStore: BalanceStore,
|
initBalanceStore: BalanceStore,
|
||||||
|
opts: { takerAssetFillAmount?: BigNumber } = {},
|
||||||
): [FillResults, FillEventArgs, BalanceStore] {
|
): [FillResults, FillEventArgs, BalanceStore] {
|
||||||
const balanceStore = LocalBalanceStore.create(initBalanceStore);
|
const balanceStore = LocalBalanceStore.create(initBalanceStore);
|
||||||
const takerAssetFillAmount =
|
const takerAssetFillAmount =
|
||||||
@ -88,6 +82,7 @@ export class FillOrderWrapper {
|
|||||||
fillResults.makerFeePaid,
|
fillResults.makerFeePaid,
|
||||||
signedOrder.makerFeeAssetData,
|
signedOrder.makerFeeAssetData,
|
||||||
);
|
);
|
||||||
|
balanceStore.burnGas(txReceipt.from, constants.DEFAULT_GAS_PRICE * txReceipt.gasUsed);
|
||||||
return [fillResults, fillEvent, balanceStore];
|
return [fillResults, fillEvent, balanceStore];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,22 +121,19 @@ export class FillOrderWrapper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param exchangeContract Insstance of the deployed exchange contract
|
* @param exchangeContract Instance of the deployed exchange contract.
|
||||||
* @param erc20Wrapper The ERC20 Wrapper used to interface with deployed erc20 tokens.
|
* @param tokenOwnersByName The addresses of token owners to assert the balances of.
|
||||||
* @param erc721Wrapper The ERC721 Wrapper used to interface with deployed erc20 tokens.
|
* @param tokenContractsByName The contracts of tokens to assert the balances of.
|
||||||
* @param erc1155ProxyWrapper The ERC1155 Proxy Wrapper used to interface with deployed erc20 tokens.
|
* @param tokenIds The tokenIds of ERC721 and ERC1155 assets to assert the balances of.
|
||||||
* @param provider Web3 provider to be used by a `Web3Wrapper` instance
|
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
exchangeContract: ExchangeContract,
|
exchangeContract: ExchangeContract,
|
||||||
erc20Wrapper: ERC20Wrapper,
|
tokenOwnersByName: TokenOwnersByName,
|
||||||
erc721Wrapper: ERC721Wrapper,
|
tokenContractsByName: Partial<TokenContractsByName>,
|
||||||
erc1155ProxyWrapper: ERC1155ProxyWrapper,
|
tokenIds: Partial<TokenIds>,
|
||||||
provider: Web3ProviderEngine | ZeroExProvider,
|
|
||||||
) {
|
) {
|
||||||
this._exchange = exchangeContract;
|
this._exchange = exchangeContract;
|
||||||
this._blockchainBalanceStore = new BlockchainBalanceStore(erc20Wrapper, erc721Wrapper, erc1155ProxyWrapper);
|
this._blockchainBalanceStore = new BlockchainBalanceStore(tokenOwnersByName, tokenContractsByName, tokenIds);
|
||||||
this._web3Wrapper = new Web3Wrapper(provider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,31 +163,32 @@ export class FillOrderWrapper {
|
|||||||
// Assert init state of exchange
|
// Assert init state of exchange
|
||||||
await this._assertOrderStateAsync(signedOrder, initTakerAssetFilledAmount);
|
await this._assertOrderStateAsync(signedOrder, initTakerAssetFilledAmount);
|
||||||
// Simulate and execute fill then assert outputs
|
// Simulate and execute fill then assert outputs
|
||||||
|
const [fillResults, fillEvent, txReceipt] = await this._fillOrderAsync(signedOrder, from, opts);
|
||||||
const [
|
const [
|
||||||
simulatedFillResults,
|
simulatedFillResults,
|
||||||
simulatedFillEvent,
|
simulatedFillEvent,
|
||||||
simulatedFinalBalanceStore,
|
simulatedFinalBalanceStore,
|
||||||
] = FillOrderWrapper.simulateFillOrder(signedOrder, from, opts, this._blockchainBalanceStore);
|
] = FillOrderWrapper.simulateFillOrder(txReceipt, signedOrder, from, this._blockchainBalanceStore, opts);
|
||||||
const [fillResults, fillEvent] = await this._fillOrderAsync(signedOrder, from, opts);
|
|
||||||
// Assert state transition
|
// Assert state transition
|
||||||
expect(simulatedFillResults, 'Fill Results').to.be.deep.equal(fillResults);
|
expect(simulatedFillResults, 'Fill Results').to.be.deep.equal(fillResults);
|
||||||
expect(simulatedFillEvent, 'Fill Events').to.be.deep.equal(fillEvent);
|
expect(simulatedFillEvent, 'Fill Events').to.be.deep.equal(fillEvent);
|
||||||
const areBalancesEqual = BalanceStore.isEqual(simulatedFinalBalanceStore, this._blockchainBalanceStore);
|
|
||||||
expect(areBalancesEqual, 'Balances After Fill').to.be.true();
|
await this._blockchainBalanceStore.updateBalancesAsync();
|
||||||
|
this._blockchainBalanceStore.assertEquals(simulatedFinalBalanceStore);
|
||||||
|
|
||||||
// Assert end state of exchange
|
// Assert end state of exchange
|
||||||
const finalTakerAssetFilledAmount = initTakerAssetFilledAmount.plus(fillResults.takerAssetFilledAmount);
|
const finalTakerAssetFilledAmount = initTakerAssetFilledAmount.plus(fillResults.takerAssetFilledAmount);
|
||||||
await this._assertOrderStateAsync(signedOrder, finalTakerAssetFilledAmount);
|
await this._assertOrderStateAsync(signedOrder, finalTakerAssetFilledAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills an order on-chain. As an optimization this function auto-updates the blockchain balance store
|
* Fills an order on-chain.
|
||||||
* used by this contract.
|
|
||||||
*/
|
*/
|
||||||
protected async _fillOrderAsync(
|
protected async _fillOrderAsync(
|
||||||
signedOrder: SignedOrder,
|
signedOrder: SignedOrder,
|
||||||
from: string,
|
from: string,
|
||||||
opts: { takerAssetFillAmount?: BigNumber } = {},
|
opts: { takerAssetFillAmount?: BigNumber } = {},
|
||||||
): Promise<[FillResults, FillEventArgs]> {
|
): Promise<[FillResults, FillEventArgs, TransactionReceiptWithDecodedLogs]> {
|
||||||
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
|
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
|
||||||
const fillResults = await this._exchange.fillOrder.callAsync(
|
const fillResults = await this._exchange.fillOrder.callAsync(
|
||||||
params.order,
|
params.order,
|
||||||
@ -203,33 +196,21 @@ export class FillOrderWrapper {
|
|||||||
params.signature,
|
params.signature,
|
||||||
{ from },
|
{ from },
|
||||||
);
|
);
|
||||||
// @TODO: Replace with `awaitTransactionAsync` once `development` is merged into `3.0` branch
|
const txReceipt = await this._exchange.fillOrder.awaitTransactionSuccessAsync(
|
||||||
const txHash = await this._exchange.fillOrder.sendTransactionAsync(
|
|
||||||
params.order,
|
params.order,
|
||||||
params.takerAssetFillAmount,
|
params.takerAssetFillAmount,
|
||||||
params.signature,
|
params.signature,
|
||||||
{ from },
|
{ from },
|
||||||
);
|
);
|
||||||
const logDecoder = new LogDecoder(this._web3Wrapper, {
|
|
||||||
...artifacts,
|
|
||||||
...proxyArtifacts,
|
|
||||||
...erc20Artifacts,
|
|
||||||
...erc721Artifacts,
|
|
||||||
});
|
|
||||||
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(txHash);
|
|
||||||
const fillEvent = FillOrderWrapper._extractFillEventsfromReceipt(txReceipt)[0];
|
const fillEvent = FillOrderWrapper._extractFillEventsfromReceipt(txReceipt)[0];
|
||||||
await this._blockchainBalanceStore.updateBalancesAsync();
|
return [fillResults, fillEvent, txReceipt];
|
||||||
return [fillResults, fillEvent];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts that the provided order's fill amount and order status
|
* Asserts that the provided order's fill amount and order status
|
||||||
* are the expected values.
|
* are the expected values.
|
||||||
* @param order The order to verify for a correct state.
|
* @param order The order to verify for a correct state.
|
||||||
* @param expectedFilledAmount The amount that the order should
|
* @param expectedFilledAmount The amount that the order should have been filled.
|
||||||
* have been filled.
|
|
||||||
* @param side The side that the provided order should be matched on.
|
|
||||||
* @param exchangeWrapper The ExchangeWrapper instance.
|
|
||||||
*/
|
*/
|
||||||
private async _assertOrderStateAsync(
|
private async _assertOrderStateAsync(
|
||||||
order: SignedOrder,
|
order: SignedOrder,
|
||||||
|
@ -1,92 +1,133 @@
|
|||||||
import { ERC1155Holdings, ERC1155HoldingsByOwner, TokenBalances } from '@0x/contracts-test-utils';
|
import { BaseContract } from '@0x/base-contract';
|
||||||
|
import { expect, TokenBalances } from '@0x/contracts-test-utils';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
/**
|
import { TokenAddresses, TokenContractsByName, TokenOwnersByName } from './types';
|
||||||
* Note - this should live in `@0x/contracts-test-utils` but that would create a circular
|
|
||||||
* dependency in `BlockchainBlanceStore`. We should be able to mvoe this once we can rely
|
|
||||||
* solely on auto-generated wrappers opposed to the existing ERC20Wrapper, ERC721Wrapper,
|
|
||||||
* and ERC1155Wrapper.
|
|
||||||
*/
|
|
||||||
export class BalanceStore {
|
export class BalanceStore {
|
||||||
protected _balances: TokenBalances;
|
protected _balances: TokenBalances;
|
||||||
|
protected _tokenAddresses: TokenAddresses;
|
||||||
|
protected _ownerAddresses: string[];
|
||||||
|
private _addressNames: {
|
||||||
|
[address: string]: string;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true iff balance stores do have the same entries.
|
* Constructor.
|
||||||
* @param lhs First balance store to compare
|
* @param tokenOwnersByName Addresses of token owners to track balances of.
|
||||||
* @param rhs Second balance store to compare
|
* @param tokenContractsByName Contracts of tokens to track balances of.
|
||||||
*/
|
*/
|
||||||
public static isEqual(lhs: BalanceStore, rhs: BalanceStore): boolean {
|
public constructor(tokenOwnersByName: TokenOwnersByName, tokenContractsByName: Partial<TokenContractsByName>) {
|
||||||
return _.isEqual(lhs.getRawBalances(), rhs.getRawBalances());
|
this._balances = { erc20: {}, erc721: {}, erc1155: {}, eth: {} };
|
||||||
|
this._ownerAddresses = Object.values(tokenOwnersByName);
|
||||||
|
|
||||||
|
_.defaults(tokenContractsByName, { erc20: {}, erc721: {}, erc1155: {} });
|
||||||
|
const tokenAddressesByName = _.mapValues(
|
||||||
|
{ ...tokenContractsByName.erc20, ...tokenContractsByName.erc721, ...tokenContractsByName.erc1155 },
|
||||||
|
contract => (contract as BaseContract).address,
|
||||||
|
);
|
||||||
|
this._addressNames = _.invert({ ...tokenOwnersByName, ...tokenAddressesByName });
|
||||||
|
|
||||||
|
this._tokenAddresses = {
|
||||||
|
erc20: Object.values(tokenContractsByName.erc20 || {}).map(contract => contract.address),
|
||||||
|
erc721: Object.values(tokenContractsByName.erc721 || {}).map(contract => contract.address),
|
||||||
|
erc1155: Object.values(tokenContractsByName.erc1155 || {}).map(contract => contract.address),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throws iff balance stores do not have the same entries.
|
* Throws iff balance stores do not have the same entries.
|
||||||
* @param lhs First balance store to compare
|
* @param rhs Balance store to compare to
|
||||||
* @param rhs Second balance store to compare
|
|
||||||
*/
|
*/
|
||||||
public static assertEqual(lhs: BalanceStore, rhs: BalanceStore): void {
|
public assertEquals(rhs: BalanceStore): void {
|
||||||
if (!BalanceStore.isEqual(lhs, rhs)) {
|
this._assertEthBalancesEqual(rhs);
|
||||||
throw new Error(`Balance stores are not equal:\n\nLeft:\n${lhs}\n\nRight:\n${rhs}`);
|
this._assertErc20BalancesEqual(rhs);
|
||||||
}
|
this._assertErc721BalancesEqual(rhs);
|
||||||
|
this._assertErc1155BalancesEqual(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restructures `ERC1155HoldingsByOwner` to be compatible with `TokenBalances.erc1155`.
|
* Copies from an existing balance store.
|
||||||
* @param erc1155HoldingsByOwner Holdings returned by `ERC1155ProxyWrapper.getBalancesAsync()`.
|
* @param balanceStore to copy from.
|
||||||
*/
|
*/
|
||||||
protected static _transformERC1155Holdings(erc1155HoldingsByOwner: ERC1155HoldingsByOwner): ERC1155Holdings {
|
public cloneFrom(balanceStore: BalanceStore): void {
|
||||||
const result = {};
|
|
||||||
for (const owner of _.keys(erc1155HoldingsByOwner.fungible)) {
|
|
||||||
for (const contract of _.keys(erc1155HoldingsByOwner.fungible[owner])) {
|
|
||||||
_.set(result as any, [owner, contract, 'fungible'], erc1155HoldingsByOwner.fungible[owner][contract]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const owner of _.keys(erc1155HoldingsByOwner.nonFungible)) {
|
|
||||||
for (const contract of _.keys(erc1155HoldingsByOwner.nonFungible[owner])) {
|
|
||||||
const tokenIds = _.flatten(_.values(erc1155HoldingsByOwner.nonFungible[owner][contract]));
|
|
||||||
_.set(result as any, [owner, contract, 'nonFungible'], _.uniqBy(tokenIds, v => v.toString(10)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encodes token balances in a way that they can be compared by lodash.
|
|
||||||
*/
|
|
||||||
protected static _encodeTokenBalances(obj: any): any {
|
|
||||||
if (!_.isPlainObject(obj)) {
|
|
||||||
if (BigNumber.isBigNumber(obj)) {
|
|
||||||
return obj.toString(10);
|
|
||||||
}
|
|
||||||
if (_.isArray(obj)) {
|
|
||||||
return _.sortBy(obj, v => BalanceStore._encodeTokenBalances(v));
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
const keys = _.keys(obj).sort();
|
|
||||||
return _.zip(keys, keys.map(k => BalanceStore._encodeTokenBalances(obj[k])));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*/
|
|
||||||
public constructor() {
|
|
||||||
this._balances = { erc20: {}, erc721: {}, erc1155: {}, eth: {} };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies the balance from an existing balance store.
|
|
||||||
* @param balanceStore to copy balances from.
|
|
||||||
*/
|
|
||||||
public copyBalancesFrom(balanceStore: BalanceStore): void {
|
|
||||||
this._balances = _.cloneDeep(balanceStore._balances);
|
this._balances = _.cloneDeep(balanceStore._balances);
|
||||||
|
this._tokenAddresses = _.cloneDeep(balanceStore._tokenAddresses);
|
||||||
|
this._ownerAddresses = _.cloneDeep(balanceStore._ownerAddresses);
|
||||||
|
this._addressNames = _.cloneDeep(balanceStore._addressNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the raw `TokenBalances` that this class encapsulates.
|
* Returns the human-readable name for the given address, if it exists.
|
||||||
|
* @param address The address to get the name for.
|
||||||
*/
|
*/
|
||||||
public getRawBalances(): TokenBalances {
|
private _readableAddressName(address: string): string {
|
||||||
return _.cloneDeep(this._balances);
|
return this._addressNames[address] || address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws iff balance stores do not have the same ETH balances.
|
||||||
|
* @param rhs Balance store to compare to
|
||||||
|
*/
|
||||||
|
private _assertEthBalancesEqual(rhs: BalanceStore): void {
|
||||||
|
for (const ownerAddress of [...this._ownerAddresses, ...rhs._ownerAddresses]) {
|
||||||
|
const thisBalance = _.get(this._balances.eth, [ownerAddress], new BigNumber(0));
|
||||||
|
const rhsBalance = _.get(rhs._balances.eth, [ownerAddress], new BigNumber(0));
|
||||||
|
expect(thisBalance, `${this._readableAddressName(ownerAddress)} ETH balance`).to.bignumber.equal(
|
||||||
|
rhsBalance,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws iff balance stores do not have the same ERC20 balances.
|
||||||
|
* @param rhs Balance store to compare to
|
||||||
|
*/
|
||||||
|
private _assertErc20BalancesEqual(rhs: BalanceStore): void {
|
||||||
|
for (const ownerAddress of [...this._ownerAddresses, ...rhs._ownerAddresses]) {
|
||||||
|
for (const tokenAddress of [...this._tokenAddresses.erc20, ...rhs._tokenAddresses.erc20]) {
|
||||||
|
const thisBalance = _.get(this._balances.erc20, [ownerAddress, tokenAddress], new BigNumber(0));
|
||||||
|
const rhsBalance = _.get(rhs._balances.erc20, [ownerAddress, tokenAddress], new BigNumber(0));
|
||||||
|
expect(
|
||||||
|
thisBalance,
|
||||||
|
`${this._readableAddressName(ownerAddress)} ${this._readableAddressName(tokenAddress)} balance`,
|
||||||
|
).to.bignumber.equal(rhsBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws iff balance stores do not have the same ERC721 balances.
|
||||||
|
* @param rhs Balance store to compare to
|
||||||
|
*/
|
||||||
|
private _assertErc721BalancesEqual(rhs: BalanceStore): void {
|
||||||
|
for (const ownerAddress of [...this._ownerAddresses, ...rhs._ownerAddresses]) {
|
||||||
|
for (const tokenAddress of [...this._tokenAddresses.erc721, ...rhs._tokenAddresses.erc721]) {
|
||||||
|
const thisBalance = _.get(this._balances.erc721, [ownerAddress, tokenAddress], []);
|
||||||
|
const rhsBalance = _.get(rhs._balances.erc721, [ownerAddress, tokenAddress], []);
|
||||||
|
expect(
|
||||||
|
thisBalance,
|
||||||
|
`${this._readableAddressName(ownerAddress)} ${this._readableAddressName(tokenAddress)} balance`,
|
||||||
|
).to.deep.equal(rhsBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws iff balance stores do not have the same ERC1155 balances.
|
||||||
|
* @param rhs Balance store to compare to
|
||||||
|
*/
|
||||||
|
private _assertErc1155BalancesEqual(rhs: BalanceStore): void {
|
||||||
|
for (const ownerAddress of [...this._ownerAddresses, ...rhs._ownerAddresses]) {
|
||||||
|
for (const tokenAddress of [...this._tokenAddresses.erc1155, ...rhs._tokenAddresses.erc1155]) {
|
||||||
|
const thisBalance = _.get(this._balances.erc1155, [ownerAddress, tokenAddress], {});
|
||||||
|
const rhsBalance = _.get(rhs._balances.erc1155, [ownerAddress, tokenAddress], {});
|
||||||
|
expect(
|
||||||
|
thisBalance,
|
||||||
|
`${this._readableAddressName(ownerAddress)} ${this._readableAddressName(tokenAddress)} balance`,
|
||||||
|
).to.deep.equal(rhsBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,140 @@
|
|||||||
import { ERC1155ProxyWrapper, ERC20Wrapper, ERC721Wrapper } from '@0x/contracts-asset-proxy';
|
import { web3Wrapper } from '@0x/contracts-test-utils';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import * as combinatorics from 'js-combinatorics';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { BalanceStore } from './balance_store';
|
import { BalanceStore } from './balance_store';
|
||||||
|
import { TokenContracts, TokenContractsByName, TokenIds, TokenOwnersByName } from './types';
|
||||||
|
|
||||||
export class BlockchainBalanceStore extends BalanceStore {
|
export class BlockchainBalanceStore extends BalanceStore {
|
||||||
private readonly _erc20Wrapper: ERC20Wrapper;
|
private readonly _tokenContracts: TokenContracts;
|
||||||
private readonly _erc721Wrapper: ERC721Wrapper;
|
private readonly _tokenIds: TokenIds;
|
||||||
private readonly _erc1155ProxyWrapper: ERC1155ProxyWrapper;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param erc20Wrapper The ERC20 Wrapper used to interface with deployed erc20 tokens.
|
* @param tokenOwnersByName The addresses of token owners whose balances will be tracked.
|
||||||
* @param erc721Wrapper The ERC721 Wrapper used to interface with deployed erc20 tokens.
|
* @param tokenContractsByName The contracts of tokens to track.
|
||||||
* @param erc1155ProxyWrapper The ERC1155 Proxy Wrapper used to interface with deployed erc20 tokens.
|
* @param tokenIds The tokenIds of ERC721 and ERC1155 assets to track.
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
erc20Wrapper: ERC20Wrapper,
|
tokenOwnersByName: TokenOwnersByName,
|
||||||
erc721Wrapper: ERC721Wrapper,
|
tokenContractsByName: Partial<TokenContractsByName>,
|
||||||
erc1155ProxyWrapper: ERC1155ProxyWrapper,
|
tokenIds: Partial<TokenIds>,
|
||||||
) {
|
) {
|
||||||
super();
|
super(tokenOwnersByName, tokenContractsByName);
|
||||||
this._erc20Wrapper = erc20Wrapper;
|
this._tokenContracts = {
|
||||||
this._erc721Wrapper = erc721Wrapper;
|
erc20: Object.values(tokenContractsByName.erc20 || {}),
|
||||||
this._erc1155ProxyWrapper = erc1155ProxyWrapper;
|
erc721: Object.values(tokenContractsByName.erc721 || {}),
|
||||||
|
erc1155: Object.values(tokenContractsByName.erc1155 || {}),
|
||||||
|
};
|
||||||
|
this._tokenIds = {
|
||||||
|
erc721: tokenIds.erc721 || {},
|
||||||
|
erc1155: tokenIds.erc1155 || {},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates balances by querying on-chain values managed by the erc20, erc721, and erc1155 wrappers.
|
* Updates balances by querying on-chain values.
|
||||||
*/
|
*/
|
||||||
public async updateBalancesAsync(): Promise<void> {
|
public async updateBalancesAsync(): Promise<void> {
|
||||||
const [erc20, erc721, erc1155] = await Promise.all([
|
await Promise.all([
|
||||||
this._erc20Wrapper.getBalancesAsync(),
|
this.updateEthBalancesAsync(),
|
||||||
this._erc721Wrapper.getBalancesAsync(),
|
this.updateErc20BalancesAsync(),
|
||||||
this._erc1155ProxyWrapper.getBalancesAsync(),
|
this.updateErc721BalancesAsync(),
|
||||||
|
this.updateErc1155BalancesAsync(),
|
||||||
]);
|
]);
|
||||||
this._balances.erc20 = erc20;
|
}
|
||||||
this._balances.erc721 = erc721;
|
|
||||||
this._balances.erc1155 = BalanceStore._transformERC1155Holdings(erc1155);
|
/**
|
||||||
|
* Updates ETH balances.
|
||||||
|
*/
|
||||||
|
public async updateEthBalancesAsync(): Promise<void> {
|
||||||
|
const ethBalances = _.zipObject(
|
||||||
|
this._ownerAddresses,
|
||||||
|
await Promise.all(this._ownerAddresses.map(address => web3Wrapper.getBalanceInWeiAsync(address))),
|
||||||
|
);
|
||||||
|
this._balances.eth = ethBalances;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates ERC20 balances.
|
||||||
|
*/
|
||||||
|
public async updateErc20BalancesAsync(): Promise<void> {
|
||||||
|
const balances = await Promise.all(
|
||||||
|
this._ownerAddresses.map(async account =>
|
||||||
|
_.zipObject(
|
||||||
|
this._tokenContracts.erc20.map(token => token.address),
|
||||||
|
await Promise.all(this._tokenContracts.erc20.map(token => token.balanceOf.callAsync(account))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
this._balances.erc20 = _.zipObject(this._ownerAddresses, balances);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates ERC721 balances.
|
||||||
|
*/
|
||||||
|
public async updateErc721BalancesAsync(): Promise<void> {
|
||||||
|
const erc721ContractsByAddress = _.zipObject(
|
||||||
|
this._tokenContracts.erc721.map(contract => contract.address),
|
||||||
|
this._tokenContracts.erc721,
|
||||||
|
);
|
||||||
|
|
||||||
|
this._balances.erc721 = {};
|
||||||
|
for (const [tokenAddress, tokenIds] of Object.entries(this._tokenIds.erc721)) {
|
||||||
|
for (const tokenId of tokenIds) {
|
||||||
|
const tokenOwner = await erc721ContractsByAddress[tokenAddress].ownerOf.callAsync(tokenId);
|
||||||
|
_.update(this._balances.erc721, [tokenOwner, tokenAddress], nfts => _.union([tokenId], nfts).sort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates ERC1155 balances.
|
||||||
|
*/
|
||||||
|
public async updateErc1155BalancesAsync(): Promise<void> {
|
||||||
|
const erc1155ContractsByAddress = _.zipObject(
|
||||||
|
this._tokenContracts.erc1155.map(contract => contract.address),
|
||||||
|
this._tokenContracts.erc1155,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const [tokenAddress, { fungible, nonFungible }] of Object.entries(this._tokenIds.erc1155)) {
|
||||||
|
const contract = erc1155ContractsByAddress[tokenAddress];
|
||||||
|
const tokenIds = [...fungible, ...nonFungible];
|
||||||
|
if (this._ownerAddresses.length === 0 || tokenIds.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [_tokenIds, _tokenOwners] = _.unzip(
|
||||||
|
combinatorics.cartesianProduct(tokenIds, this._ownerAddresses).toArray(),
|
||||||
|
);
|
||||||
|
const balances = await contract.balanceOfBatch.callAsync(
|
||||||
|
_tokenOwners as string[],
|
||||||
|
_tokenIds as BigNumber[],
|
||||||
|
);
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
for (const tokenOwner of this._ownerAddresses) {
|
||||||
|
// Fungible tokens
|
||||||
|
_.set(this._balances.erc1155, [tokenOwner, tokenAddress, 'fungible'], {});
|
||||||
|
for (const tokenId of fungible) {
|
||||||
|
_.set(
|
||||||
|
this._balances.erc1155,
|
||||||
|
[tokenOwner, tokenAddress, 'fungible', tokenId.toString()],
|
||||||
|
balances[i++],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Non-fungible tokens
|
||||||
|
_.set(this._balances.erc1155, [tokenOwner, tokenAddress, 'nonFungible'], []);
|
||||||
|
for (const tokenId of nonFungible) {
|
||||||
|
const isOwner = balances[i++];
|
||||||
|
if (isOwner.isEqualTo(1)) {
|
||||||
|
_.update(this._balances.erc1155, [tokenOwner, tokenAddress, 'nonFungible'], nfts =>
|
||||||
|
_.union([tokenId], nfts).sort(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
contracts/exchange/test/balance_stores/index.ts
Normal file
4
contracts/exchange/test/balance_stores/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export { BalanceStore } from './balance_store';
|
||||||
|
export { LocalBalanceStore } from './local_balance_store';
|
||||||
|
export { BlockchainBalanceStore } from './blockchain_balance_store';
|
||||||
|
export * from './types';
|
@ -1,28 +1,42 @@
|
|||||||
|
import { constants } from '@0x/contracts-test-utils';
|
||||||
import { assetDataUtils } from '@0x/order-utils';
|
import { assetDataUtils } from '@0x/order-utils';
|
||||||
import { AssetProxyId } from '@0x/types';
|
import { AssetProxyId } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { BalanceStore } from './balance_store';
|
import { BalanceStore } from './balance_store';
|
||||||
|
import { TokenContractsByName, TokenOwnersByName } from './types';
|
||||||
|
|
||||||
export class LocalBalanceStore extends BalanceStore {
|
export class LocalBalanceStore extends BalanceStore {
|
||||||
/**
|
/**
|
||||||
* Creates a new balance store based on an existing one.
|
* Creates a new balance store based on an existing one.
|
||||||
* @param balanceStore Existing balance store whose values should be copied.
|
* @param sourceBalanceStore Existing balance store whose values should be copied.
|
||||||
*/
|
*/
|
||||||
public static create(sourceBalanceStore?: BalanceStore): LocalBalanceStore {
|
public static create(sourceBalanceStore?: BalanceStore): LocalBalanceStore {
|
||||||
const localBalanceStore = new LocalBalanceStore();
|
const localBalanceStore = new LocalBalanceStore();
|
||||||
if (sourceBalanceStore !== undefined) {
|
if (sourceBalanceStore !== undefined) {
|
||||||
localBalanceStore.copyBalancesFrom(sourceBalanceStore);
|
localBalanceStore.cloneFrom(sourceBalanceStore);
|
||||||
}
|
}
|
||||||
return localBalanceStore;
|
return localBalanceStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
* Note that parameters are given {} defaults because `LocalBalanceStore`s will typically
|
||||||
|
* be initialized via `create`.
|
||||||
*/
|
*/
|
||||||
public constructor() {
|
public constructor(
|
||||||
super();
|
tokenOwnersByName: TokenOwnersByName = {},
|
||||||
|
tokenContractsByName: Partial<TokenContractsByName> = {},
|
||||||
|
) {
|
||||||
|
super(tokenOwnersByName, tokenContractsByName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decreases the ETH balance of an address to simulate gas usage.
|
||||||
|
*/
|
||||||
|
public burnGas(senderAddress: string, amount: BigNumber | number): void {
|
||||||
|
this._balances.eth[senderAddress] = this._balances.eth[senderAddress].minus(amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,52 +55,66 @@ export class LocalBalanceStore extends BalanceStore {
|
|||||||
case AssetProxyId.ERC20: {
|
case AssetProxyId.ERC20: {
|
||||||
const erc20AssetData = assetDataUtils.decodeERC20AssetData(assetData);
|
const erc20AssetData = assetDataUtils.decodeERC20AssetData(assetData);
|
||||||
const assetAddress = erc20AssetData.tokenAddress;
|
const assetAddress = erc20AssetData.tokenAddress;
|
||||||
const fromBalances = this._balances.erc20[fromAddress];
|
_.update(this._balances.erc20, [fromAddress, assetAddress], balance => balance.minus(amount));
|
||||||
const toBalances = this._balances.erc20[toAddress];
|
_.update(this._balances.erc20, [toAddress, assetAddress], balance =>
|
||||||
fromBalances[assetAddress] = fromBalances[assetAddress].minus(amount);
|
(balance || constants.ZERO_AMOUNT).plus(amount),
|
||||||
toBalances[assetAddress] = toBalances[assetAddress].plus(amount);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AssetProxyId.ERC721: {
|
case AssetProxyId.ERC721: {
|
||||||
const erc721AssetData = assetDataUtils.decodeERC721AssetData(assetData);
|
const erc721AssetData = assetDataUtils.decodeERC721AssetData(assetData);
|
||||||
const assetAddress = erc721AssetData.tokenAddress;
|
const assetAddress = erc721AssetData.tokenAddress;
|
||||||
const tokenId = erc721AssetData.tokenId;
|
const tokenId = erc721AssetData.tokenId;
|
||||||
const fromTokens = this._balances.erc721[fromAddress][assetAddress];
|
const fromTokens = _.get(this._balances.erc721, [fromAddress, assetAddress], []);
|
||||||
const toTokens = this._balances.erc721[toAddress][assetAddress];
|
const toTokens = _.get(this._balances.erc721, [toAddress, assetAddress], []);
|
||||||
if (amount.gte(1)) {
|
if (amount.gte(1)) {
|
||||||
const tokenIndex = _.findIndex(fromTokens, t => t.eq(tokenId));
|
const tokenIndex = _.findIndex(fromTokens as BigNumber[], t => t.eq(tokenId));
|
||||||
if (tokenIndex !== -1) {
|
if (tokenIndex !== -1) {
|
||||||
fromTokens.splice(tokenIndex, 1);
|
fromTokens.splice(tokenIndex, 1);
|
||||||
toTokens.push(tokenId);
|
toTokens.push(tokenId);
|
||||||
|
toTokens.sort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_.set(this._balances.erc721, [fromAddress, assetAddress], fromTokens);
|
||||||
|
_.set(this._balances.erc721, [toAddress, assetAddress], toTokens);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AssetProxyId.ERC1155: {
|
case AssetProxyId.ERC1155: {
|
||||||
const erc1155AssetData = assetDataUtils.decodeERC1155AssetData(assetData);
|
const erc1155AssetData = assetDataUtils.decodeERC1155AssetData(assetData);
|
||||||
const assetAddress = erc1155AssetData.tokenAddress;
|
const assetAddress = erc1155AssetData.tokenAddress;
|
||||||
const fromBalances = this._balances.erc1155[fromAddress][assetAddress];
|
const fromBalances = {
|
||||||
const toBalances = this._balances.erc1155[toAddress][assetAddress];
|
// tslint:disable-next-line:no-inferred-empty-object-type
|
||||||
for (const i of _.times(erc1155AssetData.tokenIds.length)) {
|
fungible: _.get(this._balances.erc1155, [fromAddress, assetAddress, 'fungible'], {}),
|
||||||
const tokenId = erc1155AssetData.tokenIds[i];
|
nonFungible: _.get(this._balances.erc1155, [fromAddress, assetAddress, 'nonFungible'], []),
|
||||||
|
};
|
||||||
|
const toBalances = {
|
||||||
|
// tslint:disable-next-line:no-inferred-empty-object-type
|
||||||
|
fungible: _.get(this._balances.erc1155, [toAddress, assetAddress, 'fungible'], {}),
|
||||||
|
nonFungible: _.get(this._balances.erc1155, [toAddress, assetAddress, 'nonFungible'], []),
|
||||||
|
};
|
||||||
|
for (const [i, tokenId] of erc1155AssetData.tokenIds.entries()) {
|
||||||
const tokenValue = erc1155AssetData.tokenValues[i];
|
const tokenValue = erc1155AssetData.tokenValues[i];
|
||||||
const tokenAmount = amount.times(tokenValue);
|
const tokenAmount = amount.times(tokenValue);
|
||||||
if (tokenAmount.gt(0)) {
|
if (tokenAmount.gt(0)) {
|
||||||
const tokenIndex = _.findIndex(fromBalances.nonFungible, t => t.eq(tokenId));
|
const tokenIndex = _.findIndex(fromBalances.nonFungible as BigNumber[], t => t.eq(tokenId));
|
||||||
if (tokenIndex !== -1) {
|
if (tokenIndex !== -1) {
|
||||||
// Transfer a non-fungible.
|
// Transfer a non-fungible.
|
||||||
fromBalances.nonFungible.splice(tokenIndex, 1);
|
fromBalances.nonFungible.splice(tokenIndex, 1);
|
||||||
toBalances.nonFungible.push(tokenId);
|
toBalances.nonFungible.push(tokenId);
|
||||||
|
// sort NFT's by name
|
||||||
|
toBalances.nonFungible.sort();
|
||||||
} else {
|
} else {
|
||||||
// Transfer a fungible.
|
// Transfer a fungible.
|
||||||
const _tokenId = tokenId.toString(10);
|
const _tokenId = tokenId.toString();
|
||||||
fromBalances.fungible[_tokenId] = fromBalances.fungible[_tokenId].minus(tokenAmount);
|
_.update(fromBalances.fungible, [_tokenId], balance => balance.minus(tokenAmount));
|
||||||
toBalances.fungible[_tokenId] = toBalances.fungible[_tokenId].plus(tokenAmount);
|
_.update(toBalances.fungible, [_tokenId], balance =>
|
||||||
|
(balance || constants.ZERO_AMOUNT).plus(tokenAmount),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// sort NFT's by name
|
_.set(this._balances.erc1155, [fromAddress, assetAddress], fromBalances);
|
||||||
toBalances.nonFungible.sort();
|
_.set(this._balances.erc1155, [toAddress, assetAddress], toBalances);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AssetProxyId.MultiAsset: {
|
case AssetProxyId.MultiAsset: {
|
||||||
|
51
contracts/exchange/test/balance_stores/types.ts
Normal file
51
contracts/exchange/test/balance_stores/types.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { ERC1155MintableContract } from '@0x/contracts-erc1155';
|
||||||
|
import { DummyERC20TokenContract, DummyNoReturnERC20TokenContract } from '@0x/contracts-erc20';
|
||||||
|
import { DummyERC721TokenContract } from '@0x/contracts-erc721';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
// alias for clarity
|
||||||
|
type address = string;
|
||||||
|
|
||||||
|
interface TokenData<TERC20, TERC721, TERC1155> {
|
||||||
|
erc20: TERC20;
|
||||||
|
erc721: TERC721;
|
||||||
|
erc1155: TERC1155;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TokenAddresses = TokenData<address[], address[], address[]>;
|
||||||
|
|
||||||
|
export type TokenContracts = TokenData<
|
||||||
|
Array<DummyERC20TokenContract | DummyNoReturnERC20TokenContract>,
|
||||||
|
DummyERC721TokenContract[],
|
||||||
|
ERC1155MintableContract[]
|
||||||
|
>;
|
||||||
|
|
||||||
|
interface Named<T> {
|
||||||
|
[readableName: string]: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TokenOwnersByName = Named<address>;
|
||||||
|
|
||||||
|
export type TokenAddressesByName = TokenData<Named<address>, Named<address>, Named<address>>;
|
||||||
|
|
||||||
|
export type TokenContractsByName = TokenData<
|
||||||
|
Named<DummyERC20TokenContract | DummyNoReturnERC20TokenContract>,
|
||||||
|
Named<DummyERC721TokenContract>,
|
||||||
|
Named<ERC1155MintableContract>
|
||||||
|
>;
|
||||||
|
|
||||||
|
interface ERC721TokenIds {
|
||||||
|
[tokenAddress: string]: BigNumber[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ERC1155TokenIds {
|
||||||
|
[tokenAddress: string]: {
|
||||||
|
fungible: BigNumber[];
|
||||||
|
nonFungible: BigNumber[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TokenIds {
|
||||||
|
erc721: ERC721TokenIds;
|
||||||
|
erc1155: ERC1155TokenIds;
|
||||||
|
}
|
@ -222,7 +222,27 @@ blockchainTests.resets('Exchange core', () => {
|
|||||||
};
|
};
|
||||||
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
|
||||||
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||||
fillOrderWrapper = new FillOrderWrapper(exchange, erc20Wrapper, erc721Wrapper, erc1155ProxyWrapper, provider);
|
|
||||||
|
const tokenContracts = {
|
||||||
|
erc20: { erc20TokenA, erc20TokenB, feeToken, noReturnErc20Token },
|
||||||
|
erc721: { erc721Token },
|
||||||
|
erc1155: { erc1155Contract },
|
||||||
|
};
|
||||||
|
const tokenIds = {
|
||||||
|
erc721: { [erc721Token.address]: [...erc721MakerAssetIds, ...erc721TakerAssetIds] },
|
||||||
|
erc1155: {
|
||||||
|
[erc1155Contract.address]: {
|
||||||
|
fungible: erc1155FungibleTokens,
|
||||||
|
nonFungible: [...erc1155NonFungibleTokensOwnedByMaker, ...erc1155NonFungibleTokensOwnedByTaker],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
fillOrderWrapper = new FillOrderWrapper(
|
||||||
|
exchange,
|
||||||
|
{ makerAddress, takerAddress, feeRecipientAddress },
|
||||||
|
tokenContracts,
|
||||||
|
tokenIds,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
describe('fillOrder', () => {
|
describe('fillOrder', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
@ -48,6 +48,7 @@ export {
|
|||||||
ERC1155Holdings,
|
ERC1155Holdings,
|
||||||
ERC1155NonFungibleHoldingsByOwner,
|
ERC1155NonFungibleHoldingsByOwner,
|
||||||
ERC721TokenIdsByOwner,
|
ERC721TokenIdsByOwner,
|
||||||
|
EthBalancesByOwner,
|
||||||
FillEventArgs,
|
FillEventArgs,
|
||||||
MarketBuyOrders,
|
MarketBuyOrders,
|
||||||
MarketSellOrders,
|
MarketSellOrders,
|
||||||
|
@ -37,6 +37,10 @@ export interface ERC1155HoldingsByOwner {
|
|||||||
nonFungible: ERC1155NonFungibleHoldingsByOwner;
|
nonFungible: ERC1155NonFungibleHoldingsByOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EthBalancesByOwner {
|
||||||
|
[owner: string]: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SubmissionContractEventArgs {
|
export interface SubmissionContractEventArgs {
|
||||||
transactionId: BigNumber;
|
transactionId: BigNumber;
|
||||||
}
|
}
|
||||||
@ -150,20 +154,10 @@ export interface ERC1155Holdings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TokenBalances {
|
export interface TokenBalances {
|
||||||
erc20: {
|
erc20: ERC20BalancesByOwner;
|
||||||
[owner: string]: {
|
erc721: ERC721TokenIdsByOwner;
|
||||||
[contract: string]: BigNumber;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
erc721: {
|
|
||||||
[owner: string]: {
|
|
||||||
[contract: string]: BigNumber[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
erc1155: ERC1155Holdings;
|
erc1155: ERC1155Holdings;
|
||||||
eth: {
|
eth: EthBalancesByOwner;
|
||||||
[owner: string]: BigNumber;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FillEventArgs {
|
export interface FillEventArgs {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user