Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Remove truffle contracts dependency #151

Merged
merged 31 commits into from
Sep 6, 2017
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
59fed02
Remove truffle-contract from deps
LogvinovLeon Sep 4, 2017
9818eb2
Use custom contract abstraction
LogvinovLeon Sep 4, 2017
1c2d4cb
Fix tests descriptions
LogvinovLeon Sep 4, 2017
1ad395c
Make the functions immidiately return txHash instead of awaiting for …
LogvinovLeon Sep 4, 2017
2577d8f
Use Web3.ContractInstance type
LogvinovLeon Sep 4, 2017
6325a03
Temporarily remove web3_beta, cause it breaks installation and tests
LogvinovLeon Sep 4, 2017
c9e490b
Implement zeroEx.awaitTransactionMined
LogvinovLeon Sep 4, 2017
2b547f9
Change non-exhange contracts to also return txHash
LogvinovLeon Sep 5, 2017
5d57a2f
Increase timeout
LogvinovLeon Sep 5, 2017
96d2a55
Add TransationReceipt as a public exported type
LogvinovLeon Sep 5, 2017
876032a
Update CHANGELOG
LogvinovLeon Sep 5, 2017
a12df1c
Fix gasPrice regression
LogvinovLeon Sep 5, 2017
f6a945d
Fix the comment at awaitTransactionMinedAsync
LogvinovLeon Sep 5, 2017
b5c6c91
Increase the default polling interval to 1000
LogvinovLeon Sep 5, 2017
e05dfab
Add explaining comment
LogvinovLeon Sep 5, 2017
9f12ef6
Rename x.call -> x.callAsync x() -> x.sendTransactionAsync() x.estima…
LogvinovLeon Sep 5, 2017
78c46d7
Change the order of default overriding
LogvinovLeon Sep 5, 2017
92b101f
Remove unused code
LogvinovLeon Sep 5, 2017
bc5fd31
Cast to Artifat type
LogvinovLeon Sep 5, 2017
ec22097
Don't override function arguments
LogvinovLeon Sep 5, 2017
ee00769
Use schema validation to distinguish txData argument
LogvinovLeon Sep 5, 2017
dff63f9
Use AbiType
LogvinovLeon Sep 5, 2017
b054781
Define AbiType
LogvinovLeon Sep 5, 2017
f057267
Update json-schemas
LogvinovLeon Sep 5, 2017
a7b2131
Decode logs args in awaitTransactionMinedAsync
LogvinovLeon Sep 5, 2017
2f97ddb
Fix the return types and export the required public types
LogvinovLeon Sep 5, 2017
2fd5f27
Add forgotten artifacts file
LogvinovLeon Sep 5, 2017
e6e12e9
Update CHANGELOG
LogvinovLeon Sep 5, 2017
df904f8
Add test for logs decoding in awaitTransactionMinedAsync
LogvinovLeon Sep 5, 2017
8ebc724
Add lifecycle methods
LogvinovLeon Sep 5, 2017
258b4fa
Fix a typo in test name
LogvinovLeon Sep 6, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGELOG

