aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contract-wrappers/src/contract_wrappers
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contract-wrappers/src/contract_wrappers')
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts112
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts58
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts55
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts58
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts59
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts60
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts155
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts242
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts185
9 files changed, 603 insertions, 381 deletions
diff --git a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts
index daf70253a..749aaae10 100644
--- a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts
@@ -1,7 +1,14 @@
-import { ContractArtifact } from '@0xproject/sol-compiler';
-import { AbiDecoder, intervalUtils, logUtils } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
-import { BlockParamLiteral, ContractAbi, FilterObject, LogEntry, LogWithDecodedArgs, RawLog } from 'ethereum-types';
+import { AbiDecoder, intervalUtils, logUtils } from '@0x/utils';
+import { marshaller, Web3Wrapper } from '@0x/web3-wrapper';
+import {
+ BlockParamLiteral,
+ ContractAbi,
+ FilterObject,
+ LogEntry,
+ LogWithDecodedArgs,
+ RawLog,
+ RawLogEntry,
+} from 'ethereum-types';
import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream';
import * as _ from 'lodash';
@@ -16,27 +23,15 @@ import {
import { constants } from '../utils/constants';
import { filterUtils } from '../utils/filter_utils';
-const CONTRACT_NAME_TO_NOT_FOUND_ERROR: {
- [contractName: string]: ContractWrappersError;
-} = {
- ZRX: ContractWrappersError.ZRXContractDoesNotExist,
- EtherToken: ContractWrappersError.EtherTokenContractDoesNotExist,
- ERC20Token: ContractWrappersError.ERC20TokenContractDoesNotExist,
- ERC20Proxy: ContractWrappersError.ERC20ProxyContractDoesNotExist,
- ERC721Token: ContractWrappersError.ERC721TokenContractDoesNotExist,
- ERC721Proxy: ContractWrappersError.ERC721ProxyContractDoesNotExist,
- Exchange: ContractWrappersError.ExchangeContractDoesNotExist,
-};
-
export abstract class ContractWrapper {
public abstract abi: ContractAbi;
- protected _web3Wrapper: Web3Wrapper;
protected _networkId: number;
+ protected _web3Wrapper: Web3Wrapper;
private _blockAndLogStreamerIfExists: BlockAndLogStreamer<Block, Log> | undefined;
- private _blockPollingIntervalMs: number;
+ private readonly _blockPollingIntervalMs: number;
private _blockAndLogStreamIntervalIfExists?: NodeJS.Timer;
- private _filters: { [filterToken: string]: FilterObject };
- private _filterCallbacks: {
+ private readonly _filters: { [filterToken: string]: FilterObject };
+ private readonly _filterCallbacks: {
[filterToken: string]: EventCallback<ContractEventArgs>;
};
private _onLogAddedSubscriptionToken: string | undefined;
@@ -116,38 +111,8 @@ export abstract class ContractWrapper {
const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log);
return logWithDecodedArgs;
}
- protected async _getContractAbiAndAddressFromArtifactsAsync(
- artifact: ContractArtifact,
- addressIfExists?: string,
- ): Promise<[ContractAbi, string]> {
- let contractAddress: string;
- if (_.isUndefined(addressIfExists)) {
- if (_.isUndefined(artifact.networks[this._networkId])) {
- throw new Error(ContractWrappersError.ContractNotDeployedOnNetwork);
- }
- contractAddress = artifact.networks[this._networkId].address.toLowerCase();
- } else {
- contractAddress = addressIfExists;
- }
- const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(contractAddress);
- if (!doesContractExist) {
- throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contractName]);
- }
- const abiAndAddress: [ContractAbi, string] = [artifact.compilerOutput.abi, contractAddress];
- return abiAndAddress;
- }
- protected _getContractAddress(artifact: ContractArtifact, addressIfExists?: string): string {
- if (_.isUndefined(addressIfExists)) {
- const contractAddress = artifact.networks[this._networkId].address;
- if (_.isUndefined(contractAddress)) {
- throw new Error(ContractWrappersError.ExchangeContractDoesNotExist);
- }
- return contractAddress;
- } else {
- return addressIfExists;
- }
- }
- private _onLogStateChanged<ArgsType extends ContractEventArgs>(isRemoved: boolean, log: LogEntry): void {
+ private _onLogStateChanged<ArgsType extends ContractEventArgs>(isRemoved: boolean, rawLog: RawLogEntry): void {
+ const log: LogEntry = marshaller.unmarshalLog(rawLog);
_.forEach(this._filters, (filter: FilterObject, filterToken: string) => {
if (filterUtils.matchesFilter(log, filter)) {
const decodedLog = this._tryToDecodeLogOrNoop(log) as LogWithDecodedArgs<ArgsType>;
@@ -164,8 +129,8 @@ export abstract class ContractWrapper {
throw new Error(ContractWrappersError.SubscriptionAlreadyPresent);
}
this._blockAndLogStreamerIfExists = new BlockAndLogStreamer(
- this._web3Wrapper.getBlockAsync.bind(this._web3Wrapper),
- this._web3Wrapper.getLogsAsync.bind(this._web3Wrapper),
+ this._blockstreamGetBlockOrNullAsync.bind(this),
+ this._blockstreamGetLogsAsync.bind(this),
ContractWrapper._onBlockAndLogStreamerError.bind(this, isVerbose),
);
const catchAllLogFilter = {};
@@ -184,13 +149,31 @@ export abstract class ContractWrapper {
this._onLogStateChanged.bind(this, isRemoved),
);
}
- // HACK: This should be a package-scoped method (which doesn't exist in TS)
- // We don't want this method available in the public interface for all classes
- // who inherit from ContractWrapper, and it is only used by the internal implementation
- // of those higher classes.
- // tslint:disable-next-line:no-unused-variable
- private _setNetworkId(networkId: number): void {
- this._networkId = networkId;
+ // This method only exists in order to comply with the expected interface of Blockstream's constructor
+ private async _blockstreamGetBlockOrNullAsync(hash: string): Promise<Block | null> {
+ const shouldIncludeTransactionData = false;
+ const blockOrNull = await this._web3Wrapper.sendRawPayloadAsync<Block | null>({
+ method: 'eth_getBlockByHash',
+ params: [hash, shouldIncludeTransactionData],
+ });
+ return blockOrNull;
+ }
+ // This method only exists in order to comply with the expected interface of Blockstream's constructor
+ private async _blockstreamGetLatestBlockOrNullAsync(): Promise<Block | null> {
+ const shouldIncludeTransactionData = false;
+ const blockOrNull = await this._web3Wrapper.sendRawPayloadAsync<Block | null>({
+ method: 'eth_getBlockByNumber',
+ params: [BlockParamLiteral.Latest, shouldIncludeTransactionData],
+ });
+ return blockOrNull;
+ }
+ // This method only exists in order to comply with the expected interface of Blockstream's constructor
+ private async _blockstreamGetLogsAsync(filterOptions: FilterObject): Promise<RawLogEntry[]> {
+ const logs = await this._web3Wrapper.sendRawPayloadAsync<RawLogEntry[]>({
+ method: 'eth_getLogs',
+ params: [filterOptions],
+ });
+ return logs as RawLogEntry[];
}
private _stopBlockAndLogStream(): void {
if (_.isUndefined(this._blockAndLogStreamerIfExists)) {
@@ -202,11 +185,14 @@ export abstract class ContractWrapper {
delete this._blockAndLogStreamerIfExists;
}
private async _reconcileBlockAsync(): Promise<void> {
- const latestBlock = await this._web3Wrapper.getBlockAsync(BlockParamLiteral.Latest);
+ const latestBlockOrNull = await this._blockstreamGetLatestBlockOrNullAsync();
+ if (_.isNull(latestBlockOrNull)) {
+ return; // noop
+ }
// We need to coerce to Block type cause Web3.Block includes types for mempool blocks
if (!_.isUndefined(this._blockAndLogStreamerIfExists)) {
// If we clear the interval while fetching the block - this._blockAndLogStreamer will be undefined
- await this._blockAndLogStreamerIfExists.reconcileNewBlock((latestBlock as any) as Block);
+ await this._blockAndLogStreamerIfExists.reconcileNewBlock(latestBlockOrNull);
}
}
}
diff --git a/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts
index 821d1a8a2..45460bd6d 100644
--- a/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts
@@ -1,31 +1,42 @@
-import { AssetProxyId } from '@0xproject/types';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { ERC20ProxyContract } from '@0x/abi-gen-wrappers';
+import { ERC20Proxy } from '@0x/contract-artifacts';
+import { AssetProxyId } from '@0x/types';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi } from 'ethereum-types';
import * as _ from 'lodash';
-import { artifacts } from '../artifacts';
import { assert } from '../utils/assert';
+import { _getDefaultContractAddresses } from '../utils/contract_addresses';
import { ContractWrapper } from './contract_wrapper';
-import { ERC20ProxyContract } from './generated/erc20_proxy';
/**
* This class includes the functionality related to interacting with the ERC20Proxy contract.
*/
export class ERC20ProxyWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.ERC20Proxy.compilerOutput.abi;
+ public abi: ContractAbi = ERC20Proxy.compilerOutput.abi;
+ public address: string;
private _erc20ProxyContractIfExists?: ERC20ProxyContract;
- private _contractAddressIfExists?: string;
- constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
+ /**
+ * Instantiate ERC20ProxyWrapper
+ * @param web3Wrapper Web3Wrapper instance to use
+ * @param networkId Desired networkId
+ * @param address The address of the ERC20Proxy contract. If undefined, will
+ * default to the known address corresponding to the networkId.
+ */
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, address?: string) {
super(web3Wrapper, networkId);
- this._contractAddressIfExists = contractAddressIfExists;
+ this.address = _.isUndefined(address) ? _getDefaultContractAddresses(networkId).erc20Proxy : address;
}
/**
* Get the 4 bytes ID of this asset proxy
* @return Proxy id
*/
public async getProxyIdAsync(): Promise<AssetProxyId> {
- const ERC20ProxyContractInstance = await this._getERC20ProxyContractAsync();
+ const ERC20ProxyContractInstance = this._getERC20ProxyContract();
+ // Note(albrow): Below is a TSLint false positive. Code won't compile if
+ // you remove the type assertion.
+ /* tslint:disable-next-line:no-unnecessary-type-assertion */
const proxyId = (await ERC20ProxyContractInstance.getProxyId.callAsync()) as AssetProxyId;
return proxyId;
}
@@ -37,7 +48,7 @@ export class ERC20ProxyWrapper extends ContractWrapper {
public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> {
assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress);
const normalizedExchangeContractAddress = exchangeContractAddress.toLowerCase();
- const ERC20ProxyContractInstance = await this._getERC20ProxyContractAsync();
+ const ERC20ProxyContractInstance = this._getERC20ProxyContract();
const isAuthorized = await ERC20ProxyContractInstance.authorized.callAsync(normalizedExchangeContractAddress);
return isAuthorized;
}
@@ -46,36 +57,17 @@ export class ERC20ProxyWrapper extends ContractWrapper {
* @return The list of authorized addresses.
*/
public async getAuthorizedAddressesAsync(): Promise<string[]> {
- const ERC20ProxyContractInstance = await this._getERC20ProxyContractAsync();
+ const ERC20ProxyContractInstance = this._getERC20ProxyContract();
const authorizedAddresses = await ERC20ProxyContractInstance.getAuthorizedAddresses.callAsync();
return authorizedAddresses;
}
- /**
- * Retrieves the Ethereum address of the ERC20Proxy contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the ERC20Proxy contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.ERC20Proxy, this._contractAddressIfExists);
- return contractAddress;
- }
- // HACK: We don't want this method to be visible to the other units within that package but not to the end user.
- // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused.
- // tslint:disable-next-line:no-unused-variable
- private _invalidateContractInstance(): void {
- delete this._erc20ProxyContractIfExists;
- }
- private async _getERC20ProxyContractAsync(): Promise<ERC20ProxyContract> {
+ private _getERC20ProxyContract(): ERC20ProxyContract {
if (!_.isUndefined(this._erc20ProxyContractIfExists)) {
return this._erc20ProxyContractIfExists;
}
- const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
- artifacts.ERC20Proxy,
- this._contractAddressIfExists,
- );
const contractInstance = new ERC20ProxyContract(
- abi,
- address,
+ this.abi,
+ this.address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
diff --git a/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts
index 17bda5085..5e0ec1951 100644
--- a/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts
@@ -1,10 +1,11 @@
-import { schemas } from '@0xproject/json-schemas';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { ERC20TokenContract, ERC20TokenEventArgs, ERC20TokenEvents } from '@0x/abi-gen-wrappers';
+import { ERC20Token } from '@0x/contract-artifacts';
+import { schemas } from '@0x/json-schemas';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
-import { artifacts } from '../artifacts';
import { methodOptsSchema } from '../schemas/method_opts_schema';
import { txOptsSchema } from '../schemas/tx_opts_schema';
import {
@@ -17,12 +18,10 @@ import {
} from '../types';
import { assert } from '../utils/assert';
import { constants } from '../utils/constants';
+import { utils } from '../utils/utils';
import { ContractWrapper } from './contract_wrapper';
import { ERC20ProxyWrapper } from './erc20_proxy_wrapper';
-import { ERC20TokenContract, ERC20TokenEventArgs, ERC20TokenEvents } from './generated/erc20_token';
-
-const removeUndefinedProperties = _.pickBy;
/**
* This class includes all the functionality related to interacting with ERC20 token contracts.
@@ -30,10 +29,17 @@ const removeUndefinedProperties = _.pickBy;
* to the 0x ERC20 Proxy smart contract.
*/
export class ERC20TokenWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.ERC20Token.compilerOutput.abi;
+ public abi: ContractAbi = ERC20Token.compilerOutput.abi;
public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
- private _tokenContractsByAddress: { [address: string]: ERC20TokenContract };
- private _erc20ProxyWrapper: ERC20ProxyWrapper;
+ private readonly _tokenContractsByAddress: { [address: string]: ERC20TokenContract };
+ private readonly _erc20ProxyWrapper: ERC20ProxyWrapper;
+ /**
+ * Instantiate ERC20TokenWrapper
+ * @param web3Wrapper Web3Wrapper instance to use
+ * @param networkId Desired networkId
+ * @param erc20ProxyWrapper The ERC20ProxyWrapper instance to use
+ * @param blockPollingIntervalMs The block polling interval to use for active subscriptions
+ */
constructor(
web3Wrapper: Web3Wrapper,
networkId: number,
@@ -101,7 +107,7 @@ export class ERC20TokenWrapper extends ContractWrapper {
const txHash = await tokenContract.approve.sendTransactionAsync(
normalizedSpenderAddress,
amountInBaseUnits,
- removeUndefinedProperties({
+ utils.removeUndefinedProperties({
from: normalizedOwnerAddress,
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
@@ -182,7 +188,7 @@ export class ERC20TokenWrapper extends ContractWrapper {
ownerAddress: string,
methodOpts: MethodOpts = {},
): Promise<BigNumber> {
- const proxyAddress = this._erc20ProxyWrapper.getContractAddress();
+ const proxyAddress = this._erc20ProxyWrapper.address;
const allowanceInBaseUnits = await this.getAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, methodOpts);
return allowanceInBaseUnits;
}
@@ -202,7 +208,7 @@ export class ERC20TokenWrapper extends ContractWrapper {
amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {},
): Promise<string> {
- const proxyAddress = this._erc20ProxyWrapper.getContractAddress();
+ const proxyAddress = this._erc20ProxyWrapper.address;
const txHash = await this.setAllowanceAsync(
tokenAddress,
ownerAddress,
@@ -271,7 +277,7 @@ export class ERC20TokenWrapper extends ContractWrapper {
const txHash = await tokenContract.transfer.sendTransactionAsync(
normalizedToAddress,
amountInBaseUnits,
- removeUndefinedProperties({
+ utils.removeUndefinedProperties({
from: normalizedFromAddress,
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
@@ -332,7 +338,7 @@ export class ERC20TokenWrapper extends ContractWrapper {
normalizedFromAddress,
normalizedToAddress,
amountInBaseUnits,
- removeUndefinedProperties({
+ utils.removeUndefinedProperties({
from: normalizedSenderAddress,
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
@@ -366,7 +372,7 @@ export class ERC20TokenWrapper extends ContractWrapper {
normalizedTokenAddress,
eventName,
indexFilterValues,
- artifacts.ERC20Token.compilerOutput.abi,
+ ERC20Token.compilerOutput.abi,
callback,
isVerbose,
);
@@ -411,30 +417,19 @@ export class ERC20TokenWrapper extends ContractWrapper {
eventName,
blockRange,
indexFilterValues,
- artifacts.ERC20Token.compilerOutput.abi,
+ ERC20Token.compilerOutput.abi,
);
return logs;
}
- // HACK: We don't want this method to be visible to the other units within that package but not to the end user.
- // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused.
- // tslint:disable-next-line:no-unused-variable
- private _invalidateContractInstances(): void {
- this.unsubscribeAll();
- this._tokenContractsByAddress = {};
- }
private async _getTokenContractAsync(tokenAddress: string): Promise<ERC20TokenContract> {
const normalizedTokenAddress = tokenAddress.toLowerCase();
let tokenContract = this._tokenContractsByAddress[normalizedTokenAddress];
if (!_.isUndefined(tokenContract)) {
return tokenContract;
}
- const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
- artifacts.ERC20Token,
- normalizedTokenAddress,
- );
const contractInstance = new ERC20TokenContract(
- abi,
- address,
+ this.abi,
+ normalizedTokenAddress,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
diff --git a/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts
index 38ecd4687..12758e191 100644
--- a/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts
@@ -1,31 +1,42 @@
-import { AssetProxyId } from '@0xproject/types';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { ERC721ProxyContract } from '@0x/abi-gen-wrappers';
+import { ERC721Proxy } from '@0x/contract-artifacts';
+import { AssetProxyId } from '@0x/types';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi } from 'ethereum-types';
import * as _ from 'lodash';
-import { artifacts } from '../artifacts';
import { assert } from '../utils/assert';
+import { _getDefaultContractAddresses } from '../utils/contract_addresses';
import { ContractWrapper } from './contract_wrapper';
-import { ERC721ProxyContract } from './generated/erc721_proxy';
/**
* This class includes the functionality related to interacting with the ERC721Proxy contract.
*/
export class ERC721ProxyWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.ERC20Proxy.compilerOutput.abi;
+ public abi: ContractAbi = ERC721Proxy.compilerOutput.abi;
+ public address: string;
private _erc721ProxyContractIfExists?: ERC721ProxyContract;
- private _contractAddressIfExists?: string;
- constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
+ /**
+ * Instantiate ERC721ProxyWrapper
+ * @param web3Wrapper Web3Wrapper instance to use
+ * @param networkId Desired networkId
+ * @param address The address of the ERC721Proxy contract. If undefined,
+ * will default to the known address corresponding to the networkId.
+ */
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, address?: string) {
super(web3Wrapper, networkId);
- this._contractAddressIfExists = contractAddressIfExists;
+ this.address = _.isUndefined(address) ? _getDefaultContractAddresses(networkId).erc721Proxy : address;
}
/**
* Get the 4 bytes ID of this asset proxy
* @return Proxy id
*/
public async getProxyIdAsync(): Promise<AssetProxyId> {
- const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync();
+ const ERC721ProxyContractInstance = await this._getERC721ProxyContract();
+ // Note(albrow): Below is a TSLint false positive. Code won't compile if
+ // you remove the type assertion.
+ /* tslint:disable-next-line:no-unnecessary-type-assertion */
const proxyId = (await ERC721ProxyContractInstance.getProxyId.callAsync()) as AssetProxyId;
return proxyId;
}
@@ -37,7 +48,7 @@ export class ERC721ProxyWrapper extends ContractWrapper {
public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> {
assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress);
const normalizedExchangeContractAddress = exchangeContractAddress.toLowerCase();
- const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync();
+ const ERC721ProxyContractInstance = await this._getERC721ProxyContract();
const isAuthorized = await ERC721ProxyContractInstance.authorized.callAsync(normalizedExchangeContractAddress);
return isAuthorized;
}
@@ -46,36 +57,17 @@ export class ERC721ProxyWrapper extends ContractWrapper {
* @return The list of authorized addresses.
*/
public async getAuthorizedAddressesAsync(): Promise<string[]> {
- const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync();
+ const ERC721ProxyContractInstance = await this._getERC721ProxyContract();
const authorizedAddresses = await ERC721ProxyContractInstance.getAuthorizedAddresses.callAsync();
return authorizedAddresses;
}
- /**
- * Retrieves the Ethereum address of the ERC721Proxy contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the ERC721Proxy contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.ERC721Proxy, this._contractAddressIfExists);
- return contractAddress;
- }
- // HACK: We don't want this method to be visible to the other units within that package but not to the end user.
- // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused.
- // tslint:disable-next-line:no-unused-variable
- private _invalidateContractInstance(): void {
- delete this._erc721ProxyContractIfExists;
- }
- private async _getERC721ProxyContractAsync(): Promise<ERC721ProxyContract> {
+ private _getERC721ProxyContract(): ERC721ProxyContract {
if (!_.isUndefined(this._erc721ProxyContractIfExists)) {
return this._erc721ProxyContractIfExists;
}
- const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
- artifacts.ERC721Proxy,
- this._contractAddressIfExists,
- );
const contractInstance = new ERC721ProxyContract(
- abi,
- address,
+ this.abi,
+ this.address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
diff --git a/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts
index 7231e0bde..1610af47b 100644
--- a/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts
@@ -1,10 +1,11 @@
-import { schemas } from '@0xproject/json-schemas';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { ERC721TokenContract, ERC721TokenEventArgs, ERC721TokenEvents } from '@0x/abi-gen-wrappers';
+import { ERC721Token } from '@0x/contract-artifacts';
+import { schemas } from '@0x/json-schemas';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
-import { artifacts } from '../artifacts';
import { methodOptsSchema } from '../schemas/method_opts_schema';
import { txOptsSchema } from '../schemas/tx_opts_schema';
import {
@@ -17,12 +18,10 @@ import {
} from '../types';
import { assert } from '../utils/assert';
import { constants } from '../utils/constants';
+import { utils } from '../utils/utils';
import { ContractWrapper } from './contract_wrapper';
import { ERC721ProxyWrapper } from './erc721_proxy_wrapper';
-import { ERC721TokenContract, ERC721TokenEventArgs, ERC721TokenEvents } from './generated/erc721_token';
-
-const removeUndefinedProperties = _.pickBy;
/**
* This class includes all the functionality related to interacting with ERC721 token contracts.
@@ -30,9 +29,16 @@ const removeUndefinedProperties = _.pickBy;
* to the 0x ERC721 Proxy smart contract.
*/
export class ERC721TokenWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.ERC721Token.compilerOutput.abi;
- private _tokenContractsByAddress: { [address: string]: ERC721TokenContract };
- private _erc721ProxyWrapper: ERC721ProxyWrapper;
+ public abi: ContractAbi = ERC721Token.compilerOutput.abi;
+ private readonly _tokenContractsByAddress: { [address: string]: ERC721TokenContract };
+ private readonly _erc721ProxyWrapper: ERC721ProxyWrapper;
+ /**
+ * Instantiate ERC721TokenWrapper
+ * @param web3Wrapper Web3Wrapper instance to use
+ * @param networkId Desired networkId
+ * @param erc721ProxyWrapper The ERC721ProxyWrapper instance to use
+ * @param blockPollingIntervalMs The block polling interval to use for active subscriptions
+ */
constructor(
web3Wrapper: Web3Wrapper,
networkId: number,
@@ -143,7 +149,7 @@ export class ERC721TokenWrapper extends ContractWrapper {
ownerAddress: string,
methodOpts: MethodOpts = {},
): Promise<boolean> {
- const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
+ const proxyAddress = this._erc721ProxyWrapper.address;
const isProxyApprovedForAll = await this.isApprovedForAllAsync(
tokenAddress,
ownerAddress,
@@ -192,7 +198,7 @@ export class ERC721TokenWrapper extends ContractWrapper {
tokenId: BigNumber,
methodOpts: MethodOpts = {},
): Promise<boolean> {
- const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
+ const proxyAddress = this._erc721ProxyWrapper.address;
const approvedAddress = await this.getApprovedIfExistsAsync(tokenAddress, tokenId, methodOpts);
const isProxyApproved = approvedAddress === proxyAddress;
return isProxyApproved;
@@ -228,7 +234,7 @@ export class ERC721TokenWrapper extends ContractWrapper {
const txHash = await tokenContract.setApprovalForAll.sendTransactionAsync(
normalizedOperatorAddress,
isApproved,
- removeUndefinedProperties({
+ utils.removeUndefinedProperties({
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
from: normalizedOwnerAddress,
@@ -253,7 +259,7 @@ export class ERC721TokenWrapper extends ContractWrapper {
isApproved: boolean,
txOpts: TransactionOpts = {},
): Promise<string> {
- const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
+ const proxyAddress = this._erc721ProxyWrapper.address;
const txHash = await this.setApprovalForAllAsync(tokenAddress, ownerAddress, proxyAddress, isApproved, txOpts);
return txHash;
}
@@ -288,7 +294,7 @@ export class ERC721TokenWrapper extends ContractWrapper {
const txHash = await tokenContract.approve.sendTransactionAsync(
normalizedApprovedAddress,
tokenId,
- removeUndefinedProperties({
+ utils.removeUndefinedProperties({
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
from: tokenOwnerAddress,
@@ -311,7 +317,7 @@ export class ERC721TokenWrapper extends ContractWrapper {
tokenId: BigNumber,
txOpts: TransactionOpts = {},
): Promise<string> {
- const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
+ const proxyAddress = this._erc721ProxyWrapper.address;
const txHash = await this.setApprovalAsync(tokenAddress, proxyAddress, tokenId, txOpts);
return txHash;
}
@@ -359,7 +365,7 @@ export class ERC721TokenWrapper extends ContractWrapper {
ownerAddress,
normalizedReceiverAddress,
tokenId,
- removeUndefinedProperties({
+ utils.removeUndefinedProperties({
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
from: normalizedSenderAddress,
@@ -393,7 +399,7 @@ export class ERC721TokenWrapper extends ContractWrapper {
normalizedTokenAddress,
eventName,
indexFilterValues,
- artifacts.ERC721Token.compilerOutput.abi,
+ ERC721Token.compilerOutput.abi,
callback,
isVerbose,
);
@@ -438,30 +444,19 @@ export class ERC721TokenWrapper extends ContractWrapper {
eventName,
blockRange,
indexFilterValues,
- artifacts.ERC721Token.compilerOutput.abi,
+ ERC721Token.compilerOutput.abi,
);
return logs;
}
- // HACK: We don't want this method to be visible to the other units within that package but not to the end user.
- // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused.
- // tslint:disable-next-line:no-unused-variable
- private _invalidateContractInstances(): void {
- this.unsubscribeAll();
- this._tokenContractsByAddress = {};
- }
private async _getTokenContractAsync(tokenAddress: string): Promise<ERC721TokenContract> {
const normalizedTokenAddress = tokenAddress.toLowerCase();
let tokenContract = this._tokenContractsByAddress[normalizedTokenAddress];
if (!_.isUndefined(tokenContract)) {
return tokenContract;
}
- const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
- artifacts.ERC721Token,
- normalizedTokenAddress,
- );
const contractInstance = new ERC721TokenContract(
- abi,
- address,
+ this.abi,
+ normalizedTokenAddress,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
diff --git a/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts
index 5046d3667..913c47cf7 100644
--- a/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts
@@ -1,29 +1,35 @@
-import { schemas } from '@0xproject/json-schemas';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { WETH9Contract, WETH9EventArgs, WETH9Events } from '@0x/abi-gen-wrappers';
+import { WETH9 } from '@0x/contract-artifacts';
+import { schemas } from '@0x/json-schemas';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
-import { artifacts } from '../artifacts';
import { BlockRange, ContractWrappersError, EventCallback, IndexedFilterValues, TransactionOpts } from '../types';
import { assert } from '../utils/assert';
+import { utils } from '../utils/utils';
import { ContractWrapper } from './contract_wrapper';
import { ERC20TokenWrapper } from './erc20_token_wrapper';
-import { WETH9Contract, WETH9EventArgs, WETH9Events } from './generated/weth9';
-
-const removeUndefinedProperties = _.pickBy;
/**
* This class includes all the functionality related to interacting with a wrapped Ether ERC20 token contract.
* The caller can convert ETH into the equivalent number of wrapped ETH ERC20 tokens and back.
*/
export class EtherTokenWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.EtherToken.compilerOutput.abi;
- private _etherTokenContractsByAddress: {
+ public abi: ContractAbi = WETH9.compilerOutput.abi;
+ private readonly _etherTokenContractsByAddress: {
[address: string]: WETH9Contract;
} = {};
- private _erc20TokenWrapper: ERC20TokenWrapper;
+ private readonly _erc20TokenWrapper: ERC20TokenWrapper;
+ /**
+ * Instantiate EtherTokenWrapper.
+ * @param web3Wrapper Web3Wrapper instance to use
+ * @param networkId Desired networkId
+ * @param erc20TokenWrapper The ERC20TokenWrapper instance to use
+ * @param blockPollingIntervalMs The block polling interval to use for active subscriptions
+ */
constructor(
web3Wrapper: Web3Wrapper,
networkId: number,
@@ -60,7 +66,7 @@ export class EtherTokenWrapper extends ContractWrapper {
const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
const txHash = await wethContract.deposit.sendTransactionAsync(
- removeUndefinedProperties({
+ utils.removeUndefinedProperties({
from: normalizedDepositorAddress,
value: amountInWei,
gas: txOpts.gasLimit,
@@ -102,7 +108,7 @@ export class EtherTokenWrapper extends ContractWrapper {
const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
const txHash = await wethContract.withdraw.sendTransactionAsync(
amountInWei,
- removeUndefinedProperties({
+ utils.removeUndefinedProperties({
from: normalizedWithdrawerAddress,
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
@@ -135,7 +141,7 @@ export class EtherTokenWrapper extends ContractWrapper {
eventName,
blockRange,
indexFilterValues,
- artifacts.EtherToken.compilerOutput.abi,
+ WETH9.compilerOutput.abi,
);
return logs;
}
@@ -165,7 +171,7 @@ export class EtherTokenWrapper extends ContractWrapper {
normalizedEtherTokenAddress,
eventName,
indexFilterValues,
- artifacts.EtherToken.compilerOutput.abi,
+ WETH9.compilerOutput.abi,
callback,
isVerbose,
);
@@ -185,36 +191,14 @@ export class EtherTokenWrapper extends ContractWrapper {
public unsubscribeAll(): void {
super._unsubscribeAll();
}
- /**
- * Retrieves the Ethereum address of the EtherToken contract deployed on the network
- * that the user-passed web3 provider is connected to. If it's not Kovan, Ropsten, Rinkeby, Mainnet or TestRPC
- * (networkId: 50), it will return undefined (e.g a private network).
- * @returns The Ethereum address of the EtherToken contract or undefined.
- */
- public getContractAddressIfExists(): string | undefined {
- const networkSpecificArtifact = artifacts.EtherToken.networks[this._networkId];
- const contractAddressIfExists = _.isUndefined(networkSpecificArtifact)
- ? undefined
- : networkSpecificArtifact.address;
- return contractAddressIfExists;
- }
- // tslint:disable-next-line:no-unused-variable
- private _invalidateContractInstance(): void {
- this.unsubscribeAll();
- this._etherTokenContractsByAddress = {};
- }
private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<WETH9Contract> {
let etherTokenContract = this._etherTokenContractsByAddress[etherTokenAddress];
if (!_.isUndefined(etherTokenContract)) {
return etherTokenContract;
}
- const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
- artifacts.EtherToken,
- etherTokenAddress,
- );
const contractInstance = new WETH9Contract(
- abi,
- address,
+ this.abi,
+ etherTokenAddress,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
diff --git a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts
index 5a4b40547..2e978f35b 100644
--- a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts
@@ -1,12 +1,20 @@
-import { schemas } from '@0xproject/json-schemas';
-import { assetDataUtils } from '@0xproject/order-utils';
-import { AssetProxyId, Order, SignedOrder } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
-import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
+import { ExchangeContract, ExchangeEventArgs, ExchangeEvents } from '@0x/abi-gen-wrappers';
+import { Exchange } from '@0x/contract-artifacts';
+import { schemas } from '@0x/json-schemas';
+import {
+ assetDataUtils,
+ BalanceAndProxyAllowanceLazyStore,
+ ExchangeTransferSimulator,
+ OrderValidationUtils,
+} from '@0x/order-utils';
+import { AssetProxyId, Order, SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
+import { BlockParamLiteral, ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
-import { artifacts } from '../artifacts';
+import { AssetBalanceAndProxyAllowanceFetcher } from '../fetchers/asset_balance_and_proxy_allowance_fetcher';
+import { OrderFilledCancelledFetcher } from '../fetchers/order_filled_cancelled_fetcher';
import { methodOptsSchema } from '../schemas/method_opts_schema';
import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema';
import { txOptsSchema } from '../schemas/tx_opts_schema';
@@ -18,33 +26,57 @@ import {
MethodOpts,
OrderInfo,
OrderTransactionOpts,
+ ValidateOrderFillableOpts,
} from '../types';
import { assert } from '../utils/assert';
+import { _getDefaultContractAddresses } from '../utils/contract_addresses';
import { decorators } from '../utils/decorators';
import { TransactionEncoder } from '../utils/transaction_encoder';
import { ContractWrapper } from './contract_wrapper';
-import { ExchangeContract, ExchangeEventArgs, ExchangeEvents } from './generated/exchange';
+import { ERC20TokenWrapper } from './erc20_token_wrapper';
+import { ERC721TokenWrapper } from './erc721_token_wrapper';
/**
* This class includes all the functionality related to calling methods, sending transactions and subscribing to
* events of the 0x V2 Exchange smart contract.
*/
export class ExchangeWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.Exchange.compilerOutput.abi;
+ public abi: ContractAbi = Exchange.compilerOutput.abi;
+ public address: string;
+ public zrxTokenAddress: string;
private _exchangeContractIfExists?: ExchangeContract;
- private _contractAddressIfExists?: string;
- private _zrxContractAddressIfExists?: string;
+ private readonly _erc721TokenWrapper: ERC721TokenWrapper;
+ private readonly _erc20TokenWrapper: ERC20TokenWrapper;
+ /**
+ * Instantiate ExchangeWrapper
+ * @param web3Wrapper Web3Wrapper instance to use.
+ * @param networkId Desired networkId.
+ * @param erc20TokenWrapper ERC20TokenWrapper instance to use.
+ * @param erc721TokenWrapper ERC721TokenWrapper instance to use.
+ * @param address The address of the Exchange contract. If undefined, will
+ * default to the known address corresponding to the networkId.
+ * @param zrxTokenAddress The address of the ZRXToken contract. If
+ * undefined, will default to the known address corresponding to the
+ * networkId.
+ * @param blockPollingIntervalMs The block polling interval to use for active subscriptions.
+ */
constructor(
web3Wrapper: Web3Wrapper,
networkId: number,
- contractAddressIfExists?: string,
- zrxContractAddressIfExists?: string,
+ erc20TokenWrapper: ERC20TokenWrapper,
+ erc721TokenWrapper: ERC721TokenWrapper,
+ address?: string,
+ zrxTokenAddress?: string,
blockPollingIntervalMs?: number,
) {
super(web3Wrapper, networkId, blockPollingIntervalMs);
- this._contractAddressIfExists = contractAddressIfExists;
- this._zrxContractAddressIfExists = zrxContractAddressIfExists;
+ this._erc20TokenWrapper = erc20TokenWrapper;
+ this._erc721TokenWrapper = erc721TokenWrapper;
+ this.address = _.isUndefined(address) ? _getDefaultContractAddresses(networkId).exchange : address;
+ this.zrxTokenAddress = _.isUndefined(zrxTokenAddress)
+ ? _getDefaultContractAddresses(networkId).zrxToken
+ : zrxTokenAddress;
}
/**
* Retrieve the address of an asset proxy by signature.
@@ -666,6 +698,7 @@ export class ExchangeWrapper extends ContractWrapper {
* @param leftSignedOrder First order to match.
* @param rightSignedOrder Second order to match.
* @param takerAddress The address that sends the transaction and gets the spread.
+ * @param orderTransactionOpts Optional arguments this method accepts.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
@@ -724,6 +757,7 @@ export class ExchangeWrapper extends ContractWrapper {
* @param signerAddress Address that should have signed the given hash.
* @param signature Proof that the hash has been signed by signer.
* @param senderAddress Address that should send the transaction.
+ * @param orderTransactionOpts Optional arguments this method accepts.
* @returns Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
@@ -902,7 +936,7 @@ export class ExchangeWrapper extends ContractWrapper {
/**
* Cancel a given order.
* @param order An object that conforms to the Order or SignedOrder interface. The order you would like to cancel.
- * @param transactionOpts Optional arguments this method accepts.
+ * @param orderTransactionOpts Optional arguments this method accepts.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
@@ -1022,12 +1056,11 @@ export class ExchangeWrapper extends ContractWrapper {
assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
assert.isFunction('callback', callback);
- const exchangeContractAddress = this.getContractAddress();
const subscriptionToken = this._subscribe<ArgsType>(
- exchangeContractAddress,
+ this.address,
eventName,
indexFilterValues,
- artifacts.Exchange.compilerOutput.abi,
+ Exchange.compilerOutput.abi,
callback,
isVerbose,
);
@@ -1062,40 +1095,79 @@ export class ExchangeWrapper extends ContractWrapper {
assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents);
assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
- const exchangeContractAddress = this.getContractAddress();
const logs = await this._getLogsAsync<ArgsType>(
- exchangeContractAddress,
+ this.address,
eventName,
blockRange,
indexFilterValues,
- artifacts.Exchange.compilerOutput.abi,
+ Exchange.compilerOutput.abi,
);
return logs;
}
/**
- * Retrieves the Ethereum address of the Exchange contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the Exchange contract being used.
+ * Validate if the supplied order is fillable, and throw if it isn't
+ * @param signedOrder SignedOrder of interest
+ * @param opts ValidateOrderFillableOpts options (e.g expectedFillTakerTokenAmount.
+ * If it isn't supplied, we check if the order is fillable for a non-zero amount)
*/
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.Exchange, this._contractAddressIfExists);
- return contractAddress;
+ public async validateOrderFillableOrThrowAsync(
+ signedOrder: SignedOrder,
+ opts: ValidateOrderFillableOpts = {},
+ ): Promise<void> {
+ const balanceAllowanceFetcher = new AssetBalanceAndProxyAllowanceFetcher(
+ this._erc20TokenWrapper,
+ this._erc721TokenWrapper,
+ BlockParamLiteral.Latest,
+ );
+ const balanceAllowanceStore = new BalanceAndProxyAllowanceLazyStore(balanceAllowanceFetcher);
+ const exchangeTradeSimulator = new ExchangeTransferSimulator(balanceAllowanceStore);
+
+ const expectedFillTakerTokenAmountIfExists = opts.expectedFillTakerTokenAmount;
+ const filledCancelledFetcher = new OrderFilledCancelledFetcher(this, BlockParamLiteral.Latest);
+ const orderValidationUtils = new OrderValidationUtils(filledCancelledFetcher);
+ await orderValidationUtils.validateOrderFillableOrThrowAsync(
+ exchangeTradeSimulator,
+ signedOrder,
+ this.getZRXAssetData(),
+ expectedFillTakerTokenAmountIfExists,
+ );
}
/**
- * Returns the ZRX token address used by the exchange contract.
- * @return Address of ZRX token
+ * Validate a call to FillOrder and throw if it wouldn't succeed
+ * @param signedOrder SignedOrder of interest
+ * @param fillTakerAssetAmount Amount we'd like to fill the order for
+ * @param takerAddress The taker of the order
*/
- public getZRXTokenAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.ZRXToken, this._zrxContractAddressIfExists);
- return contractAddress;
+ public async validateFillOrderThrowIfInvalidAsync(
+ signedOrder: SignedOrder,
+ fillTakerAssetAmount: BigNumber,
+ takerAddress: string,
+ ): Promise<void> {
+ const balanceAllowanceFetcher = new AssetBalanceAndProxyAllowanceFetcher(
+ this._erc20TokenWrapper,
+ this._erc721TokenWrapper,
+ BlockParamLiteral.Latest,
+ );
+ const balanceAllowanceStore = new BalanceAndProxyAllowanceLazyStore(balanceAllowanceFetcher);
+ const exchangeTradeSimulator = new ExchangeTransferSimulator(balanceAllowanceStore);
+
+ const filledCancelledFetcher = new OrderFilledCancelledFetcher(this, BlockParamLiteral.Latest);
+ const orderValidationUtils = new OrderValidationUtils(filledCancelledFetcher);
+ await orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
+ exchangeTradeSimulator,
+ this._web3Wrapper.getProvider(),
+ signedOrder,
+ fillTakerAssetAmount,
+ takerAddress,
+ this.getZRXAssetData(),
+ );
}
/**
* Returns the ZRX asset data used by the exchange contract.
* @return ZRX asset data
*/
public getZRXAssetData(): string {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxTokenAddress);
+ const zrxAssetData = assetDataUtils.encodeERC20AssetData(this.zrxTokenAddress);
return zrxAssetData;
}
/**
@@ -1108,23 +1180,14 @@ export class ExchangeWrapper extends ContractWrapper {
const encoder = new TransactionEncoder(exchangeInstance);
return encoder;
}
- // tslint:disable:no-unused-variable
- private _invalidateContractInstances(): void {
- this.unsubscribeAll();
- delete this._exchangeContractIfExists;
- }
// tslint:enable:no-unused-variable
private async _getExchangeContractAsync(): Promise<ExchangeContract> {
if (!_.isUndefined(this._exchangeContractIfExists)) {
return this._exchangeContractIfExists;
}
- const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
- artifacts.Exchange,
- this._contractAddressIfExists,
- );
const contractInstance = new ExchangeContract(
- abi,
- address,
+ this.abi,
+ this.address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
diff --git a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts
index 13ef0fe01..80742e030 100644
--- a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts
@@ -1,101 +1,145 @@
-import { schemas } from '@0xproject/json-schemas';
-import { AssetProxyId, SignedOrder } from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { ForwarderContract } from '@0x/abi-gen-wrappers';
+import { Forwarder } from '@0x/contract-artifacts';
+import { schemas } from '@0x/json-schemas';
+import { SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi } from 'ethereum-types';
import * as _ from 'lodash';
-import { artifacts } from '../artifacts';
import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema';
import { txOptsSchema } from '../schemas/tx_opts_schema';
-import { TransactionOpts } from '../types';
+import { OrderTransactionOpts } from '../types';
import { assert } from '../utils/assert';
import { calldataOptimizationUtils } from '../utils/calldata_optimization_utils';
import { constants } from '../utils/constants';
+import { _getDefaultContractAddresses } from '../utils/contract_addresses';
+import { decorators } from '../utils/decorators';
+import { utils } from '../utils/utils';
import { ContractWrapper } from './contract_wrapper';
-import { ForwarderContract } from './generated/forwarder';
/**
* This class includes the functionality related to interacting with the Forwarder contract.
*/
export class ForwarderWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.Forwarder.compilerOutput.abi;
+ public abi: ContractAbi = Forwarder.compilerOutput.abi;
+ public address: string;
+ public zrxTokenAddress: string;
+ public etherTokenAddress: string;
private _forwarderContractIfExists?: ForwarderContract;
- private _contractAddressIfExists?: string;
- private _zrxContractAddressIfExists?: string;
+
+ /**
+ * Instantiate ForwarderWrapper
+ * @param web3Wrapper Web3Wrapper instance to use.
+ * @param networkId Desired networkId.
+ * @param address The address of the Exchange contract. If undefined, will
+ * default to the known address corresponding to the networkId.
+ * @param zrxTokenAddress The address of the ZRXToken contract. If
+ * undefined, will default to the known address corresponding to the
+ * networkId.
+ * @param etherTokenAddress The address of a WETH (Ether token) contract. If
+ * undefined, will default to the known address corresponding to the
+ * networkId.
+ */
constructor(
web3Wrapper: Web3Wrapper,
networkId: number,
- contractAddressIfExists?: string,
- zrxContractAddressIfExists?: string,
+ address?: string,
+ zrxTokenAddress?: string,
+ etherTokenAddress?: string,
) {
super(web3Wrapper, networkId);
- this._contractAddressIfExists = contractAddressIfExists;
- this._zrxContractAddressIfExists = zrxContractAddressIfExists;
+ this.address = _.isUndefined(address) ? _getDefaultContractAddresses(networkId).exchange : address;
+ this.zrxTokenAddress = _.isUndefined(zrxTokenAddress)
+ ? _getDefaultContractAddresses(networkId).zrxToken
+ : zrxTokenAddress;
+ this.etherTokenAddress = _.isUndefined(etherTokenAddress)
+ ? _getDefaultContractAddresses(networkId).etherToken
+ : etherTokenAddress;
}
/**
* Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.
* Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
* 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).
* Any ETH not spent will be refunded to sender.
- * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset.
- * All orders must specify WETH as the takerAsset
- * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied
- * Provider provided at instantiation.
- * @param ethAmount The amount of eth to send with the transaction (in wei).
- * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset.
- * Used to purchase ZRX for primary order fees.
- * @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
- * Defaults to 0.
- * @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled.
- * @param txOpts Transaction parameters.
+ * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset.
+ * All orders must specify WETH as the takerAsset
+ * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied
+ * Provider provided at instantiation.
+ * @param ethAmount The amount of eth to send with the transaction (in wei).
+ * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset.
+ * Used to purchase ZRX for primary order fees.
+ * @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ * Defaults to 0.
+ * @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled.
+ * @param orderTransactionOpts Transaction parameters.
* @return Transaction hash.
*/
+ @decorators.asyncZeroExErrorHandler
public async marketSellOrdersWithEthAsync(
signedOrders: SignedOrder[],
takerAddress: string,
ethAmount: BigNumber,
signedFeeOrders: SignedOrder[] = [],
- feePercentage: BigNumber = constants.ZERO_AMOUNT,
+ feePercentage: number = 0,
feeRecipientAddress: string = constants.NULL_ADDRESS,
- txOpts: TransactionOpts = {},
+ orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true },
): Promise<string> {
// type assertions
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
assert.isBigNumber('ethAmount', ethAmount);
assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema);
- assert.isBigNumber('feePercentage', feePercentage);
+ assert.isNumber('feePercentage', feePercentage);
assert.isETHAddressHex('feeRecipientAddress', feeRecipientAddress);
- assert.doesConformToSchema('txOpts', txOpts, txOptsSchema);
+ assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
// other assertions
- assert.ordersCanBeUsedForForwarderContract(signedOrders, this.getEtherTokenAddress());
- assert.feeOrdersCanBeUsedForForwarderContract(
- signedFeeOrders,
- this.getZRXTokenAddress(),
- this.getEtherTokenAddress(),
- );
+ assert.ordersCanBeUsedForForwarderContract(signedOrders, this.etherTokenAddress);
+ assert.feeOrdersCanBeUsedForForwarderContract(signedFeeOrders, this.zrxTokenAddress, this.etherTokenAddress);
+ // format feePercentage
+ const formattedFeePercentage = utils.numberPercentageToEtherTokenAmountPercentage(feePercentage);
// lowercase input addresses
const normalizedTakerAddress = takerAddress.toLowerCase();
const normalizedFeeRecipientAddress = feeRecipientAddress.toLowerCase();
// optimize orders
const optimizedMarketOrders = calldataOptimizationUtils.optimizeForwarderOrders(signedOrders);
const optimizedFeeOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(signedFeeOrders);
- // send transaction
+ // compile signatures
+ const signatures = _.map(optimizedMarketOrders, order => order.signature);
+ const feeSignatures = _.map(optimizedFeeOrders, order => order.signature);
+ // get contract
const forwarderContractInstance = await this._getForwarderContractAsync();
+ // validate transaction
+ if (orderTransactionOpts.shouldValidate) {
+ await forwarderContractInstance.marketSellOrdersWithEth.callAsync(
+ optimizedMarketOrders,
+ signatures,
+ optimizedFeeOrders,
+ feeSignatures,
+ formattedFeePercentage,
+ normalizedFeeRecipientAddress,
+ {
+ value: ethAmount,
+ from: normalizedTakerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ }
+ // send transaction
const txHash = await forwarderContractInstance.marketSellOrdersWithEth.sendTransactionAsync(
optimizedMarketOrders,
- _.map(optimizedMarketOrders, order => order.signature),
+ signatures,
optimizedFeeOrders,
- _.map(optimizedFeeOrders, order => order.signature),
- feePercentage,
+ feeSignatures,
+ formattedFeePercentage,
feeRecipientAddress,
{
value: ethAmount,
from: normalizedTakerAddress,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
},
);
return txHash;
@@ -104,29 +148,30 @@ export class ForwarderWrapper extends ContractWrapper {
* Attempt to purchase makerAssetFillAmount of makerAsset by selling ethAmount provided with transaction.
* Any ZRX required to pay fees for primary orders will automatically be purchased by the contract.
* Any ETH not spent will be refunded to sender.
- * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset.
- * All orders must specify WETH as the takerAsset
- * @param makerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill.
- * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied
- * Provider provided at instantiation.
- * @param ethAmount The amount of eth to send with the transaction (in wei).
- * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset.
- * Used to purchase ZRX for primary order fees.
- * @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
- * Defaults to 0.
- * @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled.
- * @param txOpts Transaction parameters.
+ * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset.
+ * All orders must specify WETH as the takerAsset
+ * @param makerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill.
+ * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied
+ * Provider provided at instantiation.
+ * @param ethAmount The amount of eth to send with the transaction (in wei).
+ * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset.
+ * Used to purchase ZRX for primary order fees.
+ * @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
+ * Defaults to 0.
+ * @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled.
+ * @param orderTransactionOpts Transaction parameters.
* @return Transaction hash.
*/
+ @decorators.asyncZeroExErrorHandler
public async marketBuyOrdersWithEthAsync(
signedOrders: SignedOrder[],
makerAssetFillAmount: BigNumber,
takerAddress: string,
ethAmount: BigNumber,
signedFeeOrders: SignedOrder[] = [],
- feePercentage: BigNumber = constants.ZERO_AMOUNT,
+ feePercentage: number = 0,
feeRecipientAddress: string = constants.NULL_ADDRESS,
- txOpts: TransactionOpts = {},
+ orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true },
): Promise<string> {
// type assertions
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
@@ -134,83 +179,68 @@ export class ForwarderWrapper extends ContractWrapper {
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
assert.isBigNumber('ethAmount', ethAmount);
assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema);
- assert.isBigNumber('feePercentage', feePercentage);
+ assert.isNumber('feePercentage', feePercentage);
assert.isETHAddressHex('feeRecipientAddress', feeRecipientAddress);
- assert.doesConformToSchema('txOpts', txOpts, txOptsSchema);
+ assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
// other assertions
- assert.ordersCanBeUsedForForwarderContract(signedOrders, this.getEtherTokenAddress());
- assert.feeOrdersCanBeUsedForForwarderContract(
- signedFeeOrders,
- this.getZRXTokenAddress(),
- this.getEtherTokenAddress(),
- );
+ assert.ordersCanBeUsedForForwarderContract(signedOrders, this.etherTokenAddress);
+ assert.feeOrdersCanBeUsedForForwarderContract(signedFeeOrders, this.zrxTokenAddress, this.etherTokenAddress);
+ // format feePercentage
+ const formattedFeePercentage = utils.numberPercentageToEtherTokenAmountPercentage(feePercentage);
// lowercase input addresses
const normalizedTakerAddress = takerAddress.toLowerCase();
const normalizedFeeRecipientAddress = feeRecipientAddress.toLowerCase();
// optimize orders
const optimizedMarketOrders = calldataOptimizationUtils.optimizeForwarderOrders(signedOrders);
const optimizedFeeOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(signedFeeOrders);
- // send transaction
+ // compile signatures
+ const signatures = _.map(optimizedMarketOrders, order => order.signature);
+ const feeSignatures = _.map(optimizedFeeOrders, order => order.signature);
+ // get contract
const forwarderContractInstance = await this._getForwarderContractAsync();
+ // validate transaction
+ if (orderTransactionOpts.shouldValidate) {
+ await forwarderContractInstance.marketBuyOrdersWithEth.callAsync(
+ optimizedMarketOrders,
+ makerAssetFillAmount,
+ signatures,
+ optimizedFeeOrders,
+ feeSignatures,
+ formattedFeePercentage,
+ normalizedFeeRecipientAddress,
+ {
+ value: ethAmount,
+ from: normalizedTakerAddress,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
+ },
+ );
+ }
+ // send transaction
const txHash = await forwarderContractInstance.marketBuyOrdersWithEth.sendTransactionAsync(
optimizedMarketOrders,
makerAssetFillAmount,
- _.map(optimizedMarketOrders, order => order.signature),
+ signatures,
optimizedFeeOrders,
- _.map(optimizedFeeOrders, order => order.signature),
- feePercentage,
+ feeSignatures,
+ formattedFeePercentage,
feeRecipientAddress,
{
value: ethAmount,
from: normalizedTakerAddress,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
+ gas: orderTransactionOpts.gasLimit,
+ gasPrice: orderTransactionOpts.gasPrice,
},
);
return txHash;
}
- /**
- * Retrieves the Ethereum address of the Forwarder contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the Forwarder contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.Forwarder, this._contractAddressIfExists);
- return contractAddress;
- }
- /**
- * Returns the ZRX token address used by the forwarder contract.
- * @return Address of ZRX token
- */
- public getZRXTokenAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.ZRXToken, this._zrxContractAddressIfExists);
- return contractAddress;
- }
- /**
- * Returns the Ether token address used by the forwarder contract.
- * @return Address of Ether token
- */
- public getEtherTokenAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.EtherToken);
- return contractAddress;
- }
- // HACK: We don't want this method to be visible to the other units within that package but not to the end user.
- // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused.
- // tslint:disable-next-line:no-unused-variable
- private _invalidateContractInstance(): void {
- delete this._forwarderContractIfExists;
- }
private async _getForwarderContractAsync(): Promise<ForwarderContract> {
if (!_.isUndefined(this._forwarderContractIfExists)) {
return this._forwarderContractIfExists;
}
- const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
- artifacts.Forwarder,
- this._contractAddressIfExists,
- );
const contractInstance = new ForwarderContract(
- abi,
- address,
+ this.abi,
+ this.address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
diff --git a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts
new file mode 100644
index 000000000..c186e080e
--- /dev/null
+++ b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts
@@ -0,0 +1,185 @@
+import { OrderValidatorContract } from '@0x/abi-gen-wrappers';
+import { OrderValidator } from '@0x/contract-artifacts';
+import { schemas } from '@0x/json-schemas';
+import { SignedOrder } from '@0x/types';
+import { BigNumber } from '@0x/utils';
+import { Web3Wrapper } from '@0x/web3-wrapper';
+import { ContractAbi } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { BalanceAndAllowance, OrderAndTraderInfo, TraderInfo } from '../types';
+import { assert } from '../utils/assert';
+import { _getDefaultContractAddresses } from '../utils/contract_addresses';
+
+import { ContractWrapper } from './contract_wrapper';
+
+/**
+ * This class includes the functionality related to interacting with the OrderValidator contract.
+ */
+export class OrderValidatorWrapper extends ContractWrapper {
+ public abi: ContractAbi = OrderValidator.compilerOutput.abi;
+ public address: string;
+ private _orderValidatorContractIfExists?: OrderValidatorContract;
+ /**
+ * Instantiate OrderValidatorWrapper
+ * @param web3Wrapper Web3Wrapper instance to use.
+ * @param networkId Desired networkId.
+ * @param address The address of the OrderValidator contract. If undefined,
+ * will default to the known address corresponding to the networkId.
+ */
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, address?: string) {
+ super(web3Wrapper, networkId);
+ this.address = _.isUndefined(address) ? _getDefaultContractAddresses(networkId).exchange : address;
+ }
+ /**
+ * Get an object conforming to OrderAndTraderInfo containing on-chain information of the provided order and address
+ * @param order An object conforming to SignedOrder
+ * @param takerAddress An ethereum address
+ * @return OrderAndTraderInfo
+ */
+ public async getOrderAndTraderInfoAsync(order: SignedOrder, takerAddress: string): Promise<OrderAndTraderInfo> {
+ assert.doesConformToSchema('order', order, schemas.signedOrderSchema);
+ assert.isETHAddressHex('takerAddress', takerAddress);
+ const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync();
+ const orderAndTraderInfo = await OrderValidatorContractInstance.getOrderAndTraderInfo.callAsync(
+ order,
+ takerAddress,
+ );
+ const result = {
+ orderInfo: orderAndTraderInfo[0],
+ traderInfo: orderAndTraderInfo[1],
+ };
+ return result;
+ }
+ /**
+ * Get an array of objects conforming to OrderAndTraderInfo containing on-chain information of the provided orders and addresses
+ * @param orders An array of objects conforming to SignedOrder
+ * @param takerAddresses An array of ethereum addresses
+ * @return array of OrderAndTraderInfo
+ */
+ public async getOrdersAndTradersInfoAsync(
+ orders: SignedOrder[],
+ takerAddresses: string[],
+ ): Promise<OrderAndTraderInfo[]> {
+ assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
+ _.forEach(takerAddresses, (takerAddress, index) =>
+ assert.isETHAddressHex(`takerAddresses[${index}]`, takerAddress),
+ );
+ assert.assert(orders.length === takerAddresses.length, 'Expected orders.length to equal takerAddresses.length');
+ const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync();
+ const ordersAndTradersInfo = await OrderValidatorContractInstance.getOrdersAndTradersInfo.callAsync(
+ orders,
+ takerAddresses,
+ );
+ const orderInfos = ordersAndTradersInfo[0];
+ const traderInfos = ordersAndTradersInfo[1];
+ const result = _.map(orderInfos, (orderInfo, index) => {
+ const traderInfo = traderInfos[index];
+ return {
+ orderInfo,
+ traderInfo,
+ };
+ });
+ return result;
+ }
+ /**
+ * Get an object conforming to TraderInfo containing on-chain balance and allowances for maker and taker of order
+ * @param order An object conforming to SignedOrder
+ * @param takerAddress An ethereum address
+ * @return TraderInfo
+ */
+ public async getTraderInfoAsync(order: SignedOrder, takerAddress: string): Promise<TraderInfo> {
+ assert.doesConformToSchema('order', order, schemas.signedOrderSchema);
+ assert.isETHAddressHex('takerAddress', takerAddress);
+ const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync();
+ const result = await OrderValidatorContractInstance.getTraderInfo.callAsync(order, takerAddress);
+ return result;
+ }
+ /**
+ * Get an array of objects conforming to TraderInfo containing on-chain balance and allowances for maker and taker of order
+ * @param orders An array of objects conforming to SignedOrder
+ * @param takerAddresses An array of ethereum addresses
+ * @return array of TraderInfo
+ */
+ public async getTradersInfoAsync(orders: SignedOrder[], takerAddresses: string[]): Promise<TraderInfo[]> {
+ assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
+ _.forEach(takerAddresses, (takerAddress, index) =>
+ assert.isETHAddressHex(`takerAddresses[${index}]`, takerAddress),
+ );
+ assert.assert(orders.length === takerAddresses.length, 'Expected orders.length to equal takerAddresses.length');
+ const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync();
+ const result = await OrderValidatorContractInstance.getTradersInfo.callAsync(orders, takerAddresses);
+ return result;
+ }
+ /**
+ * Get an object conforming to BalanceAndAllowance containing on-chain balance and allowance for some address and assetData
+ * @param address An ethereum address
+ * @param assetData An encoded string that can be decoded by a specified proxy contract
+ * @return BalanceAndAllowance
+ */
+ public async getBalanceAndAllowanceAsync(address: string, assetData: string): Promise<BalanceAndAllowance> {
+ assert.isETHAddressHex('address', address);
+ assert.isHexString('assetData', assetData);
+ const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync();
+ const balanceAndAllowance = await OrderValidatorContractInstance.getBalanceAndAllowance.callAsync(
+ address,
+ assetData,
+ );
+ const result = {
+ balance: balanceAndAllowance[0],
+ allowance: balanceAndAllowance[1],
+ };
+ return result;
+ }
+ /**
+ * Get an array of objects conforming to BalanceAndAllowance containing on-chain balance and allowance for some address and array of assetDatas
+ * @param address An ethereum address
+ * @param assetDatas An array of encoded strings that can be decoded by a specified proxy contract
+ * @return BalanceAndAllowance
+ */
+ public async getBalancesAndAllowancesAsync(address: string, assetDatas: string[]): Promise<BalanceAndAllowance[]> {
+ assert.isETHAddressHex('address', address);
+ _.forEach(assetDatas, (assetData, index) => assert.isHexString(`assetDatas[${index}]`, assetData));
+ const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync();
+ const balancesAndAllowances = await OrderValidatorContractInstance.getBalancesAndAllowances.callAsync(
+ address,
+ assetDatas,
+ );
+ const balances = balancesAndAllowances[0];
+ const allowances = balancesAndAllowances[1];
+ const result = _.map(balances, (balance, index) => {
+ const allowance = allowances[index];
+ return {
+ balance,
+ allowance,
+ };
+ });
+ return result;
+ }
+ /**
+ * Get owner address of tokenId by calling `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token.
+ * @param tokenAddress An ethereum address
+ * @param tokenId An ERC721 tokenId
+ * @return Owner of tokenId or null address if unowned
+ */
+ public async getERC721TokenOwnerAsync(tokenAddress: string, tokenId: BigNumber): Promise<string | undefined> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isBigNumber('tokenId', tokenId);
+ const OrderValidatorContractInstance = await this._getOrderValidatorContractAsync();
+ const result = await OrderValidatorContractInstance.getERC721TokenOwner.callAsync(tokenAddress, tokenId);
+ return result;
+ }
+ private async _getOrderValidatorContractAsync(): Promise<OrderValidatorContract> {
+ if (!_.isUndefined(this._orderValidatorContractIfExists)) {
+ return this._orderValidatorContractIfExists;
+ }
+ const contractInstance = new OrderValidatorContract(
+ this.abi,
+ this.address,
+ this._web3Wrapper.getProvider(),
+ this._web3Wrapper.getContractDefaults(),
+ );
+ this._orderValidatorContractIfExists = contractInstance;
+ return this._orderValidatorContractIfExists;
+ }
+}