Merge pull request #571 from 0xProject/feature/contracts/proxyOwner
Update MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
}
|
||||
},
|
||||
"contracts": [
|
||||
"AssetProxyOwner",
|
||||
"DummyERC20Token",
|
||||
"DummyERC721Token",
|
||||
"ERC20Proxy",
|
||||
@@ -27,7 +28,6 @@
|
||||
"MixinAuthorizable",
|
||||
"MultiSigWallet",
|
||||
"MultiSigWalletWithTimeLock",
|
||||
"MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress",
|
||||
"TestAssetProxyDispatcher",
|
||||
"TestLibBytes",
|
||||
"TestLibs",
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
"run_mocha": "mocha 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"clean": "shx rm -rf lib src/contract_wrappers/generated",
|
||||
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers && prettier --write 'src/contract_wrappers/generated/**.ts'",
|
||||
"generate_contract_wrappers":
|
||||
"abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers && prettier --write 'src/contract_wrappers/generated/**.ts'",
|
||||
"lint": "tslint --project .",
|
||||
"coverage:report:text": "istanbul report text",
|
||||
"coverage:report:html": "istanbul report html && open coverage/index.html",
|
||||
@@ -28,7 +29,7 @@
|
||||
"test:circleci": "yarn test"
|
||||
},
|
||||
"config": {
|
||||
"abis": "../migrations/artifacts/2.0.0/@(DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TestAssetProxyDispatcher|TestLibBytes|TestLibs|TestSignatureValidator|TokenRegistry|WETH9|ZRXToken).json"
|
||||
"abis": "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock||TestAssetProxyDispatcher|TestLibBytes|TestLibs|TestSignatureValidator|TokenRegistry|WETH9|ZRXToken).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
pragma solidity ^0.4.10;
|
||||
|
||||
import { MultiSigWallet } from "../MultiSigWallet/MultiSigWallet.sol";
|
||||
import "./MultiSigWallet.sol";
|
||||
|
||||
/// @title Multisignature wallet with time lock- Allows multiple parties to execute a transaction after a time lock has passed.
|
||||
/// @author Amir Bandeali - <amir@0xProject.com>
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.10;
|
||||
|
||||
import "../../multisig/MultiSigWalletWithTimeLock.sol";
|
||||
import "../../utils/LibBytes/LibBytes.sol";
|
||||
|
||||
contract AssetProxyOwner is
|
||||
LibBytes,
|
||||
MultiSigWalletWithTimeLock
|
||||
{
|
||||
|
||||
event AssetProxyRegistration(address assetProxyContract, bool isRegistered);
|
||||
|
||||
// Mapping of AssetProxy contract address =>
|
||||
// if this contract is allowed to call the AssetProxy's removeAuthorizedAddress method without a time lock.
|
||||
mapping (address => bool) public isAssetProxyRegistered;
|
||||
|
||||
bytes4 constant REMOVE_AUTHORIZED_ADDRESS_SELECTOR = bytes4(keccak256("removeAuthorizedAddress(address)"));
|
||||
|
||||
/// @dev Function will revert if the transaction does not call `removeAuthorizedAddress`
|
||||
/// on an approved AssetProxy contract.
|
||||
modifier validRemoveAuthorizedAddressTx(uint256 transactionId) {
|
||||
Transaction storage tx = transactions[transactionId];
|
||||
require(isAssetProxyRegistered[tx.destination]);
|
||||
require(isFunctionRemoveAuthorizedAddress(tx.data));
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Contract constructor sets initial owners, required number of confirmations,
|
||||
/// time lock, and list of AssetProxy addresses.
|
||||
/// @param _owners List of initial owners.
|
||||
/// @param _assetProxyContracts Array of AssetProxy contract addresses.
|
||||
/// @param _required Number of required confirmations.
|
||||
/// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.
|
||||
function AssetProxyOwner(
|
||||
address[] memory _owners,
|
||||
address[] memory _assetProxyContracts,
|
||||
uint256 _required,
|
||||
uint256 _secondsTimeLocked
|
||||
)
|
||||
public
|
||||
MultiSigWalletWithTimeLock(_owners, _required, _secondsTimeLocked)
|
||||
{
|
||||
for (uint256 i = 0; i < _assetProxyContracts.length; i++) {
|
||||
address assetProxy = _assetProxyContracts[i];
|
||||
require(assetProxy != address(0));
|
||||
isAssetProxyRegistered[assetProxy] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Registers or deregisters an AssetProxy to be able to execute
|
||||
/// removeAuthorizedAddress without a timelock.
|
||||
/// @param assetProxyContract Address of AssetProxy contract.
|
||||
/// @param isRegistered Status of approval for AssetProxy contract.
|
||||
function registerAssetProxy(address assetProxyContract, bool isRegistered)
|
||||
public
|
||||
onlyWallet
|
||||
notNull(assetProxyContract)
|
||||
{
|
||||
isAssetProxyRegistered[assetProxyContract] = isRegistered;
|
||||
AssetProxyRegistration(assetProxyContract, isRegistered);
|
||||
}
|
||||
|
||||
/// @dev Allows execution of removeAuthorizedAddress without time lock.
|
||||
/// @param transactionId Transaction ID.
|
||||
function executeRemoveAuthorizedAddress(uint256 transactionId)
|
||||
public
|
||||
notExecuted(transactionId)
|
||||
fullyConfirmed(transactionId)
|
||||
validRemoveAuthorizedAddressTx(transactionId)
|
||||
{
|
||||
Transaction storage tx = transactions[transactionId];
|
||||
tx.executed = true;
|
||||
if (tx.destination.call.value(tx.value)(tx.data))
|
||||
Execution(transactionId);
|
||||
else {
|
||||
ExecutionFailure(transactionId);
|
||||
tx.executed = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Compares first 4 bytes of byte array to removeAuthorizedAddress function selector.
|
||||
/// @param data Transaction data.
|
||||
/// @return Successful if data is a call to removeAuthorizedAddress.
|
||||
function isFunctionRemoveAuthorizedAddress(bytes memory data)
|
||||
public
|
||||
pure
|
||||
returns (bool)
|
||||
{
|
||||
bytes4 first4Bytes = readFirst4(data);
|
||||
require(REMOVE_AUTHORIZED_ADDRESS_SELECTOR == first4Bytes);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -130,4 +130,16 @@ contract TestLibBytes is
|
||||
writeUint256(b, index, input);
|
||||
return b;
|
||||
}
|
||||
|
||||
/// @dev Reads the first 4 bytes from a byte array of arbitrary length.
|
||||
/// @param b Byte array to read first 4 bytes from.
|
||||
/// @return First 4 bytes of data.
|
||||
function publicReadFirst4(bytes memory b)
|
||||
public
|
||||
pure
|
||||
returns (bytes4 result)
|
||||
{
|
||||
result = readFirst4(b);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ pragma solidity ^0.4.24;
|
||||
contract LibBytes {
|
||||
|
||||
// Revert reasons
|
||||
string constant GTE_4_LENGTH_REQUIRED = "Length must be greater than or equal to 4.";
|
||||
string constant GTE_20_LENGTH_REQUIRED = "Length must be greater than or equal to 20.";
|
||||
string constant GTE_32_LENGTH_REQUIRED = "Length must be greater than or equal to 32.";
|
||||
|
||||
@@ -203,4 +204,22 @@ contract LibBytes {
|
||||
{
|
||||
writeBytes32(b, index, bytes32(input));
|
||||
}
|
||||
|
||||
/// @dev Reads the first 4 bytes from a byte array of arbitrary length.
|
||||
/// @param b Byte array to read first 4 bytes from.
|
||||
/// @return First 4 bytes of data.
|
||||
function readFirst4(bytes memory b)
|
||||
internal
|
||||
pure
|
||||
returns (bytes4 result)
|
||||
{
|
||||
require(
|
||||
b.length >= 4,
|
||||
GTE_4_LENGTH_REQUIRED
|
||||
);
|
||||
assembly {
|
||||
result := mload(add(b, 32))
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
pragma solidity ^0.4.10;
|
||||
|
||||
import { MultiSigWalletWithTimeLock } from "../MultiSigWalletWithTimeLock/MultiSigWalletWithTimeLock.sol";
|
||||
import "../../current/multisig/MultiSigWalletWithTimeLock.sol";
|
||||
|
||||
contract MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress is MultiSigWalletWithTimeLock {
|
||||
|
||||
@@ -79,4 +79,4 @@ contract MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress is MultiSigWall
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ContractArtifact } from '@0xproject/sol-compiler';
|
||||
|
||||
import * as AssetProxyOwner from '../artifacts/AssetProxyOwner.json';
|
||||
import * as DummyERC20Token from '../artifacts/DummyERC20Token.json';
|
||||
import * as DummyERC721Token from '../artifacts/DummyERC721Token.json';
|
||||
import * as ERC20Proxy from '../artifacts/ERC20Proxy.json';
|
||||
@@ -8,7 +9,6 @@ import * as Exchange from '../artifacts/Exchange.json';
|
||||
import * as MixinAuthorizable from '../artifacts/MixinAuthorizable.json';
|
||||
import * as MultiSigWallet from '../artifacts/MultiSigWallet.json';
|
||||
import * as MultiSigWalletWithTimeLock from '../artifacts/MultiSigWalletWithTimeLock.json';
|
||||
import * as MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress from '../artifacts/MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress.json';
|
||||
import * as TestAssetProxyDispatcher from '../artifacts/TestAssetProxyDispatcher.json';
|
||||
import * as TestLibBytes from '../artifacts/TestLibBytes.json';
|
||||
import * as TestLibs from '../artifacts/TestLibs.json';
|
||||
@@ -18,6 +18,7 @@ import * as EtherToken from '../artifacts/WETH9.json';
|
||||
import * as ZRX from '../artifacts/ZRXToken.json';
|
||||
|
||||
export const artifacts = {
|
||||
AssetProxyOwner: (AssetProxyOwner as any) as ContractArtifact,
|
||||
DummyERC20Token: (DummyERC20Token as any) as ContractArtifact,
|
||||
DummyERC721Token: (DummyERC721Token as any) as ContractArtifact,
|
||||
ERC20Proxy: (ERC20Proxy as any) as ContractArtifact,
|
||||
@@ -27,7 +28,6 @@ export const artifacts = {
|
||||
MixinAuthorizable: (MixinAuthorizable as any) as ContractArtifact,
|
||||
MultiSigWallet: (MultiSigWallet as any) as ContractArtifact,
|
||||
MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact,
|
||||
MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress: (MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress as any) as ContractArtifact,
|
||||
TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact,
|
||||
TestLibBytes: (TestLibBytes as any) as ContractArtifact,
|
||||
TestLibs: (TestLibs as any) as ContractArtifact,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Provider } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { DummyERC20TokenContract } from '../contract_wrappers/generated/dummy_e_r_c20_token';
|
||||
@@ -13,10 +14,12 @@ import { txDefaults } from './web3_wrapper';
|
||||
export class ERC20Wrapper {
|
||||
private _tokenOwnerAddresses: string[];
|
||||
private _contractOwnerAddress: string;
|
||||
private _web3Wrapper: Web3Wrapper;
|
||||
private _provider: Provider;
|
||||
private _dummyTokenContracts?: DummyERC20TokenContract[];
|
||||
private _proxyContract?: ERC20ProxyContract;
|
||||
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
@@ -68,7 +71,8 @@ export class ERC20Wrapper {
|
||||
);
|
||||
});
|
||||
});
|
||||
await Promise.all([...setBalancePromises, ...setAllowancePromises]);
|
||||
const txHashes = await Promise.all([...setBalancePromises, ...setAllowancePromises]);
|
||||
await Promise.all(_.map(txHashes, async txHash => this._web3Wrapper.awaitTransactionSuccessAsync(txHash)));
|
||||
}
|
||||
public async getBalancesAsync(): Promise<ERC20BalancesByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { generatePseudoRandomSalt } from '@0xproject/order-utils';
|
||||
import { Provider } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { DummyERC721TokenContract } from '../contract_wrappers/generated/dummy_e_r_c721_token';
|
||||
@@ -14,11 +15,13 @@ import { txDefaults } from './web3_wrapper';
|
||||
export class ERC721Wrapper {
|
||||
private _tokenOwnerAddresses: string[];
|
||||
private _contractOwnerAddress: string;
|
||||
private _web3Wrapper: Web3Wrapper;
|
||||
private _provider: Provider;
|
||||
private _dummyTokenContracts?: DummyERC721TokenContract[];
|
||||
private _proxyContract?: ERC721ProxyContract;
|
||||
private _initialTokenIdsByOwner: ERC721TokenIdsByOwner = {};
|
||||
constructor(provider: Provider, tokenOwnerAddresses: string[], contractOwnerAddress: string) {
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
this._provider = provider;
|
||||
this._tokenOwnerAddresses = tokenOwnerAddresses;
|
||||
this._contractOwnerAddress = contractOwnerAddress;
|
||||
@@ -80,7 +83,8 @@ export class ERC721Wrapper {
|
||||
);
|
||||
});
|
||||
});
|
||||
await Promise.all([...setBalancePromises, ...setAllowancePromises]);
|
||||
const txHashes = await Promise.all([...setBalancePromises, ...setAllowancePromises]);
|
||||
await Promise.all(_.map(txHashes, async txHash => this._web3Wrapper.awaitTransactionSuccessAsync(txHash)));
|
||||
}
|
||||
public async getBalancesAsync(): Promise<ERC721TokenIdsByOwner> {
|
||||
this._validateDummyTokenContractsExistOrThrow();
|
||||
|
||||
@@ -2,20 +2,18 @@ import { Provider, SignedOrder, TransactionReceiptWithDecodedLogs } from '@0xpro
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
import * as Web3 from 'web3';
|
||||
|
||||
import { ExchangeContract } from '../contract_wrappers/generated/exchange';
|
||||
|
||||
import { constants } from './constants';
|
||||
import { formatters } from './formatters';
|
||||
import { LogDecoder } from './log_decoder';
|
||||
import { logDecoder } from './log_decoder';
|
||||
import { orderUtils } from './order_utils';
|
||||
import { AssetProxyId, OrderInfo, SignedTransaction } from './types';
|
||||
|
||||
export class ExchangeWrapper {
|
||||
private _exchange: ExchangeContract;
|
||||
private _web3Wrapper: Web3Wrapper;
|
||||
private _logDecoder: LogDecoder = new LogDecoder(constants.TESTRPC_NETWORK_ID);
|
||||
constructor(exchangeContract: ExchangeContract, provider: Provider) {
|
||||
this._exchange = exchangeContract;
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
@@ -249,7 +247,7 @@ export class ExchangeWrapper {
|
||||
private async _getTxWithDecodedExchangeLogsAsync(txHash: string): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const tx = await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
|
||||
tx.logs = _.filter(tx.logs, log => log.address === this._exchange.address);
|
||||
tx.logs = _.map(tx.logs, log => this._logDecoder.decodeLogOrThrow(log));
|
||||
tx.logs = _.map(tx.logs, log => logDecoder.decodeLogOrThrow(log));
|
||||
return tx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,35 +5,29 @@ import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
||||
export class LogDecoder {
|
||||
private _abiDecoder: AbiDecoder;
|
||||
constructor(networkIdIfExists?: number) {
|
||||
if (_.isUndefined(networkIdIfExists)) {
|
||||
throw new Error('networkId not specified');
|
||||
const abiArrays: AbiDefinition[][] = [];
|
||||
_.forEach(artifacts, (artifact: ContractArtifact) => {
|
||||
const compilerOutput = artifact.compilerOutput;
|
||||
abiArrays.push(compilerOutput.abi);
|
||||
});
|
||||
const abiDecoder = new AbiDecoder(abiArrays);
|
||||
|
||||
export const logDecoder = {
|
||||
wrapLogBigNumbers(log: any): any {
|
||||
const argNames = _.keys(log.args);
|
||||
for (const argName of argNames) {
|
||||
const isWeb3BigNumber = _.startsWith(log.args[argName].constructor.toString(), 'function BigNumber(');
|
||||
if (isWeb3BigNumber) {
|
||||
log.args[argName] = new BigNumber(log.args[argName]);
|
||||
}
|
||||
}
|
||||
const abiArrays: AbiDefinition[][] = [];
|
||||
_.forEach(artifacts, (artifact: ContractArtifact) => {
|
||||
const compilerOutput = artifact.compilerOutput;
|
||||
abiArrays.push(compilerOutput.abi);
|
||||
});
|
||||
this._abiDecoder = new AbiDecoder(abiArrays);
|
||||
}
|
||||
public decodeLogOrThrow<ArgsType>(log: LogEntry): LogWithDecodedArgs<ArgsType> | RawLog {
|
||||
const logWithDecodedArgsOrLog = this._abiDecoder.tryToDecodeLogOrNoop(log);
|
||||
},
|
||||
decodeLogOrThrow<ArgsType>(log: LogEntry): LogWithDecodedArgs<ArgsType> | RawLog {
|
||||
const logWithDecodedArgsOrLog = abiDecoder.tryToDecodeLogOrNoop(log);
|
||||
if (_.isUndefined((logWithDecodedArgsOrLog as LogWithDecodedArgs<ArgsType>).args)) {
|
||||
throw new Error(`Unable to decode log: ${JSON.stringify(log)}`);
|
||||
}
|
||||
wrapLogBigNumbers(logWithDecodedArgsOrLog);
|
||||
logDecoder.wrapLogBigNumbers(logWithDecodedArgsOrLog);
|
||||
return logWithDecodedArgsOrLog;
|
||||
}
|
||||
}
|
||||
|
||||
function wrapLogBigNumbers(log: any): any {
|
||||
const argNames = _.keys(log.args);
|
||||
for (const argName of argNames) {
|
||||
const isWeb3BigNumber = _.startsWith(log.args[argName].constructor.toString(), 'function BigNumber(');
|
||||
if (isWeb3BigNumber) {
|
||||
log.args[argName] = new BigNumber(log.args[argName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,43 +1,57 @@
|
||||
import { AbiDefinition, MethodAbi } from '@0xproject/types';
|
||||
import { Provider, TransactionReceiptWithDecodedLogs } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import ABI = require('ethereumjs-abi');
|
||||
import ethUtil = require('ethereumjs-util');
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
import * as Web3 from 'web3';
|
||||
|
||||
import { AssetProxyOwnerContract } from '../contract_wrappers/generated/asset_proxy_owner';
|
||||
import { MultiSigWalletContract } from '../contract_wrappers/generated/multi_sig_wallet';
|
||||
|
||||
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 _web3Wrapper: Web3Wrapper;
|
||||
constructor(multiSigContract: MultiSigWalletContract, provider: Provider) {
|
||||
this._multiSig = multiSigContract;
|
||||
this._web3Wrapper = new Web3Wrapper(provider);
|
||||
}
|
||||
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._getTxWithDecodedMultiSigLogsAsync(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._getTxWithDecodedMultiSigLogsAsync(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._getTxWithDecodedMultiSigLogsAsync(txHash);
|
||||
return tx;
|
||||
}
|
||||
public async executeRemoveAuthorizedAddressAsync(
|
||||
txId: BigNumber,
|
||||
from: string,
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const txHash = await (this
|
||||
._multiSig as AssetProxyOwnerContract).executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from });
|
||||
const tx = await this._getTxWithDecodedMultiSigLogsAsync(txHash);
|
||||
return tx;
|
||||
}
|
||||
private async _getTxWithDecodedMultiSigLogsAsync(txHash: string): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const tx = await this._web3Wrapper.awaitTransactionMinedAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
|
||||
tx.logs = _.filter(tx.logs, log => log.address === this._multiSig.address);
|
||||
tx.logs = _.map(tx.logs, log => logDecoder.decodeLogOrThrow(log));
|
||||
return tx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ export enum ContractName {
|
||||
ZRXToken = 'ZRXToken',
|
||||
DummyERC20Token = 'DummyERC20Token',
|
||||
EtherToken = 'WETH9',
|
||||
MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress = 'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress',
|
||||
AssetProxyOwner = 'AssetProxyOwner',
|
||||
AccountLevels = 'AccountLevels',
|
||||
EtherDelta = 'EtherDelta',
|
||||
Arbitrage = 'Arbitrage',
|
||||
|
||||
345
packages/contracts/test/asset_proxy_owner.ts
Normal file
345
packages/contracts/test/asset_proxy_owner.ts
Normal file
@@ -0,0 +1,345 @@
|
||||
import { BlockchainLifecycle } from '@0xproject/dev-utils';
|
||||
import { LogWithDecodedArgs } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as _ from 'lodash';
|
||||
import 'make-promises-safe';
|
||||
import * as Web3 from 'web3';
|
||||
|
||||
import {
|
||||
AssetProxyOwnerContract,
|
||||
AssetProxyRegistrationContractEventArgs,
|
||||
ExecutionContractEventArgs,
|
||||
ExecutionFailureContractEventArgs,
|
||||
SubmissionContractEventArgs,
|
||||
} from '../src/contract_wrappers/generated/asset_proxy_owner';
|
||||
import { MixinAuthorizableContract } from '../src/contract_wrappers/generated/mixin_authorizable';
|
||||
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 { provider, txDefaults, web3Wrapper } from '../src/utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
describe('AssetProxyOwner', () => {
|
||||
let owners: string[];
|
||||
let authorized: string;
|
||||
const REQUIRED_APPROVALS = new BigNumber(2);
|
||||
const SECONDS_TIME_LOCKED = new BigNumber(1000000);
|
||||
|
||||
let erc20Proxy: MixinAuthorizableContract;
|
||||
let erc721Proxy: MixinAuthorizableContract;
|
||||
let multiSig: AssetProxyOwnerContract;
|
||||
let multiSigWrapper: MultiSigWrapper;
|
||||
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
owners = [accounts[0], accounts[1]];
|
||||
const initialOwner = (authorized = accounts[0]);
|
||||
erc20Proxy = await MixinAuthorizableContract.deployFrom0xArtifactAsync(
|
||||
artifacts.MixinAuthorizable,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
erc721Proxy = await MixinAuthorizableContract.deployFrom0xArtifactAsync(
|
||||
artifacts.MixinAuthorizable,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
const defaultAssetProxyContractAddresses: string[] = [];
|
||||
multiSig = await AssetProxyOwnerContract.deployFrom0xArtifactAsync(
|
||||
artifacts.AssetProxyOwner,
|
||||
provider,
|
||||
txDefaults,
|
||||
owners,
|
||||
defaultAssetProxyContractAddresses,
|
||||
REQUIRED_APPROVALS,
|
||||
SECONDS_TIME_LOCKED,
|
||||
);
|
||||
multiSigWrapper = new MultiSigWrapper(multiSig, provider);
|
||||
await erc20Proxy.transferOwnership.sendTransactionAsync(multiSig.address, { from: initialOwner });
|
||||
await erc721Proxy.transferOwnership.sendTransactionAsync(multiSig.address, { from: initialOwner });
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should register passed in assetProxyContracts', async () => {
|
||||
const assetProxyContractAddresses = [erc20Proxy.address, erc721Proxy.address];
|
||||
const newMultiSig = await AssetProxyOwnerContract.deployFrom0xArtifactAsync(
|
||||
artifacts.AssetProxyOwner,
|
||||
provider,
|
||||
txDefaults,
|
||||
owners,
|
||||
assetProxyContractAddresses,
|
||||
REQUIRED_APPROVALS,
|
||||
SECONDS_TIME_LOCKED,
|
||||
);
|
||||
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, constants.NULL_ADDRESS];
|
||||
return expect(
|
||||
AssetProxyOwnerContract.deployFrom0xArtifactAsync(
|
||||
artifacts.AssetProxyOwner,
|
||||
provider,
|
||||
txDefaults,
|
||||
owners,
|
||||
assetProxyContractAddresses,
|
||||
REQUIRED_APPROVALS,
|
||||
SECONDS_TIME_LOCKED,
|
||||
),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isFunctionRemoveAuthorizedAddress', () => {
|
||||
it('should throw if data is not for removeAuthorizedAddress', async () => {
|
||||
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 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;
|
||||
return 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 = constants.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 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(
|
||||
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
it('should throw if tx destination is not registered', async () => {
|
||||
const removeAuthorizedAddressData = erc721Proxy.removeAuthorizedAddress.getABIEncodedTransactionData(
|
||||
authorized,
|
||||
);
|
||||
const res = await multiSigWrapper.submitTransactionAsync(
|
||||
erc721Proxy.address,
|
||||
removeAuthorizedAddressData,
|
||||
owners[0],
|
||||
);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<SubmissionContractEventArgs>;
|
||||
const txId = log.args.transactionId;
|
||||
|
||||
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
|
||||
|
||||
return expect(
|
||||
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
it('should throw if tx data is not for removeAuthorizedAddress', async () => {
|
||||
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 multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
|
||||
|
||||
return expect(
|
||||
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
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 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);
|
||||
|
||||
return expect(
|
||||
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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 () => {
|
||||
@@ -708,7 +703,7 @@ describe('Exchange core', () => {
|
||||
// Create 3 orders with makerEpoch values: 0,1,2,3
|
||||
// Since we cancelled with makerEpoch=1, orders with makerEpoch<=1 will not be processed
|
||||
erc20Balances = await erc20Wrapper.getBalancesAsync();
|
||||
const signedOrders = await Promise.all([
|
||||
const signedOrders = [
|
||||
orderFactory.newSignedOrder({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(9), 18),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(9), 18),
|
||||
@@ -729,7 +724,7 @@ describe('Exchange core', () => {
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(7979), 18),
|
||||
salt: new BigNumber(3),
|
||||
}),
|
||||
]);
|
||||
];
|
||||
await exchangeWrapper.batchFillOrdersNoThrowAsync(signedOrders, takerAddress);
|
||||
|
||||
const newBalances = await erc20Wrapper.getBalancesAsync();
|
||||
|
||||
@@ -248,4 +248,18 @@ describe('LibBytes', () => {
|
||||
it('should fail if the length between the offset and end of the byte array is too short to hold a uint256)', async () => {});
|
||||
});
|
||||
*/
|
||||
|
||||
describe('readFirst4', () => {
|
||||
it('should revert if byte array has a length < 4', async () => {
|
||||
const byteArrayLessThan4Bytes = '0x010101';
|
||||
return expect(libBytes.publicReadFirst4.callAsync(byteArrayLessThan4Bytes)).to.be.rejectedWith(
|
||||
constants.REVERT,
|
||||
);
|
||||
});
|
||||
it('should return the first 4 bytes of a byte array of arbitrary length', async () => {
|
||||
const first4Bytes = await libBytes.publicReadFirst4.callAsync(byteArrayLongerThan32Bytes);
|
||||
const expectedFirst4Bytes = byteArrayLongerThan32Bytes.slice(0, 10);
|
||||
expect(first4Bytes).to.equal(expectedFirst4Bytes);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 REQUIRED_APPROVALS = 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),
|
||||
REQUIRED_APPROVALS,
|
||||
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,
|
||||
REQUIRED_APPROVALS,
|
||||
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,205 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* @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 * as chai from 'chai';
|
||||
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 { artifacts } from '../src/utils/artifacts';
|
||||
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;
|
||||
|
||||
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]);
|
||||
|
||||
describe('MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress', () => {
|
||||
const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID });
|
||||
let owners: 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 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,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(authorizedAddress, {
|
||||
from: initialOwner,
|
||||
});
|
||||
multiSig = await MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddressContract.deployFrom0xArtifactAsync(
|
||||
artifacts.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,
|
||||
provider,
|
||||
txDefaults,
|
||||
owners,
|
||||
requiredApprovals,
|
||||
SECONDS_TIME_LOCKED,
|
||||
tokenTransferProxy.address,
|
||||
);
|
||||
await tokenTransferProxy.transferOwnership.sendTransactionAsync(multiSig.address, {
|
||||
from: initialOwner,
|
||||
});
|
||||
multiSigWrapper = new MultiSigWrapper((multiSig as any) as MultiSigWalletContract);
|
||||
validDestination = tokenTransferProxy.address;
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
});
|
||||
afterEach(async () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
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,
|
||||
);
|
||||
});
|
||||
|
||||
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);
|
||||
expect(isFunctionRemoveAuthorizedAddress).to.be.true();
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeRemoveAuthorizedAddress', () => {
|
||||
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 txId = log.args.transactionId;
|
||||
|
||||
return expect(
|
||||
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
|
||||
).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,
|
||||
);
|
||||
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 txId = log.args.transactionId;
|
||||
await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] });
|
||||
const isConfirmed = await multiSig.isConfirmed.callAsync(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
|
||||
return expect(
|
||||
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
|
||||
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 txId = log.args.transactionId;
|
||||
await multiSig.confirmTransaction.sendTransactionAsync(txId, { from: owners[1] });
|
||||
const isConfirmed = await multiSig.isConfirmed.callAsync(txId);
|
||||
expect(isConfirmed).to.be.true();
|
||||
|
||||
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 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 tx = await multiSig.transactions.callAsync(txId);
|
||||
const isExecuted = tx[3];
|
||||
expect(isExecuted).to.be.true();
|
||||
return expect(
|
||||
multiSig.executeRemoveAuthorizedAddress.sendTransactionAsync(txId, { from: owners[1] }),
|
||||
).to.be.rejectedWith(constants.REVERT);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
*/
|
||||
File diff suppressed because one or more lines are too long
782
packages/migrations/artifacts/2.0.0/AssetProxyOwner.json
vendored
Normal file
782
packages/migrations/artifacts/2.0.0/AssetProxyOwner.json
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -71,7 +71,7 @@ export function getNormalizedErrMsg(errMsg: string): string {
|
||||
const SOLIDITY_FILE_EXTENSION_REGEX = /(.*\.sol)/;
|
||||
const errPathMatch = errMsg.match(SOLIDITY_FILE_EXTENSION_REGEX);
|
||||
if (_.isNull(errPathMatch)) {
|
||||
throw new Error('Could not find a path in error message');
|
||||
throw new Error(`Could not find a path in error message: ${errMsg}`);
|
||||
}
|
||||
const errPath = errPathMatch[0];
|
||||
const baseContract = path.basename(errPath);
|
||||
|
||||
Reference in New Issue
Block a user