diff --git a/contracts/Auth.sol b/contracts/Auth.sol new file mode 100644 index 0000000..75c7d0b --- /dev/null +++ b/contracts/Auth.sol @@ -0,0 +1,392 @@ +// This Auth Model also includes UserWallet Logics +// TODO => make it single Auth code for future launch where this Auth Contract will be the owner of UserWallet + +pragma solidity ^0.5.0; + + +/** + * @dev because math is not safe + */ +library SafeMath { + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, "math-not-safe"); + return c; + } +} + + +/** + * @title AddressRegistryInterface Interface + */ +interface AddressRegistryInterface { + function isLogicAuth(address logicAddr) external view returns (bool, bool); + function updateProxyRecord(address currentOwner, address nextOwner) external; + function guardianEnabled() external view returns (bool); + function managerEnabled() external view returns (bool); +} + + +/** + * @title Address Registry Record + */ +contract AddressRecord { + + /** + * @dev address registry of system, logic and wallet addresses + */ + address public registry; + + /** + * @param logicAddr is the logic proxy contract address + * @return the true boolean for logic proxy if authorised otherwise false + */ + function isLogicAuthorised(address logicAddr) public view returns (bool, bool) { + AddressRegistryInterface logicProxy = AddressRegistryInterface(registry); + (bool isLogic, bool isDefault) = logicProxy.isLogicAuth(logicAddr); + return (isLogic, isDefault); + } + + /** + * @dev this updates the internal proxy ownership on "registry" contract + * @param currentOwner is the current owner + * @param nextOwner is the new assigned owner + */ + function setProxyRecordOwner(address currentOwner, address nextOwner) internal { + AddressRegistryInterface initCall = AddressRegistryInterface(registry); + initCall.updateProxyRecord(currentOwner, nextOwner); + } + +} + + +/** + * @title User Auth + */ +contract UserAuth is AddressRecord { + using SafeMath for uint; + using SafeMath for uint256; + + event LogSetOwner(address indexed owner, address setter); + event LogSetPendingOwner(address indexed pendingOwner, address setter); + address public owner; + address public pendingOwner; + uint public claimOnwershipTime; // now + 7 days + uint public gracePeriod; // to set the new owner - defaults to 3 days + + /** + * @dev defines the "proxy registry" contract and sets the owner + */ + constructor() public { + gracePeriod = 3 days; + } + + /** + * @dev Throws if not called by owner or contract itself + */ + modifier auth { + require(isAuth(msg.sender), "permission-denied"); + _; + } + + /** + * @dev sets the "pending owner" and provide 3 days grace period to set the new owner via setOwner() + * Throws if called before 10 (i.e. 7 + 3) day after assigning "pending owner" + * @param nextOwner is the assigned "pending owner" + */ + function setPendingOwner(address nextOwner) public auth { + require(block.timestamp > claimOnwershipTime.add(gracePeriod), "owner-is-still-pending"); + pendingOwner = nextOwner; + claimOnwershipTime = block.timestamp.add(7 days); + emit LogSetPendingOwner(nextOwner, msg.sender); + } + + /** + * @dev sets "pending owner" as real owner + * Throws if no "pending owner" + * Throws if called before 7 day after assigning "pending owner" + */ + function setOwner() public { + require(pendingOwner != address(0), "no-pending-address"); + require(block.timestamp > claimOnwershipTime, "owner-is-still-pending"); + setProxyRecordOwner(owner, pendingOwner); + owner = pendingOwner; + pendingOwner = address(0); + 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 + */ + function isAuth(address src) public view returns (bool) { + if (src == address(this)) { + return true; + } else if (src == owner) { + return true; + } else { + return false; + } + } + +} + + +/** + * @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(uint num, address indexed prevGuardian, address indexed newGuardian); + event LogNewActivePeriod(uint newActivePeriod); + event LogSetOwnerViaGuardian(address nextOwner, address indexed guardian); + + mapping(uint => address) public guardians; + uint public lastActivity; // time when called "execute" last time + uint public activePeriod; // the period over lastActivity when guardians have no rights + + /** + * @dev Throws if guardians not enabled by system admin + */ + modifier isGuardianEnabled() { + AddressRegistryInterface initCall = AddressRegistryInterface(registry); + require(initCall.guardianEnabled(), "guardian-not-enabled"); + _; + } + + /** + * @dev guardians can set "owner" after owner stay inactive for minimum "activePeriod" + * @param nextOwner is the new owner + * @param num is the assigned guardian number + */ + function setOwnerViaGuardian(address nextOwner, uint num) public isGuardianEnabled { + require(isGuardian(msg.sender), "not-guardian"); + require(msg.sender == guardians[num], "permission-denied"); + require(block.timestamp > lastActivity.add(activePeriod), "active-period-not-over"); + owner = nextOwner; + emit LogSetOwnerViaGuardian(nextOwner, guardians[num]); + } + + /** + * @dev sets the guardian with assigned number (upto 5) + * @param num is the guardian assigned number + * @param _guardian is the new guardian address + */ + function setGuardian(uint num, address _guardian) public auth isGuardianEnabled { + require(num > 0 && num < 6, "guardians-cant-exceed-three"); + emit LogSetGuardian(num, guardians[num], _guardian); + guardians[num] = _guardian; + } + + /** + * @dev sets the guardian with assigned number (upto 5) + * @param _activePeriod is the period when guardians have no rights to dethrone the owner + */ + function updateActivePeriod(uint _activePeriod) public auth isGuardianEnabled { + activePeriod = _activePeriod; + emit LogNewActivePeriod(_activePeriod); + } + + /** + * @dev Throws if the msg.sender is not guardian + */ + function isGuardian(address _guardian) public view returns (bool) { + if ( + _guardian == guardians[1] || _guardian == guardians[2] || + _guardian == guardians[3] || _guardian == guardians[4] || + _guardian == guardians[5] + ) + { + 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 5) + * @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 < 6, "guardians-cant-exceed-three"); + emit LogSetManager(num, managers[num], _manager); + managers[num] = _manager; + } + + /** + * @dev Throws if the msg.sender is not manager + */ + function isManager(address _manager) public view returns (bool) { + if ( + _manager == managers[1] || _manager == managers[2] || + _manager == managers[3] || _manager == managers[4] || + _manager == managers[5] + ) + { + return true; + } else { + return false; + } + } + +} + + +/** + * @dev logging the execute events + */ +contract UserNote { + event LogNote( + bytes4 indexed sig, + address indexed guy, + bytes32 indexed foo, + bytes32 bar, + uint wad, + bytes fax + ); + + modifier note { + bytes32 foo; + bytes32 bar; + assembly { + foo := calldataload(4) + bar := calldataload(36) + } + emit LogNote( + msg.sig, + msg.sender, + foo, + bar, + msg.value, + msg.data + ); + _; + } +} + + +/** + * @title User Owned Contract Wallet + */ +contract InstaWallet is UserManager, UserNote { + + event LogExecute(address sender, address target, uint srcNum, uint sessionNum); + + /** + * @dev sets the "address registry", owner's last activity, owner's active period and initial owner + */ + constructor() public { + registry = msg.sender; + owner = msg.sender; // will be changed in initial call itself + lastActivity = block.timestamp; + activePeriod = 30 days; // default on deployment and changeable afterwards + } + + function() external payable {} + + /** + * @dev execute authorised calls via delegate call + * @param _target logic proxy address + * @param _data delegate call data + * @param srcNum to find the source + * @param sessionNum to find the session + */ + function execute( + address _target, + bytes memory _data, + uint srcNum, + uint sessionNum + ) + public + payable + note + isExecutable(_target) + returns (bytes memory response) + { + lastActivity = block.timestamp; + emit LogExecute( + msg.sender, + _target, + srcNum, + sessionNum + ); + + // call contract in current context + assembly { + let succeeded := delegatecall(sub(gas, 5000), _target, add(_data, 0x20), mload(_data), 0, 0) + let size := returndatasize + + response := mload(0x40) + mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f)))) + mstore(response, size) + returndatacopy(add(response, 0x20), 0, size) + + switch iszero(succeeded) + case 1 { + // throw if delegatecall failed + revert(add(response, 0x20), size) + } + } + } + + /** + * @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 + */ + modifier isExecutable(address proxyTarget) { + require(proxyTarget != address(0), "logic-proxy-address-required"); + + (bool isLogic, bool isDefault) = isLogicAuthorised(proxyTarget); + require(isLogic, "logic-proxy-address-not-allowed"); + + bool enact = false; + if (isAuth(msg.sender)) { + enact = true; + } else if (isManager(msg.sender) && !isDefault) { + enact = true; + } + + require(enact, "not-executable"); + _; + } + +} \ No newline at end of file diff --git a/contracts/Registry.sol b/contracts/Registry.sol index 810d4f0..5458dd0 100644 --- a/contracts/Registry.sol +++ b/contracts/Registry.sol @@ -106,8 +106,6 @@ contract WalletRegistry is LogicRegistry { event Created(address indexed sender, address indexed owner, address proxy); mapping(address => InstaWallet) public proxies; - bool public guardianEnabled; // user guardian mechanism enabled in overall system - bool public managerEnabled; // user manager mechanism enabled in overall system /** * @dev deploys a new proxy instance and sets msg.sender as owner of proxy @@ -123,7 +121,7 @@ contract WalletRegistry is LogicRegistry { function build(address owner) public returns (InstaWallet proxy) { require(proxies[owner] == InstaWallet(0), "multiple-proxy-per-user-not-allowed"); proxy = new InstaWallet(); - proxy.setOwnerOnce(owner); + proxy.setOwner(owner); emit Created(msg.sender, owner, address(proxy)); proxies[owner] = proxy; } @@ -138,34 +136,6 @@ contract WalletRegistry is LogicRegistry { proxies[currentOwner] = InstaWallet(0); } - /** - * @dev enable guardian in overall system - */ - function enableGuardian() public isAdmin { - guardianEnabled = true; - } - - /** - * @dev disable guardian in overall system - */ - function disableGuardian() public isAdmin { - 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; - } - } diff --git a/contracts/UserWallet.sol b/contracts/UserWallet.sol index 8dac423..e223788 100644 --- a/contracts/UserWallet.sol +++ b/contracts/UserWallet.sol @@ -19,8 +19,6 @@ library SafeMath { interface AddressRegistryInterface { function isLogicAuth(address logicAddr) external view returns (bool, bool); function updateProxyRecord(address currentOwner, address nextOwner) external; - function guardianEnabled() external view returns (bool); - function managerEnabled() external view returns (bool); } @@ -64,19 +62,8 @@ contract UserAuth is AddressRecord { using SafeMath for uint; using SafeMath for uint256; - event LogSetOwner(address indexed owner, address setter); - event LogSetPendingOwner(address indexed pendingOwner, address setter); + event LogSetOwner(address indexed owner); address public owner; - address public pendingOwner; - uint public claimOnwershipTime; // now + 7 days - uint public gracePeriod; // to set the new owner - defaults to 3 days - - /** - * @dev defines the "proxy registry" contract and sets the owner - */ - constructor() public { - gracePeriod = 3 days; - } /** * @dev Throws if not called by owner or contract itself @@ -87,41 +74,12 @@ contract UserAuth is AddressRecord { } /** - * @dev sets the "pending owner" and provide 3 days grace period to set the new owner via setOwner() - * Throws if called before 10 (i.e. 7 + 3) day after assigning "pending owner" - * @param nextOwner is the assigned "pending owner" + * @dev sets new owner */ - function setPendingOwner(address nextOwner) public auth { - require(block.timestamp > claimOnwershipTime.add(gracePeriod), "owner-is-still-pending"); - pendingOwner = nextOwner; - claimOnwershipTime = block.timestamp.add(7 days); - emit LogSetPendingOwner(nextOwner, msg.sender); - } - - /** - * @dev sets "pending owner" as real owner - * Throws if no "pending owner" - * Throws if called before 7 day after assigning "pending owner" - */ - function setOwner() public { - require(pendingOwner != address(0), "no-pending-address"); - require(block.timestamp > claimOnwershipTime, "owner-is-still-pending"); - setProxyRecordOwner(owner, pendingOwner); - owner = pendingOwner; - pendingOwner = address(0); - 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); + function setOwner(address nextOwner) public auth { + setProxyRecordOwner(owner, nextOwner); + owner = nextOwner; + emit LogSetOwner(nextOwner); } /** @@ -129,135 +87,9 @@ contract UserAuth is AddressRecord { * @param src is the address initiating the call */ function isAuth(address src) public view returns (bool) { - if (src == address(this)) { + if (src == owner) { return true; - } else if (src == owner) { - return true; - } else { - return false; - } - } - -} - - -/** - * @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(uint num, address indexed prevGuardian, address indexed newGuardian); - event LogNewActivePeriod(uint newActivePeriod); - event LogSetOwnerViaGuardian(address nextOwner, address indexed guardian); - - mapping(uint => address) public guardians; - uint public lastActivity; // time when called "execute" last time - uint public activePeriod; // the period over lastActivity when guardians have no rights - - /** - * @dev Throws if guardians not enabled by system admin - */ - modifier isGuardianEnabled() { - AddressRegistryInterface initCall = AddressRegistryInterface(registry); - require(initCall.guardianEnabled(), "guardian-not-enabled"); - _; - } - - /** - * @dev guardians can set "owner" after owner stay inactive for minimum "activePeriod" - * @param nextOwner is the new owner - * @param num is the assigned guardian number - */ - function setOwnerViaGuardian(address nextOwner, uint num) public isGuardianEnabled { - require(isGuardian(msg.sender), "not-guardian"); - require(msg.sender == guardians[num], "permission-denied"); - require(block.timestamp > lastActivity.add(activePeriod), "active-period-not-over"); - owner = nextOwner; - emit LogSetOwnerViaGuardian(nextOwner, guardians[num]); - } - - /** - * @dev sets the guardian with assigned number (upto 5) - * @param num is the guardian assigned number - * @param _guardian is the new guardian address - */ - function setGuardian(uint num, address _guardian) public auth isGuardianEnabled { - require(num > 0 && num < 6, "guardians-cant-exceed-three"); - emit LogSetGuardian(num, guardians[num], _guardian); - guardians[num] = _guardian; - } - - /** - * @dev sets the guardian with assigned number (upto 5) - * @param _activePeriod is the period when guardians have no rights to dethrone the owner - */ - function updateActivePeriod(uint _activePeriod) public auth isGuardianEnabled { - activePeriod = _activePeriod; - emit LogNewActivePeriod(_activePeriod); - } - - /** - * @dev Throws if the msg.sender is not guardian - */ - function isGuardian(address _guardian) public view returns (bool) { - if ( - _guardian == guardians[1] || _guardian == guardians[2] || - _guardian == guardians[3] || _guardian == guardians[4] || - _guardian == guardians[5] - ) - { - 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 5) - * @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 < 6, "guardians-cant-exceed-three"); - emit LogSetManager(num, managers[num], _manager); - managers[num] = _manager; - } - - /** - * @dev Throws if the msg.sender is not manager - */ - function isManager(address _manager) public view returns (bool) { - if ( - _manager == managers[1] || _manager == managers[2] || - _manager == managers[3] || _manager == managers[4] || - _manager == managers[5] - ) - { + } else if (src == address(this)) { return true; } else { return false; @@ -292,7 +124,7 @@ contract UserNote { msg.sender, foo, bar, - msg.value, + msg.value, msg.data ); _; @@ -303,7 +135,7 @@ contract UserNote { /** * @title User Owned Contract Wallet */ -contract InstaWallet is UserManager, UserNote { +contract InstaWallet is UserAuth, UserNote { event LogExecute(address sender, address target, uint srcNum, uint sessionNum); @@ -312,9 +144,7 @@ contract InstaWallet is UserManager, UserNote { */ constructor() public { registry = msg.sender; - owner = msg.sender; // will be changed in initial call itself - lastActivity = block.timestamp; - activePeriod = 30 days; // default on deployment and changeable afterwards + owner = msg.sender; } function() external payable {} @@ -335,10 +165,10 @@ contract InstaWallet is UserManager, UserNote { public payable note + auth isExecutable(_target) returns (bytes memory response) { - lastActivity = block.timestamp; emit LogExecute( msg.sender, _target, @@ -371,18 +201,8 @@ contract InstaWallet is UserManager, UserNote { */ modifier isExecutable(address proxyTarget) { require(proxyTarget != address(0), "logic-proxy-address-required"); - - (bool isLogic, bool isDefault) = isLogicAuthorised(proxyTarget); - require(isLogic, "logic-proxy-address-not-allowed"); - - bool enact = false; - if (isAuth(msg.sender)) { - enact = true; - } else if (isManager(msg.sender) && !isDefault) { - enact = true; - } - - require(enact, "not-executable"); + (bool isLogic, ) = isLogicAuthorised(proxyTarget); + require(isLogic, "logic-proxy-address-not-allowed"); _; }