aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--package.json2
-rw-r--r--src/0x.ts1
-rw-r--r--src/artifacts/EtherToken.json2
-rw-r--r--src/contract.ts28
-rw-r--r--src/contract_wrappers/contract_wrapper.ts47
-rw-r--r--src/contract_wrappers/ether_token_wrapper.ts4
-rw-r--r--src/contract_wrappers/exchange_wrapper.ts76
-rw-r--r--src/contract_wrappers/token_registry_wrapper.ts4
-rw-r--r--src/contract_wrappers/token_transfer_proxy_wrapper.ts4
-rw-r--r--src/contract_wrappers/token_wrapper.ts4
-rw-r--r--src/globals.d.ts20
-rw-r--r--src/types.ts29
-rw-r--r--src/web3_wrapper.ts28
-rw-r--r--test/exchange_wrapper_test.ts64
-rw-r--r--test/utils/blockchain_lifecycle.ts3
-rw-r--r--yarn.lock6
16 files changed, 139 insertions, 183 deletions
diff --git a/package.json b/package.json
index 1167b0ca1..909769220 100644
--- a/package.json
+++ b/package.json
@@ -86,7 +86,7 @@
"types-ethereumjs-util": "^0.0.5",
"typescript": "^2.4.1",
"web3-provider-engine": "^13.0.1",
- "web3-typescript-typings": "^0.3.2",
+ "web3-typescript-typings": "^0.4.1",
"web3_beta": "ethereum/web3.js#1.0",
"webpack": "^3.1.0"
},
diff --git a/src/0x.ts b/src/0x.ts
index ba222f2c9..3cf672a1b 100644
--- a/src/0x.ts
+++ b/src/0x.ts
@@ -3,7 +3,6 @@ import * as BigNumber from 'bignumber.js';
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';
diff --git a/src/artifacts/EtherToken.json b/src/artifacts/EtherToken.json
index eca348530..54b5a032e 100644
--- a/src/artifacts/EtherToken.json
+++ b/src/artifacts/EtherToken.json
@@ -391,4 +391,4 @@
},
"schema_version": "0.0.5",
"updated_at": 1503318938233
-} \ No newline at end of file
+}
diff --git a/src/contract.ts b/src/contract.ts
index b4a54ca09..0c76571cc 100644
--- a/src/contract.ts
+++ b/src/contract.ts
@@ -2,27 +2,37 @@ import * as Web3 from 'web3';
import * as _ from 'lodash';
import promisify = require('es6-promisify');
-export class Contract<A extends Web3.ContractInstance> implements Web3.ContractInstance {
+export class Contract implements Web3.ContractInstance {
public address: string;
public abi: Web3.ContractAbi;
- private contract: A;
+ private contract: Web3.ContractInstance;
[name: string]: any;
- constructor(web3ContractInstance: A) {
+ constructor(web3ContractInstance: Web3.ContractInstance) {
this.contract = web3ContractInstance;
this.address = web3ContractInstance.address;
this.abi = web3ContractInstance.abi;
+ this.populateEvents();
+ this.populateFunctions();
+ }
+ private populateFunctions(): void {
const functionsAbi = _.filter(this.abi, abiPart => abiPart.type === 'function');
_.forEach(functionsAbi, (functionAbi: Web3.MethodAbi) => {
- const cbStyleFunction = web3ContractInstance[functionAbi.name];
- this[functionAbi.name] = promisify(cbStyleFunction, web3ContractInstance);
+ const cbStyleFunction = this.contract[functionAbi.name];
+ this[functionAbi.name] = promisify(cbStyleFunction, this.contract);
if (functionAbi.constant) {
- const cbStyleCallFunction = web3ContractInstance[functionAbi.name].call;
- this[functionAbi.name].call = promisify(cbStyleCallFunction, web3ContractInstance);
+ const cbStyleCallFunction = this.contract[functionAbi.name].call;
+ this[functionAbi.name].call = promisify(cbStyleCallFunction, this.contract);
} else {
- const cbStyleEstimateGasFunction = web3ContractInstance[functionAbi.name].estimateGas;
+ const cbStyleEstimateGasFunction = this.contract[functionAbi.name].estimateGas;
this[functionAbi.name].estimateGas =
- promisify(cbStyleEstimateGasFunction, web3ContractInstance);
+ promisify(cbStyleEstimateGasFunction, this.contract);
}
});
}
+ private populateEvents(): void {
+ const eventsAbi = _.filter(this.abi, abiPart => abiPart.type === 'event');
+ _.forEach(eventsAbi, (eventAbi: Web3.EventAbi) => {
+ this[eventAbi.name] = this.contract[eventAbi.name];
+ });
+ }
}
diff --git a/src/contract_wrappers/contract_wrapper.ts b/src/contract_wrappers/contract_wrapper.ts
index 28df82cee..3de26148f 100644
--- a/src/contract_wrappers/contract_wrapper.ts
+++ b/src/contract_wrappers/contract_wrapper.ts
@@ -1,7 +1,7 @@
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 {
@@ -11,43 +11,10 @@ export class ContractWrapper {
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;
}
}
diff --git a/src/contract_wrappers/ether_token_wrapper.ts b/src/contract_wrappers/ether_token_wrapper.ts
index 3c282510f..a2486b15e 100644
--- a/src/contract_wrappers/ether_token_wrapper.ts
+++ b/src/contract_wrappers/ether_token_wrapper.ts
@@ -71,7 +71,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>(
+ EtherTokenArtifacts as any as Artifact,
+ );
this._etherTokenContractIfExists = contractInstance as EtherTokenContract;
return this._etherTokenContractIfExists;
}
diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts
index d09df236b..1196394e9 100644
--- a/src/contract_wrappers/exchange_wrapper.ts
+++ b/src/contract_wrappers/exchange_wrapper.ts
@@ -141,12 +141,12 @@ export class ExchangeWrapper extends ContractWrapper {
* @param takerAddress The user Ethereum address who would like to fill this order.
* Must be available via the supplied Web3.Provider
* passed to 0x.js.
- * @return The amount of the order that was filled (in taker token baseUnits).
+ * @return Transaction hash.
*/
@decorators.contractCallErrorHandler
public async fillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string): Promise<BigNumber.BigNumber> {
+ takerAddress: string): Promise<string> {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount);
assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
@@ -169,7 +169,7 @@ export class ExchangeWrapper extends ContractWrapper {
from: takerAddress,
},
);
- const response: ContractResponse = await exchangeInstance.fillOrder(
+ const txHash: string = await exchangeInstance.fillOrder(
orderAddresses,
orderValues,
fillTakerTokenAmount,
@@ -182,10 +182,7 @@ export class ExchangeWrapper extends ContractWrapper {
gas,
},
);
- this._throwErrorLogsAsErrors(response.logs);
- const logFillArgs = response.logs[0].args as LogFillContractEventArgs;
- const filledTakerTokenAmount = new BigNumber(logFillArgs.filledTakerTokenAmount);
- return filledTakerTokenAmount;
+ return txHash;
}
/**
* Sequentially and atomically fills signedOrders up to the specified takerTokenFillAmount.
@@ -201,12 +198,12 @@ export class ExchangeWrapper extends ContractWrapper {
* @param takerAddress The user Ethereum address who would like to fill these
* orders. Must be available via the supplied Web3.Provider
* passed to 0x.js.
- * @return The amount of the orders that was filled (in taker token baseUnits).
+ * @return Transaction hash.
*/
@decorators.contractCallErrorHandler
public async fillOrdersUpToAsync(signedOrders: SignedOrder[], fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string): Promise<BigNumber.BigNumber> {
+ takerAddress: string): Promise<string> {
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress);
assert.hasAtMostOneUniqueValue(takerTokenAddresses,
@@ -222,7 +219,7 @@ export class ExchangeWrapper extends ContractWrapper {
signedOrder, fillTakerTokenAmount, takerAddress);
}
if (_.isEmpty(signedOrders)) {
- return new BigNumber(0); // no-op
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
}
const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => {
@@ -251,7 +248,7 @@ export class ExchangeWrapper extends ContractWrapper {
from: takerAddress,
},
);
- const response: ContractResponse = await exchangeInstance.fillOrdersUpTo(
+ const txHash = await exchangeInstance.fillOrdersUpTo(
orderAddressesArray,
orderValuesArray,
fillTakerTokenAmount,
@@ -264,13 +261,7 @@ export class ExchangeWrapper extends ContractWrapper {
gas,
},
);
- this._throwErrorLogsAsErrors(response.logs);
- let filledTakerTokenAmount = new BigNumber(0);
- _.each(response.logs, log => {
- filledTakerTokenAmount = filledTakerTokenAmount.plus(
- (log.args as LogFillContractEventArgs).filledTakerTokenAmount);
- });
- return filledTakerTokenAmount;
+ return txHash;
}
/**
* Batch version of fillOrderAsync.
@@ -288,11 +279,12 @@ export class ExchangeWrapper extends ContractWrapper {
* @param takerAddress The user Ethereum address who would like to fill
* these orders. Must be available via the supplied
* Web3.Provider passed to 0x.js.
+ * @return Transaction hash.
*/
@decorators.contractCallErrorHandler
public async batchFillOrdersAsync(orderFillRequests: OrderFillRequest[],
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- takerAddress: string): Promise<void> {
+ takerAddress: string): Promise<string> {
assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema);
const exchangeContractAddresses = _.map(
orderFillRequests,
@@ -307,7 +299,7 @@ export class ExchangeWrapper extends ContractWrapper {
orderFillRequest.signedOrder, orderFillRequest.takerTokenFillAmount, takerAddress);
}
if (_.isEmpty(orderFillRequests)) {
- return; // no-op
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
}
const orderAddressesValuesAmountsAndSignatureArray = _.map(orderFillRequests, orderFillRequest => {
@@ -337,7 +329,7 @@ export class ExchangeWrapper extends ContractWrapper {
from: takerAddress,
},
);
- const response: ContractResponse = await exchangeInstance.batchFillOrders(
+ const txHash = await exchangeInstance.batchFillOrders(
orderAddressesArray,
orderValuesArray,
fillTakerTokenAmounts,
@@ -350,7 +342,7 @@ export class ExchangeWrapper extends ContractWrapper {
gas,
},
);
- this._throwErrorLogsAsErrors(response.logs);
+ return txHash;
}
/**
* Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled,
@@ -360,10 +352,11 @@ export class ExchangeWrapper extends ContractWrapper {
* @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill.
* @param takerAddress The user Ethereum address who would like to fill this order.
* Must be available via the supplied Web3.Provider passed to 0x.js.
+ * @return Transaction hash.
*/
@decorators.contractCallErrorHandler
public async fillOrKillOrderAsync(signedOrder: SignedOrder, fillTakerTokenAmount: BigNumber.BigNumber,
- takerAddress: string): Promise<void> {
+ takerAddress: string): Promise<string> {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isBigNumber('fillTakerTokenAmount', fillTakerTokenAmount);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
@@ -385,7 +378,7 @@ export class ExchangeWrapper extends ContractWrapper {
from: takerAddress,
},
);
- const response: ContractResponse = await exchangeInstance.fillOrKillOrder(
+ const txHash = await exchangeInstance.fillOrKillOrder(
orderAddresses,
orderValues,
fillTakerTokenAmount,
@@ -397,7 +390,7 @@ export class ExchangeWrapper extends ContractWrapper {
gas,
},
);
- this._throwErrorLogsAsErrors(response.logs);
+ return txHash;
}
/**
* Batch version of fillOrKill. Allows a taker to specify a batch of orders that will either be atomically
@@ -405,10 +398,11 @@ export class ExchangeWrapper extends ContractWrapper {
* @param orderFillOrKillRequests An array of objects that conform to the OrderFillOrKillRequest interface.
* @param takerAddress The user Ethereum address who would like to fill there orders.
* Must be available via the supplied Web3.Provider passed to 0x.js.
+ * @return Transaction hash.
*/
@decorators.contractCallErrorHandler
public async batchFillOrKillAsync(orderFillOrKillRequests: OrderFillOrKillRequest[],
- takerAddress: string): Promise<void> {
+ takerAddress: string): Promise<string> {
assert.doesConformToSchema('orderFillOrKillRequests', orderFillOrKillRequests,
schemas.orderFillOrKillRequestsSchema);
const exchangeContractAddresses = _.map(
@@ -419,7 +413,7 @@ export class ExchangeWrapper extends ContractWrapper {
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
if (_.isEmpty(orderFillOrKillRequests)) {
- return; // no-op
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
}
const exchangeInstance = await this._getExchangeContractAsync();
for (const request of orderFillOrKillRequests) {
@@ -452,7 +446,7 @@ export class ExchangeWrapper extends ContractWrapper {
from: takerAddress,
},
);
- const response: ContractResponse = await exchangeInstance.batchFillOrKillOrders(
+ const txHash = await exchangeInstance.batchFillOrKillOrders(
orderAddresses,
orderValues,
fillTakerTokenAmounts,
@@ -464,18 +458,18 @@ export class ExchangeWrapper extends ContractWrapper {
gas,
},
);
- this._throwErrorLogsAsErrors(response.logs);
+ return txHash;
}
/**
* Cancel a given fill amount of an order. Cancellations are cumulative.
* @param order An object that conforms to the Order or SignedOrder interface.
* The order you would like to cancel.
* @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel.
- * @return The amount of the order that was cancelled (in taker token baseUnits).
+ * @return Transaction hash.
*/
@decorators.contractCallErrorHandler
public async cancelOrderAsync(
- order: Order|SignedOrder, cancelTakerTokenAmount: BigNumber.BigNumber): Promise<BigNumber.BigNumber> {
+ order: Order|SignedOrder, cancelTakerTokenAmount: BigNumber.BigNumber): Promise<string> {
assert.doesConformToSchema('order', order, schemas.orderSchema);
assert.isBigNumber('takerTokenCancelAmount', cancelTakerTokenAmount);
await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper);
@@ -492,7 +486,7 @@ export class ExchangeWrapper extends ContractWrapper {
from: order.maker,
},
);
- const response: ContractResponse = await exchangeInstance.cancelOrder(
+ const txHash = await exchangeInstance.cancelOrder(
orderAddresses,
orderValues,
cancelTakerTokenAmount,
@@ -501,19 +495,17 @@ export class ExchangeWrapper extends ContractWrapper {
gas,
},
);
- this._throwErrorLogsAsErrors(response.logs);
- const logFillArgs = response.logs[0].args as LogCancelContractEventArgs;
- const cancelledTakerTokenAmount = new BigNumber(logFillArgs.cancelledTakerTokenAmount);
- return cancelledTakerTokenAmount;
+ return txHash;
}
/**
* Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction.
* All orders must be from the same maker.
* @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest
* interface.
+ * @return Transaction hash.
*/
@decorators.contractCallErrorHandler
- public async batchCancelOrdersAsync(orderCancellationRequests: OrderCancellationRequest[]): Promise<void> {
+ public async batchCancelOrdersAsync(orderCancellationRequests: OrderCancellationRequest[]): Promise<string> {
assert.doesConformToSchema('orderCancellationRequests', orderCancellationRequests,
schemas.orderCancellationRequestsSchema);
const exchangeContractAddresses = _.map(
@@ -532,7 +524,7 @@ export class ExchangeWrapper extends ContractWrapper {
);
}
if (_.isEmpty(orderCancellationRequests)) {
- return; // no-op
+ throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
}
const exchangeInstance = await this._getExchangeContractAsync();
const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(orderCancellationRequests, cancellationRequest => {
@@ -552,7 +544,7 @@ export class ExchangeWrapper extends ContractWrapper {
from: maker,
},
);
- const response: ContractResponse = await exchangeInstance.batchCancelOrders(
+ const txHash = await exchangeInstance.batchCancelOrders(
orderAddresses,
orderValues,
cancelTakerTokenAmounts,
@@ -561,7 +553,7 @@ export class ExchangeWrapper extends ContractWrapper {
gas,
},
);
- this._throwErrorLogsAsErrors(response.logs);
+ return txHash;
}
/**
* Subscribe to an event type emitted by the Exchange smart contract
@@ -730,7 +722,9 @@ export class ExchangeWrapper extends ContractWrapper {
if (!_.isUndefined(this._exchangeContractIfExists)) {
return this._exchangeContractIfExists;
}
- const contractInstance = await this._instantiateContractIfExistsAsync((ExchangeArtifacts as any));
+ const contractInstance = await this._instantiateContractIfExistsAsync<ExchangeContract>(
+ (ExchangeArtifacts as any),
+ );
this._exchangeContractIfExists = contractInstance as ExchangeContract;
return this._exchangeContractIfExists;
}
diff --git a/src/contract_wrappers/token_registry_wrapper.ts b/src/contract_wrappers/token_registry_wrapper.ts
index 822e69460..1550bfa3e 100644
--- a/src/contract_wrappers/token_registry_wrapper.ts
+++ b/src/contract_wrappers/token_registry_wrapper.ts
@@ -101,7 +101,9 @@ export class TokenRegistryWrapper extends ContractWrapper {
if (!_.isUndefined(this._tokenRegistryContractIfExists)) {
return this._tokenRegistryContractIfExists;
}
- const contractInstance = await this._instantiateContractIfExistsAsync((TokenRegistryArtifacts as any));
+ const contractInstance = await this._instantiateContractIfExistsAsync<TokenRegistryContract>(
+ TokenRegistryArtifacts as any as Artifact,
+ );
this._tokenRegistryContractIfExists = contractInstance as TokenRegistryContract;
return this._tokenRegistryContractIfExists;
}
diff --git a/src/contract_wrappers/token_transfer_proxy_wrapper.ts b/src/contract_wrappers/token_transfer_proxy_wrapper.ts
index da17d79ff..2b4b32961 100644
--- a/src/contract_wrappers/token_transfer_proxy_wrapper.ts
+++ b/src/contract_wrappers/token_transfer_proxy_wrapper.ts
@@ -44,7 +44,9 @@ export class TokenTransferProxyWrapper extends ContractWrapper {
if (!_.isUndefined(this._tokenTransferProxyContractIfExists)) {
return this._tokenTransferProxyContractIfExists;
}
- const contractInstance = await this._instantiateContractIfExistsAsync((TokenTransferProxyArtifacts as any));
+ const contractInstance = await this._instantiateContractIfExistsAsync<TokenTransferProxyContract>(
+ TokenTransferProxyArtifacts as any as Artifact,
+ );
this._tokenTransferProxyContractIfExists = contractInstance as TokenTransferProxyContract;
return this._tokenTransferProxyContractIfExists;
}
diff --git a/src/contract_wrappers/token_wrapper.ts b/src/contract_wrappers/token_wrapper.ts
index f7070f1f4..e98744cbc 100644
--- a/src/contract_wrappers/token_wrapper.ts
+++ b/src/contract_wrappers/token_wrapper.ts
@@ -267,7 +267,9 @@ export class TokenWrapper extends ContractWrapper {
if (!_.isUndefined(tokenContract)) {
return tokenContract;
}
- const contractInstance = await this._instantiateContractIfExistsAsync((TokenArtifacts as any), tokenAddress);
+ const contractInstance = await this._instantiateContractIfExistsAsync<TokenContract>(
+ TokenArtifacts as any as Artifact, tokenAddress,
+ );
tokenContract = contractInstance as TokenContract;
this._tokenContractsByAddress[tokenAddress] = tokenContract;
return tokenContract;
diff --git a/src/globals.d.ts b/src/globals.d.ts
index 9230ab02d..6f5f13b4e 100644
--- a/src/globals.d.ts
+++ b/src/globals.d.ts
@@ -33,23 +33,11 @@ declare module '*.json' {
/* tslint:enable */
}
-// truffle-contract declarations
-declare interface ContractInstance {
- address: string;
-}
-declare interface ContractFactory {
- setProvider: (providerObj: any) => void;
- deployed: () => ContractInstance;
- // Both any's are Web3.CallData, but I was unable to import it in this file
- defaults: (config: any) => any;
- at: (address: string) => ContractInstance;
-}
declare interface Artifact {
- networks: {[networkId: number]: any};
-}
-declare function contract(artifacts: Artifact): ContractFactory;
-declare module 'truffle-contract' {
- export = contract;
+ abi: any;
+ networks: {[networkId: number]: {
+ address: string;
+ }};
}
// find-version declarations
diff --git a/src/types.ts b/src/types.ts
index 2400a9a60..9f8ba9729 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -14,6 +14,7 @@ export enum ZeroExError {
InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL',
InvalidJump = 'INVALID_JUMP',
OutOfGas = 'OUT_OF_GAS',
+ NoNetworkId = 'NO_NETWORK_ID',
}
/**
@@ -60,7 +61,7 @@ export interface ExchangeContract extends ContractInstance {
fillOrder: {
(orderAddresses: OrderAddresses, orderValues: OrderValues, fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- v: number, r: string, s: string, txOpts?: TxOpts): Promise<ContractResponse>;
+ v: number, r: string, s: string, txOpts?: TxOpts): Promise<string>;
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues,
fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
@@ -69,7 +70,7 @@ export interface ExchangeContract extends ContractInstance {
batchFillOrders: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillTakerTokenAmounts: BigNumber.BigNumber[],
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- v: number[], r: string[], s: string[], txOpts?: TxOpts): Promise<ContractResponse>;
+ v: number[], r: string[], s: string[], txOpts?: TxOpts): Promise<string>;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[],
fillTakerTokenAmounts: BigNumber.BigNumber[],
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
@@ -78,7 +79,7 @@ export interface ExchangeContract extends ContractInstance {
fillOrdersUpTo: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
- v: number[], r: string[], s: string[], txOpts?: TxOpts): Promise<ContractResponse>;
+ v: number[], r: string[], s: string[], txOpts?: TxOpts): Promise<string>;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[],
fillTakerTokenAmount: BigNumber.BigNumber,
shouldThrowOnInsufficientBalanceOrAllowance: boolean,
@@ -86,28 +87,28 @@ export interface ExchangeContract extends ContractInstance {
};
cancelOrder: {
(orderAddresses: OrderAddresses, orderValues: OrderValues, cancelTakerTokenAmount: BigNumber.BigNumber,
- txOpts?: TxOpts): Promise<ContractResponse>;
+ txOpts?: TxOpts): Promise<string>;
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues,
cancelTakerTokenAmount: BigNumber.BigNumber,
txOpts?: TxOpts) => Promise<number>;
};
batchCancelOrders: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], cancelTakerTokenAmounts: BigNumber.BigNumber[],
- txOpts?: TxOpts): Promise<ContractResponse>;
+ txOpts?: TxOpts): Promise<string>;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[],
cancelTakerTokenAmounts: BigNumber.BigNumber[],
txOpts?: TxOpts) => Promise<number>;
};
fillOrKillOrder: {
(orderAddresses: OrderAddresses, orderValues: OrderValues, fillTakerTokenAmount: BigNumber.BigNumber,
- v: number, r: string, s: string, txOpts?: TxOpts): Promise<ContractResponse>;
+ v: number, r: string, s: string, txOpts?: TxOpts): Promise<string>;
estimateGas: (orderAddresses: OrderAddresses, orderValues: OrderValues,
fillTakerTokenAmount: BigNumber.BigNumber,
v: number, r: string, s: string, txOpts?: TxOpts) => Promise<number>;
};
batchFillOrKillOrders: {
(orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillTakerTokenAmounts: BigNumber.BigNumber[],
- v: number[], r: string[], s: string[], txOpts: TxOpts): Promise<ContractResponse>;
+ v: number[], r: string[], s: string[], txOpts: TxOpts): Promise<string>;
estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[],
fillTakerTokenAmounts: BigNumber.BigNumber[],
v: number[], r: string[], s: string[], txOpts?: TxOpts) => Promise<number>;
@@ -209,6 +210,7 @@ export enum ExchangeContractErrs {
InsufficientRemainingFillAmount = 'INSUFFICIENT_REMAINING_FILL_AMOUNT',
MultipleTakerTokensInFillUpToDisallowed = 'MULTIPLE_TAKER_TOKENS_IN_FILL_UP_TO_DISALLOWED',
BatchOrdersMustHaveSameExchangeAddress = 'BATCH_ORDERS_MUST_HAVE_SAME_EXCHANGE_ADDRESS',
+ BatchOrdersMustHaveAtLeastOneItem = 'BATCH_ORDERS_MUST_HAVE_AT_LEAST_ONE_ITEM',
}
export interface ContractResponse {
@@ -351,10 +353,7 @@ export type AsyncMethod = (...args: any[]) => Promise<any>;
export interface ContractInstance {
address: string;
-}
-
-export interface Artifact {
- networks: {[networkId: number]: any};
+ abi: Web3.ContractAbi;
}
export interface ContractEventEmitter {
@@ -373,14 +372,6 @@ export interface ExchangeContractByAddress {
[address: string]: ExchangeContract;
}
-export interface ContractArtifact {
- networks: {
- [networkId: number]: {
- address: string;
- };
- };
-}
-
export interface JSONRPCPayload {
params: any[];
method: string;
diff --git a/src/web3_wrapper.ts b/src/web3_wrapper.ts
index 61bac45c9..129017e7c 100644
--- a/src/web3_wrapper.ts
+++ b/src/web3_wrapper.ts
@@ -2,6 +2,8 @@ import * as _ from 'lodash';
import * as Web3 from 'web3';
import * as BigNumber from 'bignumber.js';
import promisify = require('es6-promisify');
+import {ZeroExError} from './types';
+import {Contract} from './contract';
export class Web3Wrapper {
private web3: Web3;
@@ -41,6 +43,27 @@ export class Web3Wrapper {
return undefined;
}
}
+ public async getContractInstanceFromArtifactAsync<A extends Web3.ContractInstance>(artifact: Artifact,
+ address?: string): Promise<A> {
+ if (_.isUndefined(address)) {
+ const networkIdIfExists = await this.getNetworkIdIfExistsAsync();
+ if (_.isUndefined(networkIdIfExists)) {
+ throw new Error(ZeroExError.NoNetworkId);
+ }
+ if (_.isUndefined(artifact.networks[networkIdIfExists])) {
+ throw new Error(ZeroExError.ContractNotDeployedOnNetwork);
+ }
+ address = artifact.networks[networkIdIfExists].address.toLowerCase();
+ }
+ const doesContractExist = await this.doesContractExistAtAddressAsync(address);
+ if (!doesContractExist) {
+ throw new Error(ZeroExError.ContractDoesNotExist);
+ }
+ const contractInstance = this.getContractInstance<A>(
+ artifact.abi, address,
+ );
+ return contractInstance;
+ }
public toWei(ethAmount: BigNumber.BigNumber): BigNumber.BigNumber {
const balanceWei = this.web3.toWei(ethAmount, 'ether');
return balanceWei;
@@ -68,6 +91,11 @@ export class Web3Wrapper {
const addresses: string[] = await promisify(this.web3.eth.getAccounts)();
return addresses;
}
+ private getContractInstance<A extends Web3.ContractInstance>(abi: Web3.ContractAbi, address: string): A {
+ const web3ContractInstance = this.web3.eth.contract(abi).at(address);
+ const contractInstance = new Contract(web3ContractInstance) as any as A;
+ return contractInstance;
+ }
private async getNetworkAsync(): Promise<number> {
const networkId = await promisify(this.web3.version.getNetwork)();
return networkId;
diff --git a/test/exchange_wrapper_test.ts b/test/exchange_wrapper_test.ts
index 90ac37387..15e957ee1 100644
--- a/test/exchange_wrapper_test.ts
+++ b/test/exchange_wrapper_test.ts
@@ -196,34 +196,6 @@ describe('ExchangeWrapper', () => {
expect(await zeroEx.token.getBalanceAsync(takerTokenAddress, takerAddress))
.to.be.bignumber.equal(fillableAmount.minus(partialFillAmount));
});
- it('should return filled amount', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
- );
- const partialFillAmount = new BigNumber(3);
- const filledAmount = await zeroEx.exchange.fillOrderAsync(
- signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
- expect(filledAmount).to.be.bignumber.equal(partialFillAmount);
- });
- it('should return the partially filled amount \
- if the fill amount specified is greater then the amount available', async () => {
- const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
- makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
- );
- const partialFillAmount = new BigNumber(3);
- await zeroEx.exchange.fillOrderAsync(
- signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
- const missingBalance = new BigNumber(1);
- const totalBalance = partialFillAmount.plus(missingBalance);
- await zeroEx.token.transferAsync(takerTokenAddress, coinbase, takerAddress, missingBalance);
- await zeroEx.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, totalBalance);
- await zeroEx.token.transferAsync(makerTokenAddress, coinbase, makerAddress, missingBalance);
- await zeroEx.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, totalBalance);
- const remainingFillAmount = fillableAmount.minus(partialFillAmount);
- const filledAmount = await zeroEx.exchange.fillOrderAsync(
- signedOrder, partialFillAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
- expect(filledAmount).to.be.bignumber.equal(remainingFillAmount);
- });
it('should fill the valid orders with fees', async () => {
const makerFee = new BigNumber(1);
const takerFee = new BigNumber(2);
@@ -231,8 +203,9 @@ describe('ExchangeWrapper', () => {
makerTokenAddress, takerTokenAddress, makerFee, takerFee,
makerAddress, takerAddress, fillableAmount, feeRecipient,
);
- await zeroEx.exchange.fillOrderAsync(
+ const txHash = await zeroEx.exchange.fillOrderAsync(
signedOrder, fillTakerAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
+ await blockchainLifecycle.waitUntilMinedAsync(txHash);
expect(await zeroEx.token.getBalanceAsync(zrxTokenAddress, feeRecipient))
.to.be.bignumber.equal(makerFee.plus(takerFee));
});
@@ -265,13 +238,15 @@ describe('ExchangeWrapper', () => {
];
});
describe('successful batch fills', () => {
- it('should no-op for an empty batch', async () => {
- await zeroEx.exchange.batchFillOrdersAsync(
- [], shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
+ it('should throw if a batch is empty', async () => {
+ return expect(zeroEx.exchange.batchFillOrdersAsync(
+ [], shouldThrowOnInsufficientBalanceOrAllowance, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
});
it('should successfully fill multiple orders', async () => {
- await zeroEx.exchange.batchFillOrdersAsync(
+ const txHash = await zeroEx.exchange.batchFillOrdersAsync(
orderFillBatch, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
+ await blockchainLifecycle.waitUntilMinedAsync(txHash);
const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
expect(filledAmount).to.be.bignumber.equal(fillTakerAmount);
@@ -298,26 +273,22 @@ describe('ExchangeWrapper', () => {
signedOrders = [signedOrder, anotherSignedOrder];
});
describe('successful batch fills', () => {
- it('should no-op for an empty batch', async () => {
- await zeroEx.exchange.fillOrdersUpToAsync(
- [], fillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress);
+ it('should throw if a batch is empty', async () => {
+ return expect(zeroEx.exchange.fillOrdersUpToAsync(
+ [], fillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress),
+ ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
});
it('should successfully fill up to specified amount', async () => {
- await zeroEx.exchange.fillOrdersUpToAsync(
+ const txHash = await zeroEx.exchange.fillOrdersUpToAsync(
signedOrders, fillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress,
);
+ await blockchainLifecycle.waitUntilMinedAsync(txHash);
const filledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(signedOrderHashHex);
const anotherFilledAmount = await zeroEx.exchange.getFilledTakerAmountAsync(anotherOrderHashHex);
expect(filledAmount).to.be.bignumber.equal(fillableAmount);
const remainingFillAmount = fillableAmount.minus(1);
expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount);
});
- it('should return filled amount', async () => {
- const filledTakerTokenAmount = await zeroEx.exchange.fillOrdersUpToAsync(
- signedOrders, fillUpToAmount, shouldThrowOnInsufficientBalanceOrAllowance, takerAddress,
- );
- expect(filledTakerTokenAmount).to.be.bignumber.equal(fillUpToAmount);
- });
});
});
});
@@ -344,14 +315,11 @@ describe('ExchangeWrapper', () => {
describe('#cancelOrderAsync', () => {
describe('successful cancels', () => {
it('should cancel an order', async () => {
- await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
+ const txHash = await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
+ await blockchainLifecycle.waitUntilMinedAsync(txHash);
const cancelledAmount = await zeroEx.exchange.getCanceledTakerAmountAsync(orderHashHex);
expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
});
- it('should return cancelled amount', async () => {
- const cancelledAmount = await zeroEx.exchange.cancelOrderAsync(signedOrder, cancelAmount);
- expect(cancelledAmount).to.be.bignumber.equal(cancelAmount);
- });
});
});
describe('#batchCancelOrdersAsync', () => {
diff --git a/test/utils/blockchain_lifecycle.ts b/test/utils/blockchain_lifecycle.ts
index 50eb57b95..b4f742e63 100644
--- a/test/utils/blockchain_lifecycle.ts
+++ b/test/utils/blockchain_lifecycle.ts
@@ -17,4 +17,7 @@ export class BlockchainLifecycle {
throw new Error(`Snapshot with id #${this.snapshotId} failed to revert`);
}
}
+ public async waitUntilMinedAsync(txHash: string): Promise<void> {
+ return undefined;
+ }
}
diff --git a/yarn.lock b/yarn.lock
index ac48a749f..73452ac1f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5671,9 +5671,9 @@ web3-provider-engine@^8.4.0:
xhr "^2.2.0"
xtend "^4.0.1"
-web3-typescript-typings@^0.3.2:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/web3-typescript-typings/-/web3-typescript-typings-0.3.2.tgz#75f65fe452e35e2b96192908199dbb7a9ab5bcc3"
+web3-typescript-typings@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/web3-typescript-typings/-/web3-typescript-typings-0.4.1.tgz#077f5c042c1d2625b4cabedad88b9e9427b38fb3"
dependencies:
bignumber.js "^4.0.2"