Refactor integrations directory structure; move core.ts, balance stores, and FillOrderWrapper to integrations
This commit is contained in:
143
contracts/integrations/test/framework/balances/balance_store.ts
Normal file
143
contracts/integrations/test/framework/balances/balance_store.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { BaseContract } from '@0x/base-contract';
|
||||
import { constants, expect, TokenBalances } from '@0x/contracts-test-utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { TokenAddresses, TokenContractsByName, TokenOwnersByName } from './types';
|
||||
|
||||
export class BalanceStore {
|
||||
public balances: TokenBalances;
|
||||
protected _tokenAddresses: TokenAddresses;
|
||||
protected _ownerAddresses: string[];
|
||||
private _addressNames: {
|
||||
[address: string]: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param tokenOwnersByName Addresses of token owners to track balances of.
|
||||
* @param tokenContractsByName Contracts of tokens to track balances of.
|
||||
*/
|
||||
public constructor(tokenOwnersByName: TokenOwnersByName, tokenContractsByName: Partial<TokenContractsByName>) {
|
||||
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),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given token owner in this balance store. The token owner's balance will be
|
||||
* tracked in subsequent operations.
|
||||
* @param address Address of the token owner
|
||||
* @param name Name of the token owner
|
||||
*/
|
||||
public registerTokenOwner(address: string, name: string): void {
|
||||
this._ownerAddresses.push(address);
|
||||
this._addressNames[address] = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws iff balance stores do not have the same entries.
|
||||
* @param rhs Balance store to compare to
|
||||
*/
|
||||
public assertEquals(rhs: BalanceStore): void {
|
||||
this._assertEthBalancesEqual(rhs);
|
||||
this._assertErc20BalancesEqual(rhs);
|
||||
this._assertErc721BalancesEqual(rhs);
|
||||
this._assertErc1155BalancesEqual(rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies from an existing balance store.
|
||||
* @param balanceStore to copy from.
|
||||
*/
|
||||
public cloneFrom(balanceStore: BalanceStore): void {
|
||||
this.balances = _.cloneDeep(balanceStore.balances);
|
||||
this._tokenAddresses = _.cloneDeep(balanceStore._tokenAddresses);
|
||||
this._ownerAddresses = _.cloneDeep(balanceStore._ownerAddresses);
|
||||
this._addressNames = _.cloneDeep(balanceStore._addressNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the human-readable name for the given address, if it exists.
|
||||
* @param address The address to get the name for.
|
||||
*/
|
||||
private _readableAddressName(address: string): string {
|
||||
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], constants.ZERO_AMOUNT);
|
||||
const rhsBalance = _.get(rhs.balances.eth, [ownerAddress], constants.ZERO_AMOUNT);
|
||||
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], constants.ZERO_AMOUNT);
|
||||
const rhsBalance = _.get(rhs.balances.erc20, [ownerAddress, tokenAddress], constants.ZERO_AMOUNT);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
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 { TokenContracts, TokenContractsByName, TokenIds, TokenOwnersByName } from './types';
|
||||
|
||||
export class BlockchainBalanceStore extends BalanceStore {
|
||||
private readonly _tokenContracts: TokenContracts;
|
||||
private readonly _tokenIds: TokenIds;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param tokenOwnersByName The addresses of token owners whose balances will be tracked.
|
||||
* @param tokenContractsByName The contracts of tokens to track.
|
||||
* @param tokenIds The tokenIds of ERC721 and ERC1155 assets to track.
|
||||
*/
|
||||
public constructor(
|
||||
tokenOwnersByName: TokenOwnersByName,
|
||||
tokenContractsByName: Partial<TokenContractsByName>,
|
||||
tokenIds: Partial<TokenIds> = {},
|
||||
) {
|
||||
super(tokenOwnersByName, tokenContractsByName);
|
||||
this._tokenContracts = {
|
||||
erc20: Object.values(tokenContractsByName.erc20 || {}),
|
||||
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.
|
||||
*/
|
||||
public async updateBalancesAsync(): Promise<void> {
|
||||
await Promise.all([
|
||||
this.updateEthBalancesAsync(),
|
||||
this.updateErc20BalancesAsync(),
|
||||
this.updateErc721BalancesAsync(),
|
||||
this.updateErc1155BalancesAsync(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(account).callAsync())),
|
||||
),
|
||||
),
|
||||
);
|
||||
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(tokenId).callAsync();
|
||||
_.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(_tokenOwners as string[], _tokenIds as BigNumber[])
|
||||
.callAsync();
|
||||
|
||||
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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,174 @@
|
||||
import { DevUtilsContract } from '@0x/contracts-dev-utils';
|
||||
import { constants, Numberish } from '@0x/contracts-test-utils';
|
||||
import { AssetProxyId } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { BalanceStore } from './balance_store';
|
||||
import { TokenContractsByName, TokenOwnersByName } from './types';
|
||||
|
||||
export class LocalBalanceStore extends BalanceStore {
|
||||
/**
|
||||
* Creates a new balance store based on an existing one.
|
||||
* @param sourceBalanceStore Existing balance store whose values should be copied.
|
||||
*/
|
||||
public static create(devUtils: DevUtilsContract, sourceBalanceStore?: BalanceStore): LocalBalanceStore {
|
||||
const localBalanceStore = new LocalBalanceStore(devUtils);
|
||||
if (sourceBalanceStore !== undefined) {
|
||||
localBalanceStore.cloneFrom(sourceBalanceStore);
|
||||
}
|
||||
return localBalanceStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Note that parameters are given {} defaults because `LocalBalanceStore`s will typically
|
||||
* be initialized via `create`.
|
||||
*/
|
||||
protected constructor(
|
||||
private readonly _devUtils: DevUtilsContract,
|
||||
tokenOwnersByName: TokenOwnersByName = {},
|
||||
tokenContractsByName: Partial<TokenContractsByName> = {},
|
||||
) {
|
||||
super(tokenOwnersByName, tokenContractsByName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decreases the ETH balance of an address to simulate gas usage.
|
||||
* @param senderAddress Address whose ETH balance to decrease.
|
||||
* @param amount Amount to decrease the balance by.
|
||||
*/
|
||||
public burnGas(senderAddress: string, amount: Numberish): void {
|
||||
this.balances.eth[senderAddress] = this.balances.eth[senderAddress].minus(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts some amount of the ETH balance of an address to WETH balance to simulate wrapping ETH.
|
||||
* @param senderAddress Address whose ETH to wrap.
|
||||
* @param amount Amount to wrap.
|
||||
*/
|
||||
public wrapEth(senderAddress: string, wethAddress: string, amount: Numberish): void {
|
||||
this.balances.eth[senderAddress] = this.balances.eth[senderAddress].minus(amount);
|
||||
_.update(this.balances.erc20, [senderAddress, wethAddress], balance =>
|
||||
(balance || constants.ZERO_AMOUNT).plus(amount),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends ETH from `fromAddress` to `toAddress`.
|
||||
* @param fromAddress Sender of ETH.
|
||||
* @param toAddress Receiver of ETH.
|
||||
* @param amount Amount of ETH to transfer.
|
||||
*/
|
||||
public sendEth(fromAddress: string, toAddress: string, amount: Numberish): void {
|
||||
this.balances.eth[fromAddress] = this.balances.eth[fromAddress].minus(amount);
|
||||
this.balances.eth[toAddress] = this.balances.eth[toAddress].plus(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers assets from `fromAddress` to `toAddress`.
|
||||
* @param fromAddress Sender of asset(s)
|
||||
* @param toAddress Receiver of asset(s)
|
||||
* @param amount Amount of asset(s) to transfer
|
||||
* @param assetData Asset data of assets being transferred.
|
||||
*/
|
||||
public async transferAssetAsync(
|
||||
fromAddress: string,
|
||||
toAddress: string,
|
||||
amount: BigNumber,
|
||||
assetData: string,
|
||||
): Promise<void> {
|
||||
if (fromAddress === toAddress) {
|
||||
return;
|
||||
}
|
||||
const assetProxyId = await this._devUtils.decodeAssetProxyId(assetData).callAsync();
|
||||
switch (assetProxyId) {
|
||||
case AssetProxyId.ERC20: {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
const [_proxyId, tokenAddress] = await this._devUtils.decodeERC20AssetData(assetData).callAsync();
|
||||
_.update(this.balances.erc20, [fromAddress, tokenAddress], balance => balance.minus(amount));
|
||||
_.update(this.balances.erc20, [toAddress, tokenAddress], balance =>
|
||||
(balance || constants.ZERO_AMOUNT).plus(amount),
|
||||
);
|
||||
break;
|
||||
}
|
||||
case AssetProxyId.ERC721: {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
const [_proxyId, tokenAddress, tokenId] = await this._devUtils
|
||||
.decodeERC721AssetData(assetData)
|
||||
.callAsync();
|
||||
const fromTokens = _.get(this.balances.erc721, [fromAddress, tokenAddress], []);
|
||||
const toTokens = _.get(this.balances.erc721, [toAddress, tokenAddress], []);
|
||||
if (amount.gte(1)) {
|
||||
const tokenIndex = _.findIndex(fromTokens as BigNumber[], t => t.eq(tokenId));
|
||||
if (tokenIndex !== -1) {
|
||||
fromTokens.splice(tokenIndex, 1);
|
||||
toTokens.push(tokenId);
|
||||
toTokens.sort();
|
||||
}
|
||||
}
|
||||
_.set(this.balances.erc721, [fromAddress, tokenAddress], fromTokens);
|
||||
_.set(this.balances.erc721, [toAddress, tokenAddress], toTokens);
|
||||
break;
|
||||
}
|
||||
case AssetProxyId.ERC1155: {
|
||||
const [
|
||||
_proxyId, // tslint:disable-line:no-unused-variable
|
||||
tokenAddress,
|
||||
tokenIds,
|
||||
tokenValues,
|
||||
] = await this._devUtils.decodeERC1155AssetData(assetData).callAsync();
|
||||
const fromBalances = {
|
||||
// tslint:disable-next-line:no-inferred-empty-object-type
|
||||
fungible: _.get(this.balances.erc1155, [fromAddress, tokenAddress, 'fungible'], {}),
|
||||
nonFungible: _.get(this.balances.erc1155, [fromAddress, tokenAddress, 'nonFungible'], []),
|
||||
};
|
||||
const toBalances = {
|
||||
// tslint:disable-next-line:no-inferred-empty-object-type
|
||||
fungible: _.get(this.balances.erc1155, [toAddress, tokenAddress, 'fungible'], {}),
|
||||
nonFungible: _.get(this.balances.erc1155, [toAddress, tokenAddress, 'nonFungible'], []),
|
||||
};
|
||||
for (const [i, tokenId] of tokenIds.entries()) {
|
||||
const tokenValue = tokenValues[i];
|
||||
const tokenAmount = amount.times(tokenValue);
|
||||
if (tokenAmount.gt(0)) {
|
||||
const tokenIndex = _.findIndex(fromBalances.nonFungible as BigNumber[], t => t.eq(tokenId));
|
||||
if (tokenIndex !== -1) {
|
||||
// Transfer a non-fungible.
|
||||
fromBalances.nonFungible.splice(tokenIndex, 1);
|
||||
toBalances.nonFungible.push(tokenId);
|
||||
// sort NFT's by name
|
||||
toBalances.nonFungible.sort();
|
||||
} else {
|
||||
// Transfer a fungible.
|
||||
const _tokenId = tokenId.toString();
|
||||
_.update(fromBalances.fungible, [_tokenId], balance => balance.minus(tokenAmount));
|
||||
_.update(toBalances.fungible, [_tokenId], balance =>
|
||||
(balance || constants.ZERO_AMOUNT).plus(tokenAmount),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_.set(this.balances.erc1155, [fromAddress, tokenAddress], fromBalances);
|
||||
_.set(this.balances.erc1155, [toAddress, tokenAddress], toBalances);
|
||||
break;
|
||||
}
|
||||
case AssetProxyId.MultiAsset: {
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
const [_proxyId, amounts, nestedAssetData] = await this._devUtils
|
||||
.decodeMultiAssetData(assetData)
|
||||
.callAsync();
|
||||
for (const [i, amt] of amounts.entries()) {
|
||||
const nestedAmount = amount.times(amt);
|
||||
await this.transferAssetAsync(fromAddress, toAddress, nestedAmount, nestedAssetData[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AssetProxyId.StaticCall:
|
||||
// Do nothing
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unhandled asset proxy ID: ${assetProxyId}`);
|
||||
}
|
||||
}
|
||||
}
|
51
contracts/integrations/test/framework/balances/types.ts
Normal file
51
contracts/integrations/test/framework/balances/types.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { ERC1155MintableContract } from '@0x/contracts-erc1155';
|
||||
import { DummyERC20TokenContract, DummyNoReturnERC20TokenContract, WETH9Contract } 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 | WETH9Contract>,
|
||||
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 | WETH9Contract>,
|
||||
Named<DummyERC721TokenContract>,
|
||||
Named<ERC1155MintableContract>
|
||||
>;
|
||||
|
||||
interface ERC721TokenIds {
|
||||
[tokenAddress: string]: BigNumber[];
|
||||
}
|
||||
|
||||
interface ERC1155TokenIds {
|
||||
[tokenAddress: string]: {
|
||||
fungible: BigNumber[];
|
||||
nonFungible: BigNumber[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface TokenIds {
|
||||
erc721: ERC721TokenIds;
|
||||
erc1155: ERC1155TokenIds;
|
||||
}
|
Reference in New Issue
Block a user