Last active
April 24, 2023 17:55
-
-
Save Alexintosh/1ad0bdd9658f992d05bd45ee35ad482e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.8.0; | |
import "@gnosis.pm/safe-contracts/contracts/base/Module.sol"; | |
contract PasswordRecoveryModule is Module { | |
bytes32 private passwordHash; | |
uint256 private constant BLOCKS_VALID = 200; | |
uint256 private constant RECOVERY_DELAY = 50; | |
uint256 private recoveryInitiatedBlock; | |
address private recoveryInitiator; | |
mapping(address => bool) private recoveryCancelers; | |
event PasswordHashUpdated(bytes32 newPasswordHash); | |
event RecoveryInitiated(address indexed initiator, uint256 validUntilBlock); | |
event RecoveryCancelled(address indexed canceler); | |
event CancelerAdded(address indexed canceler); | |
event CancelerRemoved(address indexed canceler); | |
constructor( | |
address _safe, | |
bytes32 _passwordHash, | |
address[] memory _recoveryCancelers | |
) { | |
require(_safe != address(0), "Invalid Safe address"); | |
require(_passwordHash != bytes32(0), "Invalid password hash"); | |
safe = Safe(_safe); | |
passwordHash = _passwordHash; | |
for (uint256 i = 0; i < _recoveryCancelers.length; i++) { | |
require(_recoveryCancelers[i] != address(0), "Invalid recovery canceler address"); | |
recoveryCancelers[_recoveryCancelers[i]] = true; | |
} | |
} | |
function setup(bytes memory initParams) public override { | |
// No setup required for this module | |
} | |
function updatePasswordHash(bytes32 newPasswordHash) public { | |
require(safe.isOwner(msg.sender), "Not authorized"); | |
require(newPasswordHash != bytes32(0), "Invalid password hash"); | |
passwordHash = newPasswordHash; | |
emit PasswordHashUpdated(newPasswordHash); | |
} | |
function initiateRecovery() public { | |
recoveryInitiatedBlock = block.number; | |
recoveryInitiator = msg.sender; | |
emit RecoveryInitiated(msg.sender, recoveryInitiatedBlock + BLOCKS_VALID); | |
} | |
function cancelRecovery() public { | |
require( | |
safe.isOwner(msg.sender) || recoveryCancelers[msg.sender], | |
"Not authorized" | |
); | |
require(recoveryInitiator != address(0), "No recovery initiated"); | |
emit RecoveryCancelled(msg.sender); | |
// Reset recovery state | |
recoveryInitiatedBlock = 0; | |
recoveryInitiator = address(0); | |
} | |
function recoverAccess(address newOwner, string memory password) public { | |
require(msg.sender == recoveryInitiator, "Not authorized"); | |
require(recoveryInitiatedBlock + RECOVERY_DELAY <= block.number, "Recovery delay not met"); | |
require(recoveryInitiatedBlock + BLOCKS_VALID >= block.number, "Recovery reservation expired"); | |
require(passwordHash == keccak256(abi.encodePacked(password)), "Invalid password"); | |
address oldOwner = safe.getOwner(msg.sender); | |
require(oldOwner != address(0), "Not an owner"); | |
safe.swapOwner(msg.sender, oldOwner, newOwner); | |
// Reset recovery state and password hash | |
recoveryInitiatedBlock = 0; | |
recoveryInitiator = address(0); | |
passwordHash = bytes32(0); | |
} | |
function addCanceler(address canceler) public { | |
require(safe.isOwner(msg.sender), "Not authorized"); | |
require(canceler != address(0), "Invalid canceler address"); | |
require(!recoveryCancelers[canceler], "Address is already a canceler"); | |
recoveryCancelers[canceler] = true; | |
emit CancelerAdded(canceler); | |
} | |
function removeCanceler(address canceler) public { | |
require(safe.isOwner(msg.sender), "Not authorized"); | |
require(canceler != address(0), "Invalid canceler address"); | |
require(recoveryCancelers[canceler], "Address is not a canceler"); | |
delete recoveryCancelers[canceler]; | |
emit CancelerRemoved(canceler); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment