Skip to content

Commit 4f8a15c

Browse files
committed
Add BridgedGovernor
1 parent f7735ed commit 4f8a15c

File tree

7 files changed

+293
-1
lines changed

7 files changed

+293
-1
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
node_modules
21
/out
32
/cache
3+
/broadcast

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@
77
[submodule "lib/chainlink"]
88
path = lib/chainlink
99
url = https://github.com/smartcontractkit/chainlink
10+
[submodule "lib/LayerZero-v2"]
11+
path = lib/LayerZero-v2
12+
url = https://github.com/LayerZero-Labs/LayerZero-v2

foundry.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ evm_version = 'shanghai'
44
optimizer_runs = 7_700
55
verbosity = 1
66
fuzz_runs = 5
7+
script = 'scripts'
78
[fmt]
89
line_length = 100
910
[fuzz]

lib/LayerZero-v2

Submodule LayerZero-v2 added at 142846c

remappings.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ ds-test/=lib/forge-std/lib/ds-test/src/
22
forge-std/=lib/forge-std/src/
33
openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/
44
chainlink/=lib/chainlink/contracts/src/v0.8/
5+
layer-zero-v2/=lib/LayerZero-v2/

scripts/DeployBridgedGovernor.s.sol

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
pragma solidity ^0.8.20;
3+
4+
import {console, Script} from "forge-std/Script.sol";
5+
import {ExecutorOptions} from "layer-zero-v2/protocol/contracts/messagelib/libs/ExecutorOptions.sol";
6+
// import {OptionsBuilder} from "layer-zero-v2/oapp/contracts/oapp/libs/OptionsBuilder.sol";
7+
import {
8+
ILayerZeroEndpointV2,
9+
IMessageLibManager,
10+
MessagingParams,
11+
MessagingReceipt
12+
} from "layer-zero-v2/protocol/contracts/interfaces/ILayerZeroEndpointV2.sol";
13+
import {SetConfigParam} from "layer-zero-v2/protocol/contracts/interfaces/IMessageLibManager.sol";
14+
import {Constant} from "layer-zero-v2/messagelib/test/util/Constant.sol";
15+
import {Strings} from "openzeppelin-contracts/utils/Strings.sol";
16+
import {BridgedGovernor, BridgedGovernorProxy, Call} from "src/BridgedGovernor.sol";
17+
18+
// Taken from layer-zero-v2/messagelib/contracts/uln/UlnBase.sol
19+
struct UlnConfig {
20+
uint64 confirmations;
21+
uint8 requiredDVNCount;
22+
uint8 optionalDVNCount;
23+
uint8 optionalDVNThreshold;
24+
address[] requiredDVNs;
25+
address[] optionalDVNs;
26+
}
27+
28+
uint256 constant SEPOLIA_CHAIN_ID = 11155111;
29+
uint256 constant MUMBAI_CHAIN_ID = 80001;
30+
31+
uint256 constant NO_GRACE_PERIOD = 0;
32+
33+
// Taken from https://docs.layerzero.network/v2/developers/evm/technical-reference/endpoints
34+
address constant SEPOLIA_ENDPOINT = 0x6EDCE65403992e310A62460808c4b910D972f10f;
35+
uint32 constant SEPOLIA_EID = 40161;
36+
address constant MUMBAI_ENDPOINT = 0x6EDCE65403992e310A62460808c4b910D972f10f;
37+
uint32 constant MUMBAI_EID = 40109;
38+
39+
// Taken from https://docs.layerzero.network/v2/developers/evm/technical-reference/messagelibs
40+
address constant MUMBAI_RECEIVE_ULN_302 = 0xfa4Fbda8E809150eE1676ce675AC746Beb9aF379;
41+
address constant SEPOLIA_SEND_ULN_302 = 0xcc1ae8Cf5D3904Cef3360A9532B477529b177cCE;
42+
43+
// Taken from https://docs.layerzero.network/v2/developers/evm/technical-reference/dvn-addresses
44+
address constant SEPOLIA_LAYER_ZERO_LABS_DVN = 0x8eebf8b423B73bFCa51a1Db4B7354AA0bFCA9193;
45+
address constant MUMBAI_LAYER_ZERO_LABS_DVN = 0x67a822F55C5F6E439550b9C4EA39E406480a40f3;
46+
address constant SEPOLIA_BWARE_LABS_DVN = 0xCA7a736be0Fe968A33Af62033B8b36D491f7999B;
47+
address constant MUMBAI_BWARE_LABS_DVN = 0x1cf01d5042d1ae231F918a2645f2762d663476E7;
48+
49+
function requireSorted(address[] memory addresses) pure {
50+
for (uint256 i = 1; i < addresses.length; i++) {
51+
require(uint160(addresses[i - 1]) < uint160(addresses[i]), "Addresses not sorted");
52+
}
53+
}
54+
55+
function messageOptions(uint128 gas) pure returns (bytes memory) {
56+
uint16 optionsType = 3;
57+
return abi.encodePacked(optionsType, executorGasOption(gas), executorOrderedOption());
58+
}
59+
60+
function executorGasOption(uint128 gas) pure returns (bytes memory) {
61+
bytes memory payload = ExecutorOptions.encodeLzReceiveOption(gas, 0);
62+
return executorOption(ExecutorOptions.OPTION_TYPE_LZRECEIVE, payload);
63+
}
64+
65+
function executorOrderedOption() pure returns (bytes memory) {
66+
return executorOption(ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, "");
67+
}
68+
69+
function executorOption(uint8 optionType, bytes memory payload) pure returns (bytes memory) {
70+
uint16 length = uint16(1 + payload.length);
71+
return abi.encodePacked(ExecutorOptions.WORKER_ID, length, optionType, payload);
72+
}
73+
74+
function addressToBytes32(address addr) pure returns (bytes32) {
75+
return bytes32(uint256(uint160(addr)));
76+
}
77+
78+
// forge script scripts/DeployBridgedGovernor.s.sol:ConfigureOnSepolia $WALLET_ARGS -f "$ETH_RPC_URL"
79+
80+
contract ConfigureOnSepolia is Script {
81+
function run() public {
82+
require(block.chainid == SEPOLIA_CHAIN_ID, "Must be run on Sepolia");
83+
84+
address[] memory optionalDVNs = new address[](2);
85+
optionalDVNs[0] = SEPOLIA_LAYER_ZERO_LABS_DVN;
86+
optionalDVNs[1] = SEPOLIA_BWARE_LABS_DVN;
87+
requireSorted(optionalDVNs);
88+
89+
SetConfigParam[] memory params = new SetConfigParam[](1);
90+
params[0] = SetConfigParam({
91+
eid: MUMBAI_EID,
92+
configType: Constant.CONFIG_TYPE_ULN,
93+
config: abi.encode(
94+
UlnConfig({
95+
confirmations: 2,
96+
requiredDVNCount: Constant.NIL_DVN_COUNT,
97+
optionalDVNCount: uint8(optionalDVNs.length),
98+
optionalDVNThreshold: 1,
99+
requiredDVNs: new address[](0),
100+
optionalDVNs: optionalDVNs
101+
})
102+
)
103+
});
104+
105+
vm.startBroadcast();
106+
ILayerZeroEndpointV2 endpoint = ILayerZeroEndpointV2(SEPOLIA_ENDPOINT);
107+
endpoint.setSendLibrary(msg.sender, MUMBAI_EID, SEPOLIA_SEND_ULN_302);
108+
endpoint.setConfig(msg.sender, SEPOLIA_SEND_ULN_302, params);
109+
vm.stopBroadcast();
110+
}
111+
}
112+
113+
// forge script scripts/DeployBridgedGovernor.s.sol:DeployToMumbai $WALLET_ARGS -f "$ETH_RPC_URL"
114+
115+
contract DeployToMumbai is Script {
116+
function run() public {
117+
require(block.chainid == MUMBAI_CHAIN_ID, "Must be run on Polygon Mumbai");
118+
119+
address[] memory optionalDVNs = new address[](2);
120+
optionalDVNs[0] = MUMBAI_BWARE_LABS_DVN;
121+
optionalDVNs[1] = MUMBAI_LAYER_ZERO_LABS_DVN;
122+
requireSorted(optionalDVNs);
123+
124+
SetConfigParam[] memory params = new SetConfigParam[](1);
125+
params[0] = SetConfigParam({
126+
eid: SEPOLIA_EID,
127+
configType: Constant.CONFIG_TYPE_ULN,
128+
config: abi.encode(
129+
UlnConfig({
130+
confirmations: 2,
131+
requiredDVNCount: Constant.NIL_DVN_COUNT,
132+
optionalDVNCount: uint8(optionalDVNs.length),
133+
optionalDVNThreshold: 1,
134+
requiredDVNs: new address[](0),
135+
optionalDVNs: optionalDVNs
136+
})
137+
)
138+
});
139+
140+
address governor = vm.computeCreateAddress(msg.sender, vm.getNonce(msg.sender) + 1);
141+
Call[] memory calls = new Call[](2);
142+
calls[0] = Call({
143+
target: MUMBAI_ENDPOINT,
144+
data: abi.encodeWithSelector(
145+
IMessageLibManager.setReceiveLibrary.selector,
146+
governor,
147+
SEPOLIA_EID,
148+
MUMBAI_RECEIVE_ULN_302,
149+
NO_GRACE_PERIOD
150+
),
151+
value: 0
152+
});
153+
calls[1] = Call({
154+
target: MUMBAI_ENDPOINT,
155+
data: abi.encodeWithSelector(
156+
IMessageLibManager.setConfig.selector, governor, MUMBAI_RECEIVE_ULN_302, params
157+
),
158+
value: 0
159+
});
160+
161+
bytes32 owner = addressToBytes32(msg.sender);
162+
163+
vm.startBroadcast();
164+
address governorLogic = address(new BridgedGovernor(MUMBAI_ENDPOINT, SEPOLIA_EID, owner));
165+
address governorProxy = address(new BridgedGovernorProxy(governorLogic, calls));
166+
vm.stopBroadcast();
167+
168+
require(governorProxy == governor, "Invalid deployment address");
169+
console.log("Deployed BridgedGovernor:", governor);
170+
}
171+
}
172+
173+
// forge script scripts/DeployBridgedGovernor.sol:SendToMumbai $WALLET_ARGS -f "$ETH_RPC_URL"
174+
175+
contract SendToMumbai is Script {
176+
function run() public {
177+
require(block.chainid == SEPOLIA_CHAIN_ID, "Must be run on Sepolia");
178+
179+
Call[] memory calls = new Call[](1);
180+
calls[0] = Call({
181+
target: 0x6D8873f56a56f0Af376091beddDD149f3592e854,
182+
data: abi.encodeWithSignature("approve(address,uint256)", address(0x1234), 5678),
183+
value: 0
184+
});
185+
186+
MessagingParams memory params = MessagingParams({
187+
dstEid: MUMBAI_EID,
188+
receiver: addressToBytes32(0xf3Fb9312d0b2413f0C79DbcaFfCfCF70Bef95fc5),
189+
message: abi.encode(calls),
190+
options: messageOptions(50_000),
191+
payInLzToken: false
192+
});
193+
194+
vm.startBroadcast();
195+
MessagingReceipt memory receipt =
196+
ILayerZeroEndpointV2(SEPOLIA_ENDPOINT).send{value: 0.001 ether}(params, msg.sender);
197+
vm.stopBroadcast();
198+
console.log("GUID:", Strings.toHexString(uint256(receipt.guid), 32));
199+
console.log("Nonce:", receipt.nonce);
200+
console.log("Cost wei:", receipt.fee.nativeFee);
201+
}
202+
}
203+
204+
// - C O N F I R M A T I O N S -
205+
//96, // 3 epochs