v0.13.0 - _TBD, 2017_
* Made all the functions submitting transactions to the network to immediately return transaction hash (#151)
* Added `zeroEx.awaitTransactionMinedAsync` (#151)
* Added `TransactionReceiptWithDecodedLogs`, `LogWithDecodedArgs`, `DecodedLogArgs` to public types (#151)

v0.12.1 - _September 2, 2017_
* Added the support for web3@1.x.x provider (#142)
* Added the optional `zeroExConfig` parameter to the constructor of `ZeroEx` (#139)
Expand Down
9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"pretest:umd": "run-s clean build:umd:dev build:commonjs",
"substitute_umd_bundle": "npm run remove_src_files_not_used_by_tests; shx mv _bundles/* lib/src",
"remove_src_files_not_used_by_tests": "find ./lib/src \\( -path ./lib/src/utils -o -path ./lib/src/subproviders -o -path ./lib/src/schemas -o -path \"./lib/src/types.*\" \\) -prune -o -type f -print | xargs rm",
"run_mocha": "mocha lib/test/**/*_test.js --timeout 4000 --bail"
"run_mocha": "mocha lib/test/**/*_test.js --timeout 5000 --bail"
},
"config": {
"artifacts": "TokenTransferProxy Exchange TokenRegistry Token EtherToken",
Expand Down Expand Up @@ -85,13 +85,13 @@
"types-bn": "^0.0.1",
"types-ethereumjs-util": "^0.0.5",
"typescript": "^2.4.1",
"web3_beta": "ethereum/web3.js#1.0",
"web3-provider-engine": "^13.0.1",
"web3-typescript-typings": "^0.3.2",
"web3-typescript-typings": "^0.5.0",
"webpack": "^3.1.0"
},
"dependencies": {
"0x-json-schemas": "^0.4.0",
"0x-json-schemas": "^0.5.1",
"abi-decoder": "^1.0.8",
"bignumber.js": "^4.0.2",
"compare-versions": "^3.0.1",
"es6-promisify": "^5.0.0",
Expand All @@ -100,7 +100,6 @@
"find-versions": "^2.0.0",
"lodash": "^4.17.4",
"publish-release": "^1.3.3",
"truffle-contract": "^2.0.1",
"web3": "^0.20.0"
}
}
76 changes: 68 additions & 8 deletions src/0x.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,35 @@
import * as _ from 'lodash';
import * as BigNumber from 'bignumber.js';
import * as Web3 from 'web3';
import * as abiDecoder from 'abi-decoder';
import {SchemaValidator, schemas} from '0x-json-schemas';
import {bigNumberConfigs} from './bignumber_config';
import * as ethUtil from 'ethereumjs-util';
import contract = require('truffle-contract');
import findVersions = require('find-versions');
import compareVersions = require('compare-versions');
import {Web3Wrapper} from './web3_wrapper';
import {constants} from './utils/constants';
import {utils} from './utils/utils';
import {signatureUtils} from './utils/signature_utils';
import {assert} from './utils/assert';
import {artifacts} from './artifacts';
import {ExchangeWrapper} from './contract_wrappers/exchange_wrapper';
import {TokenRegistryWrapper} from './contract_wrappers/token_registry_wrapper';
import {EtherTokenWrapper} from './contract_wrappers/ether_token_wrapper';
import {TokenWrapper} from './contract_wrappers/token_wrapper';
import {TokenTransferProxyWrapper} from './contract_wrappers/token_transfer_proxy_wrapper';
import {ECSignature, ZeroExError, Order, SignedOrder, Web3Provider, ZeroExConfig} from './types';
import {
ECSignature,
ZeroExError,
Order,
SignedOrder,
Web3Provider,
ZeroExConfig,
TransactionReceipt,
DecodedLogArgs,
TransactionReceiptWithDecodedLogs,
LogWithDecodedArgs,
} from './types';

// Customize our BigNumber instances
bigNumberConfigs.configure();
Expand Down Expand Up @@ -170,13 +183,17 @@ export class ZeroEx {
// 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._web3Wrapper = new Web3Wrapper(provider);
this._registerArtifactsWithinABIDecoder();
const gasPrice = _.isUndefined(config) ? undefined : config.gasPrice;
this.token = new TokenWrapper(this._web3Wrapper, gasPrice);
this.proxy = new TokenTransferProxyWrapper(this._web3Wrapper, gasPrice);
this.exchange = new ExchangeWrapper(this._web3Wrapper, this.token, gasPrice);
this.tokenRegistry = new TokenRegistryWrapper(this._web3Wrapper, gasPrice);
this.etherToken = new EtherTokenWrapper(this._web3Wrapper, this.token, gasPrice);
const defaults = {
gasPrice,
};
this._web3Wrapper = new Web3Wrapper(provider, defaults);
this.token = new TokenWrapper(this._web3Wrapper);
this.proxy = new TokenTransferProxyWrapper(this._web3Wrapper);
this.exchange = new ExchangeWrapper(this._web3Wrapper, this.token);
this.tokenRegistry = new TokenRegistryWrapper(this._web3Wrapper);
this.etherToken = new EtherTokenWrapper(this._web3Wrapper, this.token);
}
/**
* Sets a new web3 provider for 0x.js. Updating the provider will stop all
Expand Down Expand Up @@ -249,4 +266,47 @@ export class ZeroEx {

throw new Error(ZeroExError.InvalidSignature);
}
/**
* Waits for a transaction to be mined and returns the transaction receipt.
* @param txHash Transaction hash
* @param pollingIntervalMs How often (in ms) should we check if the transaction is mined.
* @return Transaction receipt with decoded log args.
*/
public async awaitTransactionMinedAsync(
txHash: string, pollingIntervalMs: number = 1000): Promise<TransactionReceiptWithDecodedLogs> {
const txReceiptPromise = new Promise(
(resolve: (receipt: TransactionReceiptWithDecodedLogs) => void, reject) => {
const intervalId = setInterval(async () => {
const transactionReceipt = await this._web3Wrapper.getTransactionReceiptAsync(txHash);
if (!_.isNull(transactionReceipt)) {
clearInterval(intervalId);
const logsWithDecodedArgs = _.map(transactionReceipt.logs, (log: Web3.LogEntry) => {
const decodedLog = abiDecoder.decodeLogs([log])[0];
const decodedArgs = decodedLog.events;
const args: DecodedLogArgs = {};
_.forEach(decodedArgs, arg => {
args[arg.name] = arg.value;
});
const logWithDecodedArgs: LogWithDecodedArgs = {
...log,
args,
event: decodedLog.name,
};
return logWithDecodedArgs;
});
const transactionReceiptWithDecodedLogArgs: TransactionReceiptWithDecodedLogs = {
...transactionReceipt,
logs: logsWithDecodedArgs,
};
resolve(transactionReceiptWithDecodedLogArgs);
}
}, pollingIntervalMs);
});
return txReceiptPromise;
}
private _registerArtifactsWithinABIDecoder(): void {
for (const artifact of _.values(artifacts)) {
abiDecoder.addABI(artifact.abi);
}
}
}
13 changes: 13 additions & 0 deletions src/artifacts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as TokenArtifact from './artifacts/Token.json';
import * as ExchangeArtifact from './artifacts/Exchange.json';
import * as EtherTokenArtifact from './artifacts/EtherToken.json';
import * as TokenRegistryArtifact from './artifacts/TokenRegistry.json';
import * as TokenTransferProxyArtifact from './artifacts/TokenTransferProxy.json';

export const artifacts = {
TokenArtifact: TokenArtifact as any as Artifact,
ExchangeArtifact: ExchangeArtifact as any as Artifact,
EtherTokenArtifact: EtherTokenArtifact as any as Artifact,
TokenRegistryArtifact: TokenRegistryArtifact as any as Artifact,
TokenTransferProxyArtifact: TokenTransferProxyArtifact as any as Artifact,
};
2 changes: 1 addition & 1 deletion src/artifacts/EtherToken.json
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,4 @@
},
"schema_version": "0.0.5",
"updated_at": 1503318938233
}
}
80 changes: 80 additions & 0 deletions src/contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as Web3 from 'web3';
import * as _ from 'lodash';
import promisify = require('es6-promisify');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use the regular import style?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately. Whenever the package exports a function (non module object) we need to do it like that.
https://stackoverflow.com/a/35706271/3546986

