aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contract-wrappers/src/contract_wrappers
diff options
context:
space:
mode:
authorLeonid Logvinov <logvinov.leon@gmail.com>2018-07-03 20:32:38 +0800
committerGitHub <noreply@github.com>2018-07-03 20:32:38 +0800
commit0e690608d39c010740d9fdb9d8e6c12d7e6c4e80 (patch)
treee03b85876abc0de57755575287b89a79e7824296 /packages/contract-wrappers/src/contract_wrappers
parent518a2da0275632a5dd61f99a105163ff5a074927 (diff)
parent09e921a562bea4fa80ec19ce7a9061bbcffbd580 (diff)
downloaddexon-sol-tools-0e690608d39c010740d9fdb9d8e6c12d7e6c4e80.tar
dexon-sol-tools-0e690608d39c010740d9fdb9d8e6c12d7e6c4e80.tar.gz
dexon-sol-tools-0e690608d39c010740d9fdb9d8e6c12d7e6c4e80.tar.bz2
dexon-sol-tools-0e690608d39c010740d9fdb9d8e6c12d7e6c4e80.tar.lz
dexon-sol-tools-0e690608d39c010740d9fdb9d8e6c12d7e6c4e80.tar.xz
dexon-sol-tools-0e690608d39c010740d9fdb9d8e6c12d7e6c4e80.tar.zst
dexon-sol-tools-0e690608d39c010740d9fdb9d8e6c12d7e6c4e80.zip
Merge pull request #782 from 0xProject/feature/contract-wrappers-v2
@0xproject/contract-wrappers V2 refactor. Part 1
Diffstat (limited to 'packages/contract-wrappers/src/contract_wrappers')
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts34
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts (renamed from packages/contract-wrappers/src/contract_wrappers/token_transfer_proxy_wrapper.ts)50
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts (renamed from packages/contract-wrappers/src/contract_wrappers/token_wrapper.ts)142
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts75
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts479
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts68
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts988
-rw-r--r--packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts133
8 files changed, 703 insertions, 1266 deletions
diff --git a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts
index 04f69bc3d..a88745485 100644
--- a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts
@@ -1,14 +1,7 @@
-import {
- Artifact,
- BlockParamLiteral,
- ContractAbi,
- FilterObject,
- LogEntry,
- LogWithDecodedArgs,
- RawLog,
-} from '@0xproject/types';
-import { intervalUtils } from '@0xproject/utils';
+import { ContractArtifact } from '@0xproject/sol-compiler';
+import { AbiDecoder, intervalUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { BlockParamLiteral, ContractAbi, FilterObject, LogEntry, LogWithDecodedArgs, RawLog } from 'ethereum-types';
import { Block, BlockAndLogStreamer } from 'ethereumjs-blockstream';
import * as _ from 'lodash';
@@ -29,9 +22,10 @@ const CONTRACT_NAME_TO_NOT_FOUND_ERROR: {
} = {
ZRX: ContractWrappersError.ZRXContractDoesNotExist,
EtherToken: ContractWrappersError.EtherTokenContractDoesNotExist,
- Token: ContractWrappersError.TokenContractDoesNotExist,
- TokenRegistry: ContractWrappersError.TokenRegistryContractDoesNotExist,
- TokenTransferProxy: ContractWrappersError.TokenTransferProxyContractDoesNotExist,
+ ERC20Token: ContractWrappersError.ERC20TokenContractDoesNotExist,
+ ERC20Proxy: ContractWrappersError.ERC20ProxyContractDoesNotExist,
+ ERC721Token: ContractWrappersError.ERC721TokenContractDoesNotExist,
+ ERC721Proxy: ContractWrappersError.ERC721ProxyContractDoesNotExist,
Exchange: ContractWrappersError.ExchangeContractDoesNotExist,
};
@@ -107,14 +101,12 @@ export abstract class ContractWrapper {
protected _tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>(
log: LogEntry,
): LogWithDecodedArgs<ArgsType> | RawLog {
- if (_.isUndefined(this._web3Wrapper.abiDecoder)) {
- throw new Error(InternalContractWrappersError.NoAbiDecoder);
- }
- const logWithDecodedArgs = this._web3Wrapper.abiDecoder.tryToDecodeLogOrNoop(log);
+ const abiDecoder = new AbiDecoder([this.abi]);
+ const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log);
return logWithDecodedArgs;
}
protected async _getContractAbiAndAddressFromArtifactsAsync(
- artifact: Artifact,
+ artifact: ContractArtifact,
addressIfExists?: string,
): Promise<[ContractAbi, string]> {
let contractAddress: string;
@@ -128,12 +120,12 @@ export abstract class ContractWrapper {
}
const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(contractAddress);
if (!doesContractExist) {
- throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contract_name]);
+ throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contractName]);
}
- const abiAndAddress: [ContractAbi, string] = [artifact.abi, contractAddress];
+ const abiAndAddress: [ContractAbi, string] = [artifact.compilerOutput.abi, contractAddress];
return abiAndAddress;
}
- protected _getContractAddress(artifact: Artifact, addressIfExists?: string): string {
+ protected _getContractAddress(artifact: ContractArtifact, addressIfExists?: string): string {
if (_.isUndefined(addressIfExists)) {
const contractAddress = artifact.networks[this._networkId].address;
if (_.isUndefined(contractAddress)) {
diff --git a/packages/contract-wrappers/src/contract_wrappers/token_transfer_proxy_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts
index 5194931d7..839248754 100644
--- a/packages/contract-wrappers/src/contract_wrappers/token_transfer_proxy_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts
@@ -1,75 +1,75 @@
import { Web3Wrapper } from '@0xproject/web3-wrapper';
-import { ContractAbi } from '@0xproject/types';
+import { ContractAbi } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts } from '../artifacts';
import { assert } from '../utils/assert';
import { ContractWrapper } from './contract_wrapper';
-import { TokenTransferProxyContract } from './generated/token_transfer_proxy';
+import { ERC20ProxyContract } from './generated/erc20_proxy';
/**
- * This class includes the functionality related to interacting with the TokenTransferProxy contract.
+ * This class includes the functionality related to interacting with the ERC20Proxy contract.
*/
-export class TokenTransferProxyWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.TokenTransferProxy.abi;
- private _tokenTransferProxyContractIfExists?: TokenTransferProxyContract;
+export class ERC20ProxyWrapper extends ContractWrapper {
+ public abi: ContractAbi = artifacts.ERC20Proxy.compilerOutput.abi;
+ private _erc20ProxyContractIfExists?: ERC20ProxyContract;
private _contractAddressIfExists?: string;
constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
super(web3Wrapper, networkId);
this._contractAddressIfExists = contractAddressIfExists;
}
/**
- * Check if the Exchange contract address is authorized by the TokenTransferProxy contract.
+ * Check if the Exchange contract address is authorized by the ERC20Proxy contract.
* @param exchangeContractAddress The hex encoded address of the Exchange contract to call.
* @return Whether the exchangeContractAddress is authorized.
*/
public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> {
assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress);
const normalizedExchangeContractAddress = exchangeContractAddress.toLowerCase();
- const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
- const isAuthorized = await tokenTransferProxyContractInstance.authorized.callAsync(
- normalizedExchangeContractAddress,
- );
+ const ERC20ProxyContractInstance = await this._getERC20ProxyContractAsync();
+ const isAuthorized = await ERC20ProxyContractInstance.authorized.callAsync(normalizedExchangeContractAddress);
return isAuthorized;
}
/**
- * Get the list of all Exchange contract addresses authorized by the TokenTransferProxy contract.
+ * Get the list of all Exchange contract addresses authorized by the ERC20Proxy contract.
* @return The list of authorized addresses.
*/
public async getAuthorizedAddressesAsync(): Promise<string[]> {
- const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
- const authorizedAddresses = await tokenTransferProxyContractInstance.getAuthorizedAddresses.callAsync();
+ const ERC20ProxyContractInstance = await this._getERC20ProxyContractAsync();
+ const authorizedAddresses = await ERC20ProxyContractInstance.getAuthorizedAddresses.callAsync();
return authorizedAddresses;
}
/**
- * Retrieves the Ethereum address of the TokenTransferProxy contract deployed on the network
+ * 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 TokenTransferProxy contract being used.
+ * @returns The Ethereum address of the ERC20Proxy contract being used.
*/
public getContractAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.TokenTransferProxy, this._contractAddressIfExists);
+ 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._tokenTransferProxyContractIfExists;
+ delete this._erc20ProxyContractIfExists;
}
- private async _getTokenTransferProxyContractAsync(): Promise<TokenTransferProxyContract> {
- if (!_.isUndefined(this._tokenTransferProxyContractIfExists)) {
- return this._tokenTransferProxyContractIfExists;
+ private async _getERC20ProxyContractAsync(): Promise<ERC20ProxyContract> {
+ if (!_.isUndefined(this._erc20ProxyContractIfExists)) {
+ return this._erc20ProxyContractIfExists;
}
const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
- artifacts.TokenTransferProxy,
+ artifacts.ERC20Proxy,
this._contractAddressIfExists,
);
- const contractInstance = new TokenTransferProxyContract(
+ const contractInstance = new ERC20ProxyContract(
abi,
address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
- this._tokenTransferProxyContractIfExists = contractInstance;
- return this._tokenTransferProxyContractIfExists;
+ this._erc20ProxyContractIfExists = contractInstance;
+ return this._erc20ProxyContractIfExists;
}
}
diff --git a/packages/contract-wrappers/src/contract_wrappers/token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts
index d9364715f..f393e4ed1 100644
--- a/packages/contract-wrappers/src/contract_wrappers/token_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts
@@ -1,10 +1,12 @@
import { schemas } from '@0xproject/json-schemas';
-import { ContractAbi, LogWithDecodedArgs } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/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 {
BlockRange,
ContractWrappersError,
@@ -17,23 +19,25 @@ import { assert } from '../utils/assert';
import { constants } from '../utils/constants';
import { ContractWrapper } from './contract_wrapper';
-import { TokenContract, TokenContractEventArgs, TokenEvents } from './generated/token';
-import { TokenTransferProxyWrapper } from './token_transfer_proxy_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.
* All ERC20 method calls are supported, along with some convenience methods for getting/setting allowances
- * to the 0x Proxy smart contract.
+ * to the 0x ERC20 Proxy smart contract.
*/
-export class TokenWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.Token.abi;
+export class ERC20TokenWrapper extends ContractWrapper {
+ public abi: ContractAbi = artifacts.ERC20Token.compilerOutput.abi;
public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
- private _tokenContractsByAddress: { [address: string]: TokenContract };
- private _tokenTransferProxyWrapper: TokenTransferProxyWrapper;
- constructor(web3Wrapper: Web3Wrapper, networkId: number, tokenTransferProxyWrapper: TokenTransferProxyWrapper) {
+ private _tokenContractsByAddress: { [address: string]: ERC20TokenContract };
+ private _erc20ProxyWrapper: ERC20ProxyWrapper;
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, erc20ProxyWrapper: ERC20ProxyWrapper) {
super(web3Wrapper, networkId);
this._tokenContractsByAddress = {};
- this._tokenTransferProxyWrapper = tokenTransferProxyWrapper;
+ this._erc20ProxyWrapper = erc20ProxyWrapper;
}
/**
* Retrieves an owner's ERC20 token balance.
@@ -49,6 +53,9 @@ export class TokenWrapper extends ContractWrapper {
): Promise<BigNumber> {
assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress);
+ if (!_.isUndefined(methodOpts)) {
+ assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema);
+ }
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedOwnerAddress = ownerAddress.toLowerCase();
@@ -81,17 +88,24 @@ export class TokenWrapper extends ContractWrapper {
assert.isETHAddressHex('spenderAddress', spenderAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress);
await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper);
+ if (!_.isUndefined(txOpts)) {
+ assert.doesConformToSchema('txOpts', txOpts, txOptsSchema);
+ }
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedSpenderAddress = spenderAddress.toLowerCase();
const normalizedOwnerAddress = ownerAddress.toLowerCase();
assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
- const txHash = await tokenContract.approve.sendTransactionAsync(normalizedSpenderAddress, amountInBaseUnits, {
- from: normalizedOwnerAddress,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
+ const txHash = await tokenContract.approve.sendTransactionAsync(
+ normalizedSpenderAddress,
+ amountInBaseUnits,
+ removeUndefinedProperties({
+ from: normalizedOwnerAddress,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ }),
+ );
return txHash;
}
/**
@@ -112,16 +126,10 @@ export class TokenWrapper extends ContractWrapper {
spenderAddress: string,
txOpts: TransactionOpts = {},
): Promise<string> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- assert.isETHAddressHex('spenderAddress', spenderAddress);
- const normalizedTokenAddress = tokenAddress.toLowerCase();
- const normalizedOwnerAddress = ownerAddress.toLowerCase();
- const normalizedSpenderAddress = spenderAddress.toLowerCase();
const txHash = await this.setAllowanceAsync(
- normalizedTokenAddress,
- normalizedOwnerAddress,
- normalizedSpenderAddress,
+ tokenAddress,
+ ownerAddress,
+ spenderAddress,
this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
txOpts,
);
@@ -144,6 +152,9 @@ export class TokenWrapper extends ContractWrapper {
assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.isETHAddressHex('spenderAddress', spenderAddress);
+ if (!_.isUndefined(methodOpts)) {
+ assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema);
+ }
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedOwnerAddress = ownerAddress.toLowerCase();
const normalizedSpenderAddress = spenderAddress.toLowerCase();
@@ -172,18 +183,8 @@ export class TokenWrapper extends ContractWrapper {
ownerAddress: string,
methodOpts?: MethodOpts,
): Promise<BigNumber> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- const normalizedTokenAddress = tokenAddress.toLowerCase();
- const normalizedOwnerAddress = ownerAddress.toLowerCase();
-
- const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
- const allowanceInBaseUnits = await this.getAllowanceAsync(
- normalizedTokenAddress,
- normalizedOwnerAddress,
- proxyAddress,
- methodOpts,
- );
+ const proxyAddress = this._erc20ProxyWrapper.getContractAddress();
+ const allowanceInBaseUnits = await this.getAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, methodOpts);
return allowanceInBaseUnits;
}
/**
@@ -202,16 +203,10 @@ export class TokenWrapper extends ContractWrapper {
amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {},
): Promise<string> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- const normalizedTokenAddress = tokenAddress.toLowerCase();
- const normalizedOwnerAddress = ownerAddress.toLowerCase();
- assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
-
- const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
+ const proxyAddress = this._erc20ProxyWrapper.getContractAddress();
const txHash = await this.setAllowanceAsync(
- normalizedTokenAddress,
- normalizedOwnerAddress,
+ tokenAddress,
+ ownerAddress,
proxyAddress,
amountInBaseUnits,
txOpts,
@@ -234,13 +229,9 @@ export class TokenWrapper extends ContractWrapper {
ownerAddress: string,
txOpts: TransactionOpts = {},
): Promise<string> {
- assert.isETHAddressHex('ownerAddress', ownerAddress);
- assert.isETHAddressHex('tokenAddress', tokenAddress);
- const normalizedTokenAddress = tokenAddress.toLowerCase();
- const normalizedOwnerAddress = ownerAddress.toLowerCase();
const txHash = await this.setProxyAllowanceAsync(
- normalizedTokenAddress,
- normalizedOwnerAddress,
+ tokenAddress,
+ ownerAddress,
this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
txOpts,
);
@@ -265,6 +256,9 @@ export class TokenWrapper extends ContractWrapper {
assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.isETHAddressHex('toAddress', toAddress);
await assert.isSenderAddressAsync('fromAddress', fromAddress, this._web3Wrapper);
+ if (!_.isUndefined(txOpts)) {
+ assert.doesConformToSchema('txOpts', txOpts, txOptsSchema);
+ }
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedFromAddress = fromAddress.toLowerCase();
const normalizedToAddress = toAddress.toLowerCase();
@@ -277,11 +271,15 @@ export class TokenWrapper extends ContractWrapper {
throw new Error(ContractWrappersError.InsufficientBalanceForTransfer);
}
- const txHash = await tokenContract.transfer.sendTransactionAsync(normalizedToAddress, amountInBaseUnits, {
- from: normalizedFromAddress,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
+ const txHash = await tokenContract.transfer.sendTransactionAsync(
+ normalizedToAddress,
+ amountInBaseUnits,
+ removeUndefinedProperties({
+ from: normalizedFromAddress,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ }),
+ );
return txHash;
}
/**
@@ -310,6 +308,9 @@ export class TokenWrapper extends ContractWrapper {
assert.isETHAddressHex('fromAddress', fromAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress);
await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper);
+ if (!_.isUndefined(txOpts)) {
+ assert.doesConformToSchema('txOpts', txOpts, txOptsSchema);
+ }
const normalizedToAddress = toAddress.toLowerCase();
const normalizedFromAddress = fromAddress.toLowerCase();
const normalizedTokenAddress = tokenAddress.toLowerCase();
@@ -336,11 +337,11 @@ export class TokenWrapper extends ContractWrapper {
normalizedFromAddress,
normalizedToAddress,
amountInBaseUnits,
- {
+ removeUndefinedProperties({
from: normalizedSenderAddress,
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
- },
+ }),
);
return txHash;
}
@@ -353,22 +354,22 @@ export class TokenWrapper extends ContractWrapper {
* @param callback Callback that gets called when a log is added/removed
* @return Subscription token used later to unsubscribe
*/
- public subscribe<ArgsType extends TokenContractEventArgs>(
+ public subscribe<ArgsType extends ERC20TokenEventArgs>(
tokenAddress: string,
- eventName: TokenEvents,
+ eventName: ERC20TokenEvents,
indexFilterValues: IndexedFilterValues,
callback: EventCallback<ArgsType>,
): string {
assert.isETHAddressHex('tokenAddress', tokenAddress);
const normalizedTokenAddress = tokenAddress.toLowerCase();
- assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
+ assert.doesBelongToStringEnum('eventName', eventName, ERC20TokenEvents);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
assert.isFunction('callback', callback);
const subscriptionToken = this._subscribe<ArgsType>(
normalizedTokenAddress,
eventName,
indexFilterValues,
- artifacts.Token.abi,
+ artifacts.ERC20Token.compilerOutput.abi,
callback,
);
return subscriptionToken;
@@ -378,6 +379,7 @@ export class TokenWrapper extends ContractWrapper {
* @param subscriptionToken Subscription token returned by `subscribe()`
*/
public unsubscribe(subscriptionToken: string): void {
+ assert.isValidSubscriptionToken('subscriptionToken', subscriptionToken);
this._unsubscribe(subscriptionToken);
}
/**
@@ -395,15 +397,15 @@ export class TokenWrapper extends ContractWrapper {
* the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
* @return Array of logs that match the parameters
*/
- public async getLogsAsync<ArgsType extends TokenContractEventArgs>(
+ public async getLogsAsync<ArgsType extends ERC20TokenEventArgs>(
tokenAddress: string,
- eventName: TokenEvents,
+ eventName: ERC20TokenEvents,
blockRange: BlockRange,
indexFilterValues: IndexedFilterValues,
): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
assert.isETHAddressHex('tokenAddress', tokenAddress);
const normalizedTokenAddress = tokenAddress.toLowerCase();
- assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
+ assert.doesBelongToStringEnum('eventName', eventName, ERC20TokenEvents);
assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
const logs = await this._getLogsAsync<ArgsType>(
@@ -411,26 +413,28 @@ export class TokenWrapper extends ContractWrapper {
eventName,
blockRange,
indexFilterValues,
- artifacts.Token.abi,
+ artifacts.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<TokenContract> {
+ 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.Token,
+ artifacts.ERC20Token,
normalizedTokenAddress,
);
- const contractInstance = new TokenContract(
+ const contractInstance = new ERC20TokenContract(
abi,
address,
this._web3Wrapper.getProvider(),
diff --git a/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts
new file mode 100644
index 000000000..fba995395
--- /dev/null
+++ b/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts
@@ -0,0 +1,75 @@
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { ContractAbi } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { artifacts } from '../artifacts';
+import { assert } from '../utils/assert';
+
+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;
+ private _erc721ProxyContractIfExists?: ERC721ProxyContract;
+ private _contractAddressIfExists?: string;
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
+ super(web3Wrapper, networkId);
+ this._contractAddressIfExists = contractAddressIfExists;
+ }
+ /**
+ * Check if the Exchange contract address is authorized by the ERC721Proxy contract.
+ * @param exchangeContractAddress The hex encoded address of the Exchange contract to call.
+ * @return Whether the exchangeContractAddress is authorized.
+ */
+ public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> {
+ assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress);
+ const normalizedExchangeContractAddress = exchangeContractAddress.toLowerCase();
+ const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync();
+ const isAuthorized = await ERC721ProxyContractInstance.authorized.callAsync(normalizedExchangeContractAddress);
+ return isAuthorized;
+ }
+ /**
+ * Get the list of all Exchange contract addresses authorized by the ERC721Proxy contract.
+ * @return The list of authorized addresses.
+ */
+ public async getAuthorizedAddressesAsync(): Promise<string[]> {
+ const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync();
+ 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> {
+ if (!_.isUndefined(this._erc721ProxyContractIfExists)) {
+ return this._erc721ProxyContractIfExists;
+ }
+ const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
+ artifacts.ERC721Proxy,
+ this._contractAddressIfExists,
+ );
+ const contractInstance = new ERC721ProxyContract(
+ abi,
+ address,
+ this._web3Wrapper.getProvider(),
+ this._web3Wrapper.getContractDefaults(),
+ );
+ this._erc721ProxyContractIfExists = contractInstance;
+ return this._erc721ProxyContractIfExists;
+ }
+}
diff --git a/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts
new file mode 100644
index 000000000..415415fd7
--- /dev/null
+++ b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts
@@ -0,0 +1,479 @@
+import { schemas } from '@0xproject/json-schemas';
+import { BigNumber } from '@0xproject/utils';
+import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
+import * as _ from 'lodash';
+
+import { constants } from '../../test/utils/constants';
+import { artifacts } from '../artifacts';
+import { methodOptsSchema } from '../schemas/method_opts_schema';
+import { txOptsSchema } from '../schemas/tx_opts_schema';
+import {
+ BlockRange,
+ ContractWrappersError,
+ EventCallback,
+ IndexedFilterValues,
+ MethodOpts,
+ TransactionOpts,
+} from '../types';
+import { assert } from '../utils/assert';
+
+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.
+ * All ERC721 method calls are supported, along with some convenience methods for getting/setting allowances
+ * 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;
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, erc721ProxyWrapper: ERC721ProxyWrapper) {
+ super(web3Wrapper, networkId);
+ this._tokenContractsByAddress = {};
+ this._erc721ProxyWrapper = erc721ProxyWrapper;
+ }
+ /**
+ * Count all NFTs assigned to an owner
+ * NFTs assigned to the zero address are considered invalid, and this function throws for queries about the zero address.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address whose balance you would like to check.
+ * @param methodOpts Optional arguments this method accepts.
+ * @return The number of NFTs owned by `ownerAddress`, possibly zero
+ */
+ public async getTokenCountAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ methodOpts?: MethodOpts,
+ ): Promise<BigNumber> {
+ assert.isETHAddressHex('ownerAddress', ownerAddress);
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ if (!_.isUndefined(methodOpts)) {
+ assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema);
+ }
+ const normalizedTokenAddress = tokenAddress.toLowerCase();
+ const normalizedOwnerAddress = ownerAddress.toLowerCase();
+
+ const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ const txData = {};
+ let balance = await tokenContract.balanceOf.callAsync(normalizedOwnerAddress, txData, defaultBlock);
+ // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
+ balance = new BigNumber(balance);
+ return balance;
+ }
+ /**
+ * Find the owner of an NFT
+ * NFTs assigned to zero address are considered invalid, and queries about them do throw.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
+ * @param tokenId The identifier for an NFT
+ * @param methodOpts Optional arguments this method accepts.
+ * @return The address of the owner of the NFT
+ */
+ public async getOwnerOfAsync(tokenAddress: string, tokenId: BigNumber, methodOpts?: MethodOpts): Promise<string> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isBigNumber('tokenId', tokenId);
+ if (!_.isUndefined(methodOpts)) {
+ assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema);
+ }
+ const normalizedTokenAddress = tokenAddress.toLowerCase();
+
+ const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ const txData = {};
+ try {
+ const tokenOwner = await tokenContract.ownerOf.callAsync(tokenId, txData, defaultBlock);
+ return tokenOwner;
+ } catch (err) {
+ throw new Error(ContractWrappersError.ERC721OwnerNotFound);
+ }
+ }
+ /**
+ * Query if an address is an authorized operator for all NFT's of `ownerAddress`
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address of the token owner.
+ * @param operatorAddress The hex encoded user Ethereum address of the operator you'd like to check if approved.
+ * @param methodOpts Optional arguments this method accepts.
+ * @return True if `operatorAddress` is an approved operator for `ownerAddress`, false otherwise
+ */
+ public async isApprovedForAllAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ operatorAddress: string,
+ methodOpts?: MethodOpts,
+ ): Promise<boolean> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isETHAddressHex('ownerAddress', ownerAddress);
+ assert.isETHAddressHex('operatorAddress', operatorAddress);
+ if (!_.isUndefined(methodOpts)) {
+ assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema);
+ }
+ const normalizedTokenAddress = tokenAddress.toLowerCase();
+ const normalizedOwnerAddress = ownerAddress.toLowerCase();
+ const normalizedOperatorAddress = operatorAddress.toLowerCase();
+
+ const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ const txData = {};
+ const isApprovedForAll = await tokenContract.isApprovedForAll.callAsync(
+ normalizedOwnerAddress,
+ normalizedOperatorAddress,
+ txData,
+ defaultBlock,
+ );
+ return isApprovedForAll;
+ }
+ /**
+ * Query if 0x proxy is an authorized operator for all NFT's of `ownerAddress`
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address of the token owner.
+ * @param methodOpts Optional arguments this method accepts.
+ * @return True if `operatorAddress` is an approved operator for `ownerAddress`, false otherwise
+ */
+ public async isProxyApprovedForAllAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ methodOpts?: MethodOpts,
+ ): Promise<boolean> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isETHAddressHex('ownerAddress', ownerAddress);
+ if (!_.isUndefined(methodOpts)) {
+ assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema);
+ }
+ const normalizedTokenAddress = tokenAddress.toLowerCase();
+ const normalizedOwnerAddress = ownerAddress.toLowerCase();
+ const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
+ const isProxyApprovedForAll = await this.isApprovedForAllAsync(
+ normalizedTokenAddress,
+ normalizedOwnerAddress,
+ proxyAddress,
+ methodOpts,
+ );
+ return isProxyApprovedForAll;
+ }
+ /**
+ * Get the approved address for a single NFT. Returns undefined if no approval was set
+ * Throws if `_tokenId` is not a valid NFT
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
+ * @param tokenId The identifier for an NFT
+ * @param methodOpts Optional arguments this method accepts.
+ * @return The approved address for this NFT, or the undefined if there is none
+ */
+ public async getApprovedIfExistsAsync(
+ tokenAddress: string,
+ tokenId: BigNumber,
+ methodOpts?: MethodOpts,
+ ): Promise<string | undefined> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isBigNumber('tokenId', tokenId);
+ if (!_.isUndefined(methodOpts)) {
+ assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema);
+ }
+ const normalizedTokenAddress = tokenAddress.toLowerCase();
+
+ const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
+ const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
+ const txData = {};
+ const approvedAddress = await tokenContract.getApproved.callAsync(tokenId, txData, defaultBlock);
+ if (approvedAddress === constants.NULL_ADDRESS) {
+ return undefined;
+ }
+ return approvedAddress;
+ }
+ /**
+ * Checks if 0x proxy is approved for a single NFT
+ * Throws if `_tokenId` is not a valid NFT
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
+ * @param tokenId The identifier for an NFT
+ * @param methodOpts Optional arguments this method accepts.
+ * @return True if 0x proxy is approved
+ */
+ public async isProxyApprovedAsync(
+ tokenAddress: string,
+ tokenId: BigNumber,
+ methodOpts?: MethodOpts,
+ ): Promise<boolean> {
+ const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
+ const approvedAddress = await this.getApprovedIfExistsAsync(tokenAddress, tokenId, methodOpts);
+ const isProxyApproved = approvedAddress === proxyAddress;
+ return isProxyApproved;
+ }
+ /**
+ * Enable or disable approval for a third party ("operator") to manage all of `ownerAddress`'s assets.
+ * Throws if `_tokenId` is not a valid NFT
+ * Emits the ApprovalForAll event.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address of the token owner.
+ * @param operatorAddress The hex encoded user Ethereum address of the operator you'd like to set approval for.
+ * @param isApproved The boolean variable to set the approval to.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setApprovalForAllAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ operatorAddress: string,
+ isApproved: boolean,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper);
+ assert.isETHAddressHex('operatorAddress', operatorAddress);
+ assert.isBoolean('isApproved', isApproved);
+ if (!_.isUndefined(txOpts)) {
+ assert.doesConformToSchema('txOpts', txOpts, txOptsSchema);
+ }
+ const normalizedTokenAddress = tokenAddress.toLowerCase();
+ const normalizedOwnerAddress = ownerAddress.toLowerCase();
+ const normalizedOperatorAddress = operatorAddress.toLowerCase();
+
+ const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
+ const txHash = await tokenContract.setApprovalForAll.sendTransactionAsync(
+ normalizedOperatorAddress,
+ isApproved,
+ removeUndefinedProperties({
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ from: normalizedOwnerAddress,
+ }),
+ );
+ return txHash;
+ }
+ /**
+ * Enable or disable approval for a third party ("operator") to manage all of `ownerAddress`'s assets.
+ * Throws if `_tokenId` is not a valid NFT
+ * Emits the ApprovalForAll event.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
+ * @param ownerAddress The hex encoded user Ethereum address of the token owner.
+ * @param operatorAddress The hex encoded user Ethereum address of the operator you'd like to set approval for.
+ * @param isApproved The boolean variable to set the approval to.
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setProxyApprovalForAllAsync(
+ tokenAddress: string,
+ ownerAddress: string,
+ isApproved: boolean,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
+ const txHash = await this.setApprovalForAllAsync(tokenAddress, ownerAddress, proxyAddress, isApproved, txOpts);
+ return txHash;
+ }
+ /**
+ * Set or reaffirm the approved address for an NFT
+ * The zero address indicates there is no approved address. Throws unless `msg.sender` is the current NFT owner,
+ * or an authorized operator of the current owner.
+ * Throws if `_tokenId` is not a valid NFT
+ * Emits the Approval event.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
+ * @param approvedAddress The hex encoded user Ethereum address you'd like to set approval for.
+ * @param tokenId The identifier for an NFT
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setApprovalAsync(
+ tokenAddress: string,
+ approvedAddress: string,
+ tokenId: BigNumber,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isETHAddressHex('approvedAddress', approvedAddress);
+ assert.isBigNumber('tokenId', tokenId);
+ if (!_.isUndefined(txOpts)) {
+ assert.doesConformToSchema('txOpts', txOpts, txOptsSchema);
+ }
+ const normalizedTokenAddress = tokenAddress.toLowerCase();
+ const normalizedApprovedAddress = approvedAddress.toLowerCase();
+
+ const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
+ const tokenOwnerAddress = await tokenContract.ownerOf.callAsync(tokenId);
+ await assert.isSenderAddressAsync('tokenOwnerAddress', tokenOwnerAddress, this._web3Wrapper);
+ const txHash = await tokenContract.approve.sendTransactionAsync(
+ normalizedApprovedAddress,
+ tokenId,
+ removeUndefinedProperties({
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ from: tokenOwnerAddress,
+ }),
+ );
+ return txHash;
+ }
+ /**
+ * Set or reaffirm 0x proxy as an approved address for an NFT
+ * Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner.
+ * Throws if `_tokenId` is not a valid NFT
+ * Emits the Approval event.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
+ * @param tokenId The identifier for an NFT
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async setProxyApprovalAsync(
+ tokenAddress: string,
+ tokenId: BigNumber,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ const proxyAddress = this._erc721ProxyWrapper.getContractAddress();
+ const txHash = await this.setApprovalAsync(tokenAddress, proxyAddress, tokenId, txOpts);
+ return txHash;
+ }
+ /**
+ * Enable or disable approval for a third party ("operator") to manage all of `ownerAddress`'s assets.
+ * Throws if `_tokenId` is not a valid NFT
+ * Emits the ApprovalForAll event.
+ * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed.
+ * @param receiverAddress The hex encoded Ethereum address of the user to send the NFT to.
+ * @param senderAddress The hex encoded Ethereum address of the user to send the NFT to.
+ * @param tokenId The identifier for an NFT
+ * @param txOpts Transaction parameters.
+ * @return Transaction hash.
+ */
+ public async transferFromAsync(
+ tokenAddress: string,
+ receiverAddress: string,
+ senderAddress: string,
+ tokenId: BigNumber,
+ txOpts: TransactionOpts = {},
+ ): Promise<string> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ assert.isETHAddressHex('receiverAddress', receiverAddress);
+ await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper);
+ if (!_.isUndefined(txOpts)) {
+ assert.doesConformToSchema('txOpts', txOpts, txOptsSchema);
+ }
+ const normalizedTokenAddress = tokenAddress.toLowerCase();
+ const normalizedReceiverAddress = receiverAddress.toLowerCase();
+ const normalizedSenderAddress = senderAddress.toLowerCase();
+ const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
+ const ownerAddress = await this.getOwnerOfAsync(tokenAddress, tokenId);
+ const isApprovedForAll = await this.isApprovedForAllAsync(
+ normalizedTokenAddress,
+ ownerAddress,
+ normalizedSenderAddress,
+ );
+ if (!isApprovedForAll) {
+ const approvedAddress = await this.getApprovedIfExistsAsync(normalizedTokenAddress, tokenId);
+ if (approvedAddress !== senderAddress) {
+ throw new Error(ContractWrappersError.ERC721NoApproval);
+ }
+ }
+ const txHash = await tokenContract.transferFrom.sendTransactionAsync(
+ ownerAddress,
+ normalizedReceiverAddress,
+ tokenId,
+ removeUndefinedProperties({
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ from: normalizedSenderAddress,
+ }),
+ );
+ return txHash;
+ }
+ /**
+ * Subscribe to an event type emitted by the Token contract.
+ * @param tokenAddress The hex encoded address where the ERC721 token is deployed.
+ * @param eventName The token contract event you would like to subscribe to.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
+ * @param callback Callback that gets called when a log is added/removed
+ * @return Subscription token used later to unsubscribe
+ */
+ public subscribe<ArgsType extends ERC721TokenEventArgs>(
+ tokenAddress: string,
+ eventName: ERC721TokenEvents,
+ indexFilterValues: IndexedFilterValues,
+ callback: EventCallback<ArgsType>,
+ ): string {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ const normalizedTokenAddress = tokenAddress.toLowerCase();
+ assert.doesBelongToStringEnum('eventName', eventName, ERC721TokenEvents);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ assert.isFunction('callback', callback);
+ const subscriptionToken = this._subscribe<ArgsType>(
+ normalizedTokenAddress,
+ eventName,
+ indexFilterValues,
+ artifacts.ERC721Token.compilerOutput.abi,
+ callback,
+ );
+ return subscriptionToken;
+ }
+ /**
+ * Cancel a subscription
+ * @param subscriptionToken Subscription token returned by `subscribe()`
+ */
+ public unsubscribe(subscriptionToken: string): void {
+ assert.isValidSubscriptionToken('subscriptionToken', subscriptionToken);
+ this._unsubscribe(subscriptionToken);
+ }
+ /**
+ * Cancels all existing subscriptions
+ */
+ public unsubscribeAll(): void {
+ super._unsubscribeAll();
+ }
+ /**
+ * Gets historical logs without creating a subscription
+ * @param tokenAddress An address of the token that emitted the logs.
+ * @param eventName The token contract event you would like to subscribe to.
+ * @param blockRange Block range to get logs from.
+ * @param indexFilterValues An object where the keys are indexed args returned by the event and
+ * the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
+ * @return Array of logs that match the parameters
+ */
+ public async getLogsAsync<ArgsType extends ERC721TokenEventArgs>(
+ tokenAddress: string,
+ eventName: ERC721TokenEvents,
+ blockRange: BlockRange,
+ indexFilterValues: IndexedFilterValues,
+ ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
+ assert.isETHAddressHex('tokenAddress', tokenAddress);
+ const normalizedTokenAddress = tokenAddress.toLowerCase();
+ assert.doesBelongToStringEnum('eventName', eventName, ERC721TokenEvents);
+ assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
+ assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
+ const logs = await this._getLogsAsync<ArgsType>(
+ normalizedTokenAddress,
+ eventName,
+ blockRange,
+ indexFilterValues,
+ artifacts.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._web3Wrapper.getProvider(),
+ this._web3Wrapper.getContractDefaults(),
+ );
+ tokenContract = contractInstance;
+ this._tokenContractsByAddress[normalizedTokenAddress] = tokenContract;
+ return tokenContract;
+ }
+}
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 36b7a234a..97872c247 100644
--- a/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts
+++ b/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts
@@ -1,7 +1,7 @@
import { schemas } from '@0xproject/json-schemas';
-import { ContractAbi, LogWithDecodedArgs } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
+import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
import { artifacts } from '../artifacts';
@@ -9,22 +9,24 @@ import { BlockRange, ContractWrappersError, EventCallback, IndexedFilterValues,
import { assert } from '../utils/assert';
import { ContractWrapper } from './contract_wrapper';
-import { EtherTokenContract, EtherTokenContractEventArgs, EtherTokenEvents } from './generated/ether_token';
-import { TokenWrapper } from './token_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.abi;
+ public abi: ContractAbi = artifacts.EtherToken.compilerOutput.abi;
private _etherTokenContractsByAddress: {
- [address: string]: EtherTokenContract;
+ [address: string]: WETH9Contract;
} = {};
- private _tokenWrapper: TokenWrapper;
- constructor(web3Wrapper: Web3Wrapper, networkId: number, tokenWrapper: TokenWrapper) {
+ private _erc20TokenWrapper: ERC20TokenWrapper;
+ constructor(web3Wrapper: Web3Wrapper, networkId: number, erc20TokenWrapper: ERC20TokenWrapper) {
super(web3Wrapper, networkId);
- this._tokenWrapper = tokenWrapper;
+ this._erc20TokenWrapper = erc20TokenWrapper;
}
/**
* Deposit ETH into the Wrapped ETH smart contract and issues the equivalent number of wrapped ETH tokens
@@ -52,12 +54,14 @@ export class EtherTokenWrapper extends ContractWrapper {
assert.assert(ethBalanceInWei.gte(amountInWei), ContractWrappersError.InsufficientEthBalanceForDeposit);
const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
- const txHash = await wethContract.deposit.sendTransactionAsync({
- from: normalizedDepositorAddress,
- value: amountInWei,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
+ const txHash = await wethContract.deposit.sendTransactionAsync(
+ removeUndefinedProperties({
+ from: normalizedDepositorAddress,
+ value: amountInWei,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ }),
+ );
return txHash;
}
/**
@@ -81,7 +85,7 @@ export class EtherTokenWrapper extends ContractWrapper {
const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
const normalizedWithdrawerAddress = withdrawer.toLowerCase();
- const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(
+ const WETHBalanceInBaseUnits = await this._erc20TokenWrapper.getBalanceAsync(
normalizedEtherTokenAddress,
normalizedWithdrawerAddress,
);
@@ -91,11 +95,14 @@ export class EtherTokenWrapper extends ContractWrapper {
);
const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
- const txHash = await wethContract.withdraw.sendTransactionAsync(amountInWei, {
- from: normalizedWithdrawerAddress,
- gas: txOpts.gasLimit,
- gasPrice: txOpts.gasPrice,
- });
+ const txHash = await wethContract.withdraw.sendTransactionAsync(
+ amountInWei,
+ removeUndefinedProperties({
+ from: normalizedWithdrawerAddress,
+ gas: txOpts.gasLimit,
+ gasPrice: txOpts.gasPrice,
+ }),
+ );
return txHash;
}
/**
@@ -107,15 +114,15 @@ export class EtherTokenWrapper extends ContractWrapper {
* the value is the value you are interested in. E.g `{_owner: aUserAddressHex}`
* @return Array of logs that match the parameters
*/
- public async getLogsAsync<ArgsType extends EtherTokenContractEventArgs>(
+ public async getLogsAsync<ArgsType extends WETH9EventArgs>(
etherTokenAddress: string,
- eventName: EtherTokenEvents,
+ eventName: WETH9Events,
blockRange: BlockRange,
indexFilterValues: IndexedFilterValues,
): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
- assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
+ assert.doesBelongToStringEnum('eventName', eventName, WETH9Events);
assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
const logs = await this._getLogsAsync<ArgsType>(
@@ -123,7 +130,7 @@ export class EtherTokenWrapper extends ContractWrapper {
eventName,
blockRange,
indexFilterValues,
- artifacts.EtherToken.abi,
+ artifacts.EtherToken.compilerOutput.abi,
);
return logs;
}
@@ -136,22 +143,22 @@ export class EtherTokenWrapper extends ContractWrapper {
* @param callback Callback that gets called when a log is added/removed
* @return Subscription token used later to unsubscribe
*/
- public subscribe<ArgsType extends EtherTokenContractEventArgs>(
+ public subscribe<ArgsType extends WETH9EventArgs>(
etherTokenAddress: string,
- eventName: EtherTokenEvents,
+ eventName: WETH9Events,
indexFilterValues: IndexedFilterValues,
callback: EventCallback<ArgsType>,
): string {
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
- assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
+ assert.doesBelongToStringEnum('eventName', eventName, WETH9Events);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
assert.isFunction('callback', callback);
const subscriptionToken = this._subscribe<ArgsType>(
normalizedEtherTokenAddress,
eventName,
indexFilterValues,
- artifacts.EtherToken.abi,
+ artifacts.EtherToken.compilerOutput.abi,
callback,
);
return subscriptionToken;
@@ -161,6 +168,7 @@ export class EtherTokenWrapper extends ContractWrapper {
* @param subscriptionToken Subscription token returned by `subscribe()`
*/
public unsubscribe(subscriptionToken: string): void {
+ assert.isValidSubscriptionToken('subscriptionToken', subscriptionToken);
this._unsubscribe(subscriptionToken);
}
/**
@@ -187,7 +195,7 @@ export class EtherTokenWrapper extends ContractWrapper {
this.unsubscribeAll();
this._etherTokenContractsByAddress = {};
}
- private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<EtherTokenContract> {
+ private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<WETH9Contract> {
let etherTokenContract = this._etherTokenContractsByAddress[etherTokenAddress];
if (!_.isUndefined(etherTokenContract)) {
return etherTokenContract;
@@ -196,7 +204,7 @@ export class EtherTokenWrapper extends ContractWrapper {
artifacts.EtherToken,
etherTokenAddress,
);
- const contractInstance = new EtherTokenContract(
+ const contractInstance = new WETH9Contract(
abi,
address,
this._web3Wrapper.getProvider(),
diff --git a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts
deleted file mode 100644
index 8548a06b6..000000000
--- a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts
+++ /dev/null
@@ -1,988 +0,0 @@
-import { schemas } from '@0xproject/json-schemas';
-import { formatters, getOrderHashHex, OrderStateUtils } from '@0xproject/order-utils';
-import {
- BlockParamLiteral,
- ContractAbi,
- DecodedLogArgs,
- ECSignature,
- ExchangeContractErrs,
- LogEntry,
- LogWithDecodedArgs,
- Order,
- OrderState,
- SignedOrder,
-} from '@0xproject/types';
-import { BigNumber } from '@0xproject/utils';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
-import * as _ from 'lodash';
-
-import { artifacts } from '../artifacts';
-import { SimpleBalanceAndProxyAllowanceFetcher } from '../fetchers/simple_balance_and_proxy_allowance_fetcher';
-import { SimpleOrderFilledCancelledFetcher } from '../fetchers/simple_order_filled_cancelled_fetcher';
-import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store';
-import {
- BlockRange,
- EventCallback,
- ExchangeContractErrCodes,
- IndexedFilterValues,
- MethodOpts,
- OrderCancellationRequest,
- OrderFillRequest,
- OrderTransactionOpts,
- ValidateOrderFillableOpts,
-} from '../types';
-import { assert } from '../utils/assert';
-import { decorators } from '../utils/decorators';
-import { ExchangeTransferSimulator } from '../utils/exchange_transfer_simulator';
-import { OrderValidationUtils } from '../utils/order_validation_utils';
-
-import { ContractWrapper } from './contract_wrapper';
-import {
- ExchangeContract,
- ExchangeContractEventArgs,
- ExchangeEvents,
- LogErrorContractEventArgs,
-} from './generated/exchange';
-import { TokenWrapper } from './token_wrapper';
-const SHOULD_VALIDATE_BY_DEFAULT = true;
-
-interface ExchangeContractErrCodesToMsgs {
- [exchangeContractErrCodes: number]: string;
-}
-
-/**
- * This class includes all the functionality related to calling methods and subscribing to
- * events of the 0x Exchange smart contract.
- */
-export class ExchangeWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.Exchange.abi;
- private _exchangeContractIfExists?: ExchangeContract;
- private _orderValidationUtilsIfExists?: OrderValidationUtils;
- private _tokenWrapper: TokenWrapper;
- private _exchangeContractErrCodesToMsg: ExchangeContractErrCodesToMsgs = {
- [ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
- [ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.OrderFillExpired,
- [ExchangeContractErrCodes.ERROR_FILL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
- [ExchangeContractErrCodes.ERROR_CANCEL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero,
- [ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.OrderFillRoundingError,
- [ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FillBalanceAllowanceError,
- };
- private _contractAddressIfExists?: string;
- private _zrxContractAddressIfExists?: string;
- constructor(
- web3Wrapper: Web3Wrapper,
- networkId: number,
- tokenWrapper: TokenWrapper,
- contractAddressIfExists?: string,
- zrxContractAddressIfExists?: string,
- ) {
- super(web3Wrapper, networkId);
- this._tokenWrapper = tokenWrapper;
- this._contractAddressIfExists = contractAddressIfExists;
- this._zrxContractAddressIfExists = zrxContractAddressIfExists;
- }
- /**
- * Returns the unavailable takerAmount of an order. Unavailable amount is defined as the total
- * amount that has been filled or cancelled. The remaining takerAmount can be calculated by
- * subtracting the unavailable amount from the total order takerAmount.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the
- * unavailable takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has either been filled or cancelled.
- */
- public async getUnavailableTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
-
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- const txData = {};
- let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync(
- orderHash,
- txData,
- defaultBlock,
- );
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- unavailableTakerTokenAmount = new BigNumber(unavailableTakerTokenAmount);
- return unavailableTakerTokenAmount;
- }
- /**
- * Retrieve the takerAmount of an order that has already been filled.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the filled takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has already been filled.
- */
- public async getFilledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
-
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- const txData = {};
- let fillAmountInBaseUnits = await exchangeContract.filled.callAsync(orderHash, txData, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- fillAmountInBaseUnits = new BigNumber(fillAmountInBaseUnits);
- return fillAmountInBaseUnits;
- }
- /**
- * Retrieve the takerAmount of an order that has been cancelled.
- * @param orderHash The hex encoded orderHash for which you would like to retrieve the
- * cancelled takerAmount.
- * @param methodOpts Optional arguments this method accepts.
- * @return The amount of the order (in taker tokens) that has been cancelled.
- */
- public async getCancelledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> {
- assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
-
- const exchangeContract = await this._getExchangeContractAsync();
- const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
- const txData = {};
- let cancelledAmountInBaseUnits = await exchangeContract.cancelled.callAsync(orderHash, txData, defaultBlock);
- // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
- cancelledAmountInBaseUnits = new BigNumber(cancelledAmountInBaseUnits);
- return cancelledAmountInBaseUnits;
- }
- /**
- * Fills a signed order with an amount denominated in baseUnits of the taker token.
- * Since the order in which transactions are included in the next block is indeterminate, race-conditions
- * could arise where a users balance or allowance changes before the fillOrder executes. Because of this,
- * we allow you to specify `shouldThrowOnInsufficientBalanceOrAllowance`.
- * If false, the smart contract will not throw if the parties
- * do not have sufficient balances/allowances, preserving gas costs. Setting it to true forgoes this check
- * and causes the smart contract to throw (using all the gas supplied) instead.
- * @param signedOrder An object that conforms to the SignedOrder interface.
- * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that
- * you wish to fill.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
- * if upon execution the tokens cannot be transferred.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Provider
- * passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrderAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
-
- const exchangeInstance = await this._getExchangeContractAsync();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- await orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
-
- const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(signedOrder);
-
- const txHash: string = await exchangeInstance.fillOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: normalizedTakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Sequentially and atomically fills signedOrders up to the specified takerTokenFillAmount.
- * If the fill amount is reached - it succeeds and does not fill the rest of the orders.
- * If fill amount is not reached - it fills as much of the fill amount as possible and succeeds.
- * @param signedOrders The array of signedOrders that you would like to fill until
- * takerTokenFillAmount is reached.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw if
- * upon execution any of the tokens cannot be transferred.
- * If set to false, the call will continue to fill subsequent
- * signedOrders even when some cannot be filled.
- * @param takerAddress The user Ethereum address who would like to fill these
- * orders. Must be available via the supplied Provider
- * passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrdersUpToAsync(
- signedOrders: SignedOrder[],
- fillTakerTokenAmount: BigNumber,
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
- const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress);
- assert.hasAtMostOneUniqueValue(
- takerTokenAddresses,
- ExchangeContractErrs.MultipleTakerTokensInFillUpToDisallowed,
- );
- const exchangeContractAddresses = _.map(signedOrders, signedOrder => signedOrder.exchangeContractAddress);
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- let filledTakerTokenAmount = new BigNumber(0);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- for (const signedOrder of signedOrders) {
- const singleFilledTakerTokenAmount = await orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount.minus(filledTakerTokenAmount),
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- filledTakerTokenAmount = filledTakerTokenAmount.plus(singleFilledTakerTokenAmount);
- if (filledTakerTokenAmount.eq(fillTakerTokenAmount)) {
- break;
- }
- }
- }
-
- if (_.isEmpty(signedOrders)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
-
- const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => {
- return [
- ...formatters.getOrderAddressesAndValues(signedOrder),
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddressesArray, orderValuesArray, vArray, rArray, sArray] = _.unzip<any>(
- orderAddressesValuesAndSignatureArray,
- );
-
- const exchangeInstance = await this._getExchangeContractAsync();
- const txHash = await exchangeInstance.fillOrdersUpTo.sendTransactionAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmount,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: normalizedTakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of fillOrderAsync.
- * Executes multiple fills atomically in a single transaction.
- * If shouldThrowOnInsufficientBalanceOrAllowance is set to false, it will continue filling subsequent orders even
- * when earlier ones fail.
- * When shouldThrowOnInsufficientBalanceOrAllowance is set to true, if any fill fails, the entire batch fails.
- * @param orderFillRequests An array of objects that conform to the
- * OrderFillRequest interface.
- * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw
- * if upon execution any of the tokens cannot be
- * transferred. If set to false, the call will continue to
- * fill subsequent signedOrders even when some
- * cannot be filled.
- * @param takerAddress The user Ethereum address who would like to fill
- * these orders. Must be available via the supplied
- * Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchFillOrdersAsync(
- orderFillRequests: OrderFillRequest[],
- shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
- const exchangeContractAddresses = _.map(
- orderFillRequests,
- orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- for (const orderFillRequest of orderFillRequests) {
- await orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- orderFillRequest.signedOrder,
- orderFillRequest.takerTokenFillAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
- }
- if (_.isEmpty(orderFillRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
-
- const orderAddressesValuesAmountsAndSignatureArray = _.map(orderFillRequests, orderFillRequest => {
- return [
- ...formatters.getOrderAddressesAndValues(orderFillRequest.signedOrder),
- orderFillRequest.takerTokenFillAmount,
- orderFillRequest.signedOrder.ecSignature.v,
- orderFillRequest.signedOrder.ecSignature.r,
- orderFillRequest.signedOrder.ecSignature.s,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddressesArray, orderValuesArray, fillTakerTokenAmounts, vArray, rArray, sArray] = _.unzip<any>(
- orderAddressesValuesAmountsAndSignatureArray,
- );
-
- const exchangeInstance = await this._getExchangeContractAsync();
- const txHash = await exchangeInstance.batchFillOrders.sendTransactionAsync(
- orderAddressesArray,
- orderValuesArray,
- fillTakerTokenAmounts,
- shouldThrowOnInsufficientBalanceOrAllowance,
- vArray,
- rArray,
- sArray,
- {
- from: normalizedTakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled,
- * the fill order is abandoned.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async fillOrKillOrderAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
-
- const exchangeInstance = await this._getExchangeContractAsync();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
-
- const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(signedOrder);
- const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmount,
- signedOrder.ecSignature.v,
- signedOrder.ecSignature.r,
- signedOrder.ecSignature.s,
- {
- from: normalizedTakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of fillOrKill. Allows a taker to specify a batch of orders that will either be atomically
- * filled (each to the specified fillAmount) or aborted.
- * @param orderFillRequests An array of objects that conform to the OrderFillRequest interface.
- * @param takerAddress The user Ethereum address who would like to fill there orders.
- * Must be available via the supplied Provider passed to 0x.js.
- * @param orderTransactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchFillOrKillAsync(
- orderFillRequests: OrderFillRequest[],
- takerAddress: string,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
- const exchangeContractAddresses = _.map(
- orderFillRequests,
- orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
- if (_.isEmpty(orderFillRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
- const exchangeInstance = await this._getExchangeContractAsync();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- for (const orderFillRequest of orderFillRequests) {
- await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- orderFillRequest.signedOrder,
- orderFillRequest.takerTokenFillAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
- }
-
- const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillRequests, request => {
- return [
- ...formatters.getOrderAddressesAndValues(request.signedOrder),
- request.takerTokenFillAmount,
- request.signedOrder.ecSignature.v,
- request.signedOrder.ecSignature.r,
- request.signedOrder.ecSignature.s,
- ];
- });
-
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] = _.unzip<any>(
- orderAddressesValuesAndTakerTokenFillAmounts,
- );
- const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync(
- orderAddresses,
- orderValues,
- fillTakerTokenAmounts,
- vParams,
- rParams,
- sParams,
- {
- from: normalizedTakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Cancel a given fill amount of an order. Cancellations are cumulative.
- * @param order An object that conforms to the Order or SignedOrder interface.
- * The order you would like to cancel.
- * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
- * @param transactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async cancelOrderAsync(
- order: Order | SignedOrder,
- cancelTakerTokenAmount: BigNumber,
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema('order', order, schemas.orderSchema);
- assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount);
- await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper);
- const normalizedMakerAddress = order.maker.toLowerCase();
-
- const exchangeInstance = await this._getExchangeContractAsync();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- const orderHash = getOrderHashHex(order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- order,
- cancelTakerTokenAmount,
- unavailableTakerTokenAmount,
- );
- }
-
- const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(order);
- const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmount,
- {
- from: normalizedMakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction.
- * All orders must be from the same maker.
- * @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest
- * interface.
- * @param transactionOpts Optional arguments this method accepts.
- * @return Transaction hash.
- */
- @decorators.asyncZeroExErrorHandler
- public async batchCancelOrdersAsync(
- orderCancellationRequests: OrderCancellationRequest[],
- orderTransactionOpts: OrderTransactionOpts = {},
- ): Promise<string> {
- assert.doesConformToSchema(
- 'orderCancellationRequests',
- orderCancellationRequests,
- schemas.orderCancellationRequestsSchema,
- );
- const exchangeContractAddresses = _.map(
- orderCancellationRequests,
- orderCancellationRequest => orderCancellationRequest.order.exchangeContractAddress,
- );
- assert.hasAtMostOneUniqueValue(
- exchangeContractAddresses,
- ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
- );
- const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker);
- assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
- const maker = makers[0];
- await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper);
- const normalizedMakerAddress = maker.toLowerCase();
-
- const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
- ? SHOULD_VALIDATE_BY_DEFAULT
- : orderTransactionOpts.shouldValidate;
- if (shouldValidate) {
- for (const orderCancellationRequest of orderCancellationRequests) {
- const orderHash = getOrderHashHex(orderCancellationRequest.order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- orderCancellationRequest.order,
- orderCancellationRequest.takerTokenCancelAmount,
- unavailableTakerTokenAmount,
- );
- }
- }
- if (_.isEmpty(orderCancellationRequests)) {
- throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
- }
- const exchangeInstance = await this._getExchangeContractAsync();
- const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(orderCancellationRequests, cancellationRequest => {
- return [
- ...formatters.getOrderAddressesAndValues(cancellationRequest.order),
- cancellationRequest.takerTokenCancelAmount,
- ];
- });
- // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'(
- const [orderAddresses, orderValues, cancelTakerTokenAmounts] = _.unzip<any>(
- orderAddressesValuesAndTakerTokenCancelAmounts,
- );
- const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync(
- orderAddresses,
- orderValues,
- cancelTakerTokenAmounts,
- {
- from: normalizedMakerAddress,
- gas: orderTransactionOpts.gasLimit,
- gasPrice: orderTransactionOpts.gasPrice,
- },
- );
- return txHash;
- }
- /**
- * Subscribe to an event type emitted by the Exchange contract.
- * @param eventName The exchange contract event you would like to subscribe to.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{maker: aUserAddressHex}`
- * @param callback Callback that gets called when a log is added/removed
- * @return Subscription token used later to unsubscribe
- */
- public subscribe<ArgsType extends ExchangeContractEventArgs>(
- eventName: ExchangeEvents,
- indexFilterValues: IndexedFilterValues,
- callback: EventCallback<ArgsType>,
- ): string {
- 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,
- eventName,
- indexFilterValues,
- artifacts.Exchange.abi,
- callback,
- );
- return subscriptionToken;
- }
- /**
- * Cancel a subscription
- * @param subscriptionToken Subscription token returned by `subscribe()`
- */
- public unsubscribe(subscriptionToken: string): void {
- this._unsubscribe(subscriptionToken);
- }
- /**
- * Cancels all existing subscriptions
- */
- public unsubscribeAll(): void {
- super._unsubscribeAll();
- }
- /**
- * Gets historical logs without creating a subscription
- * @param eventName The exchange contract event you would like to subscribe to.
- * @param blockRange Block range to get logs from.
- * @param indexFilterValues An object where the keys are indexed args returned by the event and
- * the value is the value you are interested in. E.g `{_from: aUserAddressHex}`
- * @return Array of logs that match the parameters
- */
- public async getLogsAsync<ArgsType extends ExchangeContractEventArgs>(
- eventName: ExchangeEvents,
- blockRange: BlockRange,
- indexFilterValues: IndexedFilterValues,
- ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
- 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,
- eventName,
- blockRange,
- indexFilterValues,
- artifacts.Exchange.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.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.Exchange, this._contractAddressIfExists);
- return contractAddress;
- }
- /**
- * Checks if order is still fillable and throws an error otherwise. Useful for orderbook
- * pruning where you want to remove stale orders without knowing who the taker will be.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to validate.
- * @param opts An object that conforms to the ValidateOrderFillableOpts
- * interface. Allows specifying a specific fillTakerTokenAmount
- * to validate for.
- */
- public async validateOrderFillableOrThrowAsync(
- signedOrder: SignedOrder,
- opts?: ValidateOrderFillableOpts,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- const zrxTokenAddress = this.getZRXTokenAddress();
- const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined;
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- await orderValidationUtils.validateOrderFillableOrThrowAsync(
- exchangeTradeEmulator,
- signedOrder,
- zrxTokenAddress,
- expectedFillTakerTokenAmount,
- );
- }
- /**
- * Checks if order fill will succeed and throws an error otherwise.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Provider passed to 0x.js.
- */
- public async validateFillOrderThrowIfInvalidAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- await orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
- /**
- * Checks if cancelling a given order will succeed and throws an informative error if it won't.
- * @param order An object that conforms to the Order or SignedOrder interface.
- * The order you would like to cancel.
- * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
- */
- public async validateCancelOrderThrowIfInvalidAsync(
- order: Order,
- cancelTakerTokenAmount: BigNumber,
- ): Promise<void> {
- assert.doesConformToSchema('order', order, schemas.orderSchema);
- assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount);
- const orderHash = getOrderHashHex(order);
- const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
- OrderValidationUtils.validateCancelOrderThrowIfInvalid(
- order,
- cancelTakerTokenAmount,
- unavailableTakerTokenAmount,
- );
- }
- /**
- * Checks if calling fillOrKill on a given order will succeed and throws an informative error if it won't.
- * @param signedOrder An object that conforms to the SignedOrder interface. The
- * signedOrder you wish to fill.
- * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
- * @param takerAddress The user Ethereum address who would like to fill this order.
- * Must be available via the supplied Provider passed to 0x.js.
- */
- public async validateFillOrKillOrderThrowIfInvalidAsync(
- signedOrder: SignedOrder,
- fillTakerTokenAmount: BigNumber,
- takerAddress: string,
- ): Promise<void> {
- assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
- const normalizedTakerAddress = takerAddress.toLowerCase();
- const zrxTokenAddress = this.getZRXTokenAddress();
- const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore(
- this._tokenWrapper,
- BlockParamLiteral.Latest,
- );
- const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore);
- const orderValidationUtils = await this._getOrderValidationUtilsAsync();
- await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
- exchangeTradeEmulator,
- signedOrder,
- fillTakerTokenAmount,
- normalizedTakerAddress,
- zrxTokenAddress,
- );
- }
- /**
- * Checks if rounding error will be > 0.1% when computing makerTokenAmount by doing:
- * `(fillTakerTokenAmount * makerTokenAmount) / takerTokenAmount`.
- * 0x Protocol does not accept any trades that result in large rounding errors. This means that tokens with few or
- * no decimals can only be filled in quantities and ratios that avoid large rounding errors.
- * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that you wish to fill.
- * @param takerTokenAmount The order size on the taker side
- * @param makerTokenAmount The order size on the maker side
- */
- public async isRoundingErrorAsync(
- fillTakerTokenAmount: BigNumber,
- takerTokenAmount: BigNumber,
- makerTokenAmount: BigNumber,
- ): Promise<boolean> {
- assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
- assert.isValidBaseUnitAmount('takerTokenAmount', takerTokenAmount);
- assert.isValidBaseUnitAmount('makerTokenAmount', makerTokenAmount);
- const exchangeInstance = await this._getExchangeContractAsync();
- const isRoundingError = await exchangeInstance.isRoundingError.callAsync(
- fillTakerTokenAmount,
- takerTokenAmount,
- makerTokenAmount,
- );
- return isRoundingError;
- }
- /**
- * Checks if logs contain LogError, which is emitted by Exchange contract on transaction failure.
- * @param logs Transaction logs as returned by `zeroEx.awaitTransactionMinedAsync`
- */
- public throwLogErrorsAsErrors(logs: Array<LogWithDecodedArgs<DecodedLogArgs> | LogEntry>): void {
- const errLog = _.find(logs, {
- event: ExchangeEvents.LogError,
- });
- if (!_.isUndefined(errLog)) {
- const logArgs = (errLog as LogWithDecodedArgs<LogErrorContractEventArgs>).args;
- const errCode = logArgs.errorId;
- const errMessage = this._exchangeContractErrCodesToMsg[errCode];
- throw new Error(errMessage);
- }
- }
- /**
- * Gets the latest OrderState of a signedOrder
- * @param signedOrder The signedOrder
- * @param stateLayer Optional, desired blockchain state layer (defaults to latest).
- * @return OrderState of the signedOrder
- */
- public async getOrderStateAsync(
- signedOrder: SignedOrder,
- stateLayer: BlockParamLiteral = BlockParamLiteral.Latest,
- ): Promise<OrderState> {
- const simpleBalanceAndProxyAllowanceFetcher = new SimpleBalanceAndProxyAllowanceFetcher(
- this._tokenWrapper,
- stateLayer,
- );
- const simpleOrderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher(this, stateLayer);
- const orderStateUtils = new OrderStateUtils(
- simpleBalanceAndProxyAllowanceFetcher,
- simpleOrderFilledCancelledFetcher,
- );
- const orderState = orderStateUtils.getOrderStateAsync(signedOrder);
- return orderState;
- }
- /**
- * Returns the ZRX token address used by the exchange contract.
- * @return Address of ZRX token
- */
- public getZRXTokenAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.ZRX, this._zrxContractAddressIfExists);
- return contractAddress;
- }
- // tslint:disable:no-unused-variable
- private _invalidateContractInstances(): void {
- this.unsubscribeAll();
- delete this._exchangeContractIfExists;
- }
- private async _isValidSignatureUsingContractCallAsync(
- dataHex: string,
- ecSignature: ECSignature,
- signerAddressHex: string,
- ): Promise<boolean> {
- assert.isHexString('dataHex', dataHex);
- assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema);
- assert.isETHAddressHex('signerAddressHex', signerAddressHex);
- const normalizedSignerAddress = signerAddressHex.toLowerCase();
-
- const exchangeInstance = await this._getExchangeContractAsync();
-
- const isValidSignature = await exchangeInstance.isValidSignature.callAsync(
- normalizedSignerAddress,
- dataHex,
- ecSignature.v,
- ecSignature.r,
- ecSignature.s,
- );
- return isValidSignature;
- }
- private async _getOrderHashHexUsingContractCallAsync(order: Order | SignedOrder): Promise<string> {
- const exchangeInstance = await this._getExchangeContractAsync();
- const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(order);
- const orderHashHex = await exchangeInstance.getOrderHash.callAsync(orderAddresses, orderValues);
- return orderHashHex;
- }
- private async _getOrderValidationUtilsAsync(): Promise<OrderValidationUtils> {
- if (!_.isUndefined(this._orderValidationUtilsIfExists)) {
- return this._orderValidationUtilsIfExists;
- }
- const exchangeContract = await this._getExchangeContractAsync();
- this._orderValidationUtilsIfExists = new OrderValidationUtils(exchangeContract);
- return this._orderValidationUtilsIfExists;
- }
- // 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._web3Wrapper.getProvider(),
- this._web3Wrapper.getContractDefaults(),
- );
- this._exchangeContractIfExists = contractInstance;
- return this._exchangeContractIfExists;
- }
-} // tslint:disable:max-file-line-count
diff --git a/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts
deleted file mode 100644
index 7b558ed69..000000000
--- a/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-import { ContractAbi, Token } from '@0xproject/types';
-import { Web3Wrapper } from '@0xproject/web3-wrapper';
-import * as _ from 'lodash';
-
-import { artifacts } from '../artifacts';
-import { TokenMetadata } from '../types';
-import { assert } from '../utils/assert';
-import { constants } from '../utils/constants';
-
-import { ContractWrapper } from './contract_wrapper';
-import { TokenRegistryContract } from './generated/token_registry';
-
-/**
- * This class includes all the functionality related to interacting with the 0x Token Registry smart contract.
- */
-export class TokenRegistryWrapper extends ContractWrapper {
- public abi: ContractAbi = artifacts.TokenRegistry.abi;
- private _tokenRegistryContractIfExists?: TokenRegistryContract;
- private _contractAddressIfExists?: string;
- private static _createTokenFromMetadata(metadata: TokenMetadata): Token | undefined {
- if (metadata[0] === constants.NULL_ADDRESS) {
- return undefined;
- }
- const token = {
- address: metadata[0],
- name: metadata[1],
- symbol: metadata[2],
- decimals: metadata[3],
- };
- return token;
- }
- constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) {
- super(web3Wrapper, networkId);
- this._contractAddressIfExists = contractAddressIfExists;
- }
- /**
- * Retrieves all the tokens currently listed in the Token Registry smart contract
- * @return An array of objects that conform to the Token interface.
- */
- public async getTokensAsync(): Promise<Token[]> {
- const addresses = await this.getTokenAddressesAsync();
- const tokenPromises: Array<Promise<Token | undefined>> = _.map(addresses, async (address: string) =>
- this.getTokenIfExistsAsync(address),
- );
- const tokens = await Promise.all(tokenPromises);
- return tokens as Token[];
- }
- /**
- * Retrieves all the addresses of the tokens currently listed in the Token Registry smart contract
- * @return An array of token addresses.
- */
- public async getTokenAddressesAsync(): Promise<string[]> {
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addresses = await tokenRegistryContract.getTokenAddresses.callAsync();
- const lowerCaseAddresses = _.map(addresses, address => address.toLowerCase());
- return lowerCaseAddresses;
- }
- /**
- * Retrieves a token by address currently listed in the Token Registry smart contract
- * @return An object that conforms to the Token interface or undefined if token not found.
- */
- public async getTokenIfExistsAsync(address: string): Promise<Token | undefined> {
- assert.isETHAddressHex('address', address);
- const normalizedAddress = address.toLowerCase();
-
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(normalizedAddress);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- public async getTokenAddressBySymbolIfExistsAsync(symbol: string): Promise<string | undefined> {
- assert.isString('symbol', symbol);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addressIfExists = await tokenRegistryContract.getTokenAddressBySymbol.callAsync(symbol);
- if (addressIfExists === constants.NULL_ADDRESS) {
- return undefined;
- }
- return addressIfExists;
- }
- public async getTokenAddressByNameIfExistsAsync(name: string): Promise<string | undefined> {
- assert.isString('name', name);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const addressIfExists = await tokenRegistryContract.getTokenAddressByName.callAsync(name);
- if (addressIfExists === constants.NULL_ADDRESS) {
- return undefined;
- }
- return addressIfExists;
- }
- public async getTokenBySymbolIfExistsAsync(symbol: string): Promise<Token | undefined> {
- assert.isString('symbol', symbol);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenBySymbol.callAsync(symbol);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- public async getTokenByNameIfExistsAsync(name: string): Promise<Token | undefined> {
- assert.isString('name', name);
- const tokenRegistryContract = await this._getTokenRegistryContractAsync();
- const metadata = await tokenRegistryContract.getTokenByName.callAsync(name);
- const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
- return token;
- }
- /**
- * Retrieves the Ethereum address of the TokenRegistry contract deployed on the network
- * that the user-passed web3 provider is connected to.
- * @returns The Ethereum address of the TokenRegistry contract being used.
- */
- public getContractAddress(): string {
- const contractAddress = this._getContractAddress(artifacts.TokenRegistry, this._contractAddressIfExists);
- return contractAddress;
- }
- // tslint:disable-next-line:no-unused-variable
- private _invalidateContractInstance(): void {
- delete this._tokenRegistryContractIfExists;
- }
- private async _getTokenRegistryContractAsync(): Promise<TokenRegistryContract> {
- if (!_.isUndefined(this._tokenRegistryContractIfExists)) {
- return this._tokenRegistryContractIfExists;
- }
- const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(
- artifacts.TokenRegistry,
- this._contractAddressIfExists,
- );
- const contractInstance = new TokenRegistryContract(
- abi,
- address,
- this._web3Wrapper.getProvider(),
- this._web3Wrapper.getContractDefaults(),
- );
- this._tokenRegistryContractIfExists = contractInstance;
- return this._tokenRegistryContractIfExists;
- }
-}