// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.s
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.6.12;
import "./SafeMath.sol";
import "./IWallet.sol";
import "./IModuleRegistry.sol";
import "./ILockStorage.sol";
import "./IFeature.sol";
import "./ERC20.sol";
import "./IVersionManager.sol";
/**
* @title BaseFeature
* @notice Base Feature contract that contains methods common to all Feature contracts.
* @author Julien Niset - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
*/
contract BaseFeature is IFeature {
// Empty calldata
bytes constant internal EMPTY_BYTES = "";
// Mock token address for ETH
address constant internal ETH_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
// The address of the Lock storage
ILockStorage internal lockStorage;
// The address of the Version Manager
IVersionManager internal versionManager;
event FeatureCreated(bytes32 name);
/**
* @notice Throws if the wallet is locked.
*/
modifier onlyWhenUnlocked(address _wallet) {
require(!lockStorage.isLocked(_wallet), "BF: wallet locked");
_;
}
/**
* @notice Throws if the sender is not the VersionManager.
*/
modifier onlyVersionManager() {
require(msg.sender == address(versionManager), "BF: caller must be VersionManager");
_;
}
/**
* @notice Throws if the sender is not the owner of the target wallet.
*/
modifier onlyWalletOwner(address _wallet) {
require(isOwner(_wallet, msg.sender), "BF: must be wallet owner");
_;
}
/**
* @notice Throws if the sender is not an authorised feature of the target wallet.
*/
modifier onlyWalletFeature(address _wallet) {
require(versionManager.isFeatureAuthorised(_wallet, msg.sender), "BF: must be a wallet feature");
_;
}
/**
* @notice Throws if the sender is not the owner of the target wallet or the feature itself.
*/
modifier onlyWalletOwnerOrFeature(address _wallet) {
// Wrapping in an internal method reduces deployment cost by avoiding duplication of inlined code
verifyOwnerOrAuthorisedFeature(_wallet, msg.sender);
_;
}
constructor(
ILockStorage _lockStorage,
IVersionManager _versionManager,
bytes32 _name
) public {
lockStorage = _lockStorage;
versionManager = _versionManager;
emit FeatureCreated(_name);
}
/**
* @inheritdoc IFeature
*/
function recoverToken(address _token) external virtual override {
uint total = ERC20(_token).balanceOf(address(this));
_token.call(abi.encodeWithSelector(ERC20(_token).transfer.selector, address(versionManager), total));
}
/**
* @notice Inits the feature for a wallet by doing nothing.
* @dev !! Overriding methods need make sure `init()` can only be called by the VersionManager !!
* @param _wallet The wallet.
*/
function init(address _wallet) external virtual override {}
/**
* @inheritdoc IFeature
*/
function getRequiredSignatures(address, bytes calldata) external virtual view override returns (uint256, OwnerSignature) {
revert("BF: disabled method");
}
/**
* @inheritdoc IFeature
*/
function getStaticCallSignatures() external virtual override view returns (bytes4[] memory _sigs) {}
/**
* @inheritdoc IFeature
*/
function isFeatureAuthorisedInVersionManager(address _wallet, address _feature) public override view returns (bool) {
return versionManager.isFeatureAuthorised(_wallet, _feature);
}
/**
* @notice Checks that the wallet address provided as the first parameter of _data matches _wallet
* @return false if the addresses are different.
*/
function verifyData(address _wallet, bytes calldata _data) internal pure returns (bool) {
require(_data.length >= 36, "RM: Invalid dataWallet");
address dataWallet = abi.decode(_data[4:], (address));
return dataWallet == _wallet;
}
/**
* @notice Helper method to check if an address is the owner of a target wallet.
* @param _wallet The target wallet.
* @param _addr The address.
*/
function isOwner(address _wallet, address _addr) internal view returns (bool) {
return IWallet(_wallet).owner() == _addr;
}
/**
* @notice Verify that the caller is an authorised feature or the wallet owner.
* @param _wallet The target wallet.
* @param _sender The caller.
*/
function verifyOwnerOrAuthorisedFeature(address _wallet, address _sender) internal view {
require(isFeatureAuthorisedInVersionManager(_wallet, _sender) || isOwner(_wallet, _sender), "BF: must be owner or feature");
}
/**
* @notice Helper method to invoke a wallet.
* @param _wallet The target wallet.
* @param _to The target address for the transaction.
* @param _value The value of the transaction.
* @param _data The data of the transaction.
*/
function invokeWallet(address _wallet, address _to, uint256 _value, bytes memory _data)
internal
returns (bytes memory _res)
{
_res = versionManager.checkAuthorisedFeatureAndInvokeWallet(_wallet, _to, _value, _data);
}
}
pragma solidity >=0.5.4 <0.7.0;
/**
* ERC20 contract interface.
*/
interface ERC20 {
function totalSupply() external view returns (uint);
function decimals() external view returns (uint);
function balanceOf(address tokenOwner) external view returns (uint balance);
function allowance(address tokenOwner, address spender) external view returns (uint remaining);
function transfer(address to, uint tokens) external returns (bool success);
function approve(address spender, uint tokens) external returns (bool success);
function transferFrom(address from, address to, uint tokens) external returns (bool success);
}
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.4 <0.7.0;
/**
* @title IFeature
* @notice Interface for a Feature.
* @author Julien Niset - <julien@argent.xyz>, Olivier VDB - <olivier@argent.xyz>
*/
interface IFeature {
enum OwnerSignature {
Anyone, // Anyone
Required, // Owner required
Optional, // Owner and/or guardians
Disallowed // guardians only
}
/**
* @notice Utility method to recover any ERC20 token that was sent to the Feature by mistake.
* @param _token The token to recover.
*/
function recoverToken(address _token) external;
/**
* @notice Inits a Feature for a wallet by e.g. setting some wallet specific parameters in storage.
* @param _wallet The wallet.
*/
function init(address _wallet) external;
/**
* @notice Helper method to check if an address is an authorised feature of a target wallet.
* @param _wallet The target wallet.
* @param _feature The address.
*/
function isFeatureAuthorisedInVersionManager(address _wallet, address _feature) external view returns (bool);
/**
* @notice Gets the number of valid signatures that must be provided to execute a
* specific relayed transaction.
* @param _wallet The target wallet.
* @param _data The data of the relayed transaction.
* @return The number of required signatures and the wallet owner signature requirement.
*/
function getRequiredSignatures(address _wallet, bytes calldata _data) external view returns (uint256, OwnerSignature);
/**
* @notice Gets the list of static call signatures that this feature responds to on behalf of wallets
*/
function getStaticCallSignatures() external view returns (bytes4[] memory);
}
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.4 <0.7.0;
interface IGuardianStorage {
/**
* @notice Lets an authorised module add a guardian to a wallet.
* @param _wallet The target wallet.
* @param _guardian The guardian to add.
*/
function addGuardian(address _wallet, address _guardian) external;
/**
* @notice Lets an authorised module revoke a guardian from a wallet.
* @param _wallet The target wallet.
* @param _guardian The guardian to revoke.
*/
function revokeGuardian(address _wallet, address _guardian) external;
/**
* @notice Checks if an account is a guardian for a wallet.
* @param _wallet The target wallet.
* @param _guardian The account.
* @return true if the account is a guardian for a wallet.
*/
function isGuardian(address _wallet, address _guardian) external view returns (bool);
function isLocked(address _wallet) external view returns (bool);
function getLock(address _wallet) external view returns (uint256);
function getLocker(address _wallet) external view returns (address);
function setLock(address _wallet, uint256 _releaseAfter) external;
function getGuardians(address _wallet) external view returns (address[] memory);
function guardianCount(address _wallet) external view returns (uint256);
}
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
/**
* @title ILimitStorage
* @notice LimitStorage interface
*/
interface ILimitStorage {
struct Limit {
// the current limit
uint128 current;
// the pending limit if any
uint128 pending;
// when the pending limit becomes the current limit
uint64 changeAfter;
}
struct DailySpent {
// The amount already spent during the current period
uint128 alreadySpent;
// The end of the current period
uint64 periodEnd;
}
function setLimit(address _wallet, Limit memory _limit) external;
function getLimit(address _wallet) external view returns (Limit memory _limit);
function setDailySpent(address _wallet, DailySpent memory _dailySpent) external;
function getDailySpent(address _wallet) external view returns (DailySpent memory _dailySpent);
function setLimitAndDailySpent(address _wallet, Limit memory _limit, DailySpent memory _dailySpent) external;
function getLimitAndDailySpent(address _wallet) external view returns (Limit memory _limit, DailySpent memory _dailySpent);
}
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.4 <0.7.0;
interface ILockStorage {
function isLocked(address _wallet) external view returns (bool);
function getLock(address _wallet) external view returns (uint256);
function getLocker(address _wallet) external view returns (address);
function setLock(address _wallet, address _locker, uint256 _releaseAfter) external;
}
// Copyright (C) 2020 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.4 <0.7.0;
/**
* @title IModuleRegistry
* @notice Interface for the registry of authorised modules.
*/
interface IModuleRegistry {
function registerModule(address _module, bytes32 _name) external;
function deregisterModule(address _module) external;
function registerUpgrader(address _upgrader, bytes32 _name) external;
function deregisterUpgrader(address _upgrader) external;
function recoverToken(address _token) external;
function moduleInfo(address _module) external view returns (bytes32);
function upgraderInfo(address _upgrader) external view returns (bytes32);
function isRegisteredModule(address _module) external view returns (bool);
function isRegisteredModule(address[] calldata _modules) external view returns (bool);
function isRegisteredUpgrader(address _upgrader) external view returns (bool);
}
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.4 <0.7.0;
pragma experimental ABIEncoderV2;
import "./ILimitStorage.sol";
/**
* @title IVersionManager
* @notice Interface for the VersionManager module.
* @author Olivier VDB - <olivier@argent.xyz>
*/
interface IVersionManager {
/**
* @notice Returns true if the feature is authorised for the wallet
* @param _wallet The target wallet.
* @param _feature The feature.
*/
function isFeatureAuthorised(address _wallet, address _feature) external view returns (bool);
/**
* @notice Lets a feature (caller) invoke a wallet.
* @param _wallet The target wallet.
* @param _to The target address for the transaction.
* @param _value The value of the transaction.
* @param _data The data of the transaction.
*/
function checkAuthorisedFeatureAndInvokeWallet(
address _wallet,
address _to,
uint256 _value,
bytes calldata _data
) external returns (bytes memory _res);
/* ******* Backward Compatibility with old Storages and BaseWallet *************** */
/**
* @notice Sets a new owner for the wallet.
* @param _newOwner The new owner.
*/
function setOwner(address _wallet, address _newOwner) external;
/**
* @notice Lets a feature write data to a storage contract.
* @param _wallet The target wallet.
* @param _storage The storage contract.
* @param _data The data of the call
*/
function invokeStorage(address _wallet, address _storage, bytes calldata _data) external;
/**
* @notice Upgrade a wallet to a new version.
* @param _wallet the wallet to upgrade
* @param _toVersion the new version
*/
function upgradeWallet(address _wallet, uint256 _toVersion) external;
}
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.4 <0.7.0;
/**
* @title IWallet
* @notice Interface for the BaseWallet
*/
interface IWallet {
/**
* @notice Returns the wallet owner.
* @return The wallet owner address.
*/
function owner() external view returns (address);
/**
* @notice Returns the number of authorised modules.
* @return The number of authorised modules.
*/
function modules() external view returns (uint);
/**
* @notice Sets a new owner for the wallet.
* @param _newOwner The new owner.
*/
function setOwner(address _newOwner) external;
/**
* @notice Checks if a module is authorised on the wallet.
* @param _module The module address to check.
* @return `true` if the module is authorised, otherwise `false`.
*/
function authorised(address _module) external view returns (bool);
/**
* @notice Returns the module responsible for a static call redirection.
* @param _sig The signature of the static call.
* @return the module doing the redirection
*/
function enabled(bytes4 _sig) external view returns (address);
/**
* @notice Enables/Disables a module.
* @param _module The target module.
* @param _value Set to `true` to authorise the module.
*/
function authoriseModule(address _module, bool _value) external;
/**
* @notice Enables a static method by specifying the target module to which the call must be delegated.
* @param _module The target module.
* @param _method The static method signature.
*/
function enableStaticCall(address _module, bytes4 _method) external;
}
// Copyright (C) 2018 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.6.12;
import "./Utils.sol";
import "./BaseFeature.sol";
import "./IGuardianStorage.sol";
/**
* @title RecoveryManager
* @notice Feature to manage the recovery of a wallet owner.
* Recovery is executed by a consensus of the wallet's guardians and takes 24 hours before it can be finalized.
* Once finalised the ownership of the wallet is transfered to a new address.
* @author Julien Niset - <julien@argent.xyz>
* @author Olivier Van Den Biggelaar - <olivier@argent.xyz>
*/
contract RecoveryManager is BaseFeature {
bytes32 constant NAME = "RecoveryManager";
bytes4 constant internal EXECUTE_RECOVERY_PREFIX = bytes4(keccak256("executeRecovery(address,address)"));
bytes4 constant internal FINALIZE_RECOVERY_PREFIX = bytes4(keccak256("finalizeRecovery(address)"));
bytes4 constant internal CANCEL_RECOVERY_PREFIX = bytes4(keccak256("cancelRecovery(address)"));
bytes4 constant internal TRANSFER_OWNERSHIP_PREFIX = bytes4(keccak256("transferOwnership(address,address)"));
struct RecoveryConfig {
address recovery;
uint64 executeAfter;
uint32 guardianCount;
}
// Wallet specific storage
mapping (address => RecoveryConfig) internal recoveryConfigs;
// Recovery period
uint256 public recoveryPeriod;
// Lock period
uint256 public lockPeriod;
// Guardian Storage
IGuardianStorage public guardianStorage;
// *************** Events *************************** //
event RecoveryExecuted(address indexed wallet, address indexed _recovery, uint64 executeAfter);
event RecoveryFinalized(address indexed wallet, address indexed _recovery);
event RecoveryCanceled(address indexed wallet, address indexed _recovery);
event OwnershipTransfered(address indexed wallet, address indexed _newOwner);
// *************** Modifiers ************************ //
/**
* @notice Throws if there is no ongoing recovery procedure.
*/
modifier onlyWhenRecovery(address _wallet) {
require(recoveryConfigs[_wallet].executeAfter > 0, "RM: there must be an ongoing recovery");
_;
}
/**
* @notice Throws if there is an ongoing recovery procedure.
*/
modifier notWhenRecovery(address _wallet) {
require(recoveryConfigs[_wallet].executeAfter == 0, "RM: there cannot be an ongoing recovery");
_;
}
// *************** Constructor ************************ //
constructor(
ILockStorage _lockStorage,
IGuardianStorage _guardianStorage,
IVersionManager _versionManager,
uint256 _recoveryPeriod,
uint256 _lockPeriod
)
BaseFeature(_lockStorage, _versionManager, NAME)
public
{
// For the wallet to be secure we must have recoveryPeriod >= securityPeriod + securityWindow
// where securityPeriod and securityWindow are the security parameters of adding/removing guardians
// and confirming large transfers.
require(_lockPeriod >= _recoveryPeriod, "RM: insecure security periods");
recoveryPeriod = _recoveryPeriod;
lockPeriod = _lockPeriod;
guardianStorage = _guardianStorage;
}
// *************** External functions ************************ //
/**
* @notice Lets the guardians start the execution of the recovery procedure.
* Once triggered the recovery is pending for the security period before it can be finalised.
* Must be confirmed by N guardians, where N = ((Nb Guardian + 1) / 2).
* @param _wallet The target wallet.
* @param _recovery The address to which ownership should be transferred.
*/
function executeRecovery(address _wallet, address _recovery) external onlyWalletFeature(_wallet) notWhenRecovery(_wallet) {
validateNewOwner(_wallet, _recovery);
RecoveryConfig storage config = recoveryConfigs[_wallet];
config.recovery = _recovery;
config.executeAfter = uint64(block.timestamp + recoveryPeriod);
config.guardianCount = uint32(guardianStorage.guardianCount(_wallet));
setLock(_wallet, block.timestamp + lockPeriod);
emit RecoveryExecuted(_wallet, _recovery, config.executeAfter);
}
/**
* @notice Finalizes an ongoing recovery procedure if the security period is over.
* The method is public and callable by anyone to enable orchestration.
* @param _wallet The target wallet.
*/
function finalizeRecovery(address _wallet) external onlyWhenRecovery(_wallet) {
RecoveryConfig storage config = recoveryConfigs[address(_wallet)];
require(uint64(block.timestamp) > config.executeAfter, "RM: the recovery period is not over yet");
address recoveryOwner = config.recovery;
delete recoveryConfigs[_wallet];
versionManager.setOwner(_wallet, recoveryOwner);
setLock(_wallet, 0);
emit RecoveryFinalized(_wallet, recoveryOwner);
}
/**
* @notice Lets the owner cancel an ongoing recovery procedure.
* Must be confirmed by N guardians, where N = ((Nb Guardian + 1) / 2) - 1.
* @param _wallet The target wallet.
*/
function cancelRecovery(address _wallet) external onlyWalletFeature(_wallet) onlyWhenRecovery(_wallet) {
RecoveryConfig storage config = recoveryConfigs[address(_wallet)];
address recoveryOwner = config.recovery;
delete recoveryConfigs[_wallet];
setLock(_wallet, 0);
emit RecoveryCanceled(_wallet, recoveryOwner);
}
/**
* @notice Lets the owner transfer the wallet ownership. This is executed immediately.
* @param _wallet The target wallet.
* @param _newOwner The address to which ownership should be transferred.
*/
function transferOwnership(address _wallet, address _newOwner) external onlyWalletFeature(_wallet) onlyWhenUnlocked(_wallet) {
validateNewOwner(_wallet, _newOwner);
versionManager.setOwner(_wallet, _newOwner);
emit OwnershipTransfered(_wallet, _newOwner);
}
/**
* @notice Gets the details of the ongoing recovery procedure if any.
* @param _wallet The target wallet.
*/
function getRecovery(address _wallet) external view returns(address _address, uint64 _executeAfter, uint32 _guardianCount) {
RecoveryConfig storage config = recoveryConfigs[_wallet];
return (config.recovery, config.executeAfter, config.guardianCount);
}
/**
* @inheritdoc IFeature
*/
function getRequiredSignatures(address _wallet, bytes calldata _data) external view override returns (uint256, OwnerSignature) {
bytes4 methodId = Utils.functionPrefix(_data);
if (methodId == EXECUTE_RECOVERY_PREFIX) {
uint walletGuardians = guardianStorage.guardianCount(_wallet);
require(walletGuardians > 0, "RM: no guardians set on wallet");
uint numberOfSignaturesRequired = Utils.ceil(walletGuardians, 2);
return (numberOfSignaturesRequired, OwnerSignature.Disallowed);
}
if (methodId == FINALIZE_RECOVERY_PREFIX) {
return (0, OwnerSignature.Anyone);
}
if (methodId == CANCEL_RECOVERY_PREFIX) {
uint numberOfSignaturesRequired = Utils.ceil(recoveryConfigs[_wallet].guardianCount + 1, 2);
return (numberOfSignaturesRequired, OwnerSignature.Optional);
}
if (methodId == TRANSFER_OWNERSHIP_PREFIX) {
uint majorityGuardians = Utils.ceil(guardianStorage.guardianCount(_wallet), 2);
uint numberOfSignaturesRequired = SafeMath.add(majorityGuardians, 1);
return (numberOfSignaturesRequired, OwnerSignature.Required);
}
revert("RM: unknown method");
}
// *************** Internal Functions ********************* //
function validateNewOwner(address _wallet, address _newOwner) internal view {
require(_newOwner != address(0), "RM: new owner address cannot be null");
require(!guardianStorage.isGuardian(_wallet, _newOwner), "RM: new owner address cannot be a guardian");
}
function setLock(address _wallet, uint256 _releaseAfter) internal {
versionManager.invokeStorage(
_wallet,
address(lockStorage),
abi.encodeWithSelector(lockStorage.setLock.selector, _wallet, address(this), _releaseAfter)
);
}
}
pragma solidity ^0.6.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
// Copyright (C) 2020 Argent Labs Ltd. <https://argent.xyz>
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.6.12;
/**
* @title Utils
* @notice Common utility methods used by modules.
*/
library Utils {
/**
* @notice Helper method to recover the signer at a given position from a list of concatenated signatures.
* @param _signedHash The signed hash
* @param _signatures The concatenated signatures.
* @param _index The index of the signature to recover.
*/
function recoverSigner(bytes32 _signedHash, bytes memory _signatures, uint _index) internal pure returns (address) {
uint8 v;
bytes32 r;
bytes32 s;
// we jump 32 (0x20) as the first slot of bytes contains the length
// we jump 65 (0x41) per signature
// for v we load 32 bytes ending with v (the first 31 come from s) then apply a mask
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(_signatures, add(0x20,mul(0x41,_index))))
s := mload(add(_signatures, add(0x40,mul(0x41,_index))))
v := and(mload(add(_signatures, add(0x41,mul(0x41,_index)))), 0xff)
}
require(v == 27 || v == 28);
address recoveredAddress = ecrecover(_signedHash, v, r, s);
require(recoveredAddress != address(0), "Utils: ecrecover returned 0");
return recoveredAddress;
}
/**
* @notice Helper method to parse data and extract the method signature.
*/
function functionPrefix(bytes memory _data) internal pure returns (bytes4 prefix) {
require(_data.length >= 4, "RM: Invalid functionPrefix");
// solhint-disable-next-line no-inline-assembly
assembly {
prefix := mload(add(_data, 0x20))
}
}
/**
* @notice Returns ceil(a / b).
*/
function ceil(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a / b;
if (a % b == 0) {
return c;
} else {
return c + 1;
}
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
if (a < b) {
return a;
}
return b;
}
}
{
"compilationTarget": {
"RecoveryManager.sol": "RecoveryManager"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 999
},
"remappings": []
}
[{"inputs":[{"internalType":"contract ILockStorage","name":"_lockStorage","type":"address"},{"internalType":"contract IGuardianStorage","name":"_guardianStorage","type":"address"},{"internalType":"contract IVersionManager","name":"_versionManager","type":"address"},{"internalType":"uint256","name":"_recoveryPeriod","type":"uint256"},{"internalType":"uint256","name":"_lockPeriod","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"name","type":"bytes32"}],"name":"FeatureCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransfered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"_recovery","type":"address"}],"name":"RecoveryCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"_recovery","type":"address"},{"indexed":false,"internalType":"uint64","name":"executeAfter","type":"uint64"}],"name":"RecoveryExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":true,"internalType":"address","name":"_recovery","type":"address"}],"name":"RecoveryFinalized","type":"event"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"cancelRecovery","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_recovery","type":"address"}],"name":"executeRecovery","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"finalizeRecovery","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"getRecovery","outputs":[{"internalType":"address","name":"_address","type":"address"},{"internalType":"uint64","name":"_executeAfter","type":"uint64"},{"internalType":"uint32","name":"_guardianCount","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"getRequiredSignatures","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"enum IFeature.OwnerSignature","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStaticCallSignatures","outputs":[{"internalType":"bytes4[]","name":"_sigs","type":"bytes4[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardianStorage","outputs":[{"internalType":"contract IGuardianStorage","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_feature","type":"address"}],"name":"isFeatureAuthorisedInVersionManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"recoverToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"recoveryPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wallet","type":"address"},{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]