diff options
author | Fabio Berger <me@fabioberger.com> | 2018-11-19 20:49:16 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-11-19 20:49:16 +0800 |
commit | 8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c (patch) | |
tree | 587ce5e026674e5665ad164995aac69462290aed /packages/contract-wrappers/src | |
parent | 7d2c975d7335155b85a7549c25b953d0afacf5cf (diff) | |
parent | 94de441de744ed53470335122a38e265c3a71aff (diff) | |
download | dexon-0x-contracts-8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c.tar.gz dexon-0x-contracts-8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c.tar.zst dexon-0x-contracts-8175192f60e2c2827f6e7d664fbe8bf2d9ddee9c.zip |
Merge branch 'development'
* development: (957 commits)
fix(order_utils.py): publish docs to S3, not RTD (#1264)
fix: make instant package private
feat: refer to map file in postpublish configs
feat: add new bundle name to bundle watch
fix: tslint ignore rule in wrong place
Update blog post feature
Fix disclaimer on mobile
Add smart contract docs to Developer Home
Add Apache license link
Fix capitalization in title
Remove excess semi-colon
Point directly to README for docs link
Update icons
Update LICENSE
Fix disclaimer
Add blogpost URL
Add disclaimer
Add launch kit to Developer home list of tools
feat: Deploy contracts to Rinkeby
fix: fix exceeds block gas limit error
...
Diffstat (limited to 'packages/contract-wrappers/src')
29 files changed, 480 insertions, 655 deletions
diff --git a/packages/contract-wrappers/src/abstract/abstract_balance_and_proxy_allowance_lazy_store.ts b/packages/contract-wrappers/src/abstract/abstract_balance_and_proxy_allowance_lazy_store.ts index 1f139f1ef..b095c8e79 100644 --- a/packages/contract-wrappers/src/abstract/abstract_balance_and_proxy_allowance_lazy_store.ts +++ b/packages/contract-wrappers/src/abstract/abstract_balance_and_proxy_allowance_lazy_store.ts @@ -1,4 +1,4 @@ -import { BigNumber } from '@0xproject/utils'; +import { BigNumber } from '@0x/utils'; export abstract class AbstractBalanceAndProxyAllowanceLazyStore { public abstract async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber>; diff --git a/packages/contract-wrappers/src/artifacts.ts b/packages/contract-wrappers/src/artifacts.ts deleted file mode 100644 index 925f34162..000000000 --- a/packages/contract-wrappers/src/artifacts.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ContractArtifact } from 'ethereum-types'; - -import * as DummyERC20Token from './artifacts/DummyERC20Token.json'; -import * as DummyERC721Token from './artifacts/DummyERC721Token.json'; -import * as ERC20Proxy from './artifacts/ERC20Proxy.json'; -import * as ERC20Token from './artifacts/ERC20Token.json'; -import * as ERC721Proxy from './artifacts/ERC721Proxy.json'; -import * as ERC721Token from './artifacts/ERC721Token.json'; -import * as Exchange from './artifacts/Exchange.json'; -import * as Forwarder from './artifacts/Forwarder.json'; -import * as OrderValidator from './artifacts/OrderValidator.json'; -import * as EtherToken from './artifacts/WETH9.json'; -import * as ZRXToken from './artifacts/ZRXToken.json'; - -export const artifacts = { - ZRXToken: (ZRXToken as any) as ContractArtifact, - DummyERC20Token: (DummyERC20Token as any) as ContractArtifact, - DummyERC721Token: (DummyERC721Token as any) as ContractArtifact, - ERC20Token: (ERC20Token as any) as ContractArtifact, - ERC721Token: (ERC721Token as any) as ContractArtifact, - Exchange: (Exchange as any) as ContractArtifact, - EtherToken: (EtherToken as any) as ContractArtifact, - ERC20Proxy: (ERC20Proxy as any) as ContractArtifact, - ERC721Proxy: (ERC721Proxy as any) as ContractArtifact, - Forwarder: (Forwarder as any) as ContractArtifact, - OrderValidator: (OrderValidator as any) as ContractArtifact, -}; diff --git a/packages/contract-wrappers/src/contract_wrappers.ts b/packages/contract-wrappers/src/contract_wrappers.ts index 89402029b..0c535bd5c 100644 --- a/packages/contract-wrappers/src/contract_wrappers.ts +++ b/packages/contract-wrappers/src/contract_wrappers.ts @@ -1,10 +1,17 @@ -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { + ERC20Proxy, + ERC20Token, + ERC721Proxy, + ERC721Token, + Exchange, + Forwarder, + OrderValidator, + WETH9, +} from '@0x/contract-artifacts'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; -import { constants } from './utils/constants'; - -import { artifacts } from './artifacts'; import { ERC20ProxyWrapper } from './contract_wrappers/erc20_proxy_wrapper'; import { ERC20TokenWrapper } from './contract_wrappers/erc20_token_wrapper'; import { ERC721ProxyWrapper } from './contract_wrappers/erc721_proxy_wrapper'; @@ -14,10 +21,11 @@ import { ExchangeWrapper } from './contract_wrappers/exchange_wrapper'; import { ForwarderWrapper } from './contract_wrappers/forwarder_wrapper'; import { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapper'; import { ContractWrappersConfigSchema } from './schemas/contract_wrappers_config_schema'; -import { contractWrappersPrivateNetworkConfigSchema } from './schemas/contract_wrappers_private_network_config_schema'; -import { contractWrappersPublicNetworkConfigSchema } from './schemas/contract_wrappers_public_network_config_schema'; import { ContractWrappersConfig } from './types'; import { assert } from './utils/assert'; +import { constants } from './utils/constants'; +import { _getDefaultContractAddresses } from './utils/contract_addresses'; + /** * The ContractWrappers class contains smart contract wrappers helpful when building on 0x protocol. */ @@ -61,35 +69,39 @@ export class ContractWrappers { private readonly _web3Wrapper: Web3Wrapper; /** * Instantiates a new ContractWrappers instance. - * @param provider The Provider instance you would like the 0x.js library to use for interacting with + * @param provider The Provider instance you would like the contract-wrappers library to use for interacting with * the Ethereum network. * @param config The configuration object. Look up the type for the description. * @return An instance of the ContractWrappers class. */ constructor(provider: Provider, config: ContractWrappersConfig) { assert.isWeb3Provider('provider', provider); - assert.doesConformToSchema('config', config, ContractWrappersConfigSchema, [ - contractWrappersPrivateNetworkConfigSchema, - contractWrappersPublicNetworkConfigSchema, - ]); - const artifactJSONs = _.values(artifacts); - const abiArrays = _.map(artifactJSONs, artifact => artifact.compilerOutput.abi); + assert.doesConformToSchema('config', config, ContractWrappersConfigSchema); const txDefaults = { gasPrice: config.gasPrice, }; this._web3Wrapper = new Web3Wrapper(provider, txDefaults); - _.forEach(abiArrays, abi => { - this._web3Wrapper.abiDecoder.addABI(abi); + const artifactsArray = [ + ERC20Proxy, + ERC20Token, + ERC721Proxy, + ERC721Token, + Exchange, + Forwarder, + OrderValidator, + WETH9, + ]; + _.forEach(artifactsArray, artifact => { + this._web3Wrapper.abiDecoder.addABI(artifact.compilerOutput.abi); }); const blockPollingIntervalMs = _.isUndefined(config.blockPollingIntervalMs) ? constants.DEFAULT_BLOCK_POLLING_INTERVAL : config.blockPollingIntervalMs; - this.erc20Proxy = new ERC20ProxyWrapper(this._web3Wrapper, config.networkId, config.erc20ProxyContractAddress); - this.erc721Proxy = new ERC721ProxyWrapper( - this._web3Wrapper, - config.networkId, - config.erc721ProxyContractAddress, - ); + const contractAddresses = _.isUndefined(config.contractAddresses) + ? _getDefaultContractAddresses(config.networkId) + : config.contractAddresses; + this.erc20Proxy = new ERC20ProxyWrapper(this._web3Wrapper, config.networkId, contractAddresses.erc20Proxy); + this.erc721Proxy = new ERC721ProxyWrapper(this._web3Wrapper, config.networkId, contractAddresses.erc721Proxy); this.erc20Token = new ERC20TokenWrapper( this._web3Wrapper, config.networkId, @@ -113,37 +125,34 @@ export class ContractWrappers { config.networkId, this.erc20Token, this.erc721Token, - config.exchangeContractAddress, - config.zrxContractAddress, + contractAddresses.exchange, + contractAddresses.zrxToken, blockPollingIntervalMs, ); this.forwarder = new ForwarderWrapper( this._web3Wrapper, config.networkId, - config.forwarderContractAddress, - config.zrxContractAddress, + contractAddresses.forwarder, + contractAddresses.zrxToken, + contractAddresses.etherToken, + ); + this.orderValidator = new OrderValidatorWrapper( + this._web3Wrapper, + config.networkId, + contractAddresses.orderValidator, ); - this.orderValidator = new OrderValidatorWrapper(this._web3Wrapper, config.networkId); } /** - * Sets a new web3 provider for 0x.js. Updating the provider will stop all - * subscriptions so you will need to re-subscribe to all events relevant to your app after this call. - * @param provider The Web3Provider you would like the 0x.js library to use from now on. - * @param networkId The id of the network your provider is connected to + * Unsubscribes from all subscriptions for all contracts. */ - public setProvider(provider: Provider, networkId: number): void { - this._web3Wrapper.setProvider(provider); - (this.exchange as any)._invalidateContractInstances(); - (this.exchange as any)._setNetworkId(networkId); - (this.erc20Token as any)._invalidateContractInstances(); - (this.erc20Token as any)._setNetworkId(networkId); - (this.erc20Proxy as any)._invalidateContractInstance(); - (this.erc20Proxy as any)._setNetworkId(networkId); - (this.etherToken as any)._invalidateContractInstance(); - (this.etherToken as any)._setNetworkId(networkId); + public unsubscribeAll(): void { + this.exchange.unsubscribeAll(); + this.erc20Token.unsubscribeAll(); + this.erc721Token.unsubscribeAll(); + this.etherToken.unsubscribeAll(); } /** - * Get the provider instance currently used by 0x.js + * Get the provider instance currently used by contract-wrappers * @return Web3 provider instance */ public getProvider(): Provider { diff --git a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts index f7a89e3be..749aaae10 100644 --- a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts @@ -1,9 +1,8 @@ -import { AbiDecoder, intervalUtils, logUtils } from '@0xproject/utils'; -import { marshaller, Web3Wrapper } from '@0xproject/web3-wrapper'; +import { AbiDecoder, intervalUtils, logUtils } from '@0x/utils'; +import { marshaller, Web3Wrapper } from '@0x/web3-wrapper'; import { BlockParamLiteral, ContractAbi, - ContractArtifact, FilterObject, LogEntry, LogWithDecodedArgs, @@ -24,27 +23,15 @@ import { import { constants } from '../utils/constants'; import { filterUtils } from '../utils/filter_utils'; -const CONTRACT_NAME_TO_NOT_FOUND_ERROR: { - [contractName: string]: ContractWrappersError; -} = { - ZRX: ContractWrappersError.ZRXContractDoesNotExist, - EtherToken: ContractWrappersError.EtherTokenContractDoesNotExist, - ERC20Token: ContractWrappersError.ERC20TokenContractDoesNotExist, - ERC20Proxy: ContractWrappersError.ERC20ProxyContractDoesNotExist, - ERC721Token: ContractWrappersError.ERC721TokenContractDoesNotExist, - ERC721Proxy: ContractWrappersError.ERC721ProxyContractDoesNotExist, - Exchange: ContractWrappersError.ExchangeContractDoesNotExist, -}; - export abstract class ContractWrapper { public abstract abi: ContractAbi; - protected _web3Wrapper: Web3Wrapper; protected _networkId: number; + protected _web3Wrapper: Web3Wrapper; private _blockAndLogStreamerIfExists: BlockAndLogStreamer<Block, Log> | undefined; - private _blockPollingIntervalMs: number; + private readonly _blockPollingIntervalMs: number; private _blockAndLogStreamIntervalIfExists?: NodeJS.Timer; - private _filters: { [filterToken: string]: FilterObject }; - private _filterCallbacks: { + private readonly _filters: { [filterToken: string]: FilterObject }; + private readonly _filterCallbacks: { [filterToken: string]: EventCallback<ContractEventArgs>; }; private _onLogAddedSubscriptionToken: string | undefined; @@ -124,40 +111,6 @@ export abstract class ContractWrapper { const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); return logWithDecodedArgs; } - protected async _getContractAbiAndAddressFromArtifactsAsync( - artifact: ContractArtifact, - addressIfExists?: string, - ): Promise<[ContractAbi, string]> { - let contractAddress: string; - if (_.isUndefined(addressIfExists)) { - if (_.isUndefined(artifact.networks[this._networkId])) { - throw new Error(ContractWrappersError.ContractNotDeployedOnNetwork); - } - contractAddress = artifact.networks[this._networkId].address.toLowerCase(); - } else { - contractAddress = addressIfExists; - } - const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(contractAddress); - if (!doesContractExist) { - throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contractName]); - } - const abiAndAddress: [ContractAbi, string] = [artifact.compilerOutput.abi, contractAddress]; - return abiAndAddress; - } - protected _getContractAddress(artifact: ContractArtifact, addressIfExists?: string): string { - if (_.isUndefined(addressIfExists)) { - if (_.isUndefined(artifact.networks[this._networkId])) { - throw new Error(ContractWrappersError.ContractNotDeployedOnNetwork); - } - const contractAddress = artifact.networks[this._networkId].address; - if (_.isUndefined(contractAddress)) { - throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contractName]); - } - return contractAddress; - } else { - return addressIfExists; - } - } private _onLogStateChanged<ArgsType extends ContractEventArgs>(isRemoved: boolean, rawLog: RawLogEntry): void { const log: LogEntry = marshaller.unmarshalLog(rawLog); _.forEach(this._filters, (filter: FilterObject, filterToken: string) => { @@ -222,14 +175,6 @@ export abstract class ContractWrapper { }); return logs as RawLogEntry[]; } - // HACK: This should be a package-scoped method (which doesn't exist in TS) - // We don't want this method available in the public interface for all classes - // who inherit from ContractWrapper, and it is only used by the internal implementation - // of those higher classes. - // tslint:disable-next-line:no-unused-variable - private _setNetworkId(networkId: number): void { - this._networkId = networkId; - } private _stopBlockAndLogStream(): void { if (_.isUndefined(this._blockAndLogStreamerIfExists)) { throw new Error(ContractWrappersError.SubscriptionNotFound); diff --git a/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts index ff027d78a..45460bd6d 100644 --- a/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts @@ -1,38 +1,42 @@ -import { AssetProxyId } from '@0xproject/types'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ERC20ProxyContract } from '@0x/abi-gen-wrappers'; +import { ERC20Proxy } from '@0x/contract-artifacts'; +import { AssetProxyId } from '@0x/types'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { ContractAbi } from 'ethereum-types'; import * as _ from 'lodash'; -import { artifacts } from '../artifacts'; import { assert } from '../utils/assert'; +import { _getDefaultContractAddresses } from '../utils/contract_addresses'; import { ContractWrapper } from './contract_wrapper'; -import { ERC20ProxyContract } from './generated/erc20_proxy'; /** * This class includes the functionality related to interacting with the ERC20Proxy contract. */ export class ERC20ProxyWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.ERC20Proxy.compilerOutput.abi; + public abi: ContractAbi = ERC20Proxy.compilerOutput.abi; + public address: string; private _erc20ProxyContractIfExists?: ERC20ProxyContract; - private _contractAddressIfExists?: string; /** * Instantiate ERC20ProxyWrapper * @param web3Wrapper Web3Wrapper instance to use * @param networkId Desired networkId - * @param contractAddressIfExists The contract address to use. This is usually pulled from - * the artifacts but needs to be specified when using with your own custom testnet. + * @param address The address of the ERC20Proxy contract. If undefined, will + * default to the known address corresponding to the networkId. */ - constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) { + constructor(web3Wrapper: Web3Wrapper, networkId: number, address?: string) { super(web3Wrapper, networkId); - this._contractAddressIfExists = contractAddressIfExists; + this.address = _.isUndefined(address) ? _getDefaultContractAddresses(networkId).erc20Proxy : address; } /** * Get the 4 bytes ID of this asset proxy * @return Proxy id */ public async getProxyIdAsync(): Promise<AssetProxyId> { - const ERC20ProxyContractInstance = await this._getERC20ProxyContractAsync(); + const ERC20ProxyContractInstance = this._getERC20ProxyContract(); + // Note(albrow): Below is a TSLint false positive. Code won't compile if + // you remove the type assertion. + /* tslint:disable-next-line:no-unnecessary-type-assertion */ const proxyId = (await ERC20ProxyContractInstance.getProxyId.callAsync()) as AssetProxyId; return proxyId; } @@ -44,7 +48,7 @@ export class ERC20ProxyWrapper extends ContractWrapper { public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> { assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress); const normalizedExchangeContractAddress = exchangeContractAddress.toLowerCase(); - const ERC20ProxyContractInstance = await this._getERC20ProxyContractAsync(); + const ERC20ProxyContractInstance = this._getERC20ProxyContract(); const isAuthorized = await ERC20ProxyContractInstance.authorized.callAsync(normalizedExchangeContractAddress); return isAuthorized; } @@ -53,36 +57,17 @@ export class ERC20ProxyWrapper extends ContractWrapper { * @return The list of authorized addresses. */ public async getAuthorizedAddressesAsync(): Promise<string[]> { - const ERC20ProxyContractInstance = await this._getERC20ProxyContractAsync(); + const ERC20ProxyContractInstance = this._getERC20ProxyContract(); const authorizedAddresses = await ERC20ProxyContractInstance.getAuthorizedAddresses.callAsync(); return authorizedAddresses; } - /** - * Retrieves the Ethereum address of the ERC20Proxy contract deployed on the network - * that the user-passed web3 provider is connected to. - * @returns The Ethereum address of the ERC20Proxy contract being used. - */ - public getContractAddress(): string { - const contractAddress = this._getContractAddress(artifacts.ERC20Proxy, this._contractAddressIfExists); - return contractAddress; - } - // HACK: We don't want this method to be visible to the other units within that package but not to the end user. - // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. - // tslint:disable-next-line:no-unused-variable - private _invalidateContractInstance(): void { - delete this._erc20ProxyContractIfExists; - } - private async _getERC20ProxyContractAsync(): Promise<ERC20ProxyContract> { + private _getERC20ProxyContract(): ERC20ProxyContract { if (!_.isUndefined(this._erc20ProxyContractIfExists)) { return this._erc20ProxyContractIfExists; } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.ERC20Proxy, - this._contractAddressIfExists, - ); const contractInstance = new ERC20ProxyContract( - abi, - address, + this.abi, + this.address, this._web3Wrapper.getProvider(), this._web3Wrapper.getContractDefaults(), ); diff --git a/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts index 4625cef6a..5e0ec1951 100644 --- a/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts @@ -1,10 +1,11 @@ -import { schemas } from '@0xproject/json-schemas'; -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ERC20TokenContract, ERC20TokenEventArgs, ERC20TokenEvents } from '@0x/abi-gen-wrappers'; +import { ERC20Token } from '@0x/contract-artifacts'; +import { schemas } from '@0x/json-schemas'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types'; import * as _ from 'lodash'; -import { artifacts } from '../artifacts'; import { methodOptsSchema } from '../schemas/method_opts_schema'; import { txOptsSchema } from '../schemas/tx_opts_schema'; import { @@ -17,12 +18,10 @@ import { } from '../types'; import { assert } from '../utils/assert'; import { constants } from '../utils/constants'; +import { utils } from '../utils/utils'; import { ContractWrapper } from './contract_wrapper'; import { ERC20ProxyWrapper } from './erc20_proxy_wrapper'; -import { ERC20TokenContract, ERC20TokenEventArgs, ERC20TokenEvents } from './generated/erc20_token'; - -const removeUndefinedProperties = _.pickBy; /** * This class includes all the functionality related to interacting with ERC20 token contracts. @@ -30,10 +29,10 @@ const removeUndefinedProperties = _.pickBy; * to the 0x ERC20 Proxy smart contract. */ export class ERC20TokenWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.ERC20Token.compilerOutput.abi; + public abi: ContractAbi = ERC20Token.compilerOutput.abi; public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - private _tokenContractsByAddress: { [address: string]: ERC20TokenContract }; - private _erc20ProxyWrapper: ERC20ProxyWrapper; + private readonly _tokenContractsByAddress: { [address: string]: ERC20TokenContract }; + private readonly _erc20ProxyWrapper: ERC20ProxyWrapper; /** * Instantiate ERC20TokenWrapper * @param web3Wrapper Web3Wrapper instance to use @@ -108,7 +107,7 @@ export class ERC20TokenWrapper extends ContractWrapper { const txHash = await tokenContract.approve.sendTransactionAsync( normalizedSpenderAddress, amountInBaseUnits, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ from: normalizedOwnerAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, @@ -189,7 +188,7 @@ export class ERC20TokenWrapper extends ContractWrapper { ownerAddress: string, methodOpts: MethodOpts = {}, ): Promise<BigNumber> { - const proxyAddress = this._erc20ProxyWrapper.getContractAddress(); + const proxyAddress = this._erc20ProxyWrapper.address; const allowanceInBaseUnits = await this.getAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, methodOpts); return allowanceInBaseUnits; } @@ -209,7 +208,7 @@ export class ERC20TokenWrapper extends ContractWrapper { amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}, ): Promise<string> { - const proxyAddress = this._erc20ProxyWrapper.getContractAddress(); + const proxyAddress = this._erc20ProxyWrapper.address; const txHash = await this.setAllowanceAsync( tokenAddress, ownerAddress, @@ -278,7 +277,7 @@ export class ERC20TokenWrapper extends ContractWrapper { const txHash = await tokenContract.transfer.sendTransactionAsync( normalizedToAddress, amountInBaseUnits, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ from: normalizedFromAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, @@ -339,7 +338,7 @@ export class ERC20TokenWrapper extends ContractWrapper { normalizedFromAddress, normalizedToAddress, amountInBaseUnits, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ from: normalizedSenderAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, @@ -373,7 +372,7 @@ export class ERC20TokenWrapper extends ContractWrapper { normalizedTokenAddress, eventName, indexFilterValues, - artifacts.ERC20Token.compilerOutput.abi, + ERC20Token.compilerOutput.abi, callback, isVerbose, ); @@ -418,30 +417,19 @@ export class ERC20TokenWrapper extends ContractWrapper { eventName, blockRange, indexFilterValues, - artifacts.ERC20Token.compilerOutput.abi, + ERC20Token.compilerOutput.abi, ); return logs; } - // HACK: We don't want this method to be visible to the other units within that package but not to the end user. - // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. - // tslint:disable-next-line:no-unused-variable - private _invalidateContractInstances(): void { - this.unsubscribeAll(); - this._tokenContractsByAddress = {}; - } private async _getTokenContractAsync(tokenAddress: string): Promise<ERC20TokenContract> { const normalizedTokenAddress = tokenAddress.toLowerCase(); let tokenContract = this._tokenContractsByAddress[normalizedTokenAddress]; if (!_.isUndefined(tokenContract)) { return tokenContract; } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.ERC20Token, - normalizedTokenAddress, - ); const contractInstance = new ERC20TokenContract( - abi, - address, + this.abi, + normalizedTokenAddress, this._web3Wrapper.getProvider(), this._web3Wrapper.getContractDefaults(), ); diff --git a/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts index 933c1dc27..12758e191 100644 --- a/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts @@ -1,38 +1,42 @@ -import { AssetProxyId } from '@0xproject/types'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ERC721ProxyContract } from '@0x/abi-gen-wrappers'; +import { ERC721Proxy } from '@0x/contract-artifacts'; +import { AssetProxyId } from '@0x/types'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { ContractAbi } from 'ethereum-types'; import * as _ from 'lodash'; -import { artifacts } from '../artifacts'; import { assert } from '../utils/assert'; +import { _getDefaultContractAddresses } from '../utils/contract_addresses'; import { ContractWrapper } from './contract_wrapper'; -import { ERC721ProxyContract } from './generated/erc721_proxy'; /** * This class includes the functionality related to interacting with the ERC721Proxy contract. */ export class ERC721ProxyWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.ERC20Proxy.compilerOutput.abi; + public abi: ContractAbi = ERC721Proxy.compilerOutput.abi; + public address: string; private _erc721ProxyContractIfExists?: ERC721ProxyContract; - private _contractAddressIfExists?: string; /** * Instantiate ERC721ProxyWrapper * @param web3Wrapper Web3Wrapper instance to use * @param networkId Desired networkId - * @param contractAddressIfExists The contract address to use. This is usually pulled from - * the artifacts but needs to be specified when using with your own custom testnet. + * @param address The address of the ERC721Proxy contract. If undefined, + * will default to the known address corresponding to the networkId. */ - constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) { + constructor(web3Wrapper: Web3Wrapper, networkId: number, address?: string) { super(web3Wrapper, networkId); - this._contractAddressIfExists = contractAddressIfExists; + this.address = _.isUndefined(address) ? _getDefaultContractAddresses(networkId).erc721Proxy : address; } /** * Get the 4 bytes ID of this asset proxy * @return Proxy id */ public async getProxyIdAsync(): Promise<AssetProxyId> { - const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync(); + const ERC721ProxyContractInstance = await this._getERC721ProxyContract(); + // Note(albrow): Below is a TSLint false positive. Code won't compile if + // you remove the type assertion. + /* tslint:disable-next-line:no-unnecessary-type-assertion */ const proxyId = (await ERC721ProxyContractInstance.getProxyId.callAsync()) as AssetProxyId; return proxyId; } @@ -44,7 +48,7 @@ export class ERC721ProxyWrapper extends ContractWrapper { public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> { assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress); const normalizedExchangeContractAddress = exchangeContractAddress.toLowerCase(); - const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync(); + const ERC721ProxyContractInstance = await this._getERC721ProxyContract(); const isAuthorized = await ERC721ProxyContractInstance.authorized.callAsync(normalizedExchangeContractAddress); return isAuthorized; } @@ -53,36 +57,17 @@ export class ERC721ProxyWrapper extends ContractWrapper { * @return The list of authorized addresses. */ public async getAuthorizedAddressesAsync(): Promise<string[]> { - const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync(); + const ERC721ProxyContractInstance = await this._getERC721ProxyContract(); const authorizedAddresses = await ERC721ProxyContractInstance.getAuthorizedAddresses.callAsync(); return authorizedAddresses; } - /** - * Retrieves the Ethereum address of the ERC721Proxy contract deployed on the network - * that the user-passed web3 provider is connected to. - * @returns The Ethereum address of the ERC721Proxy contract being used. - */ - public getContractAddress(): string { - const contractAddress = this._getContractAddress(artifacts.ERC721Proxy, this._contractAddressIfExists); - return contractAddress; - } - // HACK: We don't want this method to be visible to the other units within that package but not to the end user. - // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. - // tslint:disable-next-line:no-unused-variable - private _invalidateContractInstance(): void { - delete this._erc721ProxyContractIfExists; - } - private async _getERC721ProxyContractAsync(): Promise<ERC721ProxyContract> { + private _getERC721ProxyContract(): ERC721ProxyContract { if (!_.isUndefined(this._erc721ProxyContractIfExists)) { return this._erc721ProxyContractIfExists; } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.ERC721Proxy, - this._contractAddressIfExists, - ); const contractInstance = new ERC721ProxyContract( - abi, - address, + this.abi, + this.address, this._web3Wrapper.getProvider(), this._web3Wrapper.getContractDefaults(), ); diff --git a/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts index 590dbbf74..1610af47b 100644 --- a/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts @@ -1,10 +1,11 @@ -import { schemas } from '@0xproject/json-schemas'; -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ERC721TokenContract, ERC721TokenEventArgs, ERC721TokenEvents } from '@0x/abi-gen-wrappers'; +import { ERC721Token } from '@0x/contract-artifacts'; +import { schemas } from '@0x/json-schemas'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types'; import * as _ from 'lodash'; -import { artifacts } from '../artifacts'; import { methodOptsSchema } from '../schemas/method_opts_schema'; import { txOptsSchema } from '../schemas/tx_opts_schema'; import { @@ -17,12 +18,10 @@ import { } from '../types'; import { assert } from '../utils/assert'; import { constants } from '../utils/constants'; +import { utils } from '../utils/utils'; import { ContractWrapper } from './contract_wrapper'; import { ERC721ProxyWrapper } from './erc721_proxy_wrapper'; -import { ERC721TokenContract, ERC721TokenEventArgs, ERC721TokenEvents } from './generated/erc721_token'; - -const removeUndefinedProperties = _.pickBy; /** * This class includes all the functionality related to interacting with ERC721 token contracts. @@ -30,9 +29,9 @@ const removeUndefinedProperties = _.pickBy; * to the 0x ERC721 Proxy smart contract. */ export class ERC721TokenWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.ERC721Token.compilerOutput.abi; - private _tokenContractsByAddress: { [address: string]: ERC721TokenContract }; - private _erc721ProxyWrapper: ERC721ProxyWrapper; + public abi: ContractAbi = ERC721Token.compilerOutput.abi; + private readonly _tokenContractsByAddress: { [address: string]: ERC721TokenContract }; + private readonly _erc721ProxyWrapper: ERC721ProxyWrapper; /** * Instantiate ERC721TokenWrapper * @param web3Wrapper Web3Wrapper instance to use @@ -150,7 +149,7 @@ export class ERC721TokenWrapper extends ContractWrapper { ownerAddress: string, methodOpts: MethodOpts = {}, ): Promise<boolean> { - const proxyAddress = this._erc721ProxyWrapper.getContractAddress(); + const proxyAddress = this._erc721ProxyWrapper.address; const isProxyApprovedForAll = await this.isApprovedForAllAsync( tokenAddress, ownerAddress, @@ -199,7 +198,7 @@ export class ERC721TokenWrapper extends ContractWrapper { tokenId: BigNumber, methodOpts: MethodOpts = {}, ): Promise<boolean> { - const proxyAddress = this._erc721ProxyWrapper.getContractAddress(); + const proxyAddress = this._erc721ProxyWrapper.address; const approvedAddress = await this.getApprovedIfExistsAsync(tokenAddress, tokenId, methodOpts); const isProxyApproved = approvedAddress === proxyAddress; return isProxyApproved; @@ -235,7 +234,7 @@ export class ERC721TokenWrapper extends ContractWrapper { const txHash = await tokenContract.setApprovalForAll.sendTransactionAsync( normalizedOperatorAddress, isApproved, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, from: normalizedOwnerAddress, @@ -260,7 +259,7 @@ export class ERC721TokenWrapper extends ContractWrapper { isApproved: boolean, txOpts: TransactionOpts = {}, ): Promise<string> { - const proxyAddress = this._erc721ProxyWrapper.getContractAddress(); + const proxyAddress = this._erc721ProxyWrapper.address; const txHash = await this.setApprovalForAllAsync(tokenAddress, ownerAddress, proxyAddress, isApproved, txOpts); return txHash; } @@ -295,7 +294,7 @@ export class ERC721TokenWrapper extends ContractWrapper { const txHash = await tokenContract.approve.sendTransactionAsync( normalizedApprovedAddress, tokenId, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, from: tokenOwnerAddress, @@ -318,7 +317,7 @@ export class ERC721TokenWrapper extends ContractWrapper { tokenId: BigNumber, txOpts: TransactionOpts = {}, ): Promise<string> { - const proxyAddress = this._erc721ProxyWrapper.getContractAddress(); + const proxyAddress = this._erc721ProxyWrapper.address; const txHash = await this.setApprovalAsync(tokenAddress, proxyAddress, tokenId, txOpts); return txHash; } @@ -366,7 +365,7 @@ export class ERC721TokenWrapper extends ContractWrapper { ownerAddress, normalizedReceiverAddress, tokenId, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, from: normalizedSenderAddress, @@ -400,7 +399,7 @@ export class ERC721TokenWrapper extends ContractWrapper { normalizedTokenAddress, eventName, indexFilterValues, - artifacts.ERC721Token.compilerOutput.abi, + ERC721Token.compilerOutput.abi, callback, isVerbose, ); @@ -445,30 +444,19 @@ export class ERC721TokenWrapper extends ContractWrapper { eventName, blockRange, indexFilterValues, - artifacts.ERC721Token.compilerOutput.abi, + ERC721Token.compilerOutput.abi, ); return logs; } - // HACK: We don't want this method to be visible to the other units within that package but not to the end user. - // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. - // tslint:disable-next-line:no-unused-variable - private _invalidateContractInstances(): void { - this.unsubscribeAll(); - this._tokenContractsByAddress = {}; - } private async _getTokenContractAsync(tokenAddress: string): Promise<ERC721TokenContract> { const normalizedTokenAddress = tokenAddress.toLowerCase(); let tokenContract = this._tokenContractsByAddress[normalizedTokenAddress]; if (!_.isUndefined(tokenContract)) { return tokenContract; } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.ERC721Token, - normalizedTokenAddress, - ); const contractInstance = new ERC721TokenContract( - abi, - address, + this.abi, + normalizedTokenAddress, this._web3Wrapper.getProvider(), this._web3Wrapper.getContractDefaults(), ); diff --git a/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts index 1ac01812e..913c47cf7 100644 --- a/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts @@ -1,29 +1,28 @@ -import { schemas } from '@0xproject/json-schemas'; -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { WETH9Contract, WETH9EventArgs, WETH9Events } from '@0x/abi-gen-wrappers'; +import { WETH9 } from '@0x/contract-artifacts'; +import { schemas } from '@0x/json-schemas'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types'; import * as _ from 'lodash'; -import { artifacts } from '../artifacts'; import { BlockRange, ContractWrappersError, EventCallback, IndexedFilterValues, TransactionOpts } from '../types'; import { assert } from '../utils/assert'; +import { utils } from '../utils/utils'; import { ContractWrapper } from './contract_wrapper'; import { ERC20TokenWrapper } from './erc20_token_wrapper'; -import { WETH9Contract, WETH9EventArgs, WETH9Events } from './generated/weth9'; - -const removeUndefinedProperties = _.pickBy; /** * This class includes all the functionality related to interacting with a wrapped Ether ERC20 token contract. * The caller can convert ETH into the equivalent number of wrapped ETH ERC20 tokens and back. */ export class EtherTokenWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.EtherToken.compilerOutput.abi; - private _etherTokenContractsByAddress: { + public abi: ContractAbi = WETH9.compilerOutput.abi; + private readonly _etherTokenContractsByAddress: { [address: string]: WETH9Contract; } = {}; - private _erc20TokenWrapper: ERC20TokenWrapper; + private readonly _erc20TokenWrapper: ERC20TokenWrapper; /** * Instantiate EtherTokenWrapper. * @param web3Wrapper Web3Wrapper instance to use @@ -67,7 +66,7 @@ export class EtherTokenWrapper extends ContractWrapper { const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress); const txHash = await wethContract.deposit.sendTransactionAsync( - removeUndefinedProperties({ + utils.removeUndefinedProperties({ from: normalizedDepositorAddress, value: amountInWei, gas: txOpts.gasLimit, @@ -109,7 +108,7 @@ export class EtherTokenWrapper extends ContractWrapper { const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress); const txHash = await wethContract.withdraw.sendTransactionAsync( amountInWei, - removeUndefinedProperties({ + utils.removeUndefinedProperties({ from: normalizedWithdrawerAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, @@ -142,7 +141,7 @@ export class EtherTokenWrapper extends ContractWrapper { eventName, blockRange, indexFilterValues, - artifacts.EtherToken.compilerOutput.abi, + WETH9.compilerOutput.abi, ); return logs; } @@ -172,7 +171,7 @@ export class EtherTokenWrapper extends ContractWrapper { normalizedEtherTokenAddress, eventName, indexFilterValues, - artifacts.EtherToken.compilerOutput.abi, + WETH9.compilerOutput.abi, callback, isVerbose, ); @@ -192,36 +191,14 @@ export class EtherTokenWrapper extends ContractWrapper { public unsubscribeAll(): void { super._unsubscribeAll(); } - /** - * Retrieves the Ethereum address of the EtherToken contract deployed on the network - * that the user-passed web3 provider is connected to. If it's not Kovan, Ropsten, Rinkeby, Mainnet or TestRPC - * (networkId: 50), it will return undefined (e.g a private network). - * @returns The Ethereum address of the EtherToken contract or undefined. - */ - public getContractAddressIfExists(): string | undefined { - const networkSpecificArtifact = artifacts.EtherToken.networks[this._networkId]; - const contractAddressIfExists = _.isUndefined(networkSpecificArtifact) - ? undefined - : networkSpecificArtifact.address; - return contractAddressIfExists; - } - // tslint:disable-next-line:no-unused-variable - private _invalidateContractInstance(): void { - this.unsubscribeAll(); - this._etherTokenContractsByAddress = {}; - } private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<WETH9Contract> { let etherTokenContract = this._etherTokenContractsByAddress[etherTokenAddress]; if (!_.isUndefined(etherTokenContract)) { return etherTokenContract; } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.EtherToken, - etherTokenAddress, - ); const contractInstance = new WETH9Contract( - abi, - address, + this.abi, + etherTokenAddress, this._web3Wrapper.getProvider(), this._web3Wrapper.getContractDefaults(), ); diff --git a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts index dc9ffcbf7..c76e51eee 100644 --- a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts @@ -1,22 +1,24 @@ -import { schemas } from '@0xproject/json-schemas'; +import { ExchangeContract, ExchangeEventArgs, ExchangeEvents } from '@0x/abi-gen-wrappers'; +import { Exchange } from '@0x/contract-artifacts'; +import { schemas } from '@0x/json-schemas'; import { assetDataUtils, BalanceAndProxyAllowanceLazyStore, ExchangeTransferSimulator, OrderValidationUtils, -} from '@0xproject/order-utils'; -import { AssetProxyId, Order, SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +} from '@0x/order-utils'; +import { AssetProxyId, Order, SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { BlockParamLiteral, ContractAbi, LogWithDecodedArgs } from 'ethereum-types'; import * as _ from 'lodash'; -import { artifacts } from '../artifacts'; import { AssetBalanceAndProxyAllowanceFetcher } from '../fetchers/asset_balance_and_proxy_allowance_fetcher'; import { OrderFilledCancelledFetcher } from '../fetchers/order_filled_cancelled_fetcher'; import { methodOptsSchema } from '../schemas/method_opts_schema'; import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema'; import { txOptsSchema } from '../schemas/tx_opts_schema'; +import { validateOrderFillableOptsSchema } from '../schemas/validate_order_fillable_opts_schema'; import { BlockRange, EventCallback, @@ -24,54 +26,58 @@ import { IndexedFilterValues, MethodOpts, OrderInfo, - OrderStatus, OrderTransactionOpts, ValidateOrderFillableOpts, } from '../types'; import { assert } from '../utils/assert'; +import { _getDefaultContractAddresses } from '../utils/contract_addresses'; import { decorators } from '../utils/decorators'; import { TransactionEncoder } from '../utils/transaction_encoder'; import { ContractWrapper } from './contract_wrapper'; import { ERC20TokenWrapper } from './erc20_token_wrapper'; import { ERC721TokenWrapper } from './erc721_token_wrapper'; -import { ExchangeContract, ExchangeEventArgs, ExchangeEvents } from './generated/exchange'; /** * This class includes all the functionality related to calling methods, sending transactions and subscribing to * events of the 0x V2 Exchange smart contract. */ export class ExchangeWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.Exchange.compilerOutput.abi; + public abi: ContractAbi = Exchange.compilerOutput.abi; + public address: string; + public zrxTokenAddress: string; private _exchangeContractIfExists?: ExchangeContract; - private _erc721TokenWrapper: ERC721TokenWrapper; - private _erc20TokenWrapper: ERC20TokenWrapper; - private _contractAddressIfExists?: string; - private _zrxContractAddressIfExists?: string; + private readonly _erc721TokenWrapper: ERC721TokenWrapper; + private readonly _erc20TokenWrapper: ERC20TokenWrapper; /** * Instantiate ExchangeWrapper - * @param web3Wrapper Web3Wrapper instance to use - * @param networkId Desired networkId - * @param contractAddressIfExists The exchange contract address to use. This is usually pulled from - * the artifacts but needs to be specified when using with your own custom testnet. - * @param zrxContractAddressIfExists The ZRXToken contract address to use. This is usually pulled from - * the artifacts but needs to be specified when using with your own custom testnet. - * @param blockPollingIntervalMs The block polling interval to use for active subscriptions + * @param web3Wrapper Web3Wrapper instance to use. + * @param networkId Desired networkId. + * @param erc20TokenWrapper ERC20TokenWrapper instance to use. + * @param erc721TokenWrapper ERC721TokenWrapper instance to use. + * @param address The address of the Exchange contract. If undefined, will + * default to the known address corresponding to the networkId. + * @param zrxTokenAddress The address of the ZRXToken contract. If + * undefined, will default to the known address corresponding to the + * networkId. + * @param blockPollingIntervalMs The block polling interval to use for active subscriptions. */ constructor( web3Wrapper: Web3Wrapper, networkId: number, erc20TokenWrapper: ERC20TokenWrapper, erc721TokenWrapper: ERC721TokenWrapper, - contractAddressIfExists?: string, - zrxContractAddressIfExists?: string, + address?: string, + zrxTokenAddress?: string, blockPollingIntervalMs?: number, ) { super(web3Wrapper, networkId, blockPollingIntervalMs); this._erc20TokenWrapper = erc20TokenWrapper; this._erc721TokenWrapper = erc721TokenWrapper; - this._contractAddressIfExists = contractAddressIfExists; - this._zrxContractAddressIfExists = zrxContractAddressIfExists; + this.address = _.isUndefined(address) ? _getDefaultContractAddresses(networkId).exchange : address; + this.zrxTokenAddress = _.isUndefined(zrxTokenAddress) + ? _getDefaultContractAddresses(networkId).zrxToken + : zrxTokenAddress; } /** * Retrieve the address of an asset proxy by signature. @@ -1051,12 +1057,11 @@ export class ExchangeWrapper extends ContractWrapper { assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.isFunction('callback', callback); - const exchangeContractAddress = this.getContractAddress(); const subscriptionToken = this._subscribe<ArgsType>( - exchangeContractAddress, + this.address, eventName, indexFilterValues, - artifacts.Exchange.compilerOutput.abi, + Exchange.compilerOutput.abi, callback, isVerbose, ); @@ -1091,13 +1096,12 @@ export class ExchangeWrapper extends ContractWrapper { assert.doesBelongToStringEnum('eventName', eventName, ExchangeEvents); assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); - const exchangeContractAddress = this.getContractAddress(); const logs = await this._getLogsAsync<ArgsType>( - exchangeContractAddress, + this.address, eventName, blockRange, indexFilterValues, - artifacts.Exchange.compilerOutput.abi, + Exchange.compilerOutput.abi, ); return logs; } @@ -1111,6 +1115,9 @@ export class ExchangeWrapper extends ContractWrapper { signedOrder: SignedOrder, opts: ValidateOrderFillableOpts = {}, ): Promise<void> { + assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); + assert.doesConformToSchema('opts', opts, validateOrderFillableOptsSchema); + const balanceAllowanceFetcher = new AssetBalanceAndProxyAllowanceFetcher( this._erc20TokenWrapper, this._erc721TokenWrapper, @@ -1121,7 +1128,7 @@ export class ExchangeWrapper extends ContractWrapper { const expectedFillTakerTokenAmountIfExists = opts.expectedFillTakerTokenAmount; const filledCancelledFetcher = new OrderFilledCancelledFetcher(this, BlockParamLiteral.Latest); - const orderValidationUtils = new OrderValidationUtils(filledCancelledFetcher); + const orderValidationUtils = new OrderValidationUtils(filledCancelledFetcher, this._web3Wrapper.getProvider()); await orderValidationUtils.validateOrderFillableOrThrowAsync( exchangeTradeSimulator, signedOrder, @@ -1149,7 +1156,7 @@ export class ExchangeWrapper extends ContractWrapper { const exchangeTradeSimulator = new ExchangeTransferSimulator(balanceAllowanceStore); const filledCancelledFetcher = new OrderFilledCancelledFetcher(this, BlockParamLiteral.Latest); - const orderValidationUtils = new OrderValidationUtils(filledCancelledFetcher); + const orderValidationUtils = new OrderValidationUtils(filledCancelledFetcher, this._web3Wrapper.getProvider()); await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( exchangeTradeSimulator, this._web3Wrapper.getProvider(), @@ -1160,29 +1167,11 @@ export class ExchangeWrapper extends ContractWrapper { ); } /** - * 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; - } - /** - * Returns the ZRX token address used by the exchange contract. - * @return Address of ZRX token - */ - public getZRXTokenAddress(): string { - const contractAddress = this._getContractAddress(artifacts.ZRXToken, this._zrxContractAddressIfExists); - return contractAddress; - } - /** * Returns the ZRX asset data used by the exchange contract. * @return ZRX asset data */ public getZRXAssetData(): string { - const zrxTokenAddress = this.getZRXTokenAddress(); - const zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxTokenAddress); + const zrxAssetData = assetDataUtils.encodeERC20AssetData(this.zrxTokenAddress); return zrxAssetData; } /** @@ -1195,23 +1184,14 @@ export class ExchangeWrapper extends ContractWrapper { const encoder = new TransactionEncoder(exchangeInstance); return encoder; } - // tslint:disable:no-unused-variable - private _invalidateContractInstances(): void { - this.unsubscribeAll(); - delete this._exchangeContractIfExists; - } // tslint:enable:no-unused-variable private async _getExchangeContractAsync(): Promise<ExchangeContract> { if (!_.isUndefined(this._exchangeContractIfExists)) { return this._exchangeContractIfExists; } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.Exchange, - this._contractAddressIfExists, - ); const contractInstance = new ExchangeContract( - abi, - address, + this.abi, + this.address, this._web3Wrapper.getProvider(), this._web3Wrapper.getContractDefaults(), ); diff --git a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts index 906222731..80742e030 100644 --- a/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/forwarder_wrapper.ts @@ -1,58 +1,82 @@ -import { schemas } from '@0xproject/json-schemas'; -import { AssetProxyId, SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ForwarderContract } from '@0x/abi-gen-wrappers'; +import { Forwarder } from '@0x/contract-artifacts'; +import { schemas } from '@0x/json-schemas'; +import { SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { ContractAbi } from 'ethereum-types'; import * as _ from 'lodash'; -import { artifacts } from '../artifacts'; import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema'; import { txOptsSchema } from '../schemas/tx_opts_schema'; -import { TransactionOpts } from '../types'; +import { OrderTransactionOpts } from '../types'; import { assert } from '../utils/assert'; import { calldataOptimizationUtils } from '../utils/calldata_optimization_utils'; import { constants } from '../utils/constants'; +import { _getDefaultContractAddresses } from '../utils/contract_addresses'; +import { decorators } from '../utils/decorators'; import { utils } from '../utils/utils'; import { ContractWrapper } from './contract_wrapper'; -import { ForwarderContract } from './generated/forwarder'; /** * This class includes the functionality related to interacting with the Forwarder contract. */ export class ForwarderWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.Forwarder.compilerOutput.abi; + public abi: ContractAbi = Forwarder.compilerOutput.abi; + public address: string; + public zrxTokenAddress: string; + public etherTokenAddress: string; private _forwarderContractIfExists?: ForwarderContract; - private _contractAddressIfExists?: string; - private _zrxContractAddressIfExists?: string; + + /** + * Instantiate ForwarderWrapper + * @param web3Wrapper Web3Wrapper instance to use. + * @param networkId Desired networkId. + * @param address The address of the Exchange contract. If undefined, will + * default to the known address corresponding to the networkId. + * @param zrxTokenAddress The address of the ZRXToken contract. If + * undefined, will default to the known address corresponding to the + * networkId. + * @param etherTokenAddress The address of a WETH (Ether token) contract. If + * undefined, will default to the known address corresponding to the + * networkId. + */ constructor( web3Wrapper: Web3Wrapper, networkId: number, - contractAddressIfExists?: string, - zrxContractAddressIfExists?: string, + address?: string, + zrxTokenAddress?: string, + etherTokenAddress?: string, ) { super(web3Wrapper, networkId); - this._contractAddressIfExists = contractAddressIfExists; - this._zrxContractAddressIfExists = zrxContractAddressIfExists; + this.address = _.isUndefined(address) ? _getDefaultContractAddresses(networkId).exchange : address; + this.zrxTokenAddress = _.isUndefined(zrxTokenAddress) + ? _getDefaultContractAddresses(networkId).zrxToken + : zrxTokenAddress; + this.etherTokenAddress = _.isUndefined(etherTokenAddress) + ? _getDefaultContractAddresses(networkId).etherToken + : etherTokenAddress; } /** * Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value. * Any ZRX required to pay fees for primary orders will automatically be purchased by this contract. * 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH). * Any ETH not spent will be refunded to sender. - * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset. - * All orders must specify WETH as the takerAsset - * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied - * Provider provided at instantiation. - * @param ethAmount The amount of eth to send with the transaction (in wei). - * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset. - * Used to purchase ZRX for primary order fees. - * @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. - * Defaults to 0. - * @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled. - * @param txOpts Transaction parameters. + * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset. + * All orders must specify WETH as the takerAsset + * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied + * Provider provided at instantiation. + * @param ethAmount The amount of eth to send with the transaction (in wei). + * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset. + * Used to purchase ZRX for primary order fees. + * @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. + * Defaults to 0. + * @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled. + * @param orderTransactionOpts Transaction parameters. * @return Transaction hash. */ + @decorators.asyncZeroExErrorHandler public async marketSellOrdersWithEthAsync( signedOrders: SignedOrder[], takerAddress: string, @@ -60,7 +84,7 @@ export class ForwarderWrapper extends ContractWrapper { signedFeeOrders: SignedOrder[] = [], feePercentage: number = 0, feeRecipientAddress: string = constants.NULL_ADDRESS, - txOpts: TransactionOpts = {}, + orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, ): Promise<string> { // type assertions assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); @@ -69,14 +93,10 @@ export class ForwarderWrapper extends ContractWrapper { assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); assert.isNumber('feePercentage', feePercentage); assert.isETHAddressHex('feeRecipientAddress', feeRecipientAddress); - assert.doesConformToSchema('txOpts', txOpts, txOptsSchema); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); // other assertions - assert.ordersCanBeUsedForForwarderContract(signedOrders, this.getEtherTokenAddress()); - assert.feeOrdersCanBeUsedForForwarderContract( - signedFeeOrders, - this.getZRXTokenAddress(), - this.getEtherTokenAddress(), - ); + assert.ordersCanBeUsedForForwarderContract(signedOrders, this.etherTokenAddress); + assert.feeOrdersCanBeUsedForForwarderContract(signedFeeOrders, this.zrxTokenAddress, this.etherTokenAddress); // format feePercentage const formattedFeePercentage = utils.numberPercentageToEtherTokenAmountPercentage(feePercentage); // lowercase input addresses @@ -85,20 +105,41 @@ export class ForwarderWrapper extends ContractWrapper { // optimize orders const optimizedMarketOrders = calldataOptimizationUtils.optimizeForwarderOrders(signedOrders); const optimizedFeeOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(signedFeeOrders); - // send transaction + // compile signatures + const signatures = _.map(optimizedMarketOrders, order => order.signature); + const feeSignatures = _.map(optimizedFeeOrders, order => order.signature); + // get contract const forwarderContractInstance = await this._getForwarderContractAsync(); + // validate transaction + if (orderTransactionOpts.shouldValidate) { + await forwarderContractInstance.marketSellOrdersWithEth.callAsync( + optimizedMarketOrders, + signatures, + optimizedFeeOrders, + feeSignatures, + formattedFeePercentage, + normalizedFeeRecipientAddress, + { + value: ethAmount, + from: normalizedTakerAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, + }, + ); + } + // send transaction const txHash = await forwarderContractInstance.marketSellOrdersWithEth.sendTransactionAsync( optimizedMarketOrders, - _.map(optimizedMarketOrders, order => order.signature), + signatures, optimizedFeeOrders, - _.map(optimizedFeeOrders, order => order.signature), + feeSignatures, formattedFeePercentage, feeRecipientAddress, { value: ethAmount, from: normalizedTakerAddress, - gas: txOpts.gasLimit, - gasPrice: txOpts.gasPrice, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, }, ); return txHash; @@ -107,20 +148,21 @@ export class ForwarderWrapper extends ContractWrapper { * Attempt to purchase makerAssetFillAmount of makerAsset by selling ethAmount provided with transaction. * Any ZRX required to pay fees for primary orders will automatically be purchased by the contract. * Any ETH not spent will be refunded to sender. - * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset. - * All orders must specify WETH as the takerAsset - * @param makerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill. - * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied - * Provider provided at instantiation. - * @param ethAmount The amount of eth to send with the transaction (in wei). - * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset. - * Used to purchase ZRX for primary order fees. - * @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. - * Defaults to 0. - * @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled. - * @param txOpts Transaction parameters. + * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset. + * All orders must specify WETH as the takerAsset + * @param makerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill. + * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied + * Provider provided at instantiation. + * @param ethAmount The amount of eth to send with the transaction (in wei). + * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset. + * Used to purchase ZRX for primary order fees. + * @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. + * Defaults to 0. + * @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled. + * @param orderTransactionOpts Transaction parameters. * @return Transaction hash. */ + @decorators.asyncZeroExErrorHandler public async marketBuyOrdersWithEthAsync( signedOrders: SignedOrder[], makerAssetFillAmount: BigNumber, @@ -129,7 +171,7 @@ export class ForwarderWrapper extends ContractWrapper { signedFeeOrders: SignedOrder[] = [], feePercentage: number = 0, feeRecipientAddress: string = constants.NULL_ADDRESS, - txOpts: TransactionOpts = {}, + orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true }, ): Promise<string> { // type assertions assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); @@ -139,14 +181,10 @@ export class ForwarderWrapper extends ContractWrapper { assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); assert.isNumber('feePercentage', feePercentage); assert.isETHAddressHex('feeRecipientAddress', feeRecipientAddress); - assert.doesConformToSchema('txOpts', txOpts, txOptsSchema); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); // other assertions - assert.ordersCanBeUsedForForwarderContract(signedOrders, this.getEtherTokenAddress()); - assert.feeOrdersCanBeUsedForForwarderContract( - signedFeeOrders, - this.getZRXTokenAddress(), - this.getEtherTokenAddress(), - ); + assert.ordersCanBeUsedForForwarderContract(signedOrders, this.etherTokenAddress); + assert.feeOrdersCanBeUsedForForwarderContract(signedFeeOrders, this.zrxTokenAddress, this.etherTokenAddress); // format feePercentage const formattedFeePercentage = utils.numberPercentageToEtherTokenAmountPercentage(feePercentage); // lowercase input addresses @@ -155,67 +193,54 @@ export class ForwarderWrapper extends ContractWrapper { // optimize orders const optimizedMarketOrders = calldataOptimizationUtils.optimizeForwarderOrders(signedOrders); const optimizedFeeOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(signedFeeOrders); - // send transaction + // compile signatures + const signatures = _.map(optimizedMarketOrders, order => order.signature); + const feeSignatures = _.map(optimizedFeeOrders, order => order.signature); + // get contract const forwarderContractInstance = await this._getForwarderContractAsync(); + // validate transaction + if (orderTransactionOpts.shouldValidate) { + await forwarderContractInstance.marketBuyOrdersWithEth.callAsync( + optimizedMarketOrders, + makerAssetFillAmount, + signatures, + optimizedFeeOrders, + feeSignatures, + formattedFeePercentage, + normalizedFeeRecipientAddress, + { + value: ethAmount, + from: normalizedTakerAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, + }, + ); + } + // send transaction const txHash = await forwarderContractInstance.marketBuyOrdersWithEth.sendTransactionAsync( optimizedMarketOrders, makerAssetFillAmount, - _.map(optimizedMarketOrders, order => order.signature), + signatures, optimizedFeeOrders, - _.map(optimizedFeeOrders, order => order.signature), + feeSignatures, formattedFeePercentage, feeRecipientAddress, { value: ethAmount, from: normalizedTakerAddress, - gas: txOpts.gasLimit, - gasPrice: txOpts.gasPrice, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, }, ); return txHash; } - /** - * Retrieves the Ethereum address of the Forwarder contract deployed on the network - * that the user-passed web3 provider is connected to. - * @returns The Ethereum address of the Forwarder contract being used. - */ - public getContractAddress(): string { - const contractAddress = this._getContractAddress(artifacts.Forwarder, this._contractAddressIfExists); - return contractAddress; - } - /** - * Returns the ZRX token address used by the forwarder contract. - * @return Address of ZRX token - */ - public getZRXTokenAddress(): string { - const contractAddress = this._getContractAddress(artifacts.ZRXToken, this._zrxContractAddressIfExists); - return contractAddress; - } - /** - * Returns the Ether token address used by the forwarder contract. - * @return Address of Ether token - */ - public getEtherTokenAddress(): string { - const contractAddress = this._getContractAddress(artifacts.EtherToken); - return contractAddress; - } - // HACK: We don't want this method to be visible to the other units within that package but not to the end user. - // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. - // tslint:disable-next-line:no-unused-variable - private _invalidateContractInstance(): void { - delete this._forwarderContractIfExists; - } private async _getForwarderContractAsync(): Promise<ForwarderContract> { if (!_.isUndefined(this._forwarderContractIfExists)) { return this._forwarderContractIfExists; } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.Forwarder, - this._contractAddressIfExists, - ); const contractInstance = new ForwarderContract( - abi, - address, + this.abi, + this.address, this._web3Wrapper.getProvider(), this._web3Wrapper.getContractDefaults(), ); diff --git a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts index 1da88f624..c186e080e 100644 --- a/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/order_validator_wrapper.ts @@ -1,30 +1,35 @@ -import { schemas } from '@0xproject/json-schemas'; -import { SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { OrderValidatorContract } from '@0x/abi-gen-wrappers'; +import { OrderValidator } from '@0x/contract-artifacts'; +import { schemas } from '@0x/json-schemas'; +import { SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; import { ContractAbi } from 'ethereum-types'; import * as _ from 'lodash'; -import { artifacts } from '../artifacts'; import { BalanceAndAllowance, OrderAndTraderInfo, TraderInfo } from '../types'; import { assert } from '../utils/assert'; +import { _getDefaultContractAddresses } from '../utils/contract_addresses'; import { ContractWrapper } from './contract_wrapper'; -import { OrderValidatorContract } from './generated/order_validator'; /** * This class includes the functionality related to interacting with the OrderValidator contract. */ export class OrderValidatorWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.OrderValidator.compilerOutput.abi; + public abi: ContractAbi = OrderValidator.compilerOutput.abi; + public address: string; private _orderValidatorContractIfExists?: OrderValidatorContract; /** * Instantiate OrderValidatorWrapper - * @param web3Wrapper Web3Wrapper instance to use - * @param networkId Desired networkId + * @param web3Wrapper Web3Wrapper instance to use. + * @param networkId Desired networkId. + * @param address The address of the OrderValidator contract. If undefined, + * will default to the known address corresponding to the networkId. */ - constructor(web3Wrapper: Web3Wrapper, networkId: number) { + constructor(web3Wrapper: Web3Wrapper, networkId: number, address?: string) { super(web3Wrapper, networkId); + this.address = _.isUndefined(address) ? _getDefaultContractAddresses(networkId).exchange : address; } /** * Get an object conforming to OrderAndTraderInfo containing on-chain information of the provided order and address @@ -164,20 +169,13 @@ export class OrderValidatorWrapper extends ContractWrapper { const result = await OrderValidatorContractInstance.getERC721TokenOwner.callAsync(tokenAddress, tokenId); return result; } - // 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._orderValidatorContractIfExists; - } private async _getOrderValidatorContractAsync(): Promise<OrderValidatorContract> { if (!_.isUndefined(this._orderValidatorContractIfExists)) { return this._orderValidatorContractIfExists; } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync(artifacts.OrderValidator); const contractInstance = new OrderValidatorContract( - abi, - address, + this.abi, + this.address, this._web3Wrapper.getProvider(), this._web3Wrapper.getContractDefaults(), ); diff --git a/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts b/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts index 023cd5ac3..d10cffe57 100644 --- a/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts +++ b/packages/contract-wrappers/src/fetchers/asset_balance_and_proxy_allowance_fetcher.ts @@ -1,7 +1,7 @@ // tslint:disable:no-unnecessary-type-assertion -import { AbstractBalanceAndProxyAllowanceFetcher, assetDataUtils } from '@0xproject/order-utils'; -import { AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; +import { AbstractBalanceAndProxyAllowanceFetcher, assetDataUtils } from '@0x/order-utils'; +import { AssetProxyId, ERC20AssetData, ERC721AssetData } from '@0x/types'; +import { BigNumber } from '@0x/utils'; import { BlockParamLiteral } from 'ethereum-types'; import { ERC20TokenWrapper } from '../contract_wrappers/erc20_token_wrapper'; diff --git a/packages/contract-wrappers/src/fetchers/order_filled_cancelled_fetcher.ts b/packages/contract-wrappers/src/fetchers/order_filled_cancelled_fetcher.ts index 7a252aed3..5d350916c 100644 --- a/packages/contract-wrappers/src/fetchers/order_filled_cancelled_fetcher.ts +++ b/packages/contract-wrappers/src/fetchers/order_filled_cancelled_fetcher.ts @@ -1,6 +1,7 @@ // tslint:disable:no-unnecessary-type-assertion -import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils'; -import { BigNumber } from '@0xproject/utils'; +import { AbstractOrderFilledCancelledFetcher, orderHashUtils } from '@0x/order-utils'; +import { SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; import { BlockParamLiteral } from 'ethereum-types'; import { ExchangeWrapper } from '../contract_wrappers/exchange_wrapper'; @@ -18,9 +19,18 @@ export class OrderFilledCancelledFetcher implements AbstractOrderFilledCancelled }); return filledTakerAmount; } - public async isOrderCancelledAsync(orderHash: string): Promise<boolean> { + public async isOrderCancelledAsync(signedOrder: SignedOrder): Promise<boolean> { + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); const isCancelled = await this._exchange.isCancelledAsync(orderHash); - return isCancelled; + const orderEpoch = await this._exchange.getOrderEpochAsync( + signedOrder.makerAddress, + signedOrder.senderAddress, + { + defaultBlock: this._stateLayer, + }, + ); + const isCancelledByOrderEpoch = orderEpoch > signedOrder.salt; + return isCancelled || isCancelledByOrderEpoch; } public getZRXAssetData(): string { const zrxAssetData = this._exchange.getZRXAssetData(); diff --git a/packages/contract-wrappers/src/globals.d.ts b/packages/contract-wrappers/src/globals.d.ts deleted file mode 100644 index 94e63a32d..000000000 --- a/packages/contract-wrappers/src/globals.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module '*.json' { - const json: any; - /* tslint:disable */ - export default json; - /* tslint:enable */ -} diff --git a/packages/contract-wrappers/src/index.ts b/packages/contract-wrappers/src/index.ts index e8a53170e..d66ff5c9c 100644 --- a/packages/contract-wrappers/src/index.ts +++ b/packages/contract-wrappers/src/index.ts @@ -1,3 +1,30 @@ +export { ContractAddresses } from '@0x/contract-addresses'; + +export { + WETH9Events, + WETH9WithdrawalEventArgs, + WETH9ApprovalEventArgs, + WETH9EventArgs, + WETH9DepositEventArgs, + WETH9TransferEventArgs, + ERC20TokenTransferEventArgs, + ERC20TokenApprovalEventArgs, + ERC20TokenEvents, + ERC20TokenEventArgs, + ERC721TokenApprovalEventArgs, + ERC721TokenApprovalForAllEventArgs, + ERC721TokenTransferEventArgs, + ERC721TokenEvents, + ERC721TokenEventArgs, + ExchangeCancelUpToEventArgs, + ExchangeAssetProxyRegisteredEventArgs, + ExchangeSignatureValidatorApprovalEventArgs, + ExchangeFillEventArgs, + ExchangeCancelEventArgs, + ExchangeEventArgs, + ExchangeEvents, +} from '@0x/abi-gen-wrappers'; + export { ContractWrappers } from './contract_wrappers'; export { ERC20TokenWrapper } from './contract_wrappers/erc20_token_wrapper'; export { ERC721TokenWrapper } from './contract_wrappers/erc721_token_wrapper'; @@ -12,6 +39,7 @@ export { TransactionEncoder } from './utils/transaction_encoder'; export { ContractWrappersError, + ForwarderWrapperError, IndexedFilterValues, BlockRange, ContractWrappersConfig, @@ -28,7 +56,7 @@ export { ValidateOrderFillableOpts, } from './types'; -export { Order, SignedOrder, AssetProxyId } from '@0xproject/types'; +export { Order, SignedOrder, AssetProxyId } from '@0x/types'; export { BlockParamLiteral, @@ -54,41 +82,7 @@ export { StateMutability, } from 'ethereum-types'; -export { - WETH9Events, - WETH9WithdrawalEventArgs, - WETH9ApprovalEventArgs, - WETH9EventArgs, - WETH9DepositEventArgs, - WETH9TransferEventArgs, -} from './contract_wrappers/generated/weth9'; - -export { - ERC20TokenTransferEventArgs, - ERC20TokenApprovalEventArgs, - ERC20TokenEvents, - ERC20TokenEventArgs, -} from './contract_wrappers/generated/erc20_token'; - -export { - ERC721TokenApprovalEventArgs, - ERC721TokenApprovalForAllEventArgs, - ERC721TokenTransferEventArgs, - ERC721TokenEvents, - ERC721TokenEventArgs, -} from './contract_wrappers/generated/erc721_token'; - -export { - ExchangeCancelUpToEventArgs, - ExchangeAssetProxyRegisteredEventArgs, - ExchangeSignatureValidatorApprovalEventArgs, - ExchangeFillEventArgs, - ExchangeCancelEventArgs, - ExchangeEventArgs, - ExchangeEvents, -} from './contract_wrappers/generated/exchange'; - -export { AbstractBalanceAndProxyAllowanceFetcher, AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils'; +export { AbstractBalanceAndProxyAllowanceFetcher, AbstractOrderFilledCancelledFetcher } from '@0x/order-utils'; export { AssetBalanceAndProxyAllowanceFetcher } from './fetchers/asset_balance_and_proxy_allowance_fetcher'; export { OrderFilledCancelledFetcher } from './fetchers/order_filled_cancelled_fetcher'; diff --git a/packages/contract-wrappers/src/schemas/contract_wrappers_config_schema.ts b/packages/contract-wrappers/src/schemas/contract_wrappers_config_schema.ts index ac248b2d4..ae1ce668c 100644 --- a/packages/contract-wrappers/src/schemas/contract_wrappers_config_schema.ts +++ b/packages/contract-wrappers/src/schemas/contract_wrappers_config_schema.ts @@ -1,5 +1,25 @@ export const ContractWrappersConfigSchema = { id: '/ContractWrappersConfig', - oneOf: [{ $ref: '/ZeroExContractPrivateNetworkConfig' }, { $ref: '/ZeroExContractPublicNetworkConfig' }], + properties: { + networkId: { + type: 'number', + }, + gasPrice: { $ref: '/numberSchema' }, + contractAddresses: { + type: 'object', + properties: { + erc20Proxy: { $ref: '/addressSchema' }, + erc721Proxy: { $ref: '/addressSchema' }, + zrxToken: { $ref: '/addressSchema' }, + etherToken: { $ref: '/addressSchema' }, + exchange: { $ref: '/addressSchema' }, + assetProxyOwner: { $ref: '/addressSchema' }, + forwarder: { $ref: '/addressSchema' }, + orderValidator: { $ref: '/addressSchema' }, + }, + }, + blockPollingIntervalMs: { type: 'number' }, + }, type: 'object', + required: ['networkId'], }; diff --git a/packages/contract-wrappers/src/schemas/contract_wrappers_private_network_config_schema.ts b/packages/contract-wrappers/src/schemas/contract_wrappers_private_network_config_schema.ts deleted file mode 100644 index 904690ae7..000000000 --- a/packages/contract-wrappers/src/schemas/contract_wrappers_private_network_config_schema.ts +++ /dev/null @@ -1,36 +0,0 @@ -export const contractWrappersPrivateNetworkConfigSchema = { - id: '/ZeroExContractPrivateNetworkConfig', - properties: { - networkId: { - type: 'number', - minimum: 1, - }, - gasPrice: { $ref: '/numberSchema' }, - zrxContractAddress: { $ref: '/addressSchema' }, - exchangeContractAddress: { $ref: '/addressSchema' }, - erc20ProxyContractAddress: { $ref: '/addressSchema' }, - erc721ProxyContractAddress: { $ref: '/addressSchema' }, - blockPollingIntervalMs: { type: 'number' }, - orderWatcherConfig: { - type: 'object', - properties: { - pollingIntervalMs: { - type: 'number', - minimum: 0, - }, - numConfirmations: { - type: 'number', - minimum: 0, - }, - }, - }, - }, - type: 'object', - required: [ - 'networkId', - 'zrxContractAddress', - 'exchangeContractAddress', - 'erc20ProxyContractAddress', - 'erc721ProxyContractAddress', - ], -}; diff --git a/packages/contract-wrappers/src/schemas/contract_wrappers_public_network_config_schema.ts b/packages/contract-wrappers/src/schemas/contract_wrappers_public_network_config_schema.ts deleted file mode 100644 index 5cd008ae0..000000000 --- a/packages/contract-wrappers/src/schemas/contract_wrappers_public_network_config_schema.ts +++ /dev/null @@ -1,44 +0,0 @@ -const networkNameToId: { [networkName: string]: number } = { - mainnet: 1, - ropsten: 3, - rinkeby: 4, - kovan: 42, - ganache: 50, -}; - -export const contractWrappersPublicNetworkConfigSchema = { - id: '/ZeroExContractPublicNetworkConfig', - properties: { - networkId: { - type: 'number', - enum: [ - networkNameToId.mainnet, - networkNameToId.kovan, - networkNameToId.ropsten, - networkNameToId.rinkeby, - networkNameToId.ganache, - ], - }, - gasPrice: { $ref: '/numberSchema' }, - zrxContractAddress: { $ref: '/addressSchema' }, - exchangeContractAddress: { $ref: '/addressSchema' }, - erc20ProxyContractAddress: { $ref: '/addressSchema' }, - erc721ProxyContractAddress: { $ref: '/addressSchema' }, - blockPollingIntervalMs: { type: 'number' }, - orderWatcherConfig: { - type: 'object', - properties: { - pollingIntervalMs: { - type: 'number', - minimum: 0, - }, - numConfirmations: { - type: 'number', - minimum: 0, - }, - }, - }, - }, - type: 'object', - required: ['networkId'], -}; diff --git a/packages/contract-wrappers/src/schemas/validate_order_fillable_opts_schema.ts b/packages/contract-wrappers/src/schemas/validate_order_fillable_opts_schema.ts new file mode 100644 index 000000000..2e111af04 --- /dev/null +++ b/packages/contract-wrappers/src/schemas/validate_order_fillable_opts_schema.ts @@ -0,0 +1,7 @@ +export const validateOrderFillableOptsSchema = { + id: '/ValidateOrderFillableOpts', + properties: { + expectedFillTakerTokenAmount: { $ref: '/wholeNumberSchema' }, + }, + type: 'object', +}; diff --git a/packages/contract-wrappers/src/types.ts b/packages/contract-wrappers/src/types.ts index e0b12b7c9..5a5bdd530 100644 --- a/packages/contract-wrappers/src/types.ts +++ b/packages/contract-wrappers/src/types.ts @@ -1,25 +1,28 @@ -import { BigNumber } from '@0xproject/utils'; +import { + ERC20TokenEventArgs, + ERC20TokenEvents, + ERC721TokenEventArgs, + ERC721TokenEvents, + ExchangeEventArgs, + ExchangeEvents, + WETH9EventArgs, + WETH9Events, +} from '@0x/abi-gen-wrappers'; +import { ContractAddresses } from '@0x/contract-addresses'; +import { OrderState, SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; -import { OrderState, SignedOrder } from '@0xproject/types'; import { BlockParam, ContractEventArg, DecodedLogArgs, LogEntryEvent, LogWithDecodedArgs } from 'ethereum-types'; -import { ERC20TokenEventArgs, ERC20TokenEvents } from './contract_wrappers/generated/erc20_token'; -import { ERC721TokenEventArgs, ERC721TokenEvents } from './contract_wrappers/generated/erc721_token'; -import { ExchangeEventArgs, ExchangeEvents } from './contract_wrappers/generated/exchange'; -import { WETH9EventArgs, WETH9Events } from './contract_wrappers/generated/weth9'; - export enum ExchangeWrapperError { AssetDataMismatch = 'ASSET_DATA_MISMATCH', } +export enum ForwarderWrapperError { + CompleteFillFailed = 'COMPLETE_FILL_FAILED', +} + export enum ContractWrappersError { - ExchangeContractDoesNotExist = 'EXCHANGE_CONTRACT_DOES_NOT_EXIST', - ZRXContractDoesNotExist = 'ZRX_CONTRACT_DOES_NOT_EXIST', - EtherTokenContractDoesNotExist = 'ETHER_TOKEN_CONTRACT_DOES_NOT_EXIST', - ERC20ProxyContractDoesNotExist = 'ERC20_PROXY_CONTRACT_DOES_NOT_EXIST', - ERC721ProxyContractDoesNotExist = 'ERC721_PROXY_CONTRACT_DOES_NOT_EXIST', - ERC20TokenContractDoesNotExist = 'ERC20_TOKEN_CONTRACT_DOES_NOT_EXIST', - ERC721TokenContractDoesNotExist = 'ERC721_TOKEN_CONTRACT_DOES_NOT_EXIST', ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK', InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER', InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER', @@ -31,6 +34,7 @@ export enum ContractWrappersError { SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT', ERC721OwnerNotFound = 'ERC_721_OWNER_NOT_FOUND', ERC721NoApproval = 'ERC_721_NO_APPROVAL', + SignatureRequestDenied = 'SIGNATURE_REQUEST_DENIED', } export enum InternalContractWrappersError { @@ -105,22 +109,13 @@ export type SyncMethod = (...args: any[]) => any; /** * networkId: The id of the underlying ethereum network your provider is connected to. (1-mainnet, 3-ropsten, 4-rinkeby, 42-kovan, 50-testrpc) * gasPrice: Gas price to use with every transaction - * exchangeContractAddress: The address of an exchange contract to use - * zrxContractAddress: The address of the ZRX contract to use - * erc20ProxyContractAddress: The address of the erc20 token transfer proxy contract to use - * erc721ProxyContractAddress: The address of the erc721 token transfer proxy contract to use - * forwarderContractAddress: The address of the forwarder contract to use - * orderWatcherConfig: All the configs related to the orderWatcher + * contractAddresses: The address of all contracts to use. Defaults to the known addresses based on networkId. * blockPollingIntervalMs: The interval to use for block polling in event watching methods (defaults to 1000) */ export interface ContractWrappersConfig { networkId: number; gasPrice?: BigNumber; - exchangeContractAddress?: string; - zrxContractAddress?: string; - erc20ProxyContractAddress?: string; - erc721ProxyContractAddress?: string; - forwarderContractAddress?: string; + contractAddresses?: ContractAddresses; blockPollingIntervalMs?: number; } diff --git a/packages/contract-wrappers/src/utils/assert.ts b/packages/contract-wrappers/src/utils/assert.ts index 30726c546..3f02ed052 100644 --- a/packages/contract-wrappers/src/utils/assert.ts +++ b/packages/contract-wrappers/src/utils/assert.ts @@ -1,10 +1,10 @@ -import { assert as sharedAssert } from '@0xproject/assert'; +import { assert as sharedAssert } from '@0x/assert'; // HACK: We need those two unused imports because they're actually used by sharedAssert which gets injected here -import { Schema } from '@0xproject/json-schemas'; // tslint:disable-line:no-unused-variable -import { assetDataUtils, signatureUtils } from '@0xproject/order-utils'; -import { Order } from '@0xproject/types'; // tslint:disable-line:no-unused-variable -import { BigNumber } from '@0xproject/utils'; // tslint:disable-line:no-unused-variable -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { Schema } from '@0x/json-schemas'; // tslint:disable-line:no-unused-variable +import { assetDataUtils, signatureUtils } from '@0x/order-utils'; +import { Order } from '@0x/types'; // tslint:disable-line:no-unused-variable +import { BigNumber } from '@0x/utils'; // tslint:disable-line:no-unused-variable +import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; diff --git a/packages/contract-wrappers/src/utils/calldata_optimization_utils.ts b/packages/contract-wrappers/src/utils/calldata_optimization_utils.ts index 3172cf531..bee7acaa7 100644 --- a/packages/contract-wrappers/src/utils/calldata_optimization_utils.ts +++ b/packages/contract-wrappers/src/utils/calldata_optimization_utils.ts @@ -1,4 +1,4 @@ -import { SignedOrder } from '@0xproject/types'; +import { SignedOrder } from '@0x/types'; import * as _ from 'lodash'; import { constants } from './constants'; diff --git a/packages/contract-wrappers/src/utils/constants.ts b/packages/contract-wrappers/src/utils/constants.ts index 78441decf..c587ba526 100644 --- a/packages/contract-wrappers/src/utils/constants.ts +++ b/packages/contract-wrappers/src/utils/constants.ts @@ -1,4 +1,4 @@ -import { BigNumber } from '@0xproject/utils'; +import { BigNumber } from '@0x/utils'; export const constants = { NULL_ADDRESS: '0x0000000000000000000000000000000000000000', @@ -14,4 +14,5 @@ export const constants = { ZERO_AMOUNT: new BigNumber(0), ONE_AMOUNT: new BigNumber(1), ETHER_TOKEN_DECIMALS: 18, + USER_DENIED_SIGNATURE_PATTERN: 'User denied transaction signature', }; diff --git a/packages/contract-wrappers/src/utils/contract_addresses.ts b/packages/contract-wrappers/src/utils/contract_addresses.ts new file mode 100644 index 000000000..dc156e017 --- /dev/null +++ b/packages/contract-wrappers/src/utils/contract_addresses.ts @@ -0,0 +1,15 @@ +import { ContractAddresses, getContractAddressesForNetworkOrThrow, NetworkId } from '@0x/contract-addresses'; +import * as _ from 'lodash'; + +/** + * Returns the default contract addresses for the given networkId or throws with + * a context-specific error message if the networkId is not recognized. + */ +export function _getDefaultContractAddresses(networkId: number): ContractAddresses { + if (!(networkId in NetworkId)) { + throw new Error( + `No default contract addresses found for the given network id (${networkId}). If you want to use ContractWrappers on this network, you must manually pass in the contract address(es) to the constructor.`, + ); + } + return getContractAddressesForNetworkOrThrow(networkId); +} diff --git a/packages/contract-wrappers/src/utils/decorators.ts b/packages/contract-wrappers/src/utils/decorators.ts index e17246015..a4207ae4c 100644 --- a/packages/contract-wrappers/src/utils/decorators.ts +++ b/packages/contract-wrappers/src/utils/decorators.ts @@ -29,6 +29,14 @@ const schemaErrorTransformer = (error: Error) => { return error; }; +const signatureRequestErrorTransformer = (error: Error) => { + if (_.includes(error.message, constants.USER_DENIED_SIGNATURE_PATTERN)) { + const errMsg = ContractWrappersError.SignatureRequestDenied; + return new Error(errMsg); + } + return error; +}; + /** * Source: https://stackoverflow.com/a/29837695/3546986 */ @@ -87,7 +95,11 @@ const syncErrorHandlerFactory = (errorTransformer: ErrorTransformer) => { }; // _.flow(f, g) = f ∘ g -const zeroExErrorTransformer = _.flow(schemaErrorTransformer, contractCallErrorTransformer); +const zeroExErrorTransformer = _.flow( + schemaErrorTransformer, + contractCallErrorTransformer, + signatureRequestErrorTransformer, +); export const decorators = { asyncZeroExErrorHandler: asyncErrorHandlerFactory(zeroExErrorTransformer), diff --git a/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts b/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts index a7c4a238f..f374d509b 100644 --- a/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts +++ b/packages/contract-wrappers/src/utils/exchange_transfer_simulator.ts @@ -1,5 +1,5 @@ -import { ExchangeContractErrs } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; +import { ExchangeContractErrs } from '@0x/types'; +import { BigNumber } from '@0x/utils'; import { AbstractBalanceAndProxyAllowanceLazyStore } from '../abstract/abstract_balance_and_proxy_allowance_lazy_store'; import { TradeSide, TransferType } from '../types'; diff --git a/packages/contract-wrappers/src/utils/transaction_encoder.ts b/packages/contract-wrappers/src/utils/transaction_encoder.ts index 33086944b..0cf08a8fe 100644 --- a/packages/contract-wrappers/src/utils/transaction_encoder.ts +++ b/packages/contract-wrappers/src/utils/transaction_encoder.ts @@ -1,10 +1,10 @@ -import { schemas } from '@0xproject/json-schemas'; -import { eip712Utils } from '@0xproject/order-utils'; -import { Order, SignedOrder } from '@0xproject/types'; -import { BigNumber, signTypedDataUtils } from '@0xproject/utils'; -import _ = require('lodash'); +import { ExchangeContract } from '@0x/abi-gen-wrappers'; -import { ExchangeContract } from '../contract_wrappers/generated/exchange'; +import { schemas } from '@0x/json-schemas'; +import { eip712Utils } from '@0x/order-utils'; +import { Order, SignedOrder } from '@0x/types'; +import { BigNumber, signTypedDataUtils } from '@0x/utils'; +import _ = require('lodash'); import { assert } from './assert'; diff --git a/packages/contract-wrappers/src/utils/utils.ts b/packages/contract-wrappers/src/utils/utils.ts index f7949ec34..0b3270e78 100644 --- a/packages/contract-wrappers/src/utils/utils.ts +++ b/packages/contract-wrappers/src/utils/utils.ts @@ -1,5 +1,6 @@ -import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as _ from 'lodash'; import { constants } from './constants'; @@ -14,4 +15,7 @@ export const utils = { numberPercentageToEtherTokenAmountPercentage(percentage: number): BigNumber { return Web3Wrapper.toBaseUnitAmount(constants.ONE_AMOUNT, constants.ETHER_TOKEN_DECIMALS).mul(percentage); }, + removeUndefinedProperties<T extends object>(obj: T): Partial<T> { + return _.pickBy(obj); + }, }; |