Skip to content

Commit 2833827

Browse files
committed
feat: refine l2 sync mgd company contract
1 parent 8e03315 commit 2833827

File tree

4 files changed

+211
-68
lines changed

4 files changed

+211
-68
lines changed

foundry.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ src = "src"
44
out = "out"
55
libs = ["lib"]
66
optimizer = true
7-
optimizer_runs = 200
7+
optimizer_runs = 10000
88
build_info = true
99
extra_output = ["storageLayout"]
1010

src/MGDCompanyL2Sync.sol

+203-24
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,234 @@ pragma solidity 0.8.18;
33

44
import {MintGoldDustCompany} from "mgd-v2-contracts/MintGoldDustCompany.sol";
55
import {MGDL2SyncEIP712, ECDSAUpgradeable} from "./MGDL2SyncEIP712.sol";
6-
import {SafeAccountChecker, ISafe} from "./utils/SafeAccountChecker.sol";
6+
import {IL1crossDomainMessenger} from "./interfaces/IL1CrossDomainMessenger.sol";
77

88
/// @title MGDCompanyL2Sync
99
/// @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.
1111
/// @author Mint Gold Dust LLC
1212
/// @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);
1745

1846
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;
2053

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+
*/
2172
function setValidatorWithL2Sync(
2273
address account,
2374
bool state,
75+
uint256 chainId,
2476
uint256 deadline,
25-
uint8 v,
26-
bytes32 r,
27-
bytes32 s
77+
bytes calldata mgdSignature
2878
)
2979
external
3080
onlyOwner
3181
isZeroAddress(account)
3282
{
33-
if (deadline > block.timestamp + 1 days) {
34-
revert MGDCompanyL2Sync_setValidatorWithL2Sync_longDeadline();
35-
}
83+
_checkDeadline(deadline, true);
3684

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));
40129

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;
44145
}
45-
} else {}
146+
}
46147

47-
if (block.chainid == 0x1) {
48-
isAddressValidator[account] = state;
148+
if (!success) {
149+
emit FailedReceiveL1Sync(action, account, state);
49150
}
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+
}
50226

227+
function _setValidator(address account, bool state) private {
228+
isAddressValidator[account] = state;
51229
emit ValidatorAdded(account, state);
52230
}
53231

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);
56235
}
57236
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity 0.8.18;
3+
4+
interface IL1crossDomainMessenger {
5+
function xDomainMessageSender() external returns (address);
6+
function sendMessage(address _target, bytes memory _message, uint32 _gasLimit) external;
7+
}

src/utils/SafeAccountChecker.sol

-43
This file was deleted.

0 commit comments

Comments
 (0)