Merge pull request #413 from 0xProject/feature/ethers-contracts
abi-gen V2 abi and ethers-contracts
This commit is contained in:
commit
7aa070f9ea
@ -1,5 +1,4 @@
|
||||
lib
|
||||
generated
|
||||
.nyc_output
|
||||
/packages/contracts/src/artifacts
|
||||
package.json
|
||||
|
@ -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_
|
||||
|
@ -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;
|
||||
},
|
||||
};
|
@ -1,6 +0,0 @@
|
||||
{{#singleReturnValue}}
|
||||
{{#returnType outputs.0.type}}{{/returnType}}
|
||||
{{/singleReturnValue}}
|
||||
{{^singleReturnValue}}
|
||||
[{{#each outputs}}{{#returnType type}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}]
|
||||
{{/singleReturnValue}}
|
@ -1,3 +0,0 @@
|
||||
{{#each inputs}}
|
||||
{{name}}: {{#parameterType type}}{{/parameterType}},
|
||||
{{/each}}
|
@ -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",
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1,6 +1 @@
|
||||
dummy_token.ts
|
||||
ether_token.ts
|
||||
exchange.ts
|
||||
token_registry.ts
|
||||
token_transfer_proxy.ts
|
||||
token.ts
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
},
|
||||
|
@ -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
|
3
packages/contract_templates/partials/call.handlebars
Normal file
3
packages/contract_templates/partials/call.handlebars
Normal file
@ -0,0 +1,3 @@
|
||||
public {{this.name}} = {
|
||||
{{> callAsync}}
|
||||
};
|
30
packages/contract_templates/partials/callAsync.handlebars
Normal file
30
packages/contract_templates/partials/callAsync.handlebars
Normal 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}}
|
@ -1,5 +1,5 @@
|
||||
export interface {{name}}ContractEventArgs {
|
||||
{{#each inputs}}
|
||||
{{name}}: {{#returnType type}}{{/returnType}};
|
||||
{{name}}: {{#returnType type components}}{{/returnType}};
|
||||
{{/each}}
|
||||
}
|
10
packages/contract_templates/partials/return_type.handlebars
Normal file
10
packages/contract_templates/partials/return_type.handlebars
Normal 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}}
|
@ -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}}
|
||||
};
|
@ -0,0 +1,3 @@
|
||||
{{#each inputs}}
|
||||
{{name}}: {{#parameterType type components}}{{/parameterType}},
|
||||
{{/each}}
|
@ -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
|
@ -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;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
{{#each inputs}}
|
||||
{{name}},
|
||||
{{/each}}
|
@ -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}}
|
@ -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;
|
||||
},
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
{{#each inputs}}
|
||||
{{name}}: {{#parameterType type}}{{/parameterType}},
|
||||
{{/each}}
|
@ -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",
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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 || {};
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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(
|
||||
|
@ -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 () => {
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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([
|
||||
|
@ -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;
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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",
|
||||
|
@ -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] = {};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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],
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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": {
|
||||
|
@ -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}`;
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
@ -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"
|
||||
]
|
||||
|
3
packages/ethers-typescript-typings/.npmignore
Normal file
3
packages/ethers-typescript-typings/.npmignore
Normal file
@ -0,0 +1,3 @@
|
||||
.*
|
||||
yarn-error.log
|
||||
/scripts/
|
5
packages/ethers-typescript-typings/CHANGELOG.md
Normal file
5
packages/ethers-typescript-typings/CHANGELOG.md
Normal file
@ -0,0 +1,5 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v0.0.1 - _TBD, 2018_
|
||||
|
||||
* Initial types (#413)
|
49
packages/ethers-typescript-typings/README.md
Normal file
49
packages/ethers-typescript-typings/README.md
Normal 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
|
||||
```
|
28
packages/ethers-typescript-typings/index.d.ts
vendored
Normal file
28
packages/ethers-typescript-typings/index.d.ts
vendored
Normal 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);
|
||||
}
|
||||
}
|
28
packages/ethers-typescript-typings/package.json
Normal file
28
packages/ethers-typescript-typings/package.json
Normal 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"
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
const postpublish_utils = require('../../../scripts/postpublish_utils');
|
||||
const packageJSON = require('../package.json');
|
||||
|
||||
const subPackageName = packageJSON.name;
|
||||
postpublish_utils.standardPostPublishAsync(subPackageName);
|
3
packages/ethers-typescript-typings/tslint.json
Normal file
3
packages/ethers-typescript-typings/tslint.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": ["tslint-config-0xproject"]
|
||||
}
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
3
packages/utils/src/globals.d.ts
vendored
3
packages/utils/src/globals.d.ts
vendored
@ -1,3 +0,0 @@
|
||||
declare module 'web3/lib/solidity/coder' {
|
||||
const decodeParams: (types: string[], data: string) => any[];
|
||||
}
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
}
|
||||
|
69
packages/web3-wrapper/src/base_contract.ts
Normal file
69
packages/web3-wrapper/src/base_contract.ts
Normal 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);
|
||||
}
|
||||
}
|
@ -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';
|
||||
|
179
packages/web3-wrapper/src/web3_wrapper.ts
Normal file
179
packages/web3-wrapper/src/web3_wrapper.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
21
yarn.lock
21
yarn.lock
@ -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"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user