-
Notifications
You must be signed in to change notification settings - Fork 9
/
Whitelist.sol
137 lines (124 loc) · 4.8 KB
/
Whitelist.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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.21;
import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
/**
* @notice An external Whitelist module that Ion's system-wide contracts can use
* to verify that a user is permitted to borrow or lend.
*
* A merkle whitelist is used to allow for a large number of addresses to be
* whitelisted without consuming infordinate amounts of gas for the updates.
*
* There is also a protocol whitelist that can be used to allow for a protocol
* controlled address to bypass the merkle proof check. These
* protocol-controlled contract are expected to perform whitelist checks
* themsleves on their own entrypoints.
*
* @dev The full merkle tree is stored off-chain and only the root is stored
* on-chain.
*
* @custom:security-contact security@molecularlabs.io
*/
contract Whitelist is Ownable2Step {
mapping(address protocolControlledAddress => bool) public protocolWhitelist; // peripheral addresses that can bypass
// the merkle proof check
mapping(uint8 ilkIndex => bytes32) public borrowersRoot; // root of the merkle tree of borrowers for each ilk
bytes32 public lendersRoot; // root of the merkle tree of lenders for each ilk
// --- Errors ---
error NotWhitelistedBorrower(uint8 ilkIndex, address addr);
error NotWhitelistedLender(address addr);
/**
* @notice Creates a new `Whitelist` instance.
* @param _borrowersRoots List borrower merkle roots for each ilk.
* @param _lendersRoot The lender merkle root.
*/
constructor(bytes32[] memory _borrowersRoots, bytes32 _lendersRoot) Ownable(msg.sender) {
for (uint8 i = 0; i < _borrowersRoots.length; i++) {
borrowersRoot[i] = _borrowersRoots[i];
}
lendersRoot = _lendersRoot;
}
/**
* @notice Updates the borrower merkle root for a specific ilk.
* @param ilkIndex of the ilk.
* @param _borrowersRoot The new borrower merkle root.
*/
function updateBorrowersRoot(uint8 ilkIndex, bytes32 _borrowersRoot) external onlyOwner {
borrowersRoot[ilkIndex] = _borrowersRoot;
}
/**
* @notice Updates the lender merkle root.
* @param _lendersRoot The new lender merkle root.
*/
function updateLendersRoot(bytes32 _lendersRoot) external onlyOwner {
lendersRoot = _lendersRoot;
}
/**
* @notice Approves a protocol controlled address to bypass the merkle proof check.
* @param addr The address to approve.
*/
function approveProtocolWhitelist(address addr) external onlyOwner {
protocolWhitelist[addr] = true;
}
/**
* @notice Revokes a protocol controlled address to bypass the merkle proof check.
* @param addr The address to revoke approval for.
*/
function revokeProtocolWhitelist(address addr) external onlyOwner {
protocolWhitelist[addr] = false;
}
/**
* @notice Called by external modifiers to prove inclusion as a borrower.
* @dev If the root is just zero, then the whitelist is effectively turned
* off as every address will be allowed.
* @return True if the addr is part of the borrower whitelist or the
* protocol whitelist. False otherwise.
*/
function isWhitelistedBorrower(
uint8 ilkIndex,
address poolCaller,
address addr,
bytes32[] calldata proof
)
external
view
returns (bool)
{
if (protocolWhitelist[poolCaller]) return true;
bytes32 root = borrowersRoot[ilkIndex];
if (root == 0) return true;
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(addr))));
if (MerkleProof.verify(proof, root, leaf)) {
return true;
} else {
revert NotWhitelistedBorrower(ilkIndex, addr);
}
}
/**
* @notice Called by external modifiers to prove inclusion as a lender.
* @dev If the root is just zero, then the whitelist is effectively turned
* off as every address will be allowed.
* @return True if the addr is part of the lender whitelist or the protocol
* whitelist. False otherwise.
*/
function isWhitelistedLender(
address poolCaller,
address addr,
bytes32[] calldata proof
)
external
view
returns (bool)
{
if (protocolWhitelist[poolCaller]) return true;
bytes32 root = lendersRoot;
if (root == bytes32(0)) return true;
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(addr))));
if (MerkleProof.verify(proof, root, leaf)) {
return true;
} else {
revert NotWhitelistedLender(addr);
}
}
}