diff options
79 files changed, 749 insertions, 545 deletions
diff --git a/.prettierignore b/.prettierignore index c3738481a..385c15654 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,4 @@ lib -generated .nyc_output /packages/contracts/src/artifacts package.json diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md index 2710788e0..b882a1b31 100644 --- a/packages/0x.js/CHANGELOG.md +++ b/packages/0x.js/CHANGELOG.md @@ -4,6 +4,7 @@ * Validate and lowercase all addresses in public methods (#373) * Improve validation to force passing contract addresses on private networks (#385) + * Change `LogErrorContractEventArgs.errorId` type from `BigNumber` to `number` (#413) * Rename all public `_unsubscribeAll` methods to `unsubscribeAll` (#415) ## v0.32.2 - _February 9, 2018_ diff --git a/packages/0x.js/contract_templates/partials/call.handlebars b/packages/0x.js/contract_templates/partials/call.handlebars deleted file mode 100644 index 0475136f0..000000000 --- a/packages/0x.js/contract_templates/partials/call.handlebars +++ /dev/null @@ -1,15 +0,0 @@ -public {{this.name}} = { - async callAsync( - {{> typed_params inputs=inputs}} - defaultBlock?: Web3.BlockParam, - ): Promise<{{> return_type outputs=outputs}}> { - const self = this as {{contractName}}Contract; - const result = await promisify<{{> return_type outputs=outputs}}>( - self._web3ContractInstance.{{this.name}}.call, - self._web3ContractInstance, - )( - {{> params inputs=inputs}} - ); - return result; - }, -}; diff --git a/packages/0x.js/contract_templates/partials/return_type.handlebars b/packages/0x.js/contract_templates/partials/return_type.handlebars deleted file mode 100644 index 383961a40..000000000 --- a/packages/0x.js/contract_templates/partials/return_type.handlebars +++ /dev/null @@ -1,6 +0,0 @@ -{{#singleReturnValue}} -{{#returnType outputs.0.type}}{{/returnType}} -{{/singleReturnValue}} -{{^singleReturnValue}} -[{{#each outputs}}{{#returnType type}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}] -{{/singleReturnValue}} diff --git a/packages/0x.js/contract_templates/partials/typed_params.handlebars b/packages/0x.js/contract_templates/partials/typed_params.handlebars deleted file mode 100644 index 3ea4b2e95..000000000 --- a/packages/0x.js/contract_templates/partials/typed_params.handlebars +++ /dev/null @@ -1,3 +0,0 @@ -{{#each inputs}} - {{name}}: {{#parameterType type}}{{/parameterType}}, -{{/each}} diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json index 4fb0eb6ac..96ba28b75 100644 --- a/packages/0x.js/package.json +++ b/packages/0x.js/package.json @@ -17,7 +17,7 @@ "build": "run-p build:umd:prod build:commonjs; exit 0;", "docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --json $JSON_FILE_PATH $PROJECT_DIR", "upload_docs_json": "aws s3 cp generated_docs/index.json $S3_URL --profile 0xproject --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --content-type application/json", - "generate_contract_wrappers": "node ../abi-gen/lib/index.js --abis 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --template contract_templates/contract.handlebars --partials 'contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated", + "generate_contract_wrappers": "node ../abi-gen/lib/index.js --abis 'src/artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers && prettier --write 'src/contract_wrappers/generated/**.ts'", "lint": "tslint --project . 'src/**/*.ts' 'test/**/*.ts'", "test:circleci": "run-s test:coverage report_test_coverage", "test": "run-s clean test:commonjs", @@ -77,6 +77,7 @@ "types-bn": "^0.0.1", "typescript": "2.7.1", "web3-provider-engine": "^13.0.1", + "ethers-typescript-typings": "^0.0.1", "web3-typescript-typings": "^0.9.11", "webpack": "^3.1.0" }, @@ -88,6 +89,7 @@ "@0xproject/web3-wrapper": "^0.1.14", "bintrees": "^1.0.2", "bn.js": "^4.11.8", + "ethers-contracts": "^2.2.1", "ethereumjs-abi": "^0.6.4", "ethereumjs-blockstream": "^2.0.6", "ethereumjs-util": "^5.1.1", diff --git a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts index b313273b5..ad7727de5 100644 --- a/packages/0x.js/src/contract_wrappers/contract_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/contract_wrapper.ts @@ -108,10 +108,10 @@ export class ContractWrapper { const logWithDecodedArgs = this._abiDecoder.tryToDecodeLogOrNoop(log); return logWithDecodedArgs; } - protected async _instantiateContractIfExistsAsync( + protected async _getContractAbiAndAddressFromArtifactsAsync( artifact: Artifact, addressIfExists?: string, - ): Promise<Web3.ContractInstance> { + ): Promise<[Web3.ContractAbi, string]> { let contractAddress: string; if (_.isUndefined(addressIfExists)) { if (_.isUndefined(artifact.networks[this._networkId])) { @@ -125,8 +125,8 @@ export class ContractWrapper { if (!doesContractExist) { throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contract_name]); } - const contractInstance = this._web3Wrapper.getContractInstance(artifact.abi, contractAddress); - return contractInstance; + const abiAndAddress: [Web3.ContractAbi, string] = [artifact.abi, contractAddress]; + return abiAndAddress; } protected _getContractAddress(artifact: Artifact, addressIfExists?: string): string { if (_.isUndefined(addressIfExists)) { diff --git a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts index 4807eff33..42f8213a2 100644 --- a/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/ether_token_wrapper.ts @@ -187,11 +187,11 @@ export class EtherTokenWrapper extends ContractWrapper { if (!_.isUndefined(etherTokenContract)) { return etherTokenContract; } - const web3ContractInstance = await this._instantiateContractIfExistsAsync( + const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( artifacts.EtherTokenArtifact, etherTokenAddress, ); - const contractInstance = new EtherTokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults()); + const contractInstance = new EtherTokenContract(this._web3Wrapper, abi, address); etherTokenContract = contractInstance; this._etherTokenContractsByAddress[etherTokenAddress] = etherTokenContract; return etherTokenContract; diff --git a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts index 6bafc84c1..20b46c6bc 100644 --- a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts @@ -108,8 +108,10 @@ export class ExchangeWrapper extends ContractWrapper { const exchangeContract = await this._getExchangeContractAsync(); const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; + const txData = {}; let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync( orderHash, + txData, defaultBlock, ); // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber @@ -127,7 +129,8 @@ export class ExchangeWrapper extends ContractWrapper { const exchangeContract = await this._getExchangeContractAsync(); const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; - let fillAmountInBaseUnits = await exchangeContract.filled.callAsync(orderHash, defaultBlock); + const txData = {}; + let fillAmountInBaseUnits = await exchangeContract.filled.callAsync(orderHash, txData, defaultBlock); // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber fillAmountInBaseUnits = new BigNumber(fillAmountInBaseUnits); return fillAmountInBaseUnits; @@ -144,7 +147,8 @@ export class ExchangeWrapper extends ContractWrapper { const exchangeContract = await this._getExchangeContractAsync(); const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; - let cancelledAmountInBaseUnits = await exchangeContract.cancelled.callAsync(orderHash, defaultBlock); + const txData = {}; + let cancelledAmountInBaseUnits = await exchangeContract.cancelled.callAsync(orderHash, txData, defaultBlock); // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber cancelledAmountInBaseUnits = new BigNumber(cancelledAmountInBaseUnits); return cancelledAmountInBaseUnits; @@ -858,7 +862,7 @@ export class ExchangeWrapper extends ContractWrapper { }); if (!_.isUndefined(errLog)) { const logArgs = (errLog as LogWithDecodedArgs<LogErrorContractEventArgs>).args; - const errCode = logArgs.errorId.toNumber(); + const errCode = logArgs.errorId; const errMessage = this._exchangeContractErrCodesToMsg[errCode]; throw new Error(errMessage); } @@ -906,11 +910,11 @@ export class ExchangeWrapper extends ContractWrapper { if (!_.isUndefined(this._exchangeContractIfExists)) { return this._exchangeContractIfExists; } - const web3ContractInstance = await this._instantiateContractIfExistsAsync( + const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( artifacts.ExchangeArtifact, this._contractAddressIfExists, ); - const contractInstance = new ExchangeContract(web3ContractInstance, this._web3Wrapper.getContractDefaults()); + const contractInstance = new ExchangeContract(this._web3Wrapper, abi, address); this._exchangeContractIfExists = contractInstance; return this._exchangeContractIfExists; } diff --git a/packages/0x.js/src/contract_wrappers/generated/.gitignore b/packages/0x.js/src/contract_wrappers/generated/.gitignore index 834808b48..72e8ffc0d 100644 --- a/packages/0x.js/src/contract_wrappers/generated/.gitignore +++ b/packages/0x.js/src/contract_wrappers/generated/.gitignore @@ -1,6 +1 @@ -dummy_token.ts -ether_token.ts -exchange.ts -token_registry.ts -token_transfer_proxy.ts -token.ts +* diff --git a/packages/0x.js/src/contract_wrappers/generated/base_contract.ts b/packages/0x.js/src/contract_wrappers/generated/base_contract.ts deleted file mode 100644 index d8fac7eea..000000000 --- a/packages/0x.js/src/contract_wrappers/generated/base_contract.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {TxData, TxDataPayable} from '@0xproject/types'; -import * as _ from 'lodash'; -import * as Web3 from 'web3'; - -export class BaseContract { - protected _web3ContractInstance: Web3.ContractInstance; - protected _defaults: Partial<TxData>; - protected async _applyDefaultsToTxDataAsync<T extends TxData|TxDataPayable>( - txData: T, - estimateGasAsync?: (txData: T) => Promise<number>, - ): Promise<TxData> { - // Gas amount sourced with the following priorities: - // 1. Optional param passed in to public method call - // 2. Global config passed in at library instantiation - // 3. Gas estimate calculation + safety margin - const removeUndefinedProperties = _.pickBy; - const txDataWithDefaults = { - ...removeUndefinedProperties(this._defaults), - ...removeUndefinedProperties(txData as any), - // HACK: TS can't prove that T is spreadable. - // Awaiting https://github.com/Microsoft/TypeScript/pull/13288 to be merged - }; - if (_.isUndefined(txDataWithDefaults.gas) && !_.isUndefined(estimateGasAsync)) { - const estimatedGas = await estimateGasAsync(txData); - txDataWithDefaults.gas = estimatedGas; - } - return txDataWithDefaults; - } - constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial<TxData>) { - this._web3ContractInstance = web3ContractInstance; - this._defaults = defaults; - } -} diff --git a/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts index f0ae5f33c..e1806c6f2 100644 --- a/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/token_registry_wrapper.ts @@ -23,7 +23,7 @@ export class TokenRegistryWrapper extends ContractWrapper { address: metadata[0], name: metadata[1], symbol: metadata[2], - decimals: metadata[3].toNumber(), + decimals: metadata[3], }; return token; } @@ -50,7 +50,8 @@ export class TokenRegistryWrapper extends ContractWrapper { public async getTokenAddressesAsync(): Promise<string[]> { const tokenRegistryContract = await this._getTokenRegistryContractAsync(); const addresses = await tokenRegistryContract.getTokenAddresses.callAsync(); - return addresses; + const lowerCaseAddresses = _.map(addresses, address => address.toLowerCase()); + return lowerCaseAddresses; } /** * Retrieves a token by address currently listed in the Token Registry smart contract @@ -116,14 +117,11 @@ export class TokenRegistryWrapper extends ContractWrapper { if (!_.isUndefined(this._tokenRegistryContractIfExists)) { return this._tokenRegistryContractIfExists; } - const web3ContractInstance = await this._instantiateContractIfExistsAsync( + const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( artifacts.TokenRegistryArtifact, this._contractAddressIfExists, ); - const contractInstance = new TokenRegistryContract( - web3ContractInstance, - this._web3Wrapper.getContractDefaults(), - ); + const contractInstance = new TokenRegistryContract(this._web3Wrapper, abi, address); this._tokenRegistryContractIfExists = contractInstance; return this._tokenRegistryContractIfExists; } diff --git a/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts index f0cbf364d..211c7dfb4 100644 --- a/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/token_transfer_proxy_wrapper.ts @@ -59,14 +59,11 @@ export class TokenTransferProxyWrapper extends ContractWrapper { if (!_.isUndefined(this._tokenTransferProxyContractIfExists)) { return this._tokenTransferProxyContractIfExists; } - const web3ContractInstance = await this._instantiateContractIfExistsAsync( + const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( artifacts.TokenTransferProxyArtifact, this._contractAddressIfExists, ); - const contractInstance = new TokenTransferProxyContract( - web3ContractInstance, - this._web3Wrapper.getContractDefaults(), - ); + const contractInstance = new TokenTransferProxyContract(this._web3Wrapper, abi, address); this._tokenTransferProxyContractIfExists = contractInstance; return this._tokenTransferProxyContractIfExists; } diff --git a/packages/0x.js/src/contract_wrappers/token_wrapper.ts b/packages/0x.js/src/contract_wrappers/token_wrapper.ts index 11a7670c0..0f688cb71 100644 --- a/packages/0x.js/src/contract_wrappers/token_wrapper.ts +++ b/packages/0x.js/src/contract_wrappers/token_wrapper.ts @@ -51,7 +51,8 @@ export class TokenWrapper extends ContractWrapper { const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; - let balance = await tokenContract.balanceOf.callAsync(normalizedOwnerAddress, defaultBlock); + const txData = {}; + let balance = await tokenContract.balanceOf.callAsync(normalizedOwnerAddress, txData, defaultBlock); // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber balance = new BigNumber(balance); return balance; @@ -146,9 +147,11 @@ export class TokenWrapper extends ContractWrapper { const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; + const txData = {}; let allowanceInBaseUnits = await tokenContract.allowance.callAsync( normalizedOwnerAddress, normalizedSpenderAddress, + txData, defaultBlock, ); // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber @@ -419,11 +422,11 @@ export class TokenWrapper extends ContractWrapper { if (!_.isUndefined(tokenContract)) { return tokenContract; } - const web3ContractInstance = await this._instantiateContractIfExistsAsync( + const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( artifacts.TokenArtifact, normalizedTokenAddress, ); - const contractInstance = new TokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults()); + const contractInstance = new TokenContract(this._web3Wrapper, abi, address); tokenContract = contractInstance; this._tokenContractsByAddress[normalizedTokenAddress] = tokenContract; return tokenContract; diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts index 0a3037258..2f17e30c2 100644 --- a/packages/0x.js/src/types.ts +++ b/packages/0x.js/src/types.ts @@ -127,7 +127,7 @@ export interface SignedOrder extends Order { } // [address, name, symbol, decimals, ipfsHash, swarmHash] -export type TokenMetadata = [string, string, string, BigNumber, string, string]; +export type TokenMetadata = [string, string, string, number, string, string]; export interface Token { name: string; diff --git a/packages/0x.js/test/utils/fill_scenarios.ts b/packages/0x.js/test/utils/fill_scenarios.ts index 1a61487f4..8b1308298 100644 --- a/packages/0x.js/test/utils/fill_scenarios.ts +++ b/packages/0x.js/test/utils/fill_scenarios.ts @@ -35,12 +35,8 @@ export class FillScenarios { const web3Wrapper = (this._zeroEx as any)._web3Wrapper as Web3Wrapper; for (const token of this._tokens) { if (token.symbol !== 'ZRX' && token.symbol !== 'WETH') { - const contractInstance = web3Wrapper.getContractInstance( - artifacts.DummyTokenArtifact.abi, - token.address, - ); const defaults = {}; - const dummyToken = new DummyTokenContract(contractInstance, defaults); + const dummyToken = new DummyTokenContract(web3Wrapper, artifacts.DummyTokenArtifact.abi, token.address); const tokenSupply = ZeroEx.toBaseUnitAmount(INITIAL_COINBASE_TOKEN_SUPPLY_IN_UNITS, token.decimals); const txHash = await dummyToken.setBalance.sendTransactionAsync(this._coinbase, tokenSupply, { from: this._coinbase, diff --git a/packages/0x.js/tsconfig.json b/packages/0x.js/tsconfig.json index 117f51e83..a6b5c71c2 100644 --- a/packages/0x.js/tsconfig.json +++ b/packages/0x.js/tsconfig.json @@ -9,6 +9,7 @@ "./test/**/*", "../../node_modules/types-bn/index.d.ts", "../../node_modules/types-ethereumjs-util/index.d.ts", + "../../node_modules/ethers-typescript-typings/index.d.ts", "../../node_modules/web3-typescript-typings/index.d.ts", "../../node_modules/chai-typescript-typings/index.d.ts", "../../node_modules/chai-as-promised-typescript-typings/index.d.ts" diff --git a/packages/abi-gen/CHANGELOG.md b/packages/abi-gen/CHANGELOG.md index c86d6fb55..d9198d3ed 100644 --- a/packages/abi-gen/CHANGELOG.md +++ b/packages/abi-gen/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## v0.2.3 - _TBD, 2018_ + + * Add a `backend` parameter that allows you to specify the Ethereum library you use in your templates (`web3` or `ethers`). Ethers auto-converts small ints to numbers whereas Web3 doesn't. Defaults to `web3` (#413) + * Add support for [tuple types](https://solidity.readthedocs.io/en/develop/abi-spec.html#handling-tuple-types) (#413) + * Add `hasReturnValue` to context data (#413) + ## v0.2.1 - _February 9, 2018_ * Fix publishing issue where .npmignore was not properly excluding undesired content (#389) diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts index bc5a974a9..7c29f7d1d 100644 --- a/packages/abi-gen/src/index.ts +++ b/packages/abi-gen/src/index.ts @@ -11,13 +11,14 @@ import * as yargs from 'yargs'; import toSnakeCase = require('to-snake-case'); import * as Web3 from 'web3'; -import { ContextData, ParamKind } from './types'; +import { ContextData, ContractsBackend, ParamKind } from './types'; import { utils } from './utils'; const ABI_TYPE_CONSTRUCTOR = 'constructor'; const ABI_TYPE_METHOD = 'function'; const ABI_TYPE_EVENT = 'event'; const DEFAULT_NETWORK_ID = 50; +const DEFAULT_BACKEND = 'web3'; const args = yargs .option('abis', { @@ -43,6 +44,12 @@ const args = yargs demandOption: true, normalize: true, }) + .option('backend', { + describe: `The backing Ethereum library your app uses. Either 'web3' or 'ethers'. Ethers auto-converts small ints to numbers whereas Web3 doesn't.`, + type: 'string', + choices: [ContractsBackend.Web3, ContractsBackend.Ethers], + default: DEFAULT_BACKEND, + }) .option('network-id', { describe: 'ID of the network where contract ABIs are nested in artifacts', type: 'number', @@ -73,8 +80,8 @@ function writeOutputFile(name: string, renderedTsCode: string): void { utils.log(`Created: ${chalk.bold(filePath)}`); } -Handlebars.registerHelper('parameterType', utils.solTypeToTsType.bind(utils, ParamKind.Input)); -Handlebars.registerHelper('returnType', utils.solTypeToTsType.bind(utils, ParamKind.Output)); +Handlebars.registerHelper('parameterType', utils.solTypeToTsType.bind(utils, ParamKind.Input, args.backend)); +Handlebars.registerHelper('returnType', utils.solTypeToTsType.bind(utils, ParamKind.Output, args.backend)); if (args.partials) { registerPartials(args.partials); @@ -129,6 +136,7 @@ for (const abiFileName of abiFileNames) { const methodData = { ...methodAbi, singleReturnValue: methodAbi.outputs.length === 1, + hasReturnValue: methodAbi.outputs.length !== 0, }; return methodData; }); diff --git a/packages/abi-gen/src/types.ts b/packages/abi-gen/src/types.ts index e82ab824b..deddb1857 100644 --- a/packages/abi-gen/src/types.ts +++ b/packages/abi-gen/src/types.ts @@ -12,8 +12,14 @@ export enum AbiType { Fallback = 'fallback', } +export enum ContractsBackend { + Web3 = 'web3', + Ethers = 'ethers', +} + export interface Method extends Web3.MethodAbi { singleReturnValue: boolean; + hasReturnValue: boolean; } export interface ContextData { diff --git a/packages/abi-gen/src/utils.ts b/packages/abi-gen/src/utils.ts index 14255643a..3e4ff619a 100644 --- a/packages/abi-gen/src/utils.ts +++ b/packages/abi-gen/src/utils.ts @@ -3,17 +3,23 @@ import * as _ from 'lodash'; import * as path from 'path'; import * as Web3 from 'web3'; -import { AbiType, ParamKind } from './types'; +import { AbiType, ContractsBackend, ParamKind } from './types'; export const utils = { - solTypeToTsType(paramKind: ParamKind, solType: string): string { + solTypeToTsType( + paramKind: ParamKind, + backend: ContractsBackend, + solType: string, + components?: Web3.DataItem[], + ): string { const trailingArrayRegex = /\[\d*\]$/; if (solType.match(trailingArrayRegex)) { const arrayItemSolType = solType.replace(trailingArrayRegex, ''); - const arrayItemTsType = utils.solTypeToTsType(paramKind, arrayItemSolType); - const arrayTsType = utils.isUnionType(arrayItemTsType) - ? `Array<${arrayItemTsType}>` - : `${arrayItemTsType}[]`; + const arrayItemTsType = utils.solTypeToTsType(paramKind, backend, arrayItemSolType, components); + const arrayTsType = + utils.isUnionType(arrayItemTsType) || utils.isObjectType(arrayItemTsType) + ? `Array<${arrayItemTsType}>` + : `${arrayItemTsType}[]`; return arrayTsType; } else { const solTypeRegexToTsType = [ @@ -24,25 +30,49 @@ export const utils = { { regex: '^bytes\\d*$', tsType: 'string' }, ]; if (paramKind === ParamKind.Input) { - // web3 allows to pass those an non-bignumbers and that's nice - // but it always returns stuff as BigNumbers + // web3 and ethers allow to pass those as numbers instead of bignumbers solTypeRegexToTsType.unshift({ regex: '^u?int(8|16|32)?$', tsType: 'number|BigNumber', }); } + if (backend === ContractsBackend.Ethers && paramKind === ParamKind.Output) { + // ethers-contracts automatically converts small BigNumbers to numbers + solTypeRegexToTsType.unshift({ + regex: '^u?int(8|16|32|48)?$', + tsType: 'number', + }); + } for (const regexAndTxType of solTypeRegexToTsType) { const { regex, tsType } = regexAndTxType; if (solType.match(regex)) { return tsType; } } + const TUPLE_TYPE_REGEX = '^tuple$'; + if (solType.match(TUPLE_TYPE_REGEX)) { + const componentsType = _.map(components, component => { + const componentValueType = utils.solTypeToTsType( + paramKind, + backend, + component.type, + component.components, + ); + const componentType = `${component.name}: ${componentValueType}`; + return componentType; + }); + const tsType = `{${componentsType}}`; + return tsType; + } throw new Error(`Unknown Solidity type found: ${solType}`); } }, isUnionType(tsType: string): boolean { return tsType === 'number|BigNumber'; }, + isObjectType(tsType: string): boolean { + return /^{.*}$/.test(tsType); + }, log(...args: any[]): void { console.log(...args); // tslint:disable-line:no-console }, diff --git a/packages/0x.js/contract_templates/contract.handlebars b/packages/contract_templates/contract.handlebars index 33699b8a7..b5c2dd9f2 100644 --- a/packages/0x.js/contract_templates/contract.handlebars +++ b/packages/contract_templates/contract.handlebars @@ -1,15 +1,16 @@ /** * This file is auto-generated using abi-gen. Don't edit directly. - * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/0x.js/contract_templates. + * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/contract_templates. */ // tslint:disable:no-consecutive-blank-lines // tslint:disable-next-line:no-unused-variable import { TxData, TxDataPayable } from '@0xproject/types'; import { BigNumber, classUtils, promisify } from '@0xproject/utils'; +import { BaseContract, Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as ethersContracts from 'ethers-contracts'; +import * as _ from 'lodash'; import * as Web3 from 'web3'; -import {BaseContract} from './base_contract'; - {{#if events}} export type {{contractName}}ContractEventArgs = {{#each events}} @@ -28,6 +29,7 @@ export enum {{contractName}}Events { {{/each}} {{/if}} +// tslint:disable:no-parameter-reassignment export class {{contractName}}Contract extends BaseContract { {{#each methods}} {{#this.constant}} @@ -37,8 +39,8 @@ export class {{contractName}}Contract extends BaseContract { {{> tx contractName=../contractName}} {{/this.constant}} {{/each}} - constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial<TxData>) { - super(web3ContractInstance, defaults); - classUtils.bindAll(this, ['_web3ContractInstance', '_defaults']); + constructor(web3Wrapper: Web3Wrapper, abi: Web3.ContractAbi, address: string) { + super(web3Wrapper, abi, address); + classUtils.bindAll(this, ['_ethersInterface', 'address', 'abi', '_web3Wrapper']); } } // tslint:disable:max-file-line-count diff --git a/packages/contract_templates/partials/call.handlebars b/packages/contract_templates/partials/call.handlebars new file mode 100644 index 000000000..cfb9bea82 --- /dev/null +++ b/packages/contract_templates/partials/call.handlebars @@ -0,0 +1,3 @@ +public {{this.name}} = { + {{> callAsync}} +}; diff --git a/packages/contract_templates/partials/callAsync.handlebars b/packages/contract_templates/partials/callAsync.handlebars new file mode 100644 index 000000000..93d347145 --- /dev/null +++ b/packages/contract_templates/partials/callAsync.handlebars @@ -0,0 +1,30 @@ +{{#hasReturnValue}} +async callAsync( +{{> typed_params inputs=inputs}} +{{#this.payable}} + txData: TxDataPayable = {}, +{{/this.payable}} +{{^this.payable}} + txData: TxData = {}, +{{/this.payable}} + defaultBlock?: Web3.BlockParam, +): Promise<{{> return_type outputs=outputs}}> { + const self = this as {{contractName}}Contract; + const inputAbi = _.find(this.abi, {name: '{{this.name}}'}).inputs; + [{{> params inputs=inputs}}] = BaseContract._transformABIData(inputAbi, [{{> params inputs=inputs}}], BaseContract._bigNumberToString.bind(this)); + const encodedData = self._ethersInterface.functions.{{this.name}}( + {{> params inputs=inputs}} + ).data; + const callData = await self._applyDefaultsToTxDataAsync( + { + data: encodedData, + } + ) + const rawCallResult = await self._web3Wrapper.callAsync(callData, defaultBlock); + const outputAbi = _.find(this.abi, {name: '{{this.name}}'}).outputs as Web3.DataItem[]; + const outputParamsTypes = _.map(outputAbi, 'type'); + let resultArray = ethersContracts.Interface.decodeParams(outputParamsTypes, rawCallResult) as any; + resultArray = BaseContract._transformABIData(outputAbi, resultArray, BaseContract._lowercaseAddress.bind(this)); + return resultArray{{#singleReturnValue}}[0]{{/singleReturnValue}}; +}, +{{/hasReturnValue}} diff --git a/packages/0x.js/contract_templates/partials/event.handlebars b/packages/contract_templates/partials/event.handlebars index 6d68d4c0f..3c6100e4f 100644 --- a/packages/0x.js/contract_templates/partials/event.handlebars +++ b/packages/contract_templates/partials/event.handlebars @@ -1,5 +1,5 @@ export interface {{name}}ContractEventArgs { {{#each inputs}} - {{name}}: {{#returnType type}}{{/returnType}}; + {{name}}: {{#returnType type components}}{{/returnType}}; {{/each}} } diff --git a/packages/0x.js/contract_templates/partials/params.handlebars b/packages/contract_templates/partials/params.handlebars index ac5d4ae85..ac5d4ae85 100644 --- a/packages/0x.js/contract_templates/partials/params.handlebars +++ b/packages/contract_templates/partials/params.handlebars diff --git a/packages/contract_templates/partials/return_type.handlebars b/packages/contract_templates/partials/return_type.handlebars new file mode 100644 index 000000000..9dd509953 --- /dev/null +++ b/packages/contract_templates/partials/return_type.handlebars @@ -0,0 +1,10 @@ +{{#if outputs.length}} +{{#singleReturnValue}} +{{#returnType outputs.0.type components}}{{/returnType}} +{{/singleReturnValue}} +{{^singleReturnValue}} +[{{#each outputs}}{{#returnType type components}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}] +{{/singleReturnValue}} +{{else}} +void +{{/if}} diff --git a/packages/0x.js/contract_templates/partials/tx.handlebars b/packages/contract_templates/partials/tx.handlebars index 9df83266a..347a482d6 100644 --- a/packages/0x.js/contract_templates/partials/tx.handlebars +++ b/packages/contract_templates/partials/tx.handlebars @@ -9,19 +9,22 @@ public {{this.name}} = { {{/this.payable}} ): Promise<string> { const self = this as {{contractName}}Contract; + const inputAbi = _.find(this.abi, {name: '{{this.name}}'}).inputs; + [{{> params inputs=inputs}}] = BaseContract._transformABIData(inputAbi, [{{> params inputs=inputs}}], BaseContract._bigNumberToString.bind(this)); + const encodedData = this._ethersInterface.functions.{{this.name}}( + {{> params inputs=inputs}} + ).data const txDataWithDefaults = await self._applyDefaultsToTxDataAsync( - txData, + { + ...txData, + data: encodedData, + }, self.{{this.name}}.estimateGasAsync.bind( self, {{> params inputs=inputs}} ), ); - const txHash = await promisify<string>( - self._web3ContractInstance.{{this.name}}, self._web3ContractInstance, - )( - {{> params inputs=inputs}} - txDataWithDefaults, - ); + const txHash = await this._web3Wrapper.sendTransactionAsync(txDataWithDefaults); return txHash; }, async estimateGasAsync( @@ -29,15 +32,16 @@ public {{this.name}} = { txData: TxData = {}, ): Promise<number> { const self = this as {{contractName}}Contract; - const txDataWithDefaults = await self._applyDefaultsToTxDataAsync( - txData, - ); - const gas = await promisify<number>( - self._web3ContractInstance.{{this.name}}.estimateGas, self._web3ContractInstance, - )( + const encodedData = this._ethersInterface.functions.{{this.name}}( {{> params inputs=inputs}} - txDataWithDefaults, + ).data + const txDataWithDefaults = await self._applyDefaultsToTxDataAsync( + { + ...txData, + data: encodedData, + } ); + const gas = await this._web3Wrapper.estimateGasAsync(txDataWithDefaults); return gas; }, getABIEncodedTransactionData( @@ -45,7 +49,10 @@ public {{this.name}} = { txData: TxData = {}, ): string { const self = this as {{contractName}}Contract; - const abiEncodedTransactionData = self._web3ContractInstance.{{this.name}}.getData(); + const abiEncodedTransactionData = this._ethersInterface.functions.{{this.name}}( + {{> params inputs=inputs}} + ).data return abiEncodedTransactionData; }, + {{> callAsync}} }; diff --git a/packages/contract_templates/partials/typed_params.handlebars b/packages/contract_templates/partials/typed_params.handlebars new file mode 100644 index 000000000..c100e58f7 --- /dev/null +++ b/packages/contract_templates/partials/typed_params.handlebars @@ -0,0 +1,3 @@ +{{#each inputs}} + {{name}}: {{#parameterType type components}}{{/parameterType}}, +{{/each}} diff --git a/packages/contracts/contract_templates/contract.handlebars b/packages/contracts/contract_templates/contract.handlebars deleted file mode 100644 index afb9708e9..000000000 --- a/packages/contracts/contract_templates/contract.handlebars +++ /dev/null @@ -1,26 +0,0 @@ -/** - * This file is auto-generated using abi-gen. Don't edit directly. - * Templates can be found at https://github.com/0xProject/0x.js/tree/development/packages/abi-gen-templates. - */ -// tslint:disable:async-suffix member-ordering no-consecutive-blank-lines -// tslint:disable-next-line:no-unused-variable -import { TxData, TxDataPayable } from '@0xproject/types'; -import { BigNumber, classUtils, promisify } from '@0xproject/utils'; -import * as Web3 from 'web3'; - -import {BaseContract} from './base_contract'; - -export class {{contractName}}Contract extends BaseContract { -{{#each methods}} - {{#this.constant}} - {{> call contractName=../contractName}} - {{/this.constant}} - {{^this.constant}} - {{> tx contractName=../contractName}} - {{/this.constant}} -{{/each}} - constructor(web3ContractInstance: Web3.ContractInstance, defaults?: Partial<TxData>) { - super(web3ContractInstance, defaults); - classUtils.bindAll(this, ['_web3ContractInstance', '_defaults']); - } -} // tslint:disable:max-file-line-count diff --git a/packages/contracts/contract_templates/partials/call.handlebars b/packages/contracts/contract_templates/partials/call.handlebars deleted file mode 100644 index 82a45b40e..000000000 --- a/packages/contracts/contract_templates/partials/call.handlebars +++ /dev/null @@ -1,10 +0,0 @@ -public async {{this.name}}( -{{> typed_params inputs=inputs}} - defaultBlock?: Web3.BlockParam, -): Promise<{{> return_type outputs=outputs}}> { - const self = this as {{contractName}}Contract; - const result = await self._web3ContractInstance.{{this.name}}.call( - {{> params inputs=inputs}} - ); - return result; -} diff --git a/packages/contracts/contract_templates/partials/params.handlebars b/packages/contracts/contract_templates/partials/params.handlebars deleted file mode 100644 index ac5d4ae85..000000000 --- a/packages/contracts/contract_templates/partials/params.handlebars +++ /dev/null @@ -1,3 +0,0 @@ -{{#each inputs}} -{{name}}, -{{/each}} diff --git a/packages/contracts/contract_templates/partials/return_type.handlebars b/packages/contracts/contract_templates/partials/return_type.handlebars deleted file mode 100644 index 40a5dd8b0..000000000 --- a/packages/contracts/contract_templates/partials/return_type.handlebars +++ /dev/null @@ -1,10 +0,0 @@ -{{#if outputs.length}} -{{#singleReturnValue}} -{{#returnType outputs.0.type}}{{/returnType}} -{{/singleReturnValue}} -{{^singleReturnValue}} -[{{#each outputs}}{{#returnType type}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}] -{{/singleReturnValue}} -{{else}} -void -{{/if}} diff --git a/packages/contracts/contract_templates/partials/tx.handlebars b/packages/contracts/contract_templates/partials/tx.handlebars deleted file mode 100644 index 69ae982d6..000000000 --- a/packages/contracts/contract_templates/partials/tx.handlebars +++ /dev/null @@ -1,36 +0,0 @@ -public {{this.name}} = { - async sendTransactionAsync( - {{> typed_params inputs=inputs}} - {{#this.payable}} - txData: TxDataPayable = {}, - {{/this.payable}} - {{^this.payable}} - txData: TxData = {}, - {{/this.payable}} - ): Promise<string> { - const self = this as {{contractName}}Contract; - const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(txData); - const txHash = await self._web3ContractInstance.{{this.name}}( - {{> params inputs=inputs}} - txDataWithDefaults, - ); - return txHash; - }, - async callAsync( - {{> typed_params inputs=inputs}} - {{#this.payable}} - txData: TxDataPayable = {}, - {{/this.payable}} - {{^this.payable}} - txData: TxData = {}, - {{/this.payable}} - ): Promise<{{> return_type outputs=outputs}}> { - const self = this as {{contractName}}Contract; - const txDataWithDefaults = await self._applyDefaultsToTxDataAsync(txData); - const returnValue = await self._web3ContractInstance.{{this.name}}.call( - {{> params inputs=inputs}} - txDataWithDefaults, - ); - return returnValue; - }, -}; diff --git a/packages/contracts/contract_templates/partials/typed_params.handlebars b/packages/contracts/contract_templates/partials/typed_params.handlebars deleted file mode 100644 index 3ea4b2e95..000000000 --- a/packages/contracts/contract_templates/partials/typed_params.handlebars +++ /dev/null @@ -1,3 +0,0 @@ -{{#each inputs}} - {{name}}: {{#parameterType type}}{{/parameterType}}, -{{/each}} diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 0a3a6fc84..8159ba244 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -17,7 +17,7 @@ "compile:comment": "Yarn workspaces do not link binaries correctly so we need to reference them directly https://github.com/yarnpkg/yarn/issues/3846", "compile": "node ../deployer/lib/src/cli.js compile --contracts ${npm_package_config_contracts} --contracts-dir src/contracts --artifacts-dir src/artifacts", "clean": "shx rm -rf ./lib", - "generate_contract_wrappers": "node ../abi-gen/lib/index.js --abis 'src/artifacts/@(DummyToken|TokenTransferProxy|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken).json' --template contract_templates/contract.handlebars --partials 'contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated", + "generate_contract_wrappers": "node ../abi-gen/lib/index.js --abis 'src/artifacts/@(DummyToken|TokenTransferProxy|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers && prettier --write 'src/contract_wrappers/generated/**.ts'", "migrate": "node ../deployer/lib/src/cli.js migrate", "lint": "tslint --project . 'migrations/**/*.ts' 'test/**/*.ts' 'util/**/*.ts' 'deploy/**/*.ts'", "test:circleci": "yarn test" @@ -58,6 +58,7 @@ "types-bn": "^0.0.1", "types-ethereumjs-util": "0xProject/types-ethereumjs-util", "typescript": "2.7.1", + "ethers-typescript-typings": "^0.0.1", "web3-typescript-typings": "^0.9.11", "yargs": "^10.0.3" }, @@ -72,6 +73,7 @@ "bn.js": "^4.11.8", "ethereumjs-abi": "^0.6.4", "ethereumjs-util": "^5.1.1", + "ethers-contracts": "^2.2.1", "isomorphic-fetch": "^2.2.1", "lodash": "^4.17.4", "request": "^2.81.0", diff --git a/packages/contracts/src/contract_wrappers/generated/.gitignore b/packages/contracts/src/contract_wrappers/generated/.gitignore index b976a8737..72e8ffc0d 100644 --- a/packages/contracts/src/contract_wrappers/generated/.gitignore +++ b/packages/contracts/src/contract_wrappers/generated/.gitignore @@ -1,8 +1 @@ -dummy_token.ts -exchange.ts -multi_sig_wallet_with_time_lock_except_remove_authorized_address.ts -multi_sig_wallet_with_time_lock.ts -multi_sig_wallet.ts -token_registry.ts -token_transfer_proxy.ts -zrx_token.ts +* diff --git a/packages/contracts/src/contract_wrappers/generated/base_contract.ts b/packages/contracts/src/contract_wrappers/generated/base_contract.ts deleted file mode 100644 index 2d77b3ab1..000000000 --- a/packages/contracts/src/contract_wrappers/generated/base_contract.ts +++ /dev/null @@ -1,35 +0,0 @@ -import {TxData, TxDataPayable} from '@0xproject/types'; -import * as _ from 'lodash'; -import * as Web3 from 'web3'; - -export class BaseContract { - public address: string; - protected _web3ContractInstance: Web3.ContractInstance; - protected _defaults: Partial<TxData>; - protected async _applyDefaultsToTxDataAsync<T extends TxData|TxDataPayable>( - txData: T, - estimateGasAsync?: (txData: T) => Promise<number>, - ): Promise<TxData> { - // Gas amount sourced with the following priorities: - // 1. Optional param passed in to public method call - // 2. Global config passed in at library instantiation - // 3. Gas estimate calculation + safety margin - const removeUndefinedProperties = _.pickBy; - const txDataWithDefaults = { - ...removeUndefinedProperties(this._defaults), - ...removeUndefinedProperties(txData as any), - // HACK: TS can't prove that T is spreadable. - // Awaiting https://github.com/Microsoft/TypeScript/pull/13288 to be merged - }; - if (_.isUndefined(txDataWithDefaults.gas) && !_.isUndefined(estimateGasAsync)) { - const estimatedGas = await estimateGasAsync(txData); - txDataWithDefaults.gas = estimatedGas; - } - return txDataWithDefaults; - } - constructor(web3ContractInstance: Web3.ContractInstance, defaults?: Partial<TxData>) { - this.address = web3ContractInstance.address; - this._web3ContractInstance = web3ContractInstance; - this._defaults = defaults || {}; - } -} diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index 9cfc68418..303d745aa 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -1,12 +1,4 @@ -import { - LogCancelContractEventArgs, - LogErrorContractEventArgs, - LogFillContractEventArgs, - LogWithDecodedArgs, - SignedOrder, - TransactionReceiptWithDecodedLogs, - ZeroEx, -} from '0x.js'; +import { LogWithDecodedArgs, SignedOrder, TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js'; import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; @@ -15,7 +7,12 @@ import ethUtil = require('ethereumjs-util'); import * as Web3 from 'web3'; import { DummyTokenContract } from '../../src/contract_wrappers/generated/dummy_token'; -import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange'; +import { + ExchangeContract, + LogCancelContractEventArgs, + LogErrorContractEventArgs, + LogFillContractEventArgs, +} from '../../src/contract_wrappers/generated/exchange'; import { TokenTransferProxyContract } from '../../src/contract_wrappers/generated/token_transfer_proxy'; import { Balances } from '../../util/balances'; import { constants } from '../../util/constants'; @@ -63,16 +60,20 @@ describe('Exchange', () => { deployer.deployAsync(ContractName.DummyToken), deployer.deployAsync(ContractName.DummyToken), ]); - rep = new DummyTokenContract(repInstance); - dgd = new DummyTokenContract(dgdInstance); - zrx = new DummyTokenContract(zrxInstance); + rep = new DummyTokenContract(web3Wrapper, repInstance.abi, repInstance.address); + dgd = new DummyTokenContract(web3Wrapper, dgdInstance.abi, dgdInstance.address); + zrx = new DummyTokenContract(web3Wrapper, zrxInstance.abi, zrxInstance.address); const tokenTransferProxyInstance = await deployer.deployAsync(ContractName.TokenTransferProxy); - tokenTransferProxy = new TokenTransferProxyContract(tokenTransferProxyInstance); + tokenTransferProxy = new TokenTransferProxyContract( + web3Wrapper, + tokenTransferProxyInstance.abi, + tokenTransferProxyInstance.address, + ); const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [ zrx.address, tokenTransferProxy.address, ]); - exchange = new ExchangeContract(exchangeInstance); + exchange = new ExchangeContract(web3Wrapper, exchangeInstance.abi, exchangeInstance.address); await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: accounts[0] }); zeroEx = new ZeroEx(web3.currentProvider, { exchangeContractAddress: exchange.address, @@ -650,7 +651,7 @@ describe('Exchange', () => { it('should not change balances if makerTokenAddress is ZRX, makerTokenAmount + makerFee > maker allowance, \ and shouldThrowOnInsufficientBalanceOrAllowance = false', async () => { - const makerZRXAllowance = await zrx.allowance(maker, tokenTransferProxy.address); + const makerZRXAllowance = await zrx.allowance.callAsync(maker, tokenTransferProxy.address); signedOrder = await orderFactory.newSignedOrderAsync({ makerTokenAddress: zrx.address, makerTokenAmount: new BigNumber(makerZRXAllowance), @@ -676,7 +677,7 @@ describe('Exchange', () => { it('should not change balances if takerTokenAddress is ZRX, takerTokenAmount + takerFee > taker allowance, \ and shouldThrowOnInsufficientBalanceOrAllowance = false', async () => { - const takerZRXAllowance = await zrx.allowance(taker, tokenTransferProxy.address); + const takerZRXAllowance = await zrx.allowance.callAsync(taker, tokenTransferProxy.address); signedOrder = await orderFactory.newSignedOrderAsync({ takerTokenAddress: zrx.address, takerTokenAmount: new BigNumber(takerZRXAllowance), @@ -723,7 +724,7 @@ describe('Exchange', () => { const res = await exWrapper.fillOrderAsync(signedOrder, taker); expect(res.logs).to.have.length(1); const log = res.logs[0] as LogWithDecodedArgs<LogErrorContractEventArgs>; - const errCode = log.args.errorId.toNumber(); + const errCode = log.args.errorId; expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_EXPIRED); }); @@ -734,7 +735,7 @@ describe('Exchange', () => { const res = await exWrapper.fillOrderAsync(signedOrder, taker); expect(res.logs).to.have.length(1); const log = res.logs[0] as LogWithDecodedArgs<LogErrorContractEventArgs>; - const errCode = log.args.errorId.toNumber(); + const errCode = log.args.errorId; expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_FULLY_FILLED_OR_CANCELLED); }); }); @@ -862,7 +863,7 @@ describe('Exchange', () => { const res = await exWrapper.cancelOrderAsync(signedOrder, maker); expect(res.logs).to.have.length(1); const log = res.logs[0] as LogWithDecodedArgs<LogErrorContractEventArgs>; - const errCode = log.args.errorId.toNumber(); + const errCode = log.args.errorId; expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_FULLY_FILLED_OR_CANCELLED); }); @@ -874,7 +875,7 @@ describe('Exchange', () => { const res = await exWrapper.cancelOrderAsync(signedOrder, maker); expect(res.logs).to.have.length(1); const log = res.logs[0] as LogWithDecodedArgs<LogErrorContractEventArgs>; - const errCode = log.args.errorId.toNumber(); + const errCode = log.args.errorId; expect(errCode).to.be.equal(ExchangeContractErrs.ERROR_ORDER_EXPIRED); }); }); diff --git a/packages/contracts/test/exchange/helpers.ts b/packages/contracts/test/exchange/helpers.ts index 5fe00225e..9869c2155 100644 --- a/packages/contracts/test/exchange/helpers.ts +++ b/packages/contracts/test/exchange/helpers.ts @@ -5,7 +5,12 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; import ethUtil = require('ethereumjs-util'); -import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange'; +import { + ExchangeContract, + LogCancelContractEventArgs, + LogErrorContractEventArgs, + LogFillContractEventArgs, +} from '../../src/contract_wrappers/generated/exchange'; import { constants } from '../../util/constants'; import { ExchangeWrapper } from '../../util/exchange_wrapper'; import { OrderFactory } from '../../util/order_factory'; @@ -42,7 +47,7 @@ describe('Exchange', () => { zrx.address, tokenTransferProxy.address, ]); - const exchange = new ExchangeContract(exchangeInstance); + const exchange = new ExchangeContract(web3Wrapper, exchangeInstance.abi, exchangeInstance.address); await tokenTransferProxy.addAuthorizedAddress(exchange.address, { from: accounts[0] }); const zeroEx = new ZeroEx(web3.currentProvider, { networkId: constants.TESTRPC_NETWORK_ID }); exchangeWrapper = new ExchangeWrapper(exchange, zeroEx); @@ -50,8 +55,8 @@ describe('Exchange', () => { exchangeContractAddress: exchange.address, maker, feeRecipient, - makerToken: rep.address, - takerToken: dgd.address, + makerTokenAddress: rep.address, + takerTokenAddress: dgd.address, makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18), takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18), makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18), diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts index bf5a89222..4ea40cb59 100644 --- a/packages/contracts/test/exchange/wrapper.ts +++ b/packages/contracts/test/exchange/wrapper.ts @@ -7,7 +7,12 @@ import * as _ from 'lodash'; import * as Web3 from 'web3'; import { DummyTokenContract } from '../../src/contract_wrappers/generated/dummy_token'; -import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange'; +import { + ExchangeContract, + LogCancelContractEventArgs, + LogErrorContractEventArgs, + LogFillContractEventArgs, +} from '../../src/contract_wrappers/generated/exchange'; import { TokenRegistryContract } from '../../src/contract_wrappers/generated/token_registry'; import { TokenTransferProxyContract } from '../../src/contract_wrappers/generated/token_transfer_proxy'; import { Balances } from '../../util/balances'; @@ -55,18 +60,26 @@ describe('Exchange', () => { deployer.deployAsync(ContractName.DummyToken), deployer.deployAsync(ContractName.DummyToken), ]); - rep = new DummyTokenContract(repInstance); - dgd = new DummyTokenContract(dgdInstance); - zrx = new DummyTokenContract(zrxInstance); + rep = new DummyTokenContract(web3Wrapper, repInstance.abi, repInstance.address); + dgd = new DummyTokenContract(web3Wrapper, dgdInstance.abi, dgdInstance.address); + zrx = new DummyTokenContract(web3Wrapper, zrxInstance.abi, zrxInstance.address); const tokenRegistryInstance = await deployer.deployAsync(ContractName.TokenRegistry); - tokenRegistry = new TokenRegistryContract(tokenRegistryInstance); + tokenRegistry = new TokenRegistryContract( + web3Wrapper, + tokenRegistryInstance.abi, + tokenRegistryInstance.address, + ); const tokenTransferProxyInstance = await deployer.deployAsync(ContractName.TokenTransferProxy); - tokenTransferProxy = new TokenTransferProxyContract(tokenTransferProxyInstance); + tokenTransferProxy = new TokenTransferProxyContract( + web3Wrapper, + tokenTransferProxyInstance.abi, + tokenTransferProxyInstance.address, + ); const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [ zrx.address, tokenTransferProxy.address, ]); - exchange = new ExchangeContract(exchangeInstance); + exchange = new ExchangeContract(web3Wrapper, exchangeInstance.abi, exchangeInstance.address); await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: accounts[0] }); const zeroEx = new ZeroEx(web3.currentProvider, { networkId: constants.TESTRPC_NETWORK_ID }); exWrapper = new ExchangeWrapper(exchange, zeroEx); diff --git a/packages/contracts/test/multi_sig_with_time_lock.ts b/packages/contracts/test/multi_sig_with_time_lock.ts index 6812cb09f..a726814e4 100644 --- a/packages/contracts/test/multi_sig_with_time_lock.ts +++ b/packages/contracts/test/multi_sig_with_time_lock.ts @@ -59,10 +59,14 @@ describe('MultiSigWalletWithTimeLock', () => { SIGNATURES_REQUIRED, 0, ]); - multiSig = new MultiSigWalletWithTimeLockContract(multiSigInstance); + multiSig = new MultiSigWalletWithTimeLockContract( + web3Wrapper, + multiSigInstance.abi, + multiSigInstance.address, + ); multiSigWrapper = new MultiSigWrapper((multiSig as any) as MultiSigWalletContract); - const secondsTimeLocked = await multiSig.secondsTimeLocked(); + const secondsTimeLocked = await multiSig.secondsTimeLocked.callAsync(); initialSecondsTimeLocked = secondsTimeLocked.toNumber(); }); it('should throw when not called by wallet', async () => { @@ -113,7 +117,7 @@ describe('MultiSigWalletWithTimeLock', () => { const blockNum = await web3Wrapper.getBlockNumberAsync(); const blockInfo = await web3Wrapper.getBlockAsync(blockNum); const timestamp = new BigNumber(blockInfo.timestamp); - const confirmationTimeBigNum = new BigNumber(await multiSig.confirmationTimes(txId)); + const confirmationTimeBigNum = new BigNumber(await multiSig.confirmationTimes.callAsync(txId)); expect(timestamp).to.be.bignumber.equal(confirmationTimeBigNum); }); @@ -141,7 +145,7 @@ describe('MultiSigWalletWithTimeLock', () => { const res = await zeroEx.awaitTransactionMinedAsync(txHash); expect(res.logs).to.have.length(2); - const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked()); + const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.callAsync()); expect(secondsTimeLocked).to.be.bignumber.equal(SECONDS_TIME_LOCKED); }); }); @@ -152,10 +156,14 @@ describe('MultiSigWalletWithTimeLock', () => { SIGNATURES_REQUIRED, SECONDS_TIME_LOCKED, ]); - multiSig = new MultiSigWalletWithTimeLockContract(multiSigInstance); + multiSig = new MultiSigWalletWithTimeLockContract( + web3Wrapper, + multiSigInstance.abi, + multiSigInstance.address, + ); multiSigWrapper = new MultiSigWrapper((multiSig as any) as MultiSigWalletContract); - const secondsTimeLocked = await multiSig.secondsTimeLocked(); + const secondsTimeLocked = await multiSig.secondsTimeLocked.callAsync(); initialSecondsTimeLocked = secondsTimeLocked.toNumber(); const destination = multiSig.address; const from = owners[0]; @@ -187,7 +195,7 @@ describe('MultiSigWalletWithTimeLock', () => { await rpc.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber()); await multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }); - const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked()); + const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.callAsync()); expect(secondsTimeLocked).to.be.bignumber.equal(newSecondsTimeLocked); }); }); diff --git a/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts b/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts index 7e9d44730..c0299e1e1 100644 --- a/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts +++ b/packages/contracts/test/multi_sig_with_time_lock_except_remove_auth_addr.ts @@ -49,7 +49,11 @@ describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => { [authorizedAddress, unauthorizedAddress] = accounts; const initialOwner = accounts[0]; const tokenTransferProxyInstance = await deployer.deployAsync(ContractName.TokenTransferProxy); - tokenTransferProxy = new TokenTransferProxyContract(tokenTransferProxyInstance); + tokenTransferProxy = new TokenTransferProxyContract( + web3Wrapper, + tokenTransferProxyInstance.abi, + tokenTransferProxyInstance.address, + ); await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(authorizedAddress, { from: initialOwner, }); @@ -57,7 +61,11 @@ describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => { ContractName.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress, [owners, requiredApprovals, SECONDS_TIME_LOCKED, tokenTransferProxy.address], ); - multiSig = new MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddressContract(multiSigInstance); + multiSig = new MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddressContract( + web3Wrapper, + multiSigInstance.abi, + multiSigInstance.address, + ); await tokenTransferProxy.transferOwnership.sendTransactionAsync(multiSig.address, { from: initialOwner, }); @@ -74,12 +82,14 @@ describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => { describe('isFunctionRemoveAuthorizedAddress', () => { it('should throw if data is not for removeAuthorizedAddress', async () => { const data = MultiSigWrapper.encodeFnArgs('addAuthorizedAddress', PROXY_ABI, [owners[0]]); - return expect(multiSig.isFunctionRemoveAuthorizedAddress(data)).to.be.rejectedWith(constants.REVERT); + return expect(multiSig.isFunctionRemoveAuthorizedAddress.callAsync(data)).to.be.rejectedWith( + constants.REVERT, + ); }); it('should return true if data is for removeAuthorizedAddress', async () => { const data = MultiSigWrapper.encodeFnArgs('removeAuthorizedAddress', PROXY_ABI, [owners[0]]); - const isFunctionRemoveAuthorizedAddress = await multiSig.isFunctionRemoveAuthorizedAddress(data); + const isFunctionRemoveAuthorizedAddress = await multiSig.isFunctionRemoveAuthorizedAddress.callAsync(data); expect(isFunctionRemoveAuthorizedAddress).to.be.true(); }); }); @@ -114,7 +124,7 @@ describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => { const log = abiDecoder.tryToDecodeLogOrNoop(res.logs[0]) as LogWithDecodedArgs<SubmissionContractEventArgs>; const txId = log.args.transactionId; await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] }); - const isConfirmed = await multiSig.isConfirmed(txId); + const isConfirmed = await multiSig.isConfirmed.callAsync(txId); expect(isConfirmed).to.be.true(); return expect( @@ -133,7 +143,7 @@ describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => { const log = abiDecoder.tryToDecodeLogOrNoop(res.logs[0]) as LogWithDecodedArgs<SubmissionContractEventArgs>; const txId = log.args.transactionId; await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] }); - const isConfirmed = await multiSig.isConfirmed(txId); + const isConfirmed = await multiSig.isConfirmed.callAsync(txId); expect(isConfirmed).to.be.true(); return expect( @@ -152,10 +162,10 @@ describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => { const log = abiDecoder.tryToDecodeLogOrNoop(res.logs[0]) as LogWithDecodedArgs<SubmissionContractEventArgs>; const txId = log.args.transactionId; await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] }); - const isConfirmed = await multiSig.isConfirmed(txId); + const isConfirmed = await multiSig.isConfirmed.callAsync(txId); expect(isConfirmed).to.be.true(); await multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }); - const isAuthorized = await tokenTransferProxy.authorized(authorizedAddress); + const isAuthorized = await tokenTransferProxy.authorized.callAsync(authorizedAddress); expect(isAuthorized).to.be.false(); }); @@ -170,10 +180,10 @@ describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => { const log = abiDecoder.tryToDecodeLogOrNoop(res.logs[0]) as LogWithDecodedArgs<SubmissionContractEventArgs>; const txId = log.args.transactionId; await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] }); - const isConfirmed = await multiSig.isConfirmed(txId); + const isConfirmed = await multiSig.isConfirmed.callAsync(txId); expect(isConfirmed).to.be.true(); await multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }); - const tx = await multiSig.transactions(txId); + const tx = await multiSig.transactions.callAsync(txId); const isExecuted = tx[3]; expect(isExecuted).to.be.true(); return expect( diff --git a/packages/contracts/test/token_registry.ts b/packages/contracts/test/token_registry.ts index 867282d2c..eee14ad9f 100644 --- a/packages/contracts/test/token_registry.ts +++ b/packages/contracts/test/token_registry.ts @@ -31,7 +31,7 @@ describe('TokenRegistry', () => { owner = accounts[0]; notOwner = accounts[1]; const tokenRegInstance = await deployer.deployAsync(ContractName.TokenRegistry); - tokenReg = new TokenRegistryContract(tokenRegInstance); + tokenReg = new TokenRegistryContract(web3Wrapper, tokenRegInstance.abi, tokenRegInstance.address); tokenRegWrapper = new TokenRegWrapper(tokenReg); }); beforeEach(async () => { diff --git a/packages/contracts/test/token_transfer_proxy/auth.ts b/packages/contracts/test/token_transfer_proxy/auth.ts index 9d453b079..4f497dd0d 100644 --- a/packages/contracts/test/token_transfer_proxy/auth.ts +++ b/packages/contracts/test/token_transfer_proxy/auth.ts @@ -25,7 +25,11 @@ describe('TokenTransferProxy', () => { owner = address = accounts[0]; notOwner = accounts[1]; const tokenTransferProxyInstance = await deployer.deployAsync(ContractName.TokenTransferProxy); - tokenTransferProxy = new TokenTransferProxyContract(tokenTransferProxyInstance); + tokenTransferProxy = new TokenTransferProxyContract( + web3Wrapper, + tokenTransferProxyInstance.abi, + tokenTransferProxyInstance.address, + ); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -41,7 +45,7 @@ describe('TokenTransferProxy', () => { }); it('should allow owner to add an authorized address', async () => { await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }); - const isAuthorized = await tokenTransferProxy.authorized(address); + const isAuthorized = await tokenTransferProxy.authorized.callAsync(address); expect(isAuthorized).to.be.true(); }); it('should throw if owner attempts to authorize a duplicate address', async () => { @@ -67,7 +71,7 @@ describe('TokenTransferProxy', () => { await tokenTransferProxy.removeAuthorizedAddress.sendTransactionAsync(address, { from: owner, }); - const isAuthorized = await tokenTransferProxy.authorized(address); + const isAuthorized = await tokenTransferProxy.authorized.callAsync(address); expect(isAuthorized).to.be.false(); }); @@ -82,19 +86,19 @@ describe('TokenTransferProxy', () => { describe('getAuthorizedAddresses', () => { it('should return all authorized addresses', async () => { - const initial = await tokenTransferProxy.getAuthorizedAddresses(); + const initial = await tokenTransferProxy.getAuthorizedAddresses.callAsync(); expect(initial).to.have.length(0); await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(address, { from: owner, }); - const afterAdd = await tokenTransferProxy.getAuthorizedAddresses(); + const afterAdd = await tokenTransferProxy.getAuthorizedAddresses.callAsync(); expect(afterAdd).to.have.length(1); expect(afterAdd).to.include(address); await tokenTransferProxy.removeAuthorizedAddress.sendTransactionAsync(address, { from: owner, }); - const afterRemove = await tokenTransferProxy.getAuthorizedAddresses(); + const afterRemove = await tokenTransferProxy.getAuthorizedAddresses.callAsync(); expect(afterRemove).to.have.length(0); }); }); diff --git a/packages/contracts/test/token_transfer_proxy/transfer_from.ts b/packages/contracts/test/token_transfer_proxy/transfer_from.ts index c35a7276a..6b86a0e97 100644 --- a/packages/contracts/test/token_transfer_proxy/transfer_from.ts +++ b/packages/contracts/test/token_transfer_proxy/transfer_from.ts @@ -33,9 +33,13 @@ describe('TokenTransferProxy', () => { accounts = await web3Wrapper.getAvailableAddressesAsync(); owner = notAuthorized = accounts[0]; const tokenTransferProxyInstance = await deployer.deployAsync(ContractName.TokenTransferProxy); - tokenTransferProxy = new TokenTransferProxyContract(tokenTransferProxyInstance); + tokenTransferProxy = new TokenTransferProxyContract( + web3Wrapper, + tokenTransferProxyInstance.abi, + tokenTransferProxyInstance.address, + ); const repInstance = await deployer.deployAsync(ContractName.DummyToken); - rep = new DummyTokenContract(repInstance); + rep = new DummyTokenContract(web3Wrapper, repInstance.abi, repInstance.address); dmyBalances = new Balances([rep], [accounts[0], accounts[1]]); await Promise.all([ diff --git a/packages/contracts/test/unlimited_allowance_token.ts b/packages/contracts/test/unlimited_allowance_token.ts index f0a66e76b..03eb581ad 100644 --- a/packages/contracts/test/unlimited_allowance_token.ts +++ b/packages/contracts/test/unlimited_allowance_token.ts @@ -35,7 +35,7 @@ describe('UnlimitedAllowanceToken', () => { owner = accounts[0]; spender = accounts[1]; const tokenInstance = await deployer.deployAsync(ContractName.DummyToken); - token = new DummyTokenContract(tokenInstance); + token = new DummyTokenContract(web3Wrapper, tokenInstance.abi, tokenInstance.address); await token.mint.sendTransactionAsync(MAX_MINT_VALUE, { from: owner }); tokenAddress = token.address; }); diff --git a/packages/contracts/test/zrx_token.ts b/packages/contracts/test/zrx_token.ts index 1610ada12..4ccc66b36 100644 --- a/packages/contracts/test/zrx_token.ts +++ b/packages/contracts/test/zrx_token.ts @@ -36,7 +36,7 @@ describe('ZRXToken', () => { networkId: constants.TESTRPC_NETWORK_ID, }); const zrxInstance = await deployer.deployAsync(ContractName.ZRXToken); - zrx = new ZRXTokenContract(zrxInstance); + zrx = new ZRXTokenContract(web3Wrapper, zrxInstance.abi, zrxInstance.address); zrxAddress = zrx.address; MAX_UINT = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; }); @@ -48,25 +48,25 @@ describe('ZRXToken', () => { }); describe('constants', () => { it('should have 18 decimals', async () => { - const decimals = new BigNumber(await zrx.decimals()); + const decimals = new BigNumber(await zrx.decimals.callAsync()); const expectedDecimals = 18; expect(decimals).to.be.bignumber.equal(expectedDecimals); }); it('should have a total supply of 1 billion tokens', async () => { - const totalSupply = new BigNumber(await zrx.totalSupply()); + const totalSupply = new BigNumber(await zrx.totalSupply.callAsync()); const expectedTotalSupply = 1000000000; expect(ZeroEx.toUnitAmount(totalSupply, 18)).to.be.bignumber.equal(expectedTotalSupply); }); it('should be named 0x Protocol Token', async () => { - const name = await zrx.name(); + const name = await zrx.name.callAsync(); const expectedName = '0x Protocol Token'; expect(name).to.be.equal(expectedName); }); it('should have the symbol ZRX', async () => { - const symbol = await zrx.symbol(); + const symbol = await zrx.symbol.callAsync(); const expectedSymbol = 'ZRX'; expect(symbol).to.be.equal(expectedSymbol); }); @@ -75,7 +75,7 @@ describe('ZRXToken', () => { describe('constructor', () => { it('should initialize owner balance to totalSupply', async () => { const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); - const totalSupply = new BigNumber(await zrx.totalSupply()); + const totalSupply = new BigNumber(await zrx.totalSupply.callAsync()); expect(totalSupply).to.be.bignumber.equal(ownerBalance); }); }); diff --git a/packages/contracts/tsconfig.json b/packages/contracts/tsconfig.json index b618ca4e7..490531eeb 100644 --- a/packages/contracts/tsconfig.json +++ b/packages/contracts/tsconfig.json @@ -11,6 +11,7 @@ "../../node_modules/types-ethereumjs-util/index.d.ts", "../../node_modules/chai-typescript-typings/index.d.ts", "../../node_modules/web3-typescript-typings/index.d.ts", + "../../node_modules/ethers-typescript-typings/index.d.ts", "../../node_modules/chai-as-promised-typescript-typings/index.d.ts", "../../node_modules/types-ethereumjs-util/index.d.ts", "../../node_modules/types-bn/index.d.ts", diff --git a/packages/contracts/util/balances.ts b/packages/contracts/util/balances.ts index 0abc305d8..d03d4b3c5 100644 --- a/packages/contracts/util/balances.ts +++ b/packages/contracts/util/balances.ts @@ -17,7 +17,7 @@ export class Balances { const balancesByOwner: BalancesByOwner = {}; for (const tokenContractInstance of this._tokenContractInstances) { for (const ownerAddress of this._ownerAddresses) { - let balance = await tokenContractInstance.balanceOf(ownerAddress); + let balance = await tokenContractInstance.balanceOf.callAsync(ownerAddress); balance = new BigNumber(balance); if (_.isUndefined(balancesByOwner[ownerAddress])) { balancesByOwner[ownerAddress] = {}; diff --git a/packages/contracts/util/exchange_wrapper.ts b/packages/contracts/util/exchange_wrapper.ts index 03d04629d..f016067fe 100644 --- a/packages/contracts/util/exchange_wrapper.ts +++ b/packages/contracts/util/exchange_wrapper.ts @@ -186,11 +186,11 @@ export class ExchangeWrapper { public async getOrderHashAsync(signedOrder: SignedOrder): Promise<string> { const shouldThrowOnInsufficientBalanceOrAllowance = false; const params = signedOrderUtils.getOrderAddressesAndValues(signedOrder); - const orderHash = await this._exchange.getOrderHash(params.orderAddresses, params.orderValues); + const orderHash = await this._exchange.getOrderHash.callAsync(params.orderAddresses, params.orderValues); return orderHash; } public async isValidSignatureAsync(signedOrder: SignedOrder): Promise<boolean> { - const isValidSignature = await this._exchange.isValidSignature( + const isValidSignature = await this._exchange.isValidSignature.callAsync( signedOrder.maker, ZeroEx.getOrderHashHex(signedOrder), signedOrder.ecSignature.v, @@ -204,7 +204,7 @@ export class ExchangeWrapper { denominator: BigNumber, target: BigNumber, ): Promise<boolean> { - const isRoundingError = await this._exchange.isRoundingError(numerator, denominator, target); + const isRoundingError = await this._exchange.isRoundingError.callAsync(numerator, denominator, target); return isRoundingError; } public async getPartialAmountAsync( @@ -212,7 +212,9 @@ export class ExchangeWrapper { denominator: BigNumber, target: BigNumber, ): Promise<BigNumber> { - const partialAmount = new BigNumber(await this._exchange.getPartialAmount(numerator, denominator, target)); + const partialAmount = new BigNumber( + await this._exchange.getPartialAmount.callAsync(numerator, denominator, target), + ); return partialAmount; } } diff --git a/packages/contracts/util/token_registry_wrapper.ts b/packages/contracts/util/token_registry_wrapper.ts index d0af17103..d78c8a64e 100644 --- a/packages/contracts/util/token_registry_wrapper.ts +++ b/packages/contracts/util/token_registry_wrapper.ts @@ -22,36 +22,36 @@ export class TokenRegWrapper { return tx; } public async getTokenMetaDataAsync(tokenAddress: string) { - const data = await this._tokenReg.getTokenMetaData(tokenAddress); + const data = await this._tokenReg.getTokenMetaData.callAsync(tokenAddress); const token: Token = { address: data[0], name: data[1], symbol: data[2], - decimals: data[3].toNumber(), + decimals: data[3], ipfsHash: data[4], swarmHash: data[5], }; return token; } public async getTokenByNameAsync(tokenName: string) { - const data = await this._tokenReg.getTokenByName(tokenName); + const data = await this._tokenReg.getTokenByName.callAsync(tokenName); const token: Token = { address: data[0], name: data[1], symbol: data[2], - decimals: data[3].toNumber(), + decimals: data[3], ipfsHash: data[4], swarmHash: data[5], }; return token; } public async getTokenBySymbolAsync(tokenSymbol: string) { - const data = await this._tokenReg.getTokenBySymbol(tokenSymbol); + const data = await this._tokenReg.getTokenBySymbol.callAsync(tokenSymbol); const token: Token = { address: data[0], name: data[1], symbol: data[2], - decimals: data[3].toNumber(), + decimals: data[3], ipfsHash: data[4], swarmHash: data[5], }; diff --git a/packages/contracts/util/types.ts b/packages/contracts/util/types.ts index 65bc26f79..d6e4c587d 100644 --- a/packages/contracts/util/types.ts +++ b/packages/contracts/util/types.ts @@ -41,8 +41,8 @@ export interface DefaultOrderParams { exchangeContractAddress: string; maker: string; feeRecipient: string; - makerToken: string; - takerToken: string; + makerTokenAddress: string; + takerTokenAddress: string; makerTokenAmount: BigNumber; takerTokenAmount: BigNumber; makerFee: BigNumber; diff --git a/packages/deployer/package.json b/packages/deployer/package.json index f969e4eda..2a6668fa6 100644 --- a/packages/deployer/package.json +++ b/packages/deployer/package.json @@ -36,6 +36,7 @@ "tslint": "5.8.0", "types-bn": "^0.0.1", "typescript": "2.7.1", + "ethers-typescript-typings": "^0.0.1", "web3-typescript-typings": "^0.9.11" }, "dependencies": { diff --git a/packages/deployer/src/cli.ts b/packages/deployer/src/cli.ts index ba156ac20..c976e8f97 100644 --- a/packages/deployer/src/cli.ts +++ b/packages/deployer/src/cli.ts @@ -10,8 +10,8 @@ import { constants } from './utils/constants'; import { CliOptions, CompilerOptions, DeployerOptions } from './utils/types'; const DEFAULT_OPTIMIZER_ENABLED = false; -const DEFAULT_CONTRACTS_DIR = path.resolve('src'); -const DEFAULT_ARTIFACTS_DIR = path.resolve('artifacts'); +const DEFAULT_CONTRACTS_DIR = path.resolve('src/contracts'); +const DEFAULT_ARTIFACTS_DIR = path.resolve('src/artifacts'); const DEFAULT_NETWORK_ID = 50; const DEFAULT_JSONRPC_PORT = 8545; const DEFAULT_GAS_PRICE = (10 ** 9 * 2).toString(); @@ -100,6 +100,9 @@ async function onDeployCommand(argv: CliOptions): Promise<void> { */ function getContractsSetFromList(contracts: string): Set<string> { const specifiedContracts = new Set(); + if (contracts === '*') { + return new Set(['*']); + } const contractsArray = contracts.split(','); _.forEach(contractsArray, contractName => { const fileName = `${contractName}${constants.SOLIDITY_FILE_EXTENSION}`; diff --git a/packages/deployer/src/deployer.ts b/packages/deployer/src/deployer.ts index 021645fd1..6710bcc85 100644 --- a/packages/deployer/src/deployer.ts +++ b/packages/deployer/src/deployer.ts @@ -174,7 +174,7 @@ export class Deployer { const block = await this.web3Wrapper.getBlockAsync('latest'); let gas: number; try { - const gasEstimate: number = await this.web3Wrapper.estimateGasAsync(data); + const gasEstimate: number = await this.web3Wrapper.estimateGasAsync({ data }); gas = Math.min(gasEstimate + EXTRA_GAS, block.gasLimit); } catch (err) { gas = block.gasLimit; diff --git a/packages/deployer/tsconfig.json b/packages/deployer/tsconfig.json index 4e1edb510..897446b66 100644 --- a/packages/deployer/tsconfig.json +++ b/packages/deployer/tsconfig.json @@ -11,6 +11,7 @@ "../../node_modules/types-bn/index.d.ts", "../../node_modules/types-ethereumjs-util/index.d.ts", "../../node_modules/chai-typescript-typings/index.d.ts", + "../../node_modules/ethers-typescript-typings/index.d.ts", "../../node_modules/web3-typescript-typings/index.d.ts" ] } diff --git a/packages/dev-utils/tsconfig.json b/packages/dev-utils/tsconfig.json index ace978fea..1ed3fbc9c 100644 --- a/packages/dev-utils/tsconfig.json +++ b/packages/dev-utils/tsconfig.json @@ -8,6 +8,7 @@ "./test/**/*", "../../node_modules/types-bn/index.d.ts", "../../node_modules/chai-typescript-typings/index.d.ts", + "../../node_modules/ethers-typescript-typings/index.d.ts", "../../node_modules/web3-typescript-typings/index.d.ts", "../../node_modules/types-ethereumjs-util/index.d.ts" ] diff --git a/packages/ethers-typescript-typings/.npmignore b/packages/ethers-typescript-typings/.npmignore new file mode 100644 index 000000000..104d718ed --- /dev/null +++ b/packages/ethers-typescript-typings/.npmignore @@ -0,0 +1,3 @@ +.* +yarn-error.log +/scripts/ diff --git a/packages/ethers-typescript-typings/CHANGELOG.md b/packages/ethers-typescript-typings/CHANGELOG.md new file mode 100644 index 000000000..d67d5c73d --- /dev/null +++ b/packages/ethers-typescript-typings/CHANGELOG.md @@ -0,0 +1,5 @@ +# CHANGELOG + +## v0.0.1 - _TBD, 2018_ + + * Initial types (#413) diff --git a/packages/ethers-typescript-typings/README.md b/packages/ethers-typescript-typings/README.md new file mode 100644 index 000000000..56ce5f138 --- /dev/null +++ b/packages/ethers-typescript-typings/README.md @@ -0,0 +1,49 @@ +## ethers-typescript-typings + +There currently isn't an official [Ethers][ethers] +type definition included in the [DefinitelyTyped][definitelytyped] project. +Until that happens, we will continue to improve our own type definition. +If it get's close to comprehensive, we'll add it to [DefinitelyTyped][definitelytyped]. + +[ethers]: https://github.com/ethers-io/ethers.js +[definitelytyped]: https://github.com/DefinitelyTyped/DefinitelyTyped + +## Installation + +```bash +yarn add -D ethers-typescript-typings +``` + +## Usage + +Add the following line within an `include` section of your `tsconfig.json` + +```json +"./node_modules/ethers-typescript-typings/index.d.ts" +``` + +## Contributing + +We strongly encourage that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository. + +Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started. + +### Install Dependencies + +If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them: + +```bash +yarn config set workspaces-experimental true +``` + +Then install dependencies + +```bash +yarn install +``` + +### Lint + +```bash +yarn lint +``` diff --git a/packages/ethers-typescript-typings/index.d.ts b/packages/ethers-typescript-typings/index.d.ts new file mode 100644 index 000000000..e5d38819e --- /dev/null +++ b/packages/ethers-typescript-typings/index.d.ts @@ -0,0 +1,28 @@ +declare module 'ethers-contracts' { + export interface TransactionDescription { + name: string; + signature: string; + sighash: string; + data: string; + } + export interface CallDescription extends TransactionDescription { + parse: (...args: any[]) => any; + } + export interface FunctionDescription { + (...params: any[]): TransactionDescription | CallDescription; + inputs: { names: string[]; types: string[] }; + outputs: { names: string[]; types: string[] }; + } + export interface EventDescription { + parse: (...args: any[]) => any; + inputs: { names: string[]; types: string[] }; + signature: string; + topic: string; + } + export class Interface { + public functions: { [functionName: string]: FunctionDescription }; + public events: { [eventName: string]: EventDescription }; + public static decodeParams(types: string[], data: string): any[]; + constructor(abi: any); + } +} diff --git a/packages/ethers-typescript-typings/package.json b/packages/ethers-typescript-typings/package.json new file mode 100644 index 000000000..0e6f517c8 --- /dev/null +++ b/packages/ethers-typescript-typings/package.json @@ -0,0 +1,28 @@ +{ + "name": "ethers-typescript-typings", + "version": "0.0.1", + "description": "Typescript type definitions for ethers.js", + "main": "index.d.ts", + "types": "index.d.ts", + "scripts": { + "lint": "tslint index.d.ts" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/0xProject/0x.js.git" + }, + "author": "Fabio Berger", + "contributors": [ + "Leonid Logvinov <logvinov.leon@gmail.com>" + ], + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/0xProject/0x.js/issues" + }, + "homepage": "https://github.com/0xProject/0x.js/packages/ethers-typescript-typings#readme", + "devDependencies": { + "tslint": "5.8.0", + "tslint-config-0xproject": "^0.0.2", + "typescript": "2.7.1" + } +} diff --git a/packages/ethers-typescript-typings/scripts/postpublish.js b/packages/ethers-typescript-typings/scripts/postpublish.js new file mode 100644 index 000000000..b3e5407c8 --- /dev/null +++ b/packages/ethers-typescript-typings/scripts/postpublish.js @@ -0,0 +1,5 @@ +const postpublish_utils = require('../../../scripts/postpublish_utils'); +const packageJSON = require('../package.json'); + +const subPackageName = packageJSON.name; +postpublish_utils.standardPostPublishAsync(subPackageName);
\ No newline at end of file diff --git a/packages/ethers-typescript-typings/tslint.json b/packages/ethers-typescript-typings/tslint.json new file mode 100644 index 000000000..9a93a1f74 --- /dev/null +++ b/packages/ethers-typescript-typings/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": ["tslint-config-0xproject"] +} diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index b1cb721d2..a4f293d60 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## v0.2.4 - _TBD, 2018_ + + * Add `data` to `TxData` (#413) + * Add `number` as an option to `ContractEventArg` (#413) + ## v0.2.1 - _February 9, 2018_ * Fix publishing issue where .npmignore was not properly excluding undesired content (#389) diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index cb17936f7..6242d4268 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -2,6 +2,7 @@ import { BigNumber } from 'bignumber.js'; import * as Web3 from 'web3'; export interface TxData { + data?: string; from?: string; gas?: number; gasPrice?: BigNumber; @@ -38,7 +39,7 @@ export enum AbiType { Fallback = 'fallback', } -export type ContractEventArg = string | BigNumber; +export type ContractEventArg = string | BigNumber | number; export interface DecodedLogArgs { [argName: string]: ContractEventArg; diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index 19ee80e4f..81792bee8 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## v0.4.0 - _TBD, 2018_ + + * Use `ethers-contracts` as a backend to decode event args (#413) + ## v0.3.2 - _February 9, 2018_ * Fix publishing issue where .npmignore was not properly excluding undesired content (#389) diff --git a/packages/utils/package.json b/packages/utils/package.json index 14ccabcf4..4f63aa4ce 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -26,11 +26,13 @@ "shx": "^0.2.2", "tslint": "5.8.0", "typescript": "2.7.1", + "ethers-typescript-typings": "^0.0.1", "web3-typescript-typings": "^0.9.11" }, "dependencies": { "@0xproject/types": "^0.2.3", "bignumber.js": "~4.1.0", + "ethers-contracts": "^2.2.1", "js-sha3": "^0.7.0", "lodash": "^4.17.4", "web3": "^0.20.0" diff --git a/packages/utils/src/abi_decoder.ts b/packages/utils/src/abi_decoder.ts index 368973b1b..2b496eb17 100644 --- a/packages/utils/src/abi_decoder.ts +++ b/packages/utils/src/abi_decoder.ts @@ -1,7 +1,7 @@ import { AbiType, DecodedLogArgs, LogWithDecodedArgs, RawLog, SolidityTypes } from '@0xproject/types'; +import * as ethersContracts from 'ethers-contracts'; import * as _ from 'lodash'; import * as Web3 from 'web3'; -import * as SolidityCoder from 'web3/lib/solidity/coder'; import { BigNumber } from './configured_bignumber'; @@ -27,31 +27,29 @@ export class AbiDecoder { if (_.isUndefined(event)) { return log; } + const ethersInterface = new ethersContracts.Interface([event]); const logData = log.data; const decodedParams: DecodedLogArgs = {}; - let dataIndex = 0; let topicsIndex = 1; const nonIndexedInputs = _.filter(event.inputs, input => !input.indexed); const dataTypes = _.map(nonIndexedInputs, input => input.type); - const decodedData = SolidityCoder.decodeParams(dataTypes, logData.slice('0x'.length)); + const decodedData = ethersInterface.events[event.name].parse(log.data); let failedToDecode = false; - _.forEach(event.inputs, (param: Web3.EventParameter) => { + _.forEach(event.inputs, (param: Web3.EventParameter, i: number) => { // Indexed parameters are stored in topics. Non-indexed ones in decodedData - let value: BigNumber | string = param.indexed ? log.topics[topicsIndex++] : decodedData[dataIndex++]; + let value: BigNumber | string | number = param.indexed ? log.topics[topicsIndex++] : decodedData[i]; if (_.isUndefined(value)) { failedToDecode = true; return; } if (param.type === SolidityTypes.Address) { value = AbiDecoder._padZeros(new BigNumber(value).toString(16)); - } else if ( - param.type === SolidityTypes.Uint256 || - param.type === SolidityTypes.Uint8 || - param.type === SolidityTypes.Uint - ) { + } else if (param.type === SolidityTypes.Uint256 || param.type === SolidityTypes.Uint) { value = new BigNumber(value); + } else if (param.type === SolidityTypes.Uint8) { + value = new BigNumber(value).toNumber(); } decodedParams[param.name] = value; }); @@ -67,11 +65,14 @@ export class AbiDecoder { } } private _addABI(abiArray: Web3.AbiDefinition[]): void { + if (_.isUndefined(abiArray)) { + return; + } + const ethersInterface = new ethersContracts.Interface(abiArray); _.map(abiArray, (abi: Web3.AbiDefinition) => { if (abi.type === AbiType.Event) { - const signature = `${abi.name}(${_.map(abi.inputs, input => input.type).join(',')})`; - const signatureHash = new Web3().sha3(signature); - this._methodIds[signatureHash] = abi; + const topic = ethersInterface.events[abi.name].topic; + this._methodIds[topic] = abi; } }); this._savedABIs = this._savedABIs.concat(abiArray); diff --git a/packages/utils/src/globals.d.ts b/packages/utils/src/globals.d.ts deleted file mode 100644 index ade9e59db..000000000 --- a/packages/utils/src/globals.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare module 'web3/lib/solidity/coder' { - const decodeParams: (types: string[], data: string) => any[]; -} diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json index 3d967d05f..8114d99cd 100644 --- a/packages/utils/tsconfig.json +++ b/packages/utils/tsconfig.json @@ -3,5 +3,9 @@ "compilerOptions": { "outDir": "lib" }, - "include": ["./src/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"] + "include": [ + "./src/**/*", + "../../node_modules/web3-typescript-typings/index.d.ts", + "../../node_modules/ethers-typescript-typings/index.d.ts" + ] } diff --git a/packages/web3-wrapper/CHANGELOG.md b/packages/web3-wrapper/CHANGELOG.md index 387aafb3a..eef665d24 100644 --- a/packages/web3-wrapper/CHANGELOG.md +++ b/packages/web3-wrapper/CHANGELOG.md @@ -1,9 +1,12 @@ # CHANGELOG -## v0.2.XX - _TBD_ 2018 +## v0.2.0 _TBD, 2018_ - * Ensure all returned user addresses are lowercase (#373) + * Ensure all returned user addresses are lowercase (#373) + * Add `web3Wrapper.callAsync` (#413) + * Make `web3Wrapper.estimateGas` accept whole `txData` instead of `data` (#413) + * Remove `web3Wrapper.getContractInstance` (#413) -## v0.1.12 - _February 9, 2018_ +## v0.1.12 _February 9, 2018_ * Fix publishing issue where .npmignore was not properly excluding undesired content (#389) diff --git a/packages/web3-wrapper/package.json b/packages/web3-wrapper/package.json index 8f87bb118..e827842b0 100644 --- a/packages/web3-wrapper/package.json +++ b/packages/web3-wrapper/package.json @@ -26,11 +26,13 @@ "shx": "^0.2.2", "tslint": "5.8.0", "typescript": "2.7.1", + "ethers-typescript-typings": "^0.0.1", "web3-typescript-typings": "^0.9.11" }, "dependencies": { "@0xproject/types": "^0.2.3", "@0xproject/utils": "^0.3.4", + "ethers-contracts": "^2.2.1", "lodash": "^4.17.4", "web3": "^0.20.0" } diff --git a/packages/web3-wrapper/src/base_contract.ts b/packages/web3-wrapper/src/base_contract.ts new file mode 100644 index 000000000..997ce97d3 --- /dev/null +++ b/packages/web3-wrapper/src/base_contract.ts @@ -0,0 +1,69 @@ +import { TxData, TxDataPayable } from '@0xproject/types'; +import * as ethersContracts from 'ethers-contracts'; +import * as _ from 'lodash'; +import * as Web3 from 'web3'; + +import { Web3Wrapper } from './web3_wrapper'; + +export class BaseContract { + protected _ethersInterface: ethersContracts.Interface; + protected _web3Wrapper: Web3Wrapper; + public abi: Web3.ContractAbi; + public address: string; + protected static _transformABIData( + abis: Web3.DataItem[], + values: any[], + transformation: (type: string, value: any) => any, + ): any { + return _.map(values, (value: any, i: number) => + BaseContract._transformTypedData(abis[i].type, value, transformation), + ); + } + protected static _lowercaseAddress(type: string, value: string): string { + return type === 'address' ? value.toLowerCase() : value; + } + protected static _bigNumberToString(type: string, value: string): string { + return _.isObject(value) && (value as any).isBigNumber ? value.toString() : value; + } + private static _transformTypedData( + type: string, + values: any, + transformation: (type: string, value: any) => any, + ): any { + const trailingArrayRegex = /\[\d*\]$/; + if (type.match(trailingArrayRegex)) { + const arrayItemType = type.replace(trailingArrayRegex, ''); + return _.map(values, value => this._transformTypedData(arrayItemType, value, transformation)); + } else { + return transformation(type, values); + } + } + protected async _applyDefaultsToTxDataAsync<T extends Partial<TxData | TxDataPayable>>( + txData: T, + estimateGasAsync?: (txData: T) => Promise<number>, + ): Promise<TxData> { + // Gas amount sourced with the following priorities: + // 1. Optional param passed in to public method call + // 2. Global config passed in at library instantiation + // 3. Gas estimate calculation + safety margin + const removeUndefinedProperties = _.pickBy; + const txDataWithDefaults = { + to: this.address, + ...removeUndefinedProperties(this._web3Wrapper.getContractDefaults()), + ...removeUndefinedProperties(txData as any), + // HACK: TS can't prove that T is spreadable. + // Awaiting https://github.com/Microsoft/TypeScript/pull/13288 to be merged + }; + if (_.isUndefined(txDataWithDefaults.gas) && !_.isUndefined(estimateGasAsync)) { + const estimatedGas = await estimateGasAsync(txData); + txDataWithDefaults.gas = estimatedGas; + } + return txDataWithDefaults; + } + constructor(web3Wrapper: Web3Wrapper, abi: Web3.ContractAbi, address: string) { + this._web3Wrapper = web3Wrapper; + this.abi = abi; + this.address = address; + this._ethersInterface = new ethersContracts.Interface(abi); + } +} diff --git a/packages/web3-wrapper/src/index.ts b/packages/web3-wrapper/src/index.ts index 7f3452c98..56dfa227c 100644 --- a/packages/web3-wrapper/src/index.ts +++ b/packages/web3-wrapper/src/index.ts @@ -1,179 +1,2 @@ -import { TransactionReceipt, TxData } from '@0xproject/types'; -import { BigNumber, promisify } from '@0xproject/utils'; -import * as _ from 'lodash'; -import * as Web3 from 'web3'; - -interface RawLogEntry { - logIndex: string | null; - transactionIndex: string | null; - transactionHash: string; - blockHash: string | null; - blockNumber: string | null; - address: string; - data: string; - topics: string[]; -} - -export class Web3Wrapper { - private _web3: Web3; - private _defaults: Partial<TxData>; - private _jsonRpcRequestId: number; - constructor(provider: Web3.Provider, defaults?: Partial<TxData>) { - if (_.isUndefined((provider as any).sendAsync)) { - // Web3@1.0 provider doesn't support synchronous http requests, - // so it only has an async `send` method, instead of a `send` and `sendAsync` in web3@0.x.x` - // We re-assign the send method so that Web3@1.0 providers work with 0x.js - (provider as any).sendAsync = (provider as any).send; - } - this._web3 = new Web3(); - this._web3.setProvider(provider); - this._defaults = defaults || {}; - this._jsonRpcRequestId = 0; - } - public getContractDefaults(): Partial<TxData> { - return this._defaults; - } - public setProvider(provider: Web3.Provider) { - this._web3.setProvider(provider); - } - public isAddress(address: string): boolean { - return this._web3.isAddress(address); - } - public async isSenderAddressAvailableAsync(senderAddress: string): Promise<boolean> { - const addresses = await this.getAvailableAddressesAsync(); - const normalizedAddress = senderAddress.toLowerCase(); - return _.includes(addresses, normalizedAddress); - } - public async getNodeVersionAsync(): Promise<string> { - const nodeVersion = await promisify<string>(this._web3.version.getNode)(); - return nodeVersion; - } - public async getNetworkIdAsync(): Promise<number> { - const networkIdStr = await promisify<string>(this._web3.version.getNetwork)(); - const networkId = _.parseInt(networkIdStr); - return networkId; - } - public async getTransactionReceiptAsync(txHash: string): Promise<TransactionReceipt> { - const transactionReceipt = await promisify<TransactionReceipt>(this._web3.eth.getTransactionReceipt)(txHash); - if (!_.isNull(transactionReceipt)) { - transactionReceipt.status = this._normalizeTxReceiptStatus(transactionReceipt.status); - } - return transactionReceipt; - } - public getCurrentProvider(): Web3.Provider { - return this._web3.currentProvider; - } - public toWei(ethAmount: BigNumber): BigNumber { - const balanceWei = this._web3.toWei(ethAmount, 'ether'); - return balanceWei; - } - public async getBalanceInWeiAsync(owner: string): Promise<BigNumber> { - let balanceInWei = await promisify<BigNumber>(this._web3.eth.getBalance)(owner); - // Rewrap in a new BigNumber - balanceInWei = new BigNumber(balanceInWei); - return balanceInWei; - } - public async doesContractExistAtAddressAsync(address: string): Promise<boolean> { - const code = await promisify<string>(this._web3.eth.getCode)(address); - // Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients - const codeIsEmpty = /^0x0{0,40}$/i.test(code); - return !codeIsEmpty; - } - public async signTransactionAsync(address: string, message: string): Promise<string> { - const signData = await promisify<string>(this._web3.eth.sign)(address, message); - return signData; - } - public async getBlockNumberAsync(): Promise<number> { - const blockNumber = await promisify<number>(this._web3.eth.getBlockNumber)(); - return blockNumber; - } - public async getBlockAsync(blockParam: string | Web3.BlockParam): Promise<Web3.BlockWithoutTransactionData> { - const block = await promisify<Web3.BlockWithoutTransactionData>(this._web3.eth.getBlock)(blockParam); - return block; - } - public async getBlockTimestampAsync(blockParam: string | Web3.BlockParam): Promise<number> { - const { timestamp } = await this.getBlockAsync(blockParam); - return timestamp; - } - public async getAvailableAddressesAsync(): Promise<string[]> { - const addresses = await promisify<string[]>(this._web3.eth.getAccounts)(); - const normalizedAddresses = _.map(addresses, address => address.toLowerCase()); - return normalizedAddresses; - } - public async getLogsAsync(filter: Web3.FilterObject): Promise<Web3.LogEntry[]> { - let fromBlock = filter.fromBlock; - if (_.isNumber(fromBlock)) { - fromBlock = this._web3.toHex(fromBlock); - } - let toBlock = filter.toBlock; - if (_.isNumber(toBlock)) { - toBlock = this._web3.toHex(toBlock); - } - const serializedFilter = { - ...filter, - fromBlock, - toBlock, - }; - const payload = { - jsonrpc: '2.0', - id: this._jsonRpcRequestId++, - method: 'eth_getLogs', - params: [serializedFilter], - }; - const rawLogs = await this._sendRawPayloadAsync<RawLogEntry[]>(payload); - const formattedLogs = _.map(rawLogs, this._formatLog.bind(this)); - return formattedLogs; - } - public getContractFromAbi(abi: Web3.ContractAbi): Web3.Contract<any> { - const web3Contract = this._web3.eth.contract(abi); - return web3Contract; - } - public getContractInstance(abi: Web3.ContractAbi, address: string): Web3.ContractInstance { - const web3ContractInstance = this.getContractFromAbi(abi).at(address); - return web3ContractInstance; - } - public async estimateGasAsync(data: string): Promise<number> { - const gas = await promisify<number>(this._web3.eth.estimateGas)({ data }); - return gas; - } - public async sendTransactionAsync(txData: Web3.TxData): Promise<string> { - const txHash = await promisify<string>(this._web3.eth.sendTransaction)(txData); - return txHash; - } - private async _sendRawPayloadAsync<A>(payload: Web3.JSONRPCRequestPayload): Promise<A> { - const sendAsync = this._web3.currentProvider.sendAsync.bind(this._web3.currentProvider); - const response = await promisify<Web3.JSONRPCResponsePayload>(sendAsync)(payload); - const result = response.result; - return result; - } - private _normalizeTxReceiptStatus(status: undefined | null | string | 0 | 1): null | 0 | 1 { - // Transaction status might have four values - // undefined - Testrpc and other old clients - // null - New clients on old transactions - // number - Parity - // hex - Geth - if (_.isString(status)) { - return this._web3.toDecimal(status) as 0 | 1; - } else if (_.isUndefined(status)) { - return null; - } else { - return status; - } - } - private _formatLog(rawLog: RawLogEntry): Web3.LogEntry { - const formattedLog = { - ...rawLog, - logIndex: this._hexToDecimal(rawLog.logIndex), - blockNumber: this._hexToDecimal(rawLog.blockNumber), - transactionIndex: this._hexToDecimal(rawLog.transactionIndex), - }; - return formattedLog; - } - private _hexToDecimal(hex: string | null): number | null { - if (_.isNull(hex)) { - return null; - } - const decimal = this._web3.toDecimal(hex); - return decimal; - } -} +export { Web3Wrapper } from './web3_wrapper'; +export { BaseContract } from './base_contract'; diff --git a/packages/web3-wrapper/src/web3_wrapper.ts b/packages/web3-wrapper/src/web3_wrapper.ts new file mode 100644 index 000000000..a07805344 --- /dev/null +++ b/packages/web3-wrapper/src/web3_wrapper.ts @@ -0,0 +1,179 @@ +import { TransactionReceipt, TxData } from '@0xproject/types'; +import { BigNumber, promisify } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as Web3 from 'web3'; + +interface RawLogEntry { + logIndex: string | null; + transactionIndex: string | null; + transactionHash: string; + blockHash: string | null; + blockNumber: string | null; + address: string; + data: string; + topics: string[]; +} + +export class Web3Wrapper { + private _web3: Web3; + private _defaults: Partial<TxData>; + private _jsonRpcRequestId: number; + constructor(provider: Web3.Provider, defaults?: Partial<TxData>) { + if (_.isUndefined((provider as any).sendAsync)) { + // Web3@1.0 provider doesn't support synchronous http requests, + // so it only has an async `send` method, instead of a `send` and `sendAsync` in web3@0.x.x` + // We re-assign the send method so that Web3@1.0 providers work with 0x.js + (provider as any).sendAsync = (provider as any).send; + } + this._web3 = new Web3(); + this._web3.setProvider(provider); + this._defaults = defaults || {}; + this._jsonRpcRequestId = 0; + } + public getContractDefaults(): Partial<TxData> { + return this._defaults; + } + public setProvider(provider: Web3.Provider) { + this._web3.setProvider(provider); + } + public isAddress(address: string): boolean { + return this._web3.isAddress(address); + } + public async isSenderAddressAvailableAsync(senderAddress: string): Promise<boolean> { + const addresses = await this.getAvailableAddressesAsync(); + const normalizedAddress = senderAddress.toLowerCase(); + return _.includes(addresses, normalizedAddress); + } + public async getNodeVersionAsync(): Promise<string> { + const nodeVersion = await promisify<string>(this._web3.version.getNode)(); + return nodeVersion; + } + public async getNetworkIdAsync(): Promise<number> { + const networkIdStr = await promisify<string>(this._web3.version.getNetwork)(); + const networkId = _.parseInt(networkIdStr); + return networkId; + } + public async getTransactionReceiptAsync(txHash: string): Promise<TransactionReceipt> { + const transactionReceipt = await promisify<TransactionReceipt>(this._web3.eth.getTransactionReceipt)(txHash); + if (!_.isNull(transactionReceipt)) { + transactionReceipt.status = this._normalizeTxReceiptStatus(transactionReceipt.status); + } + return transactionReceipt; + } + public getCurrentProvider(): Web3.Provider { + return this._web3.currentProvider; + } + public toWei(ethAmount: BigNumber): BigNumber { + const balanceWei = this._web3.toWei(ethAmount, 'ether'); + return balanceWei; + } + public async getBalanceInWeiAsync(owner: string): Promise<BigNumber> { + let balanceInWei = await promisify<BigNumber>(this._web3.eth.getBalance)(owner); + // Rewrap in a new BigNumber + balanceInWei = new BigNumber(balanceInWei); + return balanceInWei; + } + public async doesContractExistAtAddressAsync(address: string): Promise<boolean> { + const code = await promisify<string>(this._web3.eth.getCode)(address); + // Regex matches 0x0, 0x00, 0x in order to accommodate poorly implemented clients + const codeIsEmpty = /^0x0{0,40}$/i.test(code); + return !codeIsEmpty; + } + public async signTransactionAsync(address: string, message: string): Promise<string> { + const signData = await promisify<string>(this._web3.eth.sign)(address, message); + return signData; + } + public async getBlockNumberAsync(): Promise<number> { + const blockNumber = await promisify<number>(this._web3.eth.getBlockNumber)(); + return blockNumber; + } + public async getBlockAsync(blockParam: string | Web3.BlockParam): Promise<Web3.BlockWithoutTransactionData> { + const block = await promisify<Web3.BlockWithoutTransactionData>(this._web3.eth.getBlock)(blockParam); + return block; + } + public async getBlockTimestampAsync(blockParam: string | Web3.BlockParam): Promise<number> { + const { timestamp } = await this.getBlockAsync(blockParam); + return timestamp; + } + public async getAvailableAddressesAsync(): Promise<string[]> { + const addresses = await promisify<string[]>(this._web3.eth.getAccounts)(); + const normalizedAddresses = _.map(addresses, address => address.toLowerCase()); + return normalizedAddresses; + } + public async getLogsAsync(filter: Web3.FilterObject): Promise<Web3.LogEntry[]> { + let fromBlock = filter.fromBlock; + if (_.isNumber(fromBlock)) { + fromBlock = this._web3.toHex(fromBlock); + } + let toBlock = filter.toBlock; + if (_.isNumber(toBlock)) { + toBlock = this._web3.toHex(toBlock); + } + const serializedFilter = { + ...filter, + fromBlock, + toBlock, + }; + const payload = { + jsonrpc: '2.0', + id: this._jsonRpcRequestId++, + method: 'eth_getLogs', + params: [serializedFilter], + }; + const rawLogs = await this._sendRawPayloadAsync<RawLogEntry[]>(payload); + const formattedLogs = _.map(rawLogs, this._formatLog.bind(this)); + return formattedLogs; + } + public getContractFromAbi(abi: Web3.ContractAbi): Web3.Contract<any> { + const web3Contract = this._web3.eth.contract(abi); + return web3Contract; + } + public async estimateGasAsync(txData: Partial<Web3.TxData>): Promise<number> { + const gas = await promisify<number>(this._web3.eth.estimateGas)(txData); + return gas; + } + public async callAsync(callData: Web3.CallData, defaultBlock?: Web3.BlockParam): Promise<string> { + const rawCalllResult = await promisify<string>(this._web3.eth.call)(callData, defaultBlock); + return rawCalllResult; + } + public async sendTransactionAsync(txData: Web3.TxData): Promise<string> { + const txHash = await promisify<string>(this._web3.eth.sendTransaction)(txData); + return txHash; + } + private async _sendRawPayloadAsync<A>(payload: Web3.JSONRPCRequestPayload): Promise<A> { + const sendAsync = this._web3.currentProvider.sendAsync.bind(this._web3.currentProvider); + const response = await promisify<Web3.JSONRPCResponsePayload>(sendAsync)(payload); + const result = response.result; + return result; + } + private _normalizeTxReceiptStatus(status: undefined | null | string | 0 | 1): null | 0 | 1 { + // Transaction status might have four values + // undefined - Testrpc and other old clients + // null - New clients on old transactions + // number - Parity + // hex - Geth + if (_.isString(status)) { + return this._web3.toDecimal(status) as 0 | 1; + } else if (_.isUndefined(status)) { + return null; + } else { + return status; + } + } + private _formatLog(rawLog: RawLogEntry): Web3.LogEntry { + const formattedLog = { + ...rawLog, + logIndex: this._hexToDecimal(rawLog.logIndex), + blockNumber: this._hexToDecimal(rawLog.blockNumber), + transactionIndex: this._hexToDecimal(rawLog.transactionIndex), + }; + return formattedLog; + } + private _hexToDecimal(hex: string | null): number | null { + if (_.isNull(hex)) { + return null; + } + const decimal = this._web3.toDecimal(hex); + return decimal; + } +} diff --git a/packages/web3-wrapper/tsconfig.json b/packages/web3-wrapper/tsconfig.json index 3d967d05f..7bae7f9f0 100644 --- a/packages/web3-wrapper/tsconfig.json +++ b/packages/web3-wrapper/tsconfig.json @@ -3,5 +3,9 @@ "compilerOptions": { "outDir": "lib" }, - "include": ["./src/**/*", "../../node_modules/web3-typescript-typings/index.d.ts"] + "include": [ + "./src/**/*", + "../../node_modules/ethers-typescript-typings/index.d.ts", + "../../node_modules/web3-typescript-typings/index.d.ts" + ] } @@ -3227,6 +3227,21 @@ ethereumjs-wallet@^0.6.0: utf8 "^2.1.1" uuid "^2.0.1" +ethers-contracts@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ethers-contracts/-/ethers-contracts-2.2.1.tgz#e2bf5dd5e157313ba454b50c646c8472fcd0a8b3" + dependencies: + ethers-utils "^2.1.0" + +ethers-utils@^2.1.0: + version "2.1.11" + resolved "https://registry.yarnpkg.com/ethers-utils/-/ethers-utils-2.1.11.tgz#b27535ca3226118be300211c39c896b1e5e21641" + dependencies: + bn.js "^4.4.0" + hash.js "^1.0.0" + js-sha3 "0.5.7" + xmlhttprequest "1.8.0" + ethjs-abi@0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/ethjs-abi/-/ethjs-abi-0.1.8.tgz#cd288583ed628cdfadaf8adefa3ba1dbcbca6c18" @@ -4970,6 +4985,10 @@ js-sha3@0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.5.tgz#baf0c0e8c54ad5903447df96ade7a4a1bca79a4a" +js-sha3@0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + js-sha3@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.3.1.tgz#86122802142f0828502a0d1dee1d95e253bb0243" @@ -9811,7 +9830,7 @@ xml-js@^1.3.2: dependencies: sax "^1.2.4" -xmlhttprequest@*: +xmlhttprequest@*, xmlhttprequest@1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" |