import {SchemaValidator, schemas} from '0x-json-schemas';
import {AbiType} from './types';

export class Contract implements Web3.ContractInstance {
public address: string;
public abi: Web3.ContractAbi;
private contract: Web3.ContractInstance;
private defaults: Partial<Web3.TxData>;
private validator: SchemaValidator;
// This class instance is going to be populated with functions and events depending on the ABI
// and we don't know their types in advance
[name: string]: any;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment explaining this and where it is going to be used.

constructor(web3ContractInstance: Web3.ContractInstance, defaults: Partial<Web3.TxData>) {
this.contract = web3ContractInstance;
this.address = web3ContractInstance.address;
this.abi = web3ContractInstance.abi;
this.defaults = defaults;
this.populateEvents();
this.populateFunctions();
this.validator = new SchemaValidator();
}
private populateFunctions(): void {
const functionsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Function);
_.forEach(functionsAbi, (functionAbi: Web3.MethodAbi) => {
if (functionAbi.constant) {
const cbStyleCallFunction = this.contract[functionAbi.name].call;
this[functionAbi.name] = {
callAsync: promisify(cbStyleCallFunction, this.contract),
};
} else {
const cbStyleFunction = this.contract[functionAbi.name];
const cbStyleEstimateGasFunction = this.contract[functionAbi.name].estimateGas;
this[functionAbi.name] = {
estimateGasAsync: promisify(cbStyleEstimateGasFunction, this.contract),
sendTransactionAsync: this.promisifyWithDefaultParams(cbStyleFunction),
};
}
});
}
private populateEvents(): void {
const eventsAbi = _.filter(this.abi, abiPart => abiPart.type === AbiType.Event);
_.forEach(eventsAbi, (eventAbi: Web3.EventAbi) => {
this[eventAbi.name] = this.contract[eventAbi.name];
});
}
private promisifyWithDefaultParams(fn: (...args: any[]) => void): (...args: any[]) => Promise<any> {
const promisifiedWithDefaultParams = (...args: any[]) => {
const promise = new Promise((resolve, reject) => {
const lastArg = args[args.length - 1];
let txData: Partial<Web3.TxData> = {};
if (this.isTxData(lastArg)) {
txData = args.pop();
}
txData = {
...this.defaults,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The defaults should not overwrite the txData.

...txData,
};
const callback = (err: Error, data: any) => {
if (_.isNull(err)) {
resolve(data);
} else {
reject(err);
}
};
args.push(txData);
args.push(callback);
fn.apply(this.contract, args);
});
return promise;
};
return promisifiedWithDefaultParams;
}
private isTxData(lastArg: any): boolean {
const isValid = this.validator.isValid(lastArg, schemas.txDataSchema);
return isValid;
}
}
51 changes: 8 additions & 43 deletions src/contract_wrappers/contract_wrapper.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,18 @@
import * as _ from 'lodash';
import contract = require('truffle-contract');
import * as Web3 from 'web3';
import {Web3Wrapper} from '../web3_wrapper';
import {ZeroExError, Artifact, ContractInstance} from '../types';
import {ZeroExError} from '../types';
import {utils} from '../utils/utils';