src/BridgedGovernor.sol

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// SPDX-License-Identifier: GPL-3.0-only
2+
pragma solidity ^0.8.20;
3+
4+
import {
5+
ILayerZeroReceiver,
6+
Origin
7+
} from "layer-zero-v2/protocol/contracts/interfaces/ILayerZeroReceiver.sol";
8+
import {ERC1967Proxy} from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol";
9+
import {UUPSUpgradeable} from "openzeppelin-contracts/proxy/utils/UUPSUpgradeable.sol";
10+
import {Address} from "openzeppelin-contracts/utils/Address.sol";
11+
12+
/// @notice Description of a call.
13+
/// @param target The called address.
14+
/// @param data The calldata to be used for the call.
15+
/// @param value The value of the call.
16+
struct Call {
17+
address target;
18+
bytes data;
19+
uint256 value;
20+
}
21+
22+
function runCalls(Call[] memory calls) {
23+
for (uint256 i = 0; i < calls.length; i++) {
24+
Call memory call = calls[i];
25+
Address.functionCallWithValue(call.target, call.data, call.value);
26+
}
27+
}
28+
29+
contract BridgedGovernor is UUPSUpgradeable, ILayerZeroReceiver {
30+
address public immutable endpoint;
31+
uint32 public immutable ownerEid;
32+
bytes32 public immutable owner;
33+
34+
uint64 internal _lastNonce;
35+
36+
constructor(address endpoint_, uint32 ownerEid_, bytes32 owner_) {
37+
// slither-disable-next-line missing-zero-check
38+
endpoint = endpoint_;
39+
ownerEid = ownerEid_;
40+
owner = owner_;
41+
}
42+
43+
function allowInitializePath(Origin calldata origin) public view onlyProxy returns (bool) {
44+
return origin.srcEid == ownerEid && origin.sender == owner;
45+
}
46+
47+
function nextNonce(uint32 srcEid, bytes32 sender)
48+
public
49+
view
50+
onlyProxy
51+
returns (uint64 nextNonce_)
52+
{
53+
if (srcEid == ownerEid && sender == owner) nextNonce_ = _lastNonce + 1;
54+
}
55+
56+
function lzReceive(
57+
Origin calldata origin,
58+
bytes32, /* guid */
59+
bytes calldata message,
60+
address, /* executor */
61+
bytes calldata /* extraData */
62+
) public payable onlyProxy {
63+
require(msg.sender == endpoint, "Must be called by the endpoint");
64+
require(origin.srcEid == ownerEid, "Invalid message source chain");
65+
require(origin.sender == owner, "Invalid message sender");
66+
require(origin.nonce == _lastNonce + 1, "Invalid message nonce");
67+
// slither-disable-next-line events-maths
68+
_lastNonce = origin.nonce;
69+
runCalls(abi.decode(message, (Call[])));
70+
}
71+
72+
function _authorizeUpgrade(address /* newImplementation */ ) internal view override {
73+
require(msg.sender == address(this), "Only upgradeable by self");
74+
}
75+
}
76+
77+
contract BridgedGovernorProxy is ERC1967Proxy {
78+
constructor(address logic, Call[] memory calls) ERC1967Proxy(logic, "") {
79+
runCalls(calls);
80+
}
81+
}

0 commit comments

Comments
 (0)