diff --git a/contracts/Registry.sol b/contracts/AddressRegistry.sol similarity index 64% rename from contracts/Registry.sol rename to contracts/AddressRegistry.sol index 9866078..ecae68e 100644 --- a/contracts/Registry.sol +++ b/contracts/AddressRegistry.sol @@ -11,9 +11,13 @@ contract AddressRegistry { mapping(bytes32 => address) registry; - constructor() public { - registry[keccak256(abi.encodePacked("admin"))] = msg.sender; - registry[keccak256(abi.encodePacked("owner"))] = msg.sender; + modifier isAdmin() { + require( + msg.sender == getAddress("admin") || + msg.sender == getAddress("owner"), + "permission-denied" + ); + _; } /** @@ -26,21 +30,11 @@ contract AddressRegistry { /** * @dev set new address in system registry */ - function setAddress(string memory name, address addr) public { - require( - msg.sender == getAddress("admin") || - msg.sender == getAddress("owner"), - "permission-denied" - ); + function setAddress(string memory name, address addr) public isAdmin { registry[keccak256(abi.encodePacked(name))] = addr; emit LogSetAddress(name, addr); } - modifier isAdmin() { - require(msg.sender == getAddress("admin"), "permission-denied"); - _; - } - } @@ -49,43 +43,56 @@ contract AddressRegistry { */ contract LogicRegistry is AddressRegistry { - event LogSetDefaultLogic(address logicAddr); - event LogSetLogic(address logicAddr, bool isLogic); + event LogEnableDefaultLogic(address logicAddr); + event LogEnableLogic(address logicAddr); + event LogDisableLogic(address logicAddr); mapping(address => bool) public defaultLogicProxies; mapping(address => bool) public logicProxies; /** - * @dev get the boolean of the logic proxy contract + * @dev get the boolean of the logic contract * @param logicAddr is the logic proxy address + * @return bool logic proxy is authorised by system admin + * @return bool logic proxy is default proxy */ - function getLogic(address logicAddr) public view returns (bool) { + function isLogicAuth(address logicAddr) public view returns (bool, bool) { if (defaultLogicProxies[logicAddr]) { - return true; + return (true, true); } else if (logicProxies[logicAddr]) { - return true; + return (true, false); } else { - return false; + return (false, false); } } /** * @dev this sets the default logic proxy to true + * default proxies mostly contains the logic for withdrawal of assets + * and can never be false to freely let user withdraw their assets * @param logicAddr is the default logic proxy address */ - function setDefaultLogic(address logicAddr) public isAdmin { + function enableDefaultLogic(address logicAddr) public isAdmin { defaultLogicProxies[logicAddr] = true; - emit LogSetDefaultLogic(logicAddr); + emit LogEnableDefaultLogic(logicAddr); } /** - * @dev this updates the boolean of the logic proxy + * @dev enable logic proxy address and sets true * @param logicAddr is the logic proxy address - * @param isLogic is the boolean to set for the logic proxy */ - function setLogic(address logicAddr, bool isLogic) public isAdmin { + function enableLogic(address logicAddr) public isAdmin { logicProxies[logicAddr] = true; - emit LogSetLogic(logicAddr, isLogic); + emit LogEnableLogic(logicAddr); + } + + /** + * @dev enable logic proxy address and sets false + * @param logicAddr is the logic proxy address + */ + function disableLogic(address logicAddr) public isAdmin { + logicProxies[logicAddr] = false; + emit LogDisableLogic(logicAddr); } } @@ -94,12 +101,13 @@ contract LogicRegistry is AddressRegistry { /** * @title User Wallet Registry */ -contract ProxyRegistry is LogicRegistry { +contract WalletRegistry is LogicRegistry { event Created(address indexed sender, address indexed owner, address proxy); mapping(address => UserWallet) public proxies; - bool public guardianEnabled; + bool public guardianEnabled; // user guardian mechanism enabled + bool public managerEnabled; // is user admin mechanism enabled /** * @dev deploys a new proxy instance and sets msg.sender as owner of proxy @@ -114,7 +122,8 @@ contract ProxyRegistry is LogicRegistry { */ function build(address owner) public returns (UserWallet proxy) { require(proxies[owner] == UserWallet(0), "multiple-proxy-per-user-not-allowed"); - proxy = new UserWallet(owner); + proxy = new UserWallet(); + proxy.setOwnerOnce(owner); emit Created(msg.sender, owner, address(proxy)); proxies[owner] = proxy; } @@ -143,4 +152,28 @@ contract ProxyRegistry is LogicRegistry { guardianEnabled = false; } + /** + * @dev enable user manager in overall system + */ + function enableManager() public isAdmin { + managerEnabled = true; + } + + /** + * @dev disable user manager in overall system + */ + function disableManager() public isAdmin { + managerEnabled = false; + } + +} + + +contract InstaRegistry is WalletRegistry { + + constructor() public { + registry[keccak256(abi.encodePacked("admin"))] = msg.sender; + registry[keccak256(abi.encodePacked("owner"))] = msg.sender; + } + } \ No newline at end of file diff --git a/contracts/UserWallet.sol b/contracts/UserWallet.sol index ad1a2f1..3873aa3 100644 --- a/contracts/UserWallet.sol +++ b/contracts/UserWallet.sol @@ -17,19 +17,20 @@ library SafeMath { * @title AddressRegistryInterface Interface */ interface AddressRegistryInterface { - function getLogic(address logicAddr) external view returns (bool); + function isLogicAuth(address logicAddr) external view returns (bool, bool); function updateProxyRecord(address currentOwner, address nextOwner) external; function guardianEnabled() external returns (bool); + function managerEnabled() external returns (bool); } /** - * @title Address Record + * @title Address Registry Record */ contract AddressRecord { /** - * @dev address registry of system, logic and proxy addresses + * @dev address registry of system, logic and wallet addresses */ address public registry; @@ -47,9 +48,9 @@ contract AddressRecord { * @param logicAddr is the logic proxy contract address * @return the true boolean for logic proxy if authorised otherwise false */ - function isLogicAuthorised(address logicAddr) internal view returns (bool) { + function isLogicAuthorised(address logicAddr) internal view returns (bool, bool) { AddressRegistryInterface logicProxy = AddressRegistryInterface(registry); - return logicProxy.getLogic(logicAddr); + return logicProxy.isLogicAuth(logicAddr); } } @@ -67,7 +68,7 @@ contract UserAuth is AddressRecord { address public owner; address public pendingOwner; uint public claimOnwershipTime; // now + 7 days - uint public gracePeriod; // to set the new owner + uint public gracePeriod; // to set the new owner - defaults to 3 days /** * @dev defines the "proxy registry" contract and sets the owner @@ -110,6 +111,18 @@ contract UserAuth is AddressRecord { emit LogSetOwner(owner, msg.sender); } + /** + * @dev sets owner and function is only be called once by registry on build() + * and this hack verifiy the contract on etherscan automatically + * as no dynamic owner address is sent in the constructor + * @param _owner is the new owner of this contract wallet + */ + function setOwnerOnce(address _owner) public auth { + require(msg.sender == registry, "permission-denied"); + owner = _owner; + emit LogSetOwner(owner, msg.sender); + } + /** * @dev checks if called by owner or contract itself * @param src is the address initiating the call @@ -129,10 +142,12 @@ contract UserAuth is AddressRecord { /** * @title User Guardians + * @dev the assigned guardian addresses (upto 3) can set new owners + * but only after certain period of owner's inactivity (i.e. activePeriod) */ contract UserGuardian is UserAuth { - event LogSetGuardian(address indexed guardian); + event LogSetGuardian(uint num, address indexed prevGuardian, address indexed newGuardian); event LogNewActivePeriod(uint newActivePeriod); event LogSetOwnerViaGuardian(address nextOwner, address indexed guardian); @@ -143,7 +158,7 @@ contract UserGuardian is UserAuth { /** * @dev Throws if guardians not enabled by system admin */ - modifier guard() { + modifier isGuardianEnabled() { AddressRegistryInterface initCall = AddressRegistryInterface(registry); require(initCall.guardianEnabled(), "guardian-not-enabled"); _; @@ -154,7 +169,8 @@ contract UserGuardian is UserAuth { * @param nextOwner is the new owner * @param num is the assigned guardian number */ - function setOwnerViaGuardian(address nextOwner, uint num) public guard { + function setOwnerViaGuardian(address nextOwner, uint num) public isGuardianEnabled { + require(isGuardian(), "not-guardian"); require(msg.sender == guardians[num], "permission-denied"); require(block.timestamp > lastActivity.add(activePeriod), "active-period-not-over"); owner = nextOwner; @@ -166,21 +182,77 @@ contract UserGuardian is UserAuth { * @param num is the guardian assigned number * @param _guardian is the new guardian address */ - function setGuardian(uint num, address _guardian) public auth guard { + function setGuardian(uint num, address _guardian) public auth isGuardianEnabled { require(num > 0 && num < 4, "guardians-cant-exceed-three"); + emit LogSetGuardian(num, guardians[num], _guardian); guardians[num] = _guardian; - emit LogSetGuardian(_guardian); } /** * @dev sets the guardian with assigned number (upto 3) * @param _activePeriod is the period when guardians have no rights to dethrone the owner */ - function updateActivePeriod(uint _activePeriod) public auth guard { + function updateActivePeriod(uint _activePeriod) public auth isGuardianEnabled { activePeriod = _activePeriod; emit LogNewActivePeriod(_activePeriod); } + /** + * @dev Throws if the msg.sender is not guardian + */ + function isGuardian() internal view returns (bool) { + if (msg.sender == guardians[1] || msg.sender == guardians[2] || msg.sender == guardians[3]) { + return true; + } else { + return false; + } + } + +} + + +/** + * @title User Manager + * @dev the assigned manager addresses (upto 3) can manage the wealth in contract to contract fashion + * but can't withdraw the assets on their personal address + */ +contract UserManager is UserGuardian { + + event LogSetManager(uint num, address indexed prevManager, address indexed newManager); + + mapping(uint => address) public managers; + + /** + * @dev Throws if manager not enabled by system admin + */ + modifier isManagerEnabled() { + AddressRegistryInterface initCall = AddressRegistryInterface(registry); + require(initCall.managerEnabled(), "admin-not-enabled"); + _; + } + + /** + * @dev sets the manager with assigned number (upto 3) + * @param num is the assigned number of manager + * @param _manager is the new admin address + */ + function setManager(uint num, address _manager) public auth isManagerEnabled { + require(num > 0 && num < 4, "guardians-cant-exceed-three"); + emit LogSetManager(num, managers[num], _manager); + managers[num] = _manager; + } + + /** + * @dev Throws if the msg.sender is not manager + */ + function isManager() internal view returns (bool) { + if (msg.sender == managers[1] || msg.sender == managers[2] || msg.sender == managers[3]) { + return true; + } else { + return false; + } + } + } @@ -220,20 +292,18 @@ contract UserNote { /** * @title User Owned Contract Wallet */ -contract UserWallet is UserGuardian, UserNote { +contract UserWallet is UserManager, UserNote { event LogExecute(address target, uint srcNum, uint sessionNum); /** * @dev sets the "address registry", owner's last activity, owner's active period and initial owner - * @param _owner initial owner of the contract - * @param _logicRegistryAddr address registry address which have logic proxy registry */ - constructor(address _owner) public { + constructor() public { registry = msg.sender; - owner = _owner; + owner = msg.sender; // will be changed in initial call itself lastActivity = block.timestamp; - activePeriod = 30 days; // default and changeable + activePeriod = 30 days; // default on deployment and changeable afterwards } function() external payable {} @@ -253,12 +323,11 @@ contract UserWallet is UserGuardian, UserNote { ) public payable - auth note returns (bytes memory response) { require(_target != address(0), "user-proxy-target-address-required"); - require(isLogicAuthorised(_target), "logic-proxy-address-not-allowed"); + require(isExecutable(_target), "not-executable"); lastActivity = block.timestamp; emit LogExecute(_target, srcNum, sessionNum); @@ -280,4 +349,21 @@ contract UserWallet is UserGuardian, UserNote { } } -} + /** + * @dev checks if the proxy is authorised + * and if the sender is owner or contract itself or manager + * and if manager then Throws if target is default proxy address + */ + function isExecutable(address proxyTarget) internal view returns (bool) { + (bool isLogic, bool isDefault) = isLogicAuthorised(proxyTarget); + require(isLogic, "logic-proxy-address-not-allowed"); + if (isAuth(msg.sender)) { + return true; + } else if (isManager() && !isDefault) { + return true; + } else { + return false; + } + } + +} \ No newline at end of file