export class ContractWrapper {
protected _web3Wrapper: Web3Wrapper;
private _gasPrice?: BigNumber.BigNumber;
constructor(web3Wrapper: Web3Wrapper, gasPrice?: BigNumber.BigNumber) {
constructor(web3Wrapper: Web3Wrapper) {
this._web3Wrapper = web3Wrapper;
this._gasPrice = gasPrice;
}
protected async _instantiateContractIfExistsAsync(artifact: Artifact, address?: string): Promise<ContractInstance> {
const c = await contract(artifact);
c.defaults({
gasPrice: this._gasPrice,
});
const providerObj = this._web3Wrapper.getCurrentProvider();
c.setProvider(providerObj);

const networkIdIfExists = await this._web3Wrapper.getNetworkIdIfExistsAsync();
const artifactNetworkConfigs = _.isUndefined(networkIdIfExists) ?
undefined :
artifact.networks[networkIdIfExists];
let contractAddress;
if (!_.isUndefined(address)) {
contractAddress = address;
} else if (!_.isUndefined(artifactNetworkConfigs)) {
contractAddress = artifactNetworkConfigs.address.toLowerCase();
}

if (!_.isUndefined(contractAddress)) {
const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(contractAddress);
if (!doesContractExist) {
throw new Error(ZeroExError.ContractDoesNotExist);
}
}

try {
const contractInstance = _.isUndefined(address) ? await c.deployed() : await c.at(address);
return contractInstance;
} catch (err) {
const errMsg = `${err}`;
if (_.includes(errMsg, 'not been deployed to detected network')) {
throw new Error(ZeroExError.ContractDoesNotExist);
} else {
utils.consoleLog(`Notice: Error encountered: ${err} ${err.stack}`);
throw new Error(ZeroExError.UnhandledError);
}
}
protected async _instantiateContractIfExistsAsync<A extends Web3.ContractInstance>(artifact: Artifact,
address?: string): Promise<A> {
const contractInstance =
await this._web3Wrapper.getContractInstanceFromArtifactAsync<A>(artifact, address);
return contractInstance;
}
}
22 changes: 14 additions & 8 deletions src/contract_wrappers/ether_token_wrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {ContractWrapper} from './contract_wrapper';
import {TokenWrapper} from './token_wrapper';
import {EtherTokenContract, ZeroExError} from '../types';
import {assert} from '../utils/assert';
import * as EtherTokenArtifacts from '../artifacts/EtherToken.json';
import {artifacts} from '../artifacts';

/**
* This class includes all the functionality related to interacting with a wrapped Ether ERC20 token contract.
Expand All @@ -13,8 +13,8 @@ import * as EtherTokenArtifacts from '../artifacts/EtherToken.json';
export class EtherTokenWrapper extends ContractWrapper {
private _etherTokenContractIfExists?: EtherTokenContract;
private _tokenWrapper: TokenWrapper;
constructor(web3Wrapper: Web3Wrapper, tokenWrapper: TokenWrapper, gasPrice?: BigNumber.BigNumber) {
super(web3Wrapper, gasPrice);
constructor(web3Wrapper: Web3Wrapper, tokenWrapper: TokenWrapper) {
super(web3Wrapper);
this._tokenWrapper = tokenWrapper;
}
/**
Expand All @@ -23,27 +23,30 @@ export class EtherTokenWrapper extends ContractWrapper {
* for ETH.
* @param amountInWei Amount of ETH in Wei the caller wishes to deposit.
* @param depositor The hex encoded user Ethereum address that would like to make the deposit.
* @return Transaction hash.
*/
public async depositAsync(amountInWei: BigNumber.BigNumber, depositor: string): Promise<void> {
public async depositAsync(amountInWei: BigNumber.BigNumber, depositor: string): Promise<string> {
assert.isBigNumber('amountInWei', amountInWei);
await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper);

const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(depositor);
assert.assert(ethBalanceInWei.gte(amountInWei), ZeroExError.InsufficientEthBalanceForDeposit);

const wethContract = await this._getEtherTokenContractAsync();
await wethContract.deposit({
const txHash = await wethContract.deposit.sendTransactionAsync({
from: depositor,
value: amountInWei,
});
return txHash;
}
/**
* Withdraw ETH to the withdrawer's address from the wrapped ETH smart contract in exchange for the
* equivalent number of wrapped ETH tokens.
* @param amountInWei Amount of ETH in Wei the caller wishes to withdraw.
* @param withdrawer The hex encoded user Ethereum address that would like to make the withdrawl.
* @return Transaction hash.
*/
public async withdrawAsync(amountInWei: BigNumber.BigNumber, withdrawer: string): Promise<void> {
public async withdrawAsync(amountInWei: BigNumber.BigNumber, withdrawer: string): Promise<string> {
assert.isBigNumber('amountInWei', amountInWei);
await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper);

Expand All @@ -52,9 +55,10 @@ export class EtherTokenWrapper extends ContractWrapper {
assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.InsufficientWEthBalanceForWithdrawal);

const wethContract = await this._getEtherTokenContractAsync();
await wethContract.withdraw(amountInWei, {
const txHash = await wethContract.withdraw.sendTransactionAsync(amountInWei, {
from: withdrawer,
});
return txHash;
}
/**
* Retrieves the Wrapped Ether token contract address
Expand All @@ -71,7 +75,9 @@ export class EtherTokenWrapper extends ContractWrapper {
if (!_.isUndefined(this._etherTokenContractIfExists)) {
return this._etherTokenContractIfExists;
}
const contractInstance = await this._instantiateContractIfExistsAsync((EtherTokenArtifacts as any));
const contractInstance = await this._instantiateContractIfExistsAsync<EtherTokenContract>(
artifacts.EtherTokenArtifact,
);
this._etherTokenContractIfExists = contractInstance as EtherTokenContract;
return this._etherTokenContractIfExists;
}
Expand Down
Loading