22pragma solidity 0.8.26 ;
33
44import {Roles} from "./Roles.sol " ;
5+ import {SanctionsList} from "./interfaces/SanctionsList.sol " ;
56import {RolesLib} from "./libraries/RolesLib.sol " ;
67import {WhitelistableLib} from "./libraries/WhitelistableLib.sol " ;
8+ import {AccessMode} from "./primitives/Enums.sol " ;
79
810abstract contract Whitelistable is Roles {
911 /// @custom:storage-definition erc7201:hopper.storage.Whitelistable
1012 /// @param isWhitelisted The mapping of whitelisted addresses.
1113 /// @param isActivated The flag to check if the whitelist is activated.
1214 struct WhitelistableStorage {
1315 mapping (address => bool ) isWhitelisted;
14- bool isActivated;
16+ // in v0.6.0, we replace the bool isActivated with a enum AccessMode
17+ // bool isActivated; --> AccessMode accessMode;
18+ AccessMode accessMode;
19+ // added in v0.6.0
20+ mapping (address => bool ) isBlacklisted;
21+ SanctionsList externalSanctionList;
1522 }
1623
1724 /// @dev Initializes the whitelist.
18- /// @param activate if the whitelist should be activated .
25+ /// @param accessMode the access mode of the whitelist .
1926 // solhint-disable-next-line func-name-mixedcase
2027 function __Whitelistable_init (
21- bool activate
28+ AccessMode accessMode ,
29+ address externalSanctionsList
2230 ) internal onlyInitializing {
23- if (activate) {
24- WhitelistableStorage storage $ = WhitelistableLib._getWhitelistableStorage ();
25- $.isActivated = true ;
26- }
31+ WhitelistableLib.switchAccessMode (accessMode);
32+ WhitelistableLib.setExternalSanctionsList (SanctionsList (externalSanctionsList));
2733 }
2834
29- /// @notice Deactivates the whitelist
30- function disableWhitelist () public onlyOwner {
31- WhitelistableLib.disableWhitelist ();
35+ function switchAccessMode (
36+ AccessMode newMode
37+ ) public onlyOwner {
38+ WhitelistableLib.switchAccessMode (newMode);
3239 }
3340
34- /// @notice Checks if an account is whitelisted
41+ /// @notice Checks if an account is whitelisted or blacklisted
42+ /// @dev In v0.6.0, this function is extended to also enforce blacklist checks.
3543 /// @param account The address of the account to check
36- /// @return True if the account is whitelisted, false otherwise
44+ /// @return True if the account is whitelisted or not blacklisted , false otherwise
3745 function isWhitelisted (
3846 address account
3947 ) public view returns (bool ) {
40- WhitelistableStorage storage $ = WhitelistableLib._getWhitelistableStorage ();
41- if (RolesLib._getRolesStorage ().feeRegistry.protocolFeeReceiver () == account) {
42- return true ;
43- }
44- return $.isActivated ? $.isWhitelisted[account] : true ;
48+ return WhitelistableLib.isWhitelisted (account);
4549 }
4650
4751 /// @notice Adds multiple accounts to the whitelist
@@ -58,4 +62,82 @@ abstract contract Whitelistable is Roles {
5862 ) external onlyWhitelistManager {
5963 WhitelistableLib.revokeFromWhitelist (accounts);
6064 }
65+
66+ /// @notice Adds multiple accounts to the blacklist
67+ function addToBlacklist (
68+ address [] memory accounts
69+ ) external onlyWhitelistManager {
70+ WhitelistableLib.addToBlacklist (accounts);
71+ }
72+
73+ /// @notice Removes multiple accounts from the blacklist
74+ function revokeFromBlacklist (
75+ address [] memory accounts
76+ ) external onlyWhitelistManager {
77+ WhitelistableLib.revokeFromBlacklist (accounts);
78+ }
79+
80+ /// @notice Sets the external sanctions list
81+ function setExternalSanctionsList (
82+ SanctionsList sanctionsList
83+ ) external onlyWhitelistManager {
84+ WhitelistableLib.setExternalSanctionsList (sanctionsList);
85+ }
6186}
87+
88+ // v0.6.0 storage layout changes:
89+
90+ // ==================== ORIGINAL STORAGE LAYOUT ====================
91+ // slot 0: isWhitelisted (mapping pointer)
92+ // Type: mapping(address => bool)
93+ // Description: Pointer to the mapping of whitelisted addresses
94+ // Visual representation (bytes32):
95+ // 0x0000000000000000000000000000000000000000000000000000000000000000
96+ // | 32 bytes |
97+
98+ // slot 1: isActivated
99+ // Type: bool
100+ // Description: Activation flag (only 1 byte used, right-aligned)
101+ // Visual representation (bytes32):
102+ // isActivated
103+ // 0x0000000000000000000000000000000000000000000000000000000000000000
104+ // | 31 bytes unused |xx|
105+ // (1 byte)
106+
107+ // ==================== NEW STORAGE LAYOUT ====================
108+ // slot 0: isWhitelisted (mapping pointer)
109+ // Type: mapping(address => bool)
110+ // Description: Pointer to the mapping of whitelisted addresses
111+ // Visual representation (bytes32):
112+ // 0x0000000000000000000000000000000000000000000000000000000000000000
113+ // | 32 bytes |
114+ // Note: The actual mapping data is stored at keccak256(key . slot)
115+
116+ // slot 1: accessMode
117+ // Type: enum AccessMode (uint8)
118+ // Description: Access mode enum value (only 1 byte used, right-aligned)
119+ // Possible values: 0x00 (BlacklistMode), 0x01 (WhitelistMode), 0x02 (Deactivated), etc.
120+ // Visual representation (bytes32):
121+ // accessMode
122+ // 0x0000000000000000000000000000000000000000000000000000000000000001
123+ // | 31 bytes unused |xx|
124+ // (1 byte)
125+
126+ // slot 2: isBlacklisted (mapping pointer)
127+ // Type: mapping(address => bool)
128+ // Description: Pointer to the mapping of blacklisted addresses
129+ // Visual representation (bytes32):
130+ // 0x0000000000000000000000000000000000000000000000000000000000000000
131+ // | 32 bytes |
132+ // Note: The actual mapping data is stored at keccak256(key . slot)
133+
134+ // Upgrades scenario:
135+ // we upgrade the whitelistable contract from v0.5.0 to v0.6.0
136+ // 1) isActivated is 0 (deactivated)
137+ // 1.1) accessMode is 0 (blacklist)
138+ // --> vault remains as accessible as before the upgrade
139+ // if the admin wants to disable the blacklist, he can call the disableWhitelist function
140+ // 2) isActivated is 1 (activated)
141+ // 2.1) accessMode is 1 (whitelist)
142+ // --> vault remains in whitelist mode
143+ // if the admin wants to disable the whitelist, he can call the disableWhitelist function
0 commit comments