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

Commit

Permalink
Merge pull request #151 from 0xProject/feature/remove-truffle-contracts
Browse files Browse the repository at this point in the history
Remove truffle contracts dependency
  • Loading branch information
LogvinovLeon authored Sep 6, 2017
2 parents 0275ac9 + 258b4fa commit f0a5ad2
Show file tree
Hide file tree
Showing 23 changed files with 658 additions and 1,145 deletions.
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 [email protected] 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 [email protected] 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');
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;
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,
...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

0 comments on commit f0a5ad2

Please sign in to comment.