Merge pull request #413 from 0xProject/feature/ethers-contracts

abi-gen V2 abi and ethers-contracts
This commit is contained in:
Leonid Logvinov 2018-02-27 14:41:59 -08:00 committed by GitHub
commit 7aa070f9ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
79 changed files with 749 additions and 545 deletions

View File

@ -1,5 +1,4 @@
lib
generated
.nyc_output
/packages/contracts/src/artifacts
package.json

View File

@ -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_

View File

@ -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;
},
};

View File

@ -1,6 +0,0 @@
{{#singleReturnValue}}
{{#returnType outputs.0.type}}{{/returnType}}
{{/singleReturnValue}}
{{^singleReturnValue}}
[{{#each outputs}}{{#returnType type}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}]
{{/singleReturnValue}}

View File

@ -1,3 +0,0 @@
{{#each inputs}}
{{name}}: {{#parameterType type}}{{/parameterType}},
{{/each}}

View File

@ -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",

View File

@ -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)) {

View File

@ -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;

View File

@ -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;
}

View File

@ -1,6 +1 @@
dummy_token.ts
ether_token.ts
exchange.ts
token_registry.ts
token_transfer_proxy.ts
token.ts
*

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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"

View File

@ -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)

View File

@ -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;
});

View File

@ -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 {

View File

@ -3,15 +3,21 @@ 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)
const arrayItemTsType = utils.solTypeToTsType(paramKind, backend, arrayItemSolType, components);
const arrayTsType =
utils.isUnionType(arrayItemTsType) || utils.isObjectType(arrayItemTsType)
? `Array<${arrayItemTsType}>`
: `${arrayItemTsType}[]`;
return arrayTsType;
@ -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
},

View File

@ -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

View File

@ -0,0 +1,3 @@
public {{this.name}} = {
{{> callAsync}}
};

View File

@ -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}}

View File

@ -1,5 +1,5 @@
export interface {{name}}ContractEventArgs {
{{#each inputs}}
{{name}}: {{#returnType type}}{{/returnType}};
{{name}}: {{#returnType type components}}{{/returnType}};
{{/each}}
}

View File

@ -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}}

View File

@ -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}}
};

View File

@ -0,0 +1,3 @@
{{#each inputs}}
{{name}}: {{#parameterType type components}}{{/parameterType}},
{{/each}}

View File

@ -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

View File

@ -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;
}

View File

@ -1,3 +0,0 @@
{{#each inputs}}
{{name}},
{{/each}}

View File

@ -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}}

View File

@ -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;
},
};

View File

@ -1,3 +0,0 @@
{{#each inputs}}
{{name}}: {{#parameterType type}}{{/parameterType}},
{{/each}}

View File

@ -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",

View File

@ -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
*

View File

@ -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 || {};
}
}

View File

@ -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);
});
});

View File

@ -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),

View File

@ -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);

View File

@ -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);
});
});

View File

@ -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(

View File

@ -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 () => {

View File

@ -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);
});
});

View File

@ -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([

View File

@ -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;
});

View File

@ -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);
});
});

View File

@ -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",

View File

@ -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] = {};

View File

@ -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;
}
}

View File

@ -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],
};

View File

@ -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;

View File

@ -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": {

View File

@ -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}`;

View File

@ -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;

View File

@ -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"
]
}

View File

@ -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"
]

View File

@ -0,0 +1,3 @@
.*
yarn-error.log
/scripts/

View File

@ -0,0 +1,5 @@
# CHANGELOG
## v0.0.1 - _TBD, 2018_
* Initial types (#413)

View File

@ -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
```

View File

@ -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);
}
}

View File

@ -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"
}
}

View File

@ -0,0 +1,5 @@
const postpublish_utils = require('../../../scripts/postpublish_utils');
const packageJSON = require('../package.json');
const subPackageName = packageJSON.name;
postpublish_utils.standardPostPublishAsync(subPackageName);

View File

@ -0,0 +1,3 @@
{
"extends": ["tslint-config-0xproject"]
}

View File

@ -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)

View File

@ -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;

View File

@ -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)

View File

@ -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"

View File

@ -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);

View File

@ -1,3 +0,0 @@
declare module 'web3/lib/solidity/coder' {
const decodeParams: (types: string[], data: string) => any[];
}

View File

@ -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"
]
}

View File

@ -1,9 +1,12 @@
# CHANGELOG
## v0.2.XX - _TBD_ 2018
## v0.2.0 _TBD, 2018_
* 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)

View File

@ -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"
}

View File

@ -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);
}
}

View File

@ -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';

View File

@ -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;
}
}

View File

@ -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"
]
}

View File

@ -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"