aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts/src')
-rw-r--r--packages/contracts/src/utils/assertions.ts63
-rw-r--r--packages/contracts/src/utils/constants.ts7
-rw-r--r--packages/contracts/src/utils/exchange_wrapper.ts12
-rw-r--r--packages/contracts/src/utils/increase_time.ts31
-rw-r--r--packages/contracts/src/utils/web3_wrapper.ts39
5 files changed, 144 insertions, 8 deletions
diff --git a/packages/contracts/src/utils/assertions.ts b/packages/contracts/src/utils/assertions.ts
new file mode 100644
index 000000000..615e648f3
--- /dev/null
+++ b/packages/contracts/src/utils/assertions.ts
@@ -0,0 +1,63 @@
+import * as chai from 'chai';
+import * as _ from 'lodash';
+
+import { constants } from './constants';
+
+const expect = chai.expect;
+
+function _expectEitherErrorAsync<T>(p: Promise<T>, error1: string, error2: string): PromiseLike<void> {
+ return expect(p)
+ .to.be.rejected()
+ .then(e => {
+ expect(e).to.satisfy(
+ (err: Error) => _.includes(err.message, error1) || _.includes(err.message, error2),
+ `expected promise to reject with error message that includes "${error1}" or "${error2}", but got: ` +
+ `"${e.message}"\n`,
+ );
+ });
+}
+
+/**
+ * Rejects if the given Promise does not reject with an error indicating
+ * insufficient funds.
+ * @param p the Promise which is expected to reject
+ * @returns a new Promise which will reject if the conditions are not met and
+ * otherwise resolve with no value.
+ */
+export function expectInsufficientFundsAsync<T>(p: Promise<T>): PromiseLike<void> {
+ return _expectEitherErrorAsync(p, 'insufficient funds', "sender doesn't have enough funds");
+}
+
+/**
+ * Rejects if the given Promise does not reject with a "revert" error or the
+ * given otherError.
+ * @param p the Promise which is expected to reject
+ * @param otherError the other error which is accepted as a valid reject error.
+ * @returns a new Promise which will reject if the conditions are not met and
+ * otherwise resolve with no value.
+ */
+export function expectRevertOrOtherErrorAsync<T>(p: Promise<T>, otherError: string): PromiseLike<void> {
+ return _expectEitherErrorAsync(p, constants.REVERT, otherError);
+}
+
+/**
+ * Rejects if the given Promise does not reject with a "revert" or "always
+ * failing transaction" error.
+ * @param p the Promise which is expected to reject
+ * @returns a new Promise which will reject if the conditions are not met and
+ * otherwise resolve with no value.
+ */
+export function expectRevertOrAlwaysFailingTransactionAsync<T>(p: Promise<T>): PromiseLike<void> {
+ return expectRevertOrOtherErrorAsync(p, 'always failing transaction');
+}
+
+/**
+ * Rejects if the given Promise does not reject with a "revert" or "Contract
+ * call failed" error.
+ * @param p the Promise which is expected to reject
+ * @returns a new Promise which will reject if the conditions are not met and
+ * otherwise resolve with no value.
+ */
+export function expectRevertOrContractCallFailedAsync<T>(p: Promise<T>): PromiseLike<void> {
+ return expectRevertOrOtherErrorAsync<T>(p, 'Contract call failed');
+}
diff --git a/packages/contracts/src/utils/constants.ts b/packages/contracts/src/utils/constants.ts
index 9b0b92545..fa2a4af3c 100644
--- a/packages/contracts/src/utils/constants.ts
+++ b/packages/contracts/src/utils/constants.ts
@@ -19,6 +19,13 @@ const TESTRPC_PRIVATE_KEYS_STRINGS = [
export const constants = {
INVALID_OPCODE: 'invalid opcode',
REVERT: 'revert',
+ LIB_BYTES_GT_ZERO_LENGTH_REQUIRED: 'Length must be greater than 0.',
+ LIB_BYTES_GTE_4_LENGTH_REQUIRED: 'Length must be greater than or equal to 4.',
+ LIB_BYTES_GTE_20_LENGTH_REQUIRED: 'Length must be greater than or equal to 20.',
+ LIB_BYTES_GTE_32_LENGTH_REQUIRED: 'Length must be greater than or equal to 32.',
+ LIB_BYTES_INDEX_OUT_OF_BOUNDS: 'Specified array index is out of bounds.',
+ ERC20_INSUFFICIENT_BALANCE: 'Insufficient balance to complete transfer.',
+ ERC20_INSUFFICIENT_ALLOWANCE: 'Insufficient allowance to complete transfer.',
TESTRPC_NETWORK_ID: 50,
AWAIT_TRANSACTION_MINED_MS: 100,
MAX_ETHERTOKEN_WITHDRAW_GAS: 43000,
diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts
index ea8eb47c7..7af243466 100644
--- a/packages/contracts/src/utils/exchange_wrapper.ts
+++ b/packages/contracts/src/utils/exchange_wrapper.ts
@@ -60,14 +60,14 @@ export class ExchangeWrapper {
public async fillOrderNoThrowAsync(
signedOrder: SignedOrder,
from: string,
- opts: { takerAssetFillAmount?: BigNumber } = {},
+ opts: { takerAssetFillAmount?: BigNumber; gas?: number } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount);
const txHash = await this._exchange.fillOrderNoThrow.sendTransactionAsync(
params.order,
params.takerAssetFillAmount,
params.signature,
- { from },
+ { from, gas: opts.gas },
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
@@ -105,14 +105,14 @@ export class ExchangeWrapper {
public async batchFillOrdersNoThrowAsync(
orders: SignedOrder[],
from: string,
- opts: { takerAssetFillAmounts?: BigNumber[] } = {},
+ opts: { takerAssetFillAmounts?: BigNumber[]; gas?: number } = {},
): Promise<TransactionReceiptWithDecodedLogs> {
const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts);
const txHash = await this._exchange.batchFillOrdersNoThrow.sendTransactionAsync(
params.orders,
params.takerAssetFillAmounts,
params.signatures,
- { from },
+ { from, gas: opts.gas },
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
@@ -135,14 +135,14 @@ export class ExchangeWrapper {
public async marketSellOrdersNoThrowAsync(
orders: SignedOrder[],
from: string,
- opts: { takerAssetFillAmount: BigNumber },
+ opts: { takerAssetFillAmount: BigNumber; gas?: number },
): Promise<TransactionReceiptWithDecodedLogs> {
const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount);
const txHash = await this._exchange.marketSellOrdersNoThrow.sendTransactionAsync(
params.orders,
params.takerAssetFillAmount,
params.signatures,
- { from },
+ { from, gas: opts.gas },
);
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
diff --git a/packages/contracts/src/utils/increase_time.ts b/packages/contracts/src/utils/increase_time.ts
new file mode 100644
index 000000000..4565d8dbc
--- /dev/null
+++ b/packages/contracts/src/utils/increase_time.ts
@@ -0,0 +1,31 @@
+import * as _ from 'lodash';
+
+import { constants } from './constants';
+import { web3Wrapper } from './web3_wrapper';
+
+let firstAccount: string | undefined;
+
+/**
+ * Increases time by the given number of seconds and then mines a block so that
+ * the current block timestamp has the offset applied.
+ * @param seconds the number of seconds by which to incrase the time offset.
+ * @returns a new Promise which will resolve with the new total time offset or
+ * reject if the time could not be increased.
+ */
+export async function increaseTimeAndMineBlockAsync(seconds: number): Promise<number> {
+ if (_.isUndefined(firstAccount)) {
+ const accounts = await web3Wrapper.getAvailableAddressesAsync();
+ firstAccount = accounts[0];
+ }
+
+ const offset = await web3Wrapper.increaseTimeAsync(seconds);
+ // Note: we need to send a transaction after increasing time so
+ // that a block is actually mined. The contract looks at the
+ // last mined block for the timestamp.
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await web3Wrapper.sendTransactionAsync({ from: firstAccount, to: firstAccount, value: 0 }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+
+ return offset;
+}
diff --git a/packages/contracts/src/utils/web3_wrapper.ts b/packages/contracts/src/utils/web3_wrapper.ts
index 95fb55753..df9bf88c8 100644
--- a/packages/contracts/src/utils/web3_wrapper.ts
+++ b/packages/contracts/src/utils/web3_wrapper.ts
@@ -4,15 +4,50 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { coverage } from './coverage';
-export const txDefaults = {
+enum ProviderType {
+ Ganache = 'ganache',
+ Geth = 'geth',
+}
+
+let testProvider: ProviderType;
+switch (process.env.TEST_PROVIDER) {
+ case undefined:
+ testProvider = ProviderType.Ganache;
+ break;
+ case 'ganache':
+ testProvider = ProviderType.Ganache;
+ break;
+ case 'geth':
+ testProvider = ProviderType.Geth;
+ break;
+ default:
+ throw new Error(`Unknown TEST_PROVIDER: ${process.env.TEST_PROVIDER}`);
+}
+
+const ganacheTxDefaults = {
from: devConstants.TESTRPC_FIRST_ADDRESS,
gas: devConstants.GAS_LIMIT,
};
-const providerConfigs = { shouldUseInProcessGanache: true };
+const gethTxDefaults = {
+ from: devConstants.TESTRPC_FIRST_ADDRESS,
+};
+export const txDefaults = testProvider === ProviderType.Ganache ? ganacheTxDefaults : gethTxDefaults;
+
+const gethConfigs = {
+ shouldUseInProcessGanache: false,
+ rpcUrl: 'http://localhost:8501',
+ shouldUseFakeGasEstimate: false,
+};
+const ganacheConfigs = {
+ shouldUseInProcessGanache: true,
+};
+const providerConfigs = testProvider === ProviderType.Ganache ? ganacheConfigs : gethConfigs;
+
export const provider = web3Factory.getRpcProvider(providerConfigs);
const isCoverageEnabled = env.parseBoolean(EnvVars.SolidityCoverage);
if (isCoverageEnabled) {
const coverageSubprovider = coverage.getCoverageSubproviderSingleton();
prependSubprovider(provider, coverageSubprovider);
}
+
export const web3Wrapper = new Web3Wrapper(provider);