@@ -3,55 +3,234 @@ pragma solidity 0.8.18;
3
3
4
4
import {MintGoldDustCompany} from "mgd-v2-contracts/MintGoldDustCompany.sol " ;
5
5
import {MGDL2SyncEIP712, ECDSAUpgradeable} from "./MGDL2SyncEIP712.sol " ;
6
- import {SafeAccountChecker, ISafe } from "./utils/SafeAccountChecker .sol " ;
6
+ import {IL1crossDomainMessenger } from "./interfaces/IL1CrossDomainMessenger .sol " ;
7
7
8
8
/// @title MGDCompanyL2Sync
9
9
/// @notice An extension to {MintGoldDustCompany} containing functions that
10
- /// syncs access levels management changes with a L2.
10
+ /// syncs access levels management changes with a L2.
11
11
/// @author Mint Gold Dust LLC
12
12
/// @custom:contact klvh@mintgolddust.io
13
- contract MGDCompanyL2Sync is MGDL2SyncEIP712 , SafeAccountChecker , MintGoldDustCompany {
14
- /// Custom errors
15
- error MGDCompanyL2Sync_setValidatorWithL2Sync_longDeadline ();
16
- error MGDCompanyL2Sync_setValidatorWithL2Sync_notValidSigner ();
13
+ contract MGDCompanyL2Sync is MGDL2SyncEIP712 , MintGoldDustCompany {
14
+ enum CrossAction {
15
+ SetValidator,
16
+ SetWhitelist
17
+ }
18
+
19
+ /**
20
+ * @dev Emit when `setCrossDomainMessenger()` is called.
21
+ * @param messenger address to be set
22
+ */
23
+ event SetCrossDomainMessenger (address messenger );
24
+
25
+ /**
26
+ * @dev Emit when `setCrossDomainMGDCompany()` is called.
27
+ * @param chainId of domain
28
+ * @param mgdCompany address in the indicated domain
29
+ */
30
+ event SetCrossDomainMGDCompany (uint256 indexed chainId , address mgdCompany );
31
+
32
+ /**
33
+ * @dev Emit for soft failing functions.
34
+ * @param deadline of the signature
35
+ */
36
+ event ExpiredDeadline (uint256 deadline );
37
+
38
+ /**
39
+ * @dev Emit when `receiveL1Sync()` fails.
40
+ * @param action intended
41
+ * @param account address
42
+ * @param state change
43
+ */
44
+ event FailedReceiveL1Sync (CrossAction action , address account , bool state );
17
45
18
46
bytes32 private constant _SETVALIDATOR_TYPEHASH =
19
- keccak256 ("SetValidator(address account,bool state,uint256 deadline) " );
47
+ keccak256 ("SetValidator(address account,bool state,uint256 chainId,uint256 deadline) " );
48
+
49
+ bytes32 private constant _WHITELIST_TYPEHASH =
50
+ keccak256 ("Whitelist(address account,bool state,uint256 chainId,uint256 deadline) " );
51
+
52
+ IL1crossDomainMessenger public crossDomainMessenger;
20
53
54
+ /// chain Id => MGDCompanyL2Sync address
55
+ mapping (uint256 => address ) public crossDomainMGDCompany;
56
+
57
+ modifier onlyCrossMessenger () {
58
+ require (msg .sender == address (crossDomainMessenger));
59
+ _;
60
+ }
61
+
62
+ /**
63
+ * @notice Similar to `setValidator()` with L2 synchronizaton.
64
+ * @param account to set as validator
65
+ * @param state to be set
66
+ * @param chainId where the syncing mgdCompany contract exists
67
+ * @param deadline for the syncing to occur via this `signature`
68
+ * @param mgdSignature generated from this `publicKey()`
69
+ * @dev Requirements:
70
+ * - `mgdSignature` should be generated by `MintGoldDustCompany.publicKey()`
71
+ */
21
72
function setValidatorWithL2Sync (
22
73
address account ,
23
74
bool state ,
75
+ uint256 chainId ,
24
76
uint256 deadline ,
25
- uint8 v ,
26
- bytes32 r ,
27
- bytes32 s
77
+ bytes calldata mgdSignature
28
78
)
29
79
external
30
80
onlyOwner
31
81
isZeroAddress (account)
32
82
{
33
- if (deadline > block .timestamp + 1 days) {
34
- revert MGDCompanyL2Sync_setValidatorWithL2Sync_longDeadline ();
35
- }
83
+ _checkDeadline (deadline, true );
36
84
37
- bytes32 structHash = keccak256 (abi.encode (_SETVALIDATOR_TYPEHASH, account, state, deadline));
38
- bytes32 digest = _hashTypedDataV4 (structHash);
39
- address signer = ECDSAUpgradeable.recover (digest, v, r, s);
85
+ bytes32 structHash =
86
+ keccak256 (abi.encode (_SETVALIDATOR_TYPEHASH, account, state, chainId, deadline));
87
+
88
+ require (_verifySignature (publicKey, structHash, mgdSignature), "Invalid signature " );
89
+
90
+ _performL2Call (CrossAction.SetValidator, account, state, chainId, deadline, mgdSignature);
91
+ _setValidator (account, state);
92
+ }
93
+
94
+ /**
95
+ * @notice Similar to `whitelist()` with L2 synchronizaton.
96
+ * @param account to set as validator
97
+ * @param state to be set
98
+ * @param chainId where the syncing mgdCompany contract exists
99
+ * @param deadline for the syncing to occur via this `signature`
100
+ * @param mgdSignature generated from this `publicKey()`
101
+ * @dev Requirements:
102
+ * - `mgdSignature` should be generated by `MintGoldDustCompany.publicKey()`
103
+ */
104
+ function whitelistWithL2Sync (
105
+ address account ,
106
+ bool state ,
107
+ uint256 chainId ,
108
+ uint256 deadline ,
109
+ bytes calldata mgdSignature
110
+ )
111
+ external
112
+ isValidatorOrOwner
113
+ isZeroAddress (account)
114
+ {
115
+ _checkDeadline (deadline, true );
116
+
117
+ bytes32 structHash =
118
+ keccak256 (abi.encode (_WHITELIST_TYPEHASH, account, state, chainId, deadline));
119
+
120
+ require (_verifySignature (publicKey, structHash, mgdSignature), "Invalid signature " );
121
+
122
+ _performL2Call (CrossAction.SetWhitelist, account, state, chainId, deadline, mgdSignature);
123
+ _whitelist (account, state);
124
+ }
125
+
126
+ function receiveL1Sync (bytes memory data ) external onlyCrossMessenger {
127
+ (CrossAction action , address account , bool state , uint256 deadline , bytes memory mgdSignature ) =
128
+ abi.decode (data, (CrossAction, address , bool , uint256 , bytes ));
40
129
41
- if (_isOwnerAContract () && isAddressASafe (owner ())) {
42
- if (! isAccountOwnerInSafe (signer, owner ())) {
43
- revert MGDCompanyL2Sync_setValidatorWithL2Sync_notValidSigner ();
130
+ bool success;
131
+
132
+ if (action == CrossAction.SetValidator) {
133
+ bytes32 structHash =
134
+ keccak256 (abi.encode (_SETVALIDATOR_TYPEHASH, account, state, block .chainid , deadline));
135
+ if (_verifySignature (publicKey, structHash, mgdSignature)) {
136
+ _setValidator (account, state);
137
+ success = true ;
138
+ }
139
+ } else if (action == CrossAction.SetWhitelist) {
140
+ bytes32 structHash =
141
+ keccak256 (abi.encode (_WHITELIST_TYPEHASH, account, state, block .chainid , deadline));
142
+ if (_verifySignature (publicKey, structHash, mgdSignature)) {
143
+ _whitelist (account, state);
144
+ success = true ;
44
145
}
45
- } else {}
146
+ }
46
147
47
- if (block . chainid == 0x1 ) {
48
- isAddressValidator[account] = state;
148
+ if (! success ) {
149
+ emit FailedReceiveL1Sync (action, account, state) ;
49
150
}
151
+ }
152
+
153
+ /**
154
+ * @notice Sets defined cross domain messenger address between
155
+ * L1<>L2 or L2<>L1
156
+ * @param messenger canonical address between L1 or L2
157
+ */
158
+ function setCrossDomainMessenger (address messenger ) external onlyOwner isZeroAddress (messenger) {
159
+ crossDomainMessenger = IL1crossDomainMessenger (messenger);
160
+ emit SetCrossDomainMessenger (messenger);
161
+ }
162
+
163
+ /**
164
+ * @notice Sets the mapping between a `chainId` and the {MGDCompany} contract
165
+ * address there
166
+ * @param chainId of the domain
167
+ * @param mgdCompany address in the indicated domain
168
+ */
169
+ function setCrossDomainMGDCompany (
170
+ uint32 chainId ,
171
+ address mgdCompany
172
+ )
173
+ external
174
+ onlyOwner
175
+ isZeroAddress (mgdCompany)
176
+ {
177
+ require (chainId != 0 , "Invalid chainId " );
178
+ crossDomainMGDCompany[chainId] = mgdCompany;
179
+ emit SetCrossDomainMGDCompany (chainId, mgdCompany);
180
+ }
181
+
182
+ function _performL2Call (
183
+ CrossAction action ,
184
+ address account ,
185
+ bool state ,
186
+ uint256 chainId ,
187
+ uint256 deadline ,
188
+ bytes calldata mgdSignature
189
+ )
190
+ private
191
+ {
192
+ bytes memory message = abi.encodeWithSelector (
193
+ this .receiveL1Sync.selector , abi.encode (action, account, state, deadline, mgdSignature)
194
+ );
195
+ crossDomainMessenger.sendMessage (crossDomainMGDCompany[chainId], message, 1000000 );
196
+ }
197
+
198
+ function _checkDeadline (uint256 deadline , bool withRevert ) private {
199
+ if (withRevert) {
200
+ require (deadline < (block .timestamp + 1 days), "Short deadline " );
201
+ } else if (deadline > (block .timestamp + 1 days)) {
202
+ emit ExpiredDeadline (deadline);
203
+ }
204
+ }
205
+
206
+ /**
207
+ * @notice Verify a `signature` of a message was signed
208
+ * by an `expectedSigner`.
209
+ * @param expectedSigner is the signer address.
210
+ * @param structHash is the _signature of the eip712 object generated off chain.
211
+ * @param signature of the message
212
+ */
213
+ function _verifySignature (
214
+ address expectedSigner ,
215
+ bytes32 structHash ,
216
+ bytes memory signature
217
+ )
218
+ private
219
+ view
220
+ returns (bool )
221
+ {
222
+ bytes32 digest = _hashTypedDataV4 (structHash);
223
+ address signer = ECDSAUpgradeable.recover (digest, signature);
224
+ return signer == expectedSigner;
225
+ }
50
226
227
+ function _setValidator (address account , bool state ) private {
228
+ isAddressValidator[account] = state;
51
229
emit ValidatorAdded (account, state);
52
230
}
53
231
54
- function _isOwnerAContract () internal view returns (bool ) {
55
- return address (owner ()).code.length > 0 ;
232
+ function _whitelist (address account , bool state ) private {
233
+ isArtistApproved[account] = state;
234
+ emit ArtistWhitelisted (account, state);
56
235
}
57
236
}
0 commit comments