forked from OpenZeppelin/damn-vulnerable-defi
-
Notifications
You must be signed in to change notification settings - Fork 845
/
WalletRegistry.sol
97 lines (76 loc) · 3.57 KB
/
WalletRegistry.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol";
import "@gnosis.pm/safe-contracts/contracts/proxies/IProxyCreationCallback.sol";
/**
* @title WalletRegistry
* @notice A registry for Gnosis Safe wallets.
When known beneficiaries deploy and register their wallets, the registry sends some Damn Valuable Tokens to the wallet.
* @dev The registry has embedded verifications to ensure only legitimate Gnosis Safe wallets are stored.
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
*/
contract WalletRegistry is IProxyCreationCallback, Ownable {
uint256 private constant MAX_OWNERS = 1;
uint256 private constant MAX_THRESHOLD = 1;
uint256 private constant TOKEN_PAYMENT = 10 ether; // 10 * 10 ** 18
address public immutable masterCopy;
address public immutable walletFactory;
IERC20 public immutable token;
mapping (address => bool) public beneficiaries;
// owner => wallet
mapping (address => address) public wallets;
constructor(
address masterCopyAddress,
address walletFactoryAddress,
address tokenAddress,
address[] memory initialBeneficiaries
) {
require(masterCopyAddress != address(0));
require(walletFactoryAddress != address(0));
masterCopy = masterCopyAddress;
walletFactory = walletFactoryAddress;
token = IERC20(tokenAddress);
for (uint256 i = 0; i < initialBeneficiaries.length; i++) {
addBeneficiary(initialBeneficiaries[i]);
}
}
function addBeneficiary(address beneficiary) public onlyOwner {
beneficiaries[beneficiary] = true;
}
function _removeBeneficiary(address beneficiary) private {
beneficiaries[beneficiary] = false;
}
/**
@notice Function executed when user creates a Gnosis Safe wallet via GnosisSafeProxyFactory::createProxyWithCallback
setting the registry's address as the callback.
*/
function proxyCreated(
GnosisSafeProxy proxy,
address singleton,
bytes calldata initializer,
uint256
) external override {
// Make sure we have enough DVT to pay
require(token.balanceOf(address(this)) >= TOKEN_PAYMENT, "Not enough funds to pay");
address payable walletAddress = payable(proxy);
// Ensure correct factory and master copy
require(msg.sender == walletFactory, "Caller must be factory");
require(singleton == masterCopy, "Fake mastercopy used");
// Ensure initial calldata was a call to `GnosisSafe::setup`
require(bytes4(initializer[:4]) == GnosisSafe.setup.selector, "Wrong initialization");
// Ensure wallet initialization is the expected
require(GnosisSafe(walletAddress).getThreshold() == MAX_THRESHOLD, "Invalid threshold");
require(GnosisSafe(walletAddress).getOwners().length == MAX_OWNERS, "Invalid number of owners");
// Ensure the owner is a registered beneficiary
address walletOwner = GnosisSafe(walletAddress).getOwners()[0];
require(beneficiaries[walletOwner], "Owner is not registered as beneficiary");
// Remove owner as beneficiary
_removeBeneficiary(walletOwner);
// Register the wallet under the owner's address
wallets[walletOwner] = walletAddress;
// Pay tokens to the newly created wallet
token.transfer(walletAddress, TOKEN_PAYMENT);
}
}