aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/test/multisig/multi_sig_with_time_lock.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts/test/multisig/multi_sig_with_time_lock.ts')
-rw-r--r--packages/contracts/test/multisig/multi_sig_with_time_lock.ts184
1 files changed, 180 insertions, 4 deletions
diff --git a/packages/contracts/test/multisig/multi_sig_with_time_lock.ts b/packages/contracts/test/multisig/multi_sig_with_time_lock.ts
index 8eeeeca6b..05d8bbb36 100644
--- a/packages/contracts/test/multisig/multi_sig_with_time_lock.ts
+++ b/packages/contracts/test/multisig/multi_sig_with_time_lock.ts
@@ -1,14 +1,21 @@
import { BlockchainLifecycle } from '@0xproject/dev-utils';
+import { RevertReason } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
import { LogWithDecodedArgs } from 'ethereum-types';
+import * as _ from 'lodash';
+import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token';
import {
+ MultiSigWalletWithTimeLockConfirmationEventArgs,
+ MultiSigWalletWithTimeLockConfirmationTimeSetEventArgs,
MultiSigWalletWithTimeLockContract,
+ MultiSigWalletWithTimeLockExecutionEventArgs,
+ MultiSigWalletWithTimeLockExecutionFailureEventArgs,
MultiSigWalletWithTimeLockSubmissionEventArgs,
} from '../../generated_contract_wrappers/multi_sig_wallet_with_time_lock';
import { artifacts } from '../utils/artifacts';
-import { expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
+import { expectTransactionFailedAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
import { increaseTimeAndMineBlockAsync } from '../utils/block_timestamp';
import { chaiSetup } from '../utils/chai_setup';
import { constants } from '../utils/constants';
@@ -21,6 +28,7 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:no-unnecessary-type-assertion
describe('MultiSigWalletWithTimeLock', () => {
let owners: string[];
+ let notOwner: string;
const REQUIRED_APPROVALS = new BigNumber(2);
const SECONDS_TIME_LOCKED = new BigNumber(1000000);
@@ -32,7 +40,8 @@ describe('MultiSigWalletWithTimeLock', () => {
});
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
- owners = [accounts[0], accounts[1]];
+ owners = [accounts[0], accounts[1], accounts[2]];
+ notOwner = accounts[3];
});
let multiSig: MultiSigWalletWithTimeLockContract;
@@ -45,6 +54,171 @@ describe('MultiSigWalletWithTimeLock', () => {
await blockchainLifecycle.revertAsync();
});
+ describe('external_call', () => {
+ it('should be internal', async () => {
+ const secondsTimeLocked = new BigNumber(0);
+ multiSig = await MultiSigWalletWithTimeLockContract.deployFrom0xArtifactAsync(
+ artifacts.MultiSigWalletWithTimeLock,
+ provider,
+ txDefaults,
+ owners,
+ REQUIRED_APPROVALS,
+ secondsTimeLocked,
+ );
+ expect(_.isUndefined((multiSig as any).external_call)).to.be.equal(true);
+ });
+ });
+ describe('confirmTransaction', () => {
+ let txId: BigNumber;
+ beforeEach(async () => {
+ const secondsTimeLocked = new BigNumber(0);
+ multiSig = await MultiSigWalletWithTimeLockContract.deployFrom0xArtifactAsync(
+ artifacts.MultiSigWalletWithTimeLock,
+ provider,
+ txDefaults,
+ owners,
+ REQUIRED_APPROVALS,
+ secondsTimeLocked,
+ );
+ multiSigWrapper = new MultiSigWrapper(multiSig, provider);
+ const destination = notOwner;
+ const data = constants.NULL_BYTES;
+ const txReceipt = await multiSigWrapper.submitTransactionAsync(destination, data, owners[0]);
+ txId = (txReceipt.logs[0] as LogWithDecodedArgs<MultiSigWalletWithTimeLockSubmissionEventArgs>).args
+ .transactionId;
+ });
+ it('should revert if called by a non-owner', async () => {
+ await expectTransactionFailedWithoutReasonAsync(multiSigWrapper.confirmTransactionAsync(txId, notOwner));
+ });
+ it('should revert if transaction does not exist', async () => {
+ const nonexistentTxId = new BigNumber(123456789);
+ await expectTransactionFailedWithoutReasonAsync(
+ multiSigWrapper.confirmTransactionAsync(nonexistentTxId, owners[1]),
+ );
+ });
+ it('should revert if transaction is already confirmed by caller', async () => {
+ await expectTransactionFailedWithoutReasonAsync(multiSigWrapper.confirmTransactionAsync(txId, owners[0]));
+ });
+ it('should confirm transaction for caller and log a Confirmation event', async () => {
+ const txReceipt = await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ const log = txReceipt.logs[0] as LogWithDecodedArgs<MultiSigWalletWithTimeLockConfirmationEventArgs>;
+ expect(log.event).to.be.equal('Confirmation');
+ expect(log.args.sender).to.be.equal(owners[1]);
+ expect(log.args.transactionId).to.be.bignumber.equal(txId);
+ });
+ it('should revert if fully confirmed', async () => {
+ await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await expectTransactionFailedAsync(
+ multiSigWrapper.confirmTransactionAsync(txId, owners[2]),
+ RevertReason.TxFullyConfirmed,
+ );
+ });
+ it('should set the confirmation time of the transaction if it becomes fully confirmed', async () => {
+ const txReceipt = await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ const blockNum = await web3Wrapper.getBlockNumberAsync();
+ const timestamp = new BigNumber(await web3Wrapper.getBlockTimestampAsync(blockNum));
+ const log = txReceipt.logs[1] as LogWithDecodedArgs<MultiSigWalletWithTimeLockConfirmationTimeSetEventArgs>;
+ expect(log.args.confirmationTime).to.be.bignumber.equal(timestamp);
+ expect(log.args.transactionId).to.be.bignumber.equal(txId);
+ });
+ });
+ describe('executeTransaction', () => {
+ let txId: BigNumber;
+ const secondsTimeLocked = new BigNumber(1000000);
+ beforeEach(async () => {
+ multiSig = await MultiSigWalletWithTimeLockContract.deployFrom0xArtifactAsync(
+ artifacts.MultiSigWalletWithTimeLock,
+ provider,
+ txDefaults,
+ owners,
+ REQUIRED_APPROVALS,
+ secondsTimeLocked,
+ );
+ multiSigWrapper = new MultiSigWrapper(multiSig, provider);
+ const destination = notOwner;
+ const data = constants.NULL_BYTES;
+ const txReceipt = await multiSigWrapper.submitTransactionAsync(destination, data, owners[0]);
+ txId = (txReceipt.logs[0] as LogWithDecodedArgs<MultiSigWalletWithTimeLockSubmissionEventArgs>).args
+ .transactionId;
+ });
+ it('should revert if transaction has not been fully confirmed', async () => {
+ await increaseTimeAndMineBlockAsync(secondsTimeLocked.toNumber());
+ await expectTransactionFailedAsync(
+ multiSigWrapper.executeTransactionAsync(txId, owners[1]),
+ RevertReason.TxNotFullyConfirmed,
+ );
+ });
+ it('should revert if time lock has not passed', async () => {
+ await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await expectTransactionFailedAsync(
+ multiSigWrapper.executeTransactionAsync(txId, owners[1]),
+ RevertReason.TimeLockIncomplete,
+ );
+ });
+ it('should execute a transaction and log an Execution event if successful and called by owner', async () => {
+ await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await increaseTimeAndMineBlockAsync(secondsTimeLocked.toNumber());
+ const txReceipt = await multiSigWrapper.executeTransactionAsync(txId, owners[1]);
+ const log = txReceipt.logs[0] as LogWithDecodedArgs<MultiSigWalletWithTimeLockExecutionEventArgs>;
+ expect(log.event).to.be.equal('Execution');
+ expect(log.args.transactionId).to.be.bignumber.equal(txId);
+ });
+ it('should execute a transaction and log an Execution event if successful and called by non-owner', async () => {
+ await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await increaseTimeAndMineBlockAsync(secondsTimeLocked.toNumber());
+ const txReceipt = await multiSigWrapper.executeTransactionAsync(txId, notOwner);
+ const log = txReceipt.logs[0] as LogWithDecodedArgs<MultiSigWalletWithTimeLockExecutionEventArgs>;
+ expect(log.event).to.be.equal('Execution');
+ expect(log.args.transactionId).to.be.bignumber.equal(txId);
+ });
+ it('should revert if a required confirmation is revoked before executeTransaction is called', async () => {
+ await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await increaseTimeAndMineBlockAsync(secondsTimeLocked.toNumber());
+ await multiSigWrapper.revokeConfirmationAsync(txId, owners[0]);
+ await expectTransactionFailedAsync(
+ multiSigWrapper.executeTransactionAsync(txId, owners[1]),
+ RevertReason.TxNotFullyConfirmed,
+ );
+ });
+ it('should revert if transaction has been executed', async () => {
+ await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
+ await increaseTimeAndMineBlockAsync(secondsTimeLocked.toNumber());
+ const txReceipt = await multiSigWrapper.executeTransactionAsync(txId, owners[1]);
+ const log = txReceipt.logs[0] as LogWithDecodedArgs<MultiSigWalletWithTimeLockExecutionEventArgs>;
+ expect(log.args.transactionId).to.be.bignumber.equal(txId);
+ await expectTransactionFailedWithoutReasonAsync(multiSigWrapper.executeTransactionAsync(txId, owners[1]));
+ });
+ it("should log an ExecutionFailure event and not update the transaction's execution state if unsuccessful", async () => {
+ const contractWithoutFallback = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
+ artifacts.DummyERC20Token,
+ provider,
+ txDefaults,
+ constants.DUMMY_TOKEN_NAME,
+ constants.DUMMY_TOKEN_SYMBOL,
+ constants.DUMMY_TOKEN_DECIMALS,
+ constants.DUMMY_TOKEN_TOTAL_SUPPLY,
+ );
+ const data = constants.NULL_BYTES;
+ const value = new BigNumber(10);
+ const submissionTxReceipt = await multiSigWrapper.submitTransactionAsync(
+ contractWithoutFallback.address,
+ data,
+ owners[0],
+ { value },
+ );
+ const newTxId = (submissionTxReceipt.logs[0] as LogWithDecodedArgs<
+ MultiSigWalletWithTimeLockSubmissionEventArgs
+ >).args.transactionId;
+ await multiSigWrapper.confirmTransactionAsync(newTxId, owners[1]);
+ await increaseTimeAndMineBlockAsync(secondsTimeLocked.toNumber());
+ const txReceipt = await multiSigWrapper.executeTransactionAsync(newTxId, owners[1]);
+ const executionFailureLog = txReceipt.logs[0] as LogWithDecodedArgs<
+ MultiSigWalletWithTimeLockExecutionFailureEventArgs
+ >;
+ expect(executionFailureLog.event).to.be.equal('ExecutionFailure');
+ expect(executionFailureLog.args.transactionId).to.be.bignumber.equal(newTxId);
+ });
+ });
describe('changeTimeLock', () => {
describe('initially non-time-locked', async () => {
before(async () => {
@@ -78,8 +252,9 @@ describe('MultiSigWalletWithTimeLock', () => {
const res = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]);
const log = res.logs[0] as LogWithDecodedArgs<MultiSigWalletWithTimeLockSubmissionEventArgs>;
const txId = log.args.transactionId;
- return expectTransactionFailedWithoutReasonAsync(
+ return expectTransactionFailedAsync(
multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
+ RevertReason.TxNotFullyConfirmed,
);
});
@@ -147,8 +322,9 @@ describe('MultiSigWalletWithTimeLock', () => {
});
it('should throw if it has enough confirmations but is not past the time lock', async () => {
- return expectTransactionFailedWithoutReasonAsync(
+ return expectTransactionFailedAsync(
multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
+ RevertReason.TimeLockIncomplete,
);
});