Update multisig tests and utils
This commit is contained in:
@@ -1,43 +1,61 @@
|
||||
import { AbiDefinition, MethodAbi } from '@0xproject/types';
|
||||
import { TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import ABI = require('ethereumjs-abi');
|
||||
import ethUtil = require('ethereumjs-util');
|
||||
import * as _ from 'lodash';
|
||||
import * as Web3 from 'web3';
|
||||
|
||||
import { MultiSigWalletContract } from '../contract_wrappers/generated/multi_sig_wallet';
|
||||
import { MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddressContract } from '../contract_wrappers/generated/multi_sig_wallet_with_time_lock_except_remove_authorized_address';
|
||||
|
||||
import { TransactionDataParams } from './types';
|
||||
import { constants } from './constants';
|
||||
import { LogDecoder } from './log_decoder';
|
||||
|
||||
export class MultiSigWrapper {
|
||||
private _multiSig: MultiSigWalletContract;
|
||||
public static encodeFnArgs(name: string, abi: AbiDefinition[], args: any[]): string {
|
||||
const abiEntity = _.find(abi, { name }) as MethodAbi;
|
||||
if (_.isUndefined(abiEntity)) {
|
||||
throw new Error(`Did not find abi entry for name: ${name}`);
|
||||
}
|
||||
const types = _.map(abiEntity.inputs, input => input.type);
|
||||
const funcSig = ethUtil.bufferToHex(ABI.methodID(name, types));
|
||||
const argsData = _.map(args, arg => {
|
||||
const target = _.isBoolean(arg) ? +arg : arg;
|
||||
const targetBuff = ethUtil.toBuffer(target);
|
||||
return ethUtil.setLengthLeft(targetBuff, 32).toString('hex');
|
||||
});
|
||||
return funcSig + argsData.join('');
|
||||
}
|
||||
constructor(multiSigContract: MultiSigWalletContract) {
|
||||
private _logDecoder: LogDecoder = new LogDecoder(constants.TESTRPC_NETWORK_ID);
|
||||
private _zeroEx: ZeroEx;
|
||||
constructor(multiSigContract: MultiSigWalletContract, zeroEx: ZeroEx) {
|
||||
this._multiSig = multiSigContract;
|
||||
this._zeroEx = zeroEx;
|
||||
}
|
||||
public async submitTransactionAsync(
|
||||
destination: string,
|
||||
data: string,
|
||||
from: string,
|
||||
dataParams: TransactionDataParams,
|
||||
value: BigNumber = new BigNumber(0),
|
||||
): Promise<string> {
|
||||
const { name, abi, args = [] } = dataParams;
|
||||
const encoded = MultiSigWrapper.encodeFnArgs(name, abi, args);
|
||||
return this._multiSig.submitTransaction.sendTransactionAsync(destination, value, encoded, {
|
||||
opts: { value?: BigNumber } = {},
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const value = _.isUndefined(opts.value) ? new BigNumber(0) : opts.value;
|
||||
const txHash = await this._multiSig.submitTransaction.sendTransactionAsync(destination, value, data, {
|
||||
from,
|
||||
});
|
||||
const tx = await this._getTxWithDecodedMultiSigLogs(txHash);
|
||||
return tx;
|
||||
}
|
||||
public async confirmTransactionAsync(txId: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await this._multiSig.confirmTransaction.sendTransactionAsync(txId, { from });
|
||||
const tx = await this._getTxWithDecodedMultiSigLogs(txHash);
|
||||
return tx;
|
||||
}
|
||||
public async executeTransactionAsync(txId: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await this._multiSig.executeTransaction.sendTransactionAsync(txId, { from });
|
||||
const tx = await this._getTxWithDecodedMultiSigLogs(txHash);
|
||||
return tx;
|
||||
}
|
||||
public async executeRemoveAuthorizedAddressAsync(
|
||||
txId: BigNumber,
|
||||
from: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await (this
|
||||
._multiSig as MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddressContract).executeRemoveAuthorizedAddress.sendTransactionAsync(
|
||||
txId,
|
||||
{ from },
|
||||
);
|
||||
const tx = await this._getTxWithDecodedMultiSigLogs(txHash);
|
||||
return tx;
|
||||
}
|
||||
private async _getTxWithDecodedMultiSigLogs(txHash: string) {
|
||||
const tx = await this._zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
tx.logs = _.filter(tx.logs, log => log.address === this._multiSig.address);
|
||||
tx.logs = _.map(tx.logs, log => this._logDecoder.decodeLogOrThrow(log));
|
||||
return tx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,11 +127,6 @@ describe('Exchange core', () => {
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
describe('internal functions', () => {
|
||||
it('should include transferViaTokenTransferProxy', () => {
|
||||
expect((exchange as any).transferViaTokenTransferProxy).to.be.undefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fillOrder', () => {
|
||||
beforeEach(async () => {
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
import { BlockchainLifecycle, web3Factory } from '@0xproject/dev-utils';
|
||||
import { LogWithDecodedArgs } from '@0xproject/types';
|
||||
import { AbiDecoder, BigNumber } from '@0xproject/utils';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import * as chai from 'chai';
|
||||
import * as _ from 'lodash';
|
||||
import 'make-promises-safe';
|
||||
import * as Web3 from 'web3';
|
||||
|
||||
import * as multiSigWalletJSON from '../../build/contracts/MultiSigWalletWithTimeLock.json';
|
||||
import { MultiSigWalletContract } from '../src/contract_wrappers/generated/multi_sig_wallet';
|
||||
import { MultiSigWalletWithTimeLockContract } from '../src/contract_wrappers/generated/multi_sig_wallet_with_time_lock';
|
||||
import {
|
||||
MultiSigWalletWithTimeLockContract,
|
||||
SubmissionContractEventArgs,
|
||||
} from '../src/contract_wrappers/generated/multi_sig_wallet_with_time_lock';
|
||||
import { artifacts } from '../src/utils/artifacts';
|
||||
import { chaiSetup } from '../src/utils/chai_setup';
|
||||
import { constants } from '../src/utils/constants';
|
||||
import { MultiSigWrapper } from '../src/utils/multi_sig_wrapper';
|
||||
import { SubmissionContractEventArgs } from '../src/utils/types';
|
||||
import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper';
|
||||
|
||||
const MULTI_SIG_ABI = artifacts.MultiSigWalletWithTimeLock.compilerOutput.abi;
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
const abiDecoder = new AbiDecoder([MULTI_SIG_ABI]);
|
||||
|
||||
describe('MultiSigWalletWithTimeLock', () => {
|
||||
let owners: string[];
|
||||
const requiredApprovals = new BigNumber(2);
|
||||
const SECONDS_TIME_LOCKED = new BigNumber(1000000);
|
||||
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
owners = [accounts[0], accounts[1]];
|
||||
});
|
||||
const SIGNATURES_REQUIRED = new BigNumber(2);
|
||||
const SECONDS_TIME_LOCKED = new BigNumber(10000);
|
||||
|
||||
let multiSig: MultiSigWalletWithTimeLockContract;
|
||||
let multiSigWrapper: MultiSigWrapper;
|
||||
let txId: BigNumber;
|
||||
let initialSecondsTimeLocked: number;
|
||||
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
@@ -51,19 +50,18 @@ describe('MultiSigWalletWithTimeLock', () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before('deploy a wallet', async () => {
|
||||
const secondsTimeLocked = new BigNumber(0);
|
||||
multiSig = await MultiSigWalletWithTimeLockContract.deployFrom0xArtifactAsync(
|
||||
artifacts.MultiSigWalletWithTimeLock,
|
||||
provider,
|
||||
txDefaults,
|
||||
owners,
|
||||
SIGNATURES_REQUIRED,
|
||||
new BigNumber(0),
|
||||
requiredApprovals,
|
||||
secondsTimeLocked,
|
||||
);
|
||||
multiSigWrapper = new MultiSigWrapper((multiSig as any) as MultiSigWalletContract);
|
||||
|
||||
const secondsTimeLocked = await multiSig.secondsTimeLocked.callAsync();
|
||||
initialSecondsTimeLocked = secondsTimeLocked.toNumber();
|
||||
multiSigWrapper = new MultiSigWrapper(multiSig, provider);
|
||||
});
|
||||
|
||||
it('should throw when not called by wallet', async () => {
|
||||
return expect(
|
||||
multiSig.changeTimeLock.sendTransactionAsync(SECONDS_TIME_LOCKED, { from: owners[0] }),
|
||||
@@ -72,22 +70,11 @@ describe('MultiSigWalletWithTimeLock', () => {
|
||||
|
||||
it('should throw without enough confirmations', async () => {
|
||||
const destination = multiSig.address;
|
||||
const from = owners[0];
|
||||
const dataParams = {
|
||||
name: 'changeTimeLock',
|
||||
abi: MULTI_SIG_ABI,
|
||||
args: [SECONDS_TIME_LOCKED.toNumber()],
|
||||
};
|
||||
const txHash = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
|
||||
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
txHash,
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const log = abiDecoder.tryToDecodeLogOrNoop(txReceipt.logs[0]) as LogWithDecodedArgs<
|
||||
SubmissionContractEventArgs
|
||||
>;
|
||||
const changeTimeLockData = multiSig.changeTimeLock.getABIEncodedTransactionData(SECONDS_TIME_LOCKED);
|
||||
const res = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = log.args.transactionId;
|
||||
|
||||
txId = log.args.transactionId;
|
||||
return expect(
|
||||
multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
@@ -95,28 +82,13 @@ describe('MultiSigWalletWithTimeLock', () => {
|
||||
|
||||
it('should set confirmation time with enough confirmations', async () => {
|
||||
const destination = multiSig.address;
|
||||
const from = owners[0];
|
||||
const dataParams = {
|
||||
name: 'changeTimeLock',
|
||||
abi: MULTI_SIG_ABI,
|
||||
args: [SECONDS_TIME_LOCKED.toNumber()],
|
||||
};
|
||||
let txHash = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
|
||||
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
txHash,
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const log = abiDecoder.tryToDecodeLogOrNoop(txReceipt.logs[0]) as LogWithDecodedArgs<
|
||||
SubmissionContractEventArgs
|
||||
>;
|
||||
const changeTimeLockData = multiSig.changeTimeLock.getABIEncodedTransactionData(SECONDS_TIME_LOCKED);
|
||||
const subRes = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]);
|
||||
const subLog = subRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = subLog.args.transactionId;
|
||||
|
||||
txId = log.args.transactionId;
|
||||
txHash = await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] });
|
||||
const res = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
txHash,
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
expect(res.logs).to.have.length(2);
|
||||
const confirmRes = await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
|
||||
expect(confirmRes.logs).to.have.length(2);
|
||||
|
||||
const blockNum = await web3Wrapper.getBlockNumberAsync();
|
||||
const blockInfo = await web3Wrapper.getBlockAsync(blockNum);
|
||||
@@ -128,33 +100,13 @@ describe('MultiSigWalletWithTimeLock', () => {
|
||||
|
||||
it('should be executable with enough confirmations and secondsTimeLocked of 0', async () => {
|
||||
const destination = multiSig.address;
|
||||
const from = owners[0];
|
||||
const dataParams = {
|
||||
name: 'changeTimeLock',
|
||||
abi: MULTI_SIG_ABI,
|
||||
args: [SECONDS_TIME_LOCKED.toNumber()],
|
||||
};
|
||||
let txHash = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
|
||||
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
txHash,
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
const log = abiDecoder.tryToDecodeLogOrNoop(txReceipt.logs[0]) as LogWithDecodedArgs<
|
||||
SubmissionContractEventArgs
|
||||
>;
|
||||
const changeTimeLockData = multiSig.changeTimeLock.getABIEncodedTransactionData(SECONDS_TIME_LOCKED);
|
||||
const subRes = await multiSigWrapper.submitTransactionAsync(destination, changeTimeLockData, owners[0]);
|
||||
const subLog = subRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = subLog.args.transactionId;
|
||||
|
||||
txId = log.args.transactionId;
|
||||
txHash = await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] });
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
|
||||
|
||||
expect(initialSecondsTimeLocked).to.be.equal(0);
|
||||
|
||||
txHash = await multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] });
|
||||
const res = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
txHash,
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
expect(res.logs).to.have.length(2);
|
||||
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
|
||||
await multiSigWrapper.executeTransactionAsync(txId, owners[1]);
|
||||
|
||||
const secondsTimeLocked = new BigNumber(await multiSig.secondsTimeLocked.callAsync());
|
||||
expect(secondsTimeLocked).to.be.bignumber.equal(SECONDS_TIME_LOCKED);
|
||||
@@ -167,45 +119,29 @@ describe('MultiSigWalletWithTimeLock', () => {
|
||||
after(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
before('deploy a wallet', async () => {
|
||||
let txId: BigNumber;
|
||||
const newSecondsTimeLocked = new BigNumber(0);
|
||||
before('deploy a wallet, submit transaction to change timelock, and confirm the transaction', async () => {
|
||||
multiSig = await MultiSigWalletWithTimeLockContract.deployFrom0xArtifactAsync(
|
||||
artifacts.MultiSigWalletWithTimeLock,
|
||||
provider,
|
||||
txDefaults,
|
||||
owners,
|
||||
SIGNATURES_REQUIRED,
|
||||
requiredApprovals,
|
||||
SECONDS_TIME_LOCKED,
|
||||
);
|
||||
multiSigWrapper = new MultiSigWrapper((multiSig as any) as MultiSigWalletContract);
|
||||
multiSigWrapper = new MultiSigWrapper(multiSig, provider);
|
||||
|
||||
const secondsTimeLocked = await multiSig.secondsTimeLocked.callAsync();
|
||||
initialSecondsTimeLocked = secondsTimeLocked.toNumber();
|
||||
const destination = multiSig.address;
|
||||
const from = owners[0];
|
||||
const dataParams = {
|
||||
name: 'changeTimeLock',
|
||||
abi: MULTI_SIG_ABI,
|
||||
args: [newSecondsTimeLocked],
|
||||
};
|
||||
let txHash = await multiSigWrapper.submitTransactionAsync(destination, from, dataParams);
|
||||
let txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
txHash,
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
const changeTimeLockData = multiSig.changeTimeLock.getABIEncodedTransactionData(newSecondsTimeLocked);
|
||||
const res = await multiSigWrapper.submitTransactionAsync(
|
||||
multiSig.address,
|
||||
changeTimeLockData,
|
||||
owners[0],
|
||||
);
|
||||
const log = abiDecoder.tryToDecodeLogOrNoop(txReceipt.logs[0]) as LogWithDecodedArgs<
|
||||
SubmissionContractEventArgs
|
||||
>;
|
||||
const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
txId = log.args.transactionId;
|
||||
txHash = await multiSig.confirmTransaction.sendTransactionAsync(txId, {
|
||||
from: owners[1],
|
||||
});
|
||||
txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
txHash,
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
expect(txReceipt.logs).to.have.length(2);
|
||||
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
|
||||
});
|
||||
const newSecondsTimeLocked = 0;
|
||||
it('should throw if it has enough confirmations but is not past the time lock', async () => {
|
||||
return expect(
|
||||
multiSig.executeTransaction.sendTransactionAsync(txId, { from: owners[0] }),
|
||||
|
||||
@@ -1,67 +1,52 @@
|
||||
/*
|
||||
*
|
||||
* @TODO: Before deploying, the MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress contract must be updated
|
||||
* to have a mapping of all approved addresses. These tests must be updated appropriately.
|
||||
* For now, these tests have been commented out by @hysz (greg@0xproject.com).
|
||||
*
|
||||
import { LogWithDecodedArgs, ZeroEx } from '0x.js';
|
||||
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
|
||||
import { AbiDecoder, BigNumber } from '@0xproject/utils';
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import { BlockchainLifecycle } from '@0xproject/dev-utils';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as _ from 'lodash';
|
||||
import 'make-promises-safe';
|
||||
import * as Web3 from 'web3';
|
||||
|
||||
import { MultiSigWalletContract } from '../src/contract_wrappers/generated/multi_sig_wallet';
|
||||
import { MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddressContract } from '../src/contract_wrappers/generated/multi_sig_wallet_with_time_lock_except_remove_authorized_address';
|
||||
import { TokenTransferProxyContract } from '../src/contract_wrappers/generated/token_transfer_proxy';
|
||||
import { AuthorizableContract } from '../src/contract_wrappers/generated/authorizable';
|
||||
import {
|
||||
AssetProxyRegistrationContractEventArgs,
|
||||
ExecutionContractEventArgs,
|
||||
ExecutionFailureContractEventArgs,
|
||||
MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddressContract,
|
||||
SubmissionContractEventArgs,
|
||||
} from '../src/contract_wrappers/generated/multi_sig_wallet_with_time_lock_except_remove_authorized_address';
|
||||
import { artifacts } from '../src/utils/artifacts';
|
||||
import { chaiSetup } from '../src/utils/chai_setup';
|
||||
import { constants } from '../src/utils/constants';
|
||||
import { crypto } from '../src/utils/crypto';
|
||||
import { MultiSigWrapper } from '../src/utils/multi_sig_wrapper';
|
||||
import { ContractName, SubmissionContractEventArgs, TransactionDataParams } from '../src/utils/types';
|
||||
|
||||
import { chaiSetup } from './utils/chai_setup';
|
||||
|
||||
import { provider, txDefaults, web3Wrapper } from './utils/web3_wrapper';
|
||||
|
||||
const PROXY_ABI = artifacts.TokenTransferProxy.compilerOutput.abi;
|
||||
const MUTISIG_WALLET_WITH_TIME_LOCK_EXCEPT_REMOVE_AUTHORIZED_ADDRESS_ABI =
|
||||
artifacts.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.compilerOutput.abi;
|
||||
import { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
const abiDecoder = new AbiDecoder([MUTISIG_WALLET_WITH_TIME_LOCK_EXCEPT_REMOVE_AUTHORIZED_ADDRESS_ABI]);
|
||||
const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID });
|
||||
|
||||
describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => {
|
||||
const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID });
|
||||
let owners: string[];
|
||||
let authorized: string;
|
||||
const requiredApprovals = new BigNumber(2);
|
||||
const SECONDS_TIME_LOCKED = new BigNumber(1000000);
|
||||
|
||||
// initialize fake addresses
|
||||
let authorizedAddress: string;
|
||||
let unauthorizedAddress: string;
|
||||
|
||||
let tokenTransferProxy: TokenTransferProxyContract;
|
||||
let erc20Proxy: AuthorizableContract;
|
||||
let erc721Proxy: AuthorizableContract;
|
||||
let multiSig: MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddressContract;
|
||||
let multiSigWrapper: MultiSigWrapper;
|
||||
|
||||
let validDestination: string;
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
owners = [accounts[0], accounts[1]];
|
||||
[authorizedAddress, unauthorizedAddress] = accounts;
|
||||
const initialOwner = accounts[0];
|
||||
tokenTransferProxy = await TokenTransferProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TokenTransferProxy,
|
||||
const initialOwner = (authorized = accounts[0]);
|
||||
erc20Proxy = await AuthorizableContract.deployFrom0xArtifactAsync(artifacts.Authorizable, provider, txDefaults);
|
||||
erc721Proxy = await AuthorizableContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Authorizable,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(authorizedAddress, {
|
||||
from: initialOwner,
|
||||
});
|
||||
const defaultAssetProxyContractAddresses: string[] = [];
|
||||
multiSig = await MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddressContract.deployFrom0xArtifactAsync(
|
||||
artifacts.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,
|
||||
provider,
|
||||
@@ -69,13 +54,11 @@ describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => {
|
||||
owners,
|
||||
requiredApprovals,
|
||||
SECONDS_TIME_LOCKED,
|
||||
tokenTransferProxy.address,
|
||||
defaultAssetProxyContractAddresses,
|
||||
);
|
||||
await tokenTransferProxy.transferOwnership.sendTransactionAsync(multiSig.address, {
|
||||
from: initialOwner,
|
||||
});
|
||||
multiSigWrapper = new MultiSigWrapper((multiSig as any) as MultiSigWalletContract);
|
||||
validDestination = tokenTransferProxy.address;
|
||||
multiSigWrapper = new MultiSigWrapper(multiSig, zeroEx);
|
||||
await erc20Proxy.transferOwnership.sendTransactionAsync(multiSig.address, { from: initialOwner });
|
||||
await erc721Proxy.transferOwnership.sendTransactionAsync(multiSig.address, { from: initialOwner });
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -84,31 +67,195 @@ describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should register passed in assetProxyContracts', async () => {
|
||||
const assetProxyContractAddresses = [erc20Proxy.address, erc721Proxy.address];
|
||||
const newMultiSig = await MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddressContract.deployFrom0xArtifactAsync(
|
||||
artifacts.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,
|
||||
provider,
|
||||
txDefaults,
|
||||
owners,
|
||||
requiredApprovals,
|
||||
SECONDS_TIME_LOCKED,
|
||||
assetProxyContractAddresses,
|
||||
);
|
||||
const isErc20ProxyRegistered = await newMultiSig.isAssetProxyRegistered.callAsync(erc20Proxy.address);
|
||||
const isErc721ProxyRegistered = await newMultiSig.isAssetProxyRegistered.callAsync(erc721Proxy.address);
|
||||
expect(isErc20ProxyRegistered).to.equal(true);
|
||||
expect(isErc721ProxyRegistered).to.equal(true);
|
||||
});
|
||||
it('should throw if a null address is included in assetProxyContracts', async () => {
|
||||
const assetProxyContractAddresses = [erc20Proxy.address, ZeroEx.NULL_ADDRESS];
|
||||
expect(
|
||||
MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddressContract.deployFrom0xArtifactAsync(
|
||||
artifacts.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,
|
||||
provider,
|
||||
txDefaults,
|
||||
owners,
|
||||
requiredApprovals,
|
||||
SECONDS_TIME_LOCKED,
|
||||
assetProxyContractAddresses,
|
||||
),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
});
|
||||
describe('readFirst4', () => {
|
||||
it('should return the first 4 bytes of a byte array of arbitrary length', async () => {
|
||||
const addAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData(owners[0]);
|
||||
const removeAuthorizedAddressData = erc20Proxy.removeAuthorizedAddress.getABIEncodedTransactionData(
|
||||
owners[0],
|
||||
);
|
||||
const expectedAddAuthorizedAddressSelector = addAuthorizedAddressData.slice(0, 10);
|
||||
const expectedRemoveAuthorizedAddressSelector = removeAuthorizedAddressData.slice(0, 10);
|
||||
const [addAuthorizedAddressSelector, removeAuthorizedAddressSelector] = await Promise.all([
|
||||
multiSig.readFirst4.callAsync(addAuthorizedAddressData),
|
||||
multiSig.readFirst4.callAsync(removeAuthorizedAddressData),
|
||||
]);
|
||||
expect(expectedAddAuthorizedAddressSelector).to.equal(addAuthorizedAddressSelector);
|
||||
expect(expectedRemoveAuthorizedAddressSelector).to.equal(removeAuthorizedAddressSelector);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isFunctionRemoveAuthorizedAddress', () => {
|
||||
it('should throw if data is not for removeAuthorizedAddress', async () => {
|
||||
const data = MultiSigWrapper.encodeFnArgs('addAuthorizedAddress', PROXY_ABI, [owners[0]]);
|
||||
return expect(multiSig.isFunctionRemoveAuthorizedAddress.callAsync(data)).to.be.rejectedWith(
|
||||
constants.REVERT,
|
||||
const notRemoveAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData(
|
||||
owners[0],
|
||||
);
|
||||
return expect(
|
||||
multiSig.isFunctionRemoveAuthorizedAddress.callAsync(notRemoveAuthorizedAddressData),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
it('should return true if data is for removeAuthorizedAddress', async () => {
|
||||
const data = MultiSigWrapper.encodeFnArgs('removeAuthorizedAddress', PROXY_ABI, [owners[0]]);
|
||||
const isFunctionRemoveAuthorizedAddress = await multiSig.isFunctionRemoveAuthorizedAddress.callAsync(data);
|
||||
const removeAuthorizedAddressData = erc20Proxy.removeAuthorizedAddress.getABIEncodedTransactionData(
|
||||
owners[0],
|
||||
);
|
||||
const isFunctionRemoveAuthorizedAddress = await multiSig.isFunctionRemoveAuthorizedAddress.callAsync(
|
||||
removeAuthorizedAddressData,
|
||||
);
|
||||
expect(isFunctionRemoveAuthorizedAddress).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('registerAssetProxy', () => {
|
||||
it('should throw if not called by multisig', async () => {
|
||||
const isRegistered = true;
|
||||
expect(
|
||||
multiSig.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, isRegistered, { from: owners[0] }),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
it('should register an address if called by multisig after timelock', async () => {
|
||||
const addressToRegister = erc20Proxy.address;
|
||||
const isRegistered = true;
|
||||
const registerAssetProxyData = multiSig.registerAssetProxy.getABIEncodedTransactionData(
|
||||
addressToRegister,
|
||||
isRegistered,
|
||||
);
|
||||
const submitTxRes = await multiSigWrapper.submitTransactionAsync(
|
||||
multiSig.address,
|
||||
registerAssetProxyData,
|
||||
owners[0],
|
||||
);
|
||||
const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = log.args.transactionId;
|
||||
|
||||
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
|
||||
await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber());
|
||||
|
||||
const executeTxRes = await multiSigWrapper.executeTransactionAsync(txId, owners[0]);
|
||||
const registerLog = executeTxRes.logs[0] as LogWithDecodedArgs<AssetProxyRegistrationContractEventArgs>;
|
||||
expect(registerLog.args.assetProxyContract).to.equal(addressToRegister);
|
||||
expect(registerLog.args.isRegistered).to.equal(isRegistered);
|
||||
|
||||
const isAssetProxyRegistered = await multiSig.isAssetProxyRegistered.callAsync(addressToRegister);
|
||||
expect(isAssetProxyRegistered).to.equal(isRegistered);
|
||||
});
|
||||
|
||||
it('should fail if registering a null address', async () => {
|
||||
const addressToRegister = ZeroEx.NULL_ADDRESS;
|
||||
const isRegistered = true;
|
||||
const registerAssetProxyData = multiSig.registerAssetProxy.getABIEncodedTransactionData(
|
||||
addressToRegister,
|
||||
isRegistered,
|
||||
);
|
||||
const submitTxRes = await multiSigWrapper.submitTransactionAsync(
|
||||
multiSig.address,
|
||||
registerAssetProxyData,
|
||||
owners[0],
|
||||
);
|
||||
const log = submitTxRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = log.args.transactionId;
|
||||
|
||||
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
|
||||
await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber());
|
||||
|
||||
const executeTxRes = await multiSigWrapper.executeTransactionAsync(txId, owners[0]);
|
||||
const failureLog = executeTxRes.logs[0] as LogWithDecodedArgs<ExecutionFailureContractEventArgs>;
|
||||
expect(failureLog.args.transactionId).to.be.bignumber.equal(txId);
|
||||
|
||||
const isAssetProxyRegistered = await multiSig.isAssetProxyRegistered.callAsync(addressToRegister);
|
||||
expect(isAssetProxyRegistered).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeRemoveAuthorizedAddress', () => {
|
||||
before('authorize both proxies and register erc20 proxy', async () => {
|
||||
// Only register ERC20 proxy
|
||||
const addressToRegister = erc20Proxy.address;
|
||||
const isRegistered = true;
|
||||
const registerAssetProxyData = multiSig.registerAssetProxy.getABIEncodedTransactionData(
|
||||
addressToRegister,
|
||||
isRegistered,
|
||||
);
|
||||
const registerAssetProxySubmitRes = await multiSigWrapper.submitTransactionAsync(
|
||||
multiSig.address,
|
||||
registerAssetProxyData,
|
||||
owners[0],
|
||||
);
|
||||
const registerAssetProxySubmitLog = registerAssetProxySubmitRes.logs[0] as LogWithDecodedArgs<
|
||||
SubmissionContractEventArgs
|
||||
>;
|
||||
const registerAssetProxyTxId = registerAssetProxySubmitLog.args.transactionId;
|
||||
await multiSigWrapper.confirmTransactionAsync(registerAssetProxyTxId, owners[1]);
|
||||
|
||||
const addAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData(authorized);
|
||||
const erc20AddAuthorizedAddressSubmitRes = await multiSigWrapper.submitTransactionAsync(
|
||||
erc20Proxy.address,
|
||||
addAuthorizedAddressData,
|
||||
owners[0],
|
||||
);
|
||||
const erc721AddAuthorizedAddressSubmitRes = await multiSigWrapper.submitTransactionAsync(
|
||||
erc721Proxy.address,
|
||||
addAuthorizedAddressData,
|
||||
owners[0],
|
||||
);
|
||||
const erc20AddAuthorizedAddressSubmitLog = erc20AddAuthorizedAddressSubmitRes.logs[0] as LogWithDecodedArgs<
|
||||
SubmissionContractEventArgs
|
||||
>;
|
||||
const erc721AddAuthorizedAddressSubmitLog = erc721AddAuthorizedAddressSubmitRes
|
||||
.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const erc20AddAuthorizedAddressTxId = erc20AddAuthorizedAddressSubmitLog.args.transactionId;
|
||||
const erc721AddAuthorizedAddressTxId = erc721AddAuthorizedAddressSubmitLog.args.transactionId;
|
||||
|
||||
await multiSigWrapper.confirmTransactionAsync(erc20AddAuthorizedAddressTxId, owners[1]);
|
||||
await multiSigWrapper.confirmTransactionAsync(erc721AddAuthorizedAddressTxId, owners[1]);
|
||||
await web3Wrapper.increaseTimeAsync(SECONDS_TIME_LOCKED.toNumber());
|
||||
await multiSigWrapper.executeTransactionAsync(registerAssetProxyTxId, owners[0]);
|
||||
await multiSigWrapper.executeTransactionAsync(erc20AddAuthorizedAddressTxId, owners[0]);
|
||||
await multiSigWrapper.executeTransactionAsync(erc721AddAuthorizedAddressTxId, owners[0]);
|
||||
});
|
||||
|
||||
it('should throw without the required confirmations', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const txHash = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const res = await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
const log = abiDecoder.tryToDecodeLogOrNoop(res.logs[0]) as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const removeAuthorizedAddressData = erc20Proxy.removeAuthorizedAddress.getABIEncodedTransactionData(
|
||||
authorized,
|
||||
);
|
||||
const res = await multiSigWrapper.submitTransactionAsync(
|
||||
erc20Proxy.address,
|
||||
removeAuthorizedAddressData,
|
||||
owners[0],
|
||||
);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = log.args.transactionId;
|
||||
|
||||
return expect(
|
||||
@@ -116,25 +263,19 @@ describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => {
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
it('should throw if tx destination is not the tokenTransferProxy', async () => {
|
||||
const invalidTokenTransferProxy = await TokenTransferProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TokenTransferProxy,
|
||||
provider,
|
||||
txDefaults,
|
||||
it('should throw if tx destination is not registered', async () => {
|
||||
const removeAuthorizedAddressData = erc721Proxy.removeAuthorizedAddress.getABIEncodedTransactionData(
|
||||
authorized,
|
||||
);
|
||||
const invalidDestination = invalidTokenTransferProxy.address;
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const txHash = await multiSigWrapper.submitTransactionAsync(invalidDestination, owners[0], dataParams);
|
||||
const res = await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
const log = abiDecoder.tryToDecodeLogOrNoop(res.logs[0]) as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const res = await multiSigWrapper.submitTransactionAsync(
|
||||
erc721Proxy.address,
|
||||
removeAuthorizedAddressData,
|
||||
owners[0],
|
||||
);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = log.args.transactionId;
|
||||
await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] });
|
||||
const isConfirmed = await multiSig.isConfirmed.callAsync(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
|
||||
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
|
||||
|
||||
return expect(
|
||||
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
|
||||
@@ -142,64 +283,76 @@ describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => {
|
||||
});
|
||||
|
||||
it('should throw if tx data is not for removeAuthorizedAddress', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'addAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [unauthorizedAddress],
|
||||
};
|
||||
const txHash = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const res = await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
const log = abiDecoder.tryToDecodeLogOrNoop(res.logs[0]) as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const newAuthorized = owners[1];
|
||||
const addAuthorizedAddressData = erc20Proxy.addAuthorizedAddress.getABIEncodedTransactionData(
|
||||
newAuthorized,
|
||||
);
|
||||
const res = await multiSigWrapper.submitTransactionAsync(
|
||||
erc20Proxy.address,
|
||||
addAuthorizedAddressData,
|
||||
owners[0],
|
||||
);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = log.args.transactionId;
|
||||
await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] });
|
||||
const isConfirmed = await multiSig.isConfirmed.callAsync(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
|
||||
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
|
||||
|
||||
return expect(
|
||||
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
it('should execute removeAuthorizedAddress for valid tokenTransferProxy if fully confirmed', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const txHash = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const res = await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
const log = abiDecoder.tryToDecodeLogOrNoop(res.logs[0]) as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = log.args.transactionId;
|
||||
await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] });
|
||||
const isConfirmed = await multiSig.isConfirmed.callAsync(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
await multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] });
|
||||
const isAuthorized = await tokenTransferProxy.authorized.callAsync(authorizedAddress);
|
||||
expect(isAuthorized).to.be.false();
|
||||
it('should execute removeAuthorizedAddress for registered address if fully confirmed', async () => {
|
||||
const removeAuthorizedAddressData = erc20Proxy.removeAuthorizedAddress.getABIEncodedTransactionData(
|
||||
authorized,
|
||||
);
|
||||
const submitRes = await multiSigWrapper.submitTransactionAsync(
|
||||
erc20Proxy.address,
|
||||
removeAuthorizedAddressData,
|
||||
owners[0],
|
||||
);
|
||||
const submitLog = submitRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = submitLog.args.transactionId;
|
||||
|
||||
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
|
||||
|
||||
const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAsync(txId, owners[0]);
|
||||
const execLog = execRes.logs[0] as LogWithDecodedArgs<ExecutionContractEventArgs>;
|
||||
expect(execLog.args.transactionId).to.be.bignumber.equal(txId);
|
||||
|
||||
const tx = await multiSig.transactions.callAsync(txId);
|
||||
const isExecuted = tx[3];
|
||||
expect(isExecuted).to.equal(true);
|
||||
|
||||
const isAuthorized = await erc20Proxy.authorized.callAsync(authorized);
|
||||
expect(isAuthorized).to.equal(false);
|
||||
});
|
||||
|
||||
it('should throw if already executed', async () => {
|
||||
const dataParams: TransactionDataParams = {
|
||||
name: 'removeAuthorizedAddress',
|
||||
abi: PROXY_ABI,
|
||||
args: [authorizedAddress],
|
||||
};
|
||||
const txHash = await multiSigWrapper.submitTransactionAsync(validDestination, owners[0], dataParams);
|
||||
const res = await zeroEx.awaitTransactionMinedAsync(txHash);
|
||||
const log = abiDecoder.tryToDecodeLogOrNoop(res.logs[0]) as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = log.args.transactionId;
|
||||
await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] });
|
||||
const isConfirmed = await multiSig.isConfirmed.callAsync(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
await multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] });
|
||||
const removeAuthorizedAddressData = erc20Proxy.removeAuthorizedAddress.getABIEncodedTransactionData(
|
||||
authorized,
|
||||
);
|
||||
const submitRes = await multiSigWrapper.submitTransactionAsync(
|
||||
erc20Proxy.address,
|
||||
removeAuthorizedAddressData,
|
||||
owners[0],
|
||||
);
|
||||
const submitLog = submitRes.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = submitLog.args.transactionId;
|
||||
|
||||
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
|
||||
|
||||
const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAsync(txId, owners[0]);
|
||||
const execLog = execRes.logs[0] as LogWithDecodedArgs<ExecutionContractEventArgs>;
|
||||
expect(execLog.args.transactionId).to.be.bignumber.equal(txId);
|
||||
|
||||
const tx = await multiSig.transactions.callAsync(txId);
|
||||
const isExecuted = tx[3];
|
||||
expect(isExecuted).to.be.true();
|
||||
expect(isExecuted).to.equal(true);
|
||||
|
||||
return expect(
|
||||
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user