Skip to content

Commit

Permalink
Add BridgedGovernor
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeSandwich committed Apr 9, 2024
1 parent 30be8e4 commit 8c2dd91
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
node_modules
/out
/cache
/broadcast
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "lib/chainlink"]
path = lib/chainlink
url = https://github.com/smartcontractkit/chainlink
[submodule "lib/LayerZero-v2"]
path = lib/LayerZero-v2
url = https://github.com/LayerZero-Labs/LayerZero-v2
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ evm_version = 'shanghai'
optimizer = false
verbosity = 1
fuzz_runs = 5
script = 'scripts'
[profile.optimized]
via_ir = true
optimizer = true
Expand Down
1 change: 1 addition & 0 deletions lib/LayerZero-v2
Submodule LayerZero-v2 added at 142846
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/
chainlink/=lib/chainlink/contracts/src/v0.8/
layer-zero-v2/=lib/LayerZero-v2/
202 changes: 202 additions & 0 deletions scripts/DeployBridgedGovernor.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.24;

import {console, Script} from "forge-std/Script.sol";
import {ExecutorOptions} from "layer-zero-v2/protocol/contracts/messagelib/libs/ExecutorOptions.sol";
// import {OptionsBuilder} from "layer-zero-v2/oapp/contracts/oapp/libs/OptionsBuilder.sol";
import {
ILayerZeroEndpointV2,IMessageLibManager,
MessagingParams, MessagingReceipt
} from "layer-zero-v2/protocol/contracts/interfaces/ILayerZeroEndpointV2.sol";
import {SetConfigParam} from "layer-zero-v2/protocol/contracts/interfaces/IMessageLibManager.sol";
import {Constant} from "layer-zero-v2/messagelib/test/util/Constant.sol";
import {Strings} from "openzeppelin-contracts/utils/Strings.sol";
import {BridgedGovernor, BridgedGovernorProxy, Call} from "src/BridgedGovernor.sol";

// Taken from layer-zero-v2/messagelib/contracts/uln/UlnBase.sol
struct UlnConfig {
uint64 confirmations;
uint8 requiredDVNCount;
uint8 optionalDVNCount;
uint8 optionalDVNThreshold;
address[] requiredDVNs;
address[] optionalDVNs;
}

uint256 constant SEPOLIA_CHAIN_ID = 11155111;
uint256 constant MUMBAI_CHAIN_ID = 80001;

uint256 constant NO_GRACE_PERIOD = 0;

// Taken from https://docs.layerzero.network/v2/developers/evm/technical-reference/endpoints
address constant SEPOLIA_ENDPOINT = 0x6EDCE65403992e310A62460808c4b910D972f10f;
uint32 constant SEPOLIA_EID = 40161;
address constant MUMBAI_ENDPOINT = 0x6EDCE65403992e310A62460808c4b910D972f10f;
uint32 constant MUMBAI_EID = 40109;

// Taken from https://docs.layerzero.network/v2/developers/evm/technical-reference/messagelibs
address constant MUMBAI_RECEIVE_ULN_302 = 0xfa4Fbda8E809150eE1676ce675AC746Beb9aF379;
address constant SEPOLIA_SEND_ULN_302 = 0xcc1ae8Cf5D3904Cef3360A9532B477529b177cCE;

// Taken from https://docs.layerzero.network/v2/developers/evm/technical-reference/dvn-addresses
address constant SEPOLIA_LAYER_ZERO_LABS_DVN = 0x8eebf8b423B73bFCa51a1Db4B7354AA0bFCA9193;
address constant MUMBAI_LAYER_ZERO_LABS_DVN = 0x67a822F55C5F6E439550b9C4EA39E406480a40f3;
address constant SEPOLIA_BWARE_LABS_DVN = 0xCA7a736be0Fe968A33Af62033B8b36D491f7999B;
address constant MUMBAI_BWARE_LABS_DVN = 0x1cf01d5042d1ae231F918a2645f2762d663476E7;

function requireSorted(address[] memory addresses) pure {
for (uint256 i = 1; i < addresses.length; i++) {
require(uint160(addresses[i - 1]) < uint160(addresses[i]), "Addresses not sorted");
}
}

function messageOptions(uint128 gas) pure returns (bytes memory) {
uint16 optionsType = 3;
return abi.encodePacked(optionsType, executorGasOption(gas), executorOrderedOption());
}

function executorGasOption(uint128 gas) pure returns (bytes memory) {
bytes memory payload = ExecutorOptions.encodeLzReceiveOption(gas, 0);
return executorOption(ExecutorOptions.OPTION_TYPE_LZRECEIVE, payload);
}

function executorOrderedOption() pure returns (bytes memory) {
return executorOption(ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, "");
}

function executorOption(uint8 optionType, bytes memory payload) pure returns (bytes memory) {
uint16 length = uint16(1 + payload.length);
return abi.encodePacked(ExecutorOptions.WORKER_ID, length, optionType, payload);
}

function addressToBytes32(address addr) pure returns (bytes32) {
return bytes32(uint256(uint160(addr)));
}

// forge script scripts/DeployBridgedGovernor.s.sol:ConfigureOnSepolia $WALLET_ARGS -f "$ETH_RPC_URL"

contract ConfigureOnSepolia is Script {
function run() public {
require(block.chainid == SEPOLIA_CHAIN_ID, "Must be run on Sepolia");

address[] memory optionalDVNs = new address[](2);
optionalDVNs[0] = SEPOLIA_LAYER_ZERO_LABS_DVN;
optionalDVNs[1] = SEPOLIA_BWARE_LABS_DVN;
requireSorted(optionalDVNs);

SetConfigParam[] memory params = new SetConfigParam[](1);
params[0] = SetConfigParam({
eid: MUMBAI_EID,
configType: Constant.CONFIG_TYPE_ULN,
config: abi.encode(
UlnConfig({
confirmations: 2,
requiredDVNCount: Constant.NIL_DVN_COUNT,
optionalDVNCount: uint8(optionalDVNs.length),
optionalDVNThreshold: 1,
requiredDVNs: new address[](0),
optionalDVNs: optionalDVNs
})
)
});

vm.startBroadcast();
ILayerZeroEndpointV2 endpoint = ILayerZeroEndpointV2(SEPOLIA_ENDPOINT);
endpoint.setSendLibrary(msg.sender, MUMBAI_EID, SEPOLIA_SEND_ULN_302);
endpoint.setConfig(msg.sender, SEPOLIA_SEND_ULN_302, params);
vm.stopBroadcast();
}
}

// forge script scripts/DeployBridgedGovernor.s.sol:DeployToMumbai $WALLET_ARGS -f "$ETH_RPC_URL"

contract DeployToMumbai is Script {
function run() public {
require(block.chainid == MUMBAI_CHAIN_ID, "Must be run on Polygon Mumbai");

address[] memory optionalDVNs = new address[](2);
optionalDVNs[0] = MUMBAI_BWARE_LABS_DVN;
optionalDVNs[1] = MUMBAI_LAYER_ZERO_LABS_DVN;
requireSorted(optionalDVNs);

SetConfigParam[] memory params = new SetConfigParam[](1);
params[0] = SetConfigParam({
eid: SEPOLIA_EID,
configType: Constant.CONFIG_TYPE_ULN,
config: abi.encode(
UlnConfig({
confirmations: 2,
requiredDVNCount: Constant.NIL_DVN_COUNT,
optionalDVNCount: uint8(optionalDVNs.length),
optionalDVNThreshold: 1,
requiredDVNs: new address[](0),
optionalDVNs: optionalDVNs
})
)
});

address governor = vm.computeCreateAddress(msg.sender, vm.getNonce(msg.sender) + 1);
Call[] memory calls = new Call[](2);
calls[0] = Call({
target: MUMBAI_ENDPOINT,
data: abi.encodeWithSelector(
IMessageLibManager.setReceiveLibrary.selector,
governor,
SEPOLIA_EID,
MUMBAI_RECEIVE_ULN_302,
NO_GRACE_PERIOD
),
value: 0
});
calls[1] = Call({
target: MUMBAI_ENDPOINT,
data: abi.encodeWithSelector(
IMessageLibManager.setConfig.selector, governor, MUMBAI_RECEIVE_ULN_302, params
),
value: 0
});

bytes32 owner = addressToBytes32(msg.sender);

vm.startBroadcast();
address governorLogic = address(new BridgedGovernor(MUMBAI_ENDPOINT, SEPOLIA_EID, owner));
address governorProxy = address(new BridgedGovernorProxy(governorLogic, calls));
vm.stopBroadcast();

require(governorProxy == governor, "Invalid deployment address");
console.log("Deployed BridgedGovernor:", governor);
}
}

// forge script scripts/DeployBridgedGovernor.sol:SendToMumbai $WALLET_ARGS -f "$ETH_RPC_URL"

contract SendToMumbai is Script {
function run() public {
require(block.chainid == SEPOLIA_CHAIN_ID, "Must be run on Sepolia");

Call[] memory calls = new Call[](1);
calls[0] = Call({
target: 0x6D8873f56a56f0Af376091beddDD149f3592e854,
data: abi.encodeWithSignature("approve(address,uint256)", address(0x1234), 5678),
value: 0
});

MessagingParams memory params = MessagingParams({
dstEid: MUMBAI_EID,
receiver: addressToBytes32(0xf3Fb9312d0b2413f0C79DbcaFfCfCF70Bef95fc5),
message: abi.encode(calls),
options: messageOptions(50_000),
payInLzToken: false
});

vm.startBroadcast();
MessagingReceipt memory receipt = ILayerZeroEndpointV2(SEPOLIA_ENDPOINT).send{value: 0.001 ether}(params, msg.sender);
vm.stopBroadcast();
console.log("GUID:", Strings.toHexString(uint256(receipt.guid), 32));
console.log("Nonce:", receipt.nonce);
console.log("Cost wei:", receipt.fee.nativeFee);
}
}

// - C O N F I R M A T I O N S -
//96, // 3 epochs
81 changes: 81 additions & 0 deletions src/BridgedGovernor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.24;

import {
ILayerZeroReceiver,
Origin
} from "layer-zero-v2/protocol/contracts/interfaces/ILayerZeroReceiver.sol";
import {ERC1967Proxy} from "openzeppelin-contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {UUPSUpgradeable} from "openzeppelin-contracts/proxy/utils/UUPSUpgradeable.sol";
import {Address} from "openzeppelin-contracts/utils/Address.sol";

/// @notice Description of a call.
/// @param target The called address.
/// @param data The calldata to be used for the call.
/// @param value The value of the call.
struct Call {
address target;
bytes data;
uint256 value;
}

function runCalls(Call[] memory calls) {
for (uint256 i = 0; i < calls.length; i++) {
Call memory call = calls[i];
Address.functionCallWithValue(call.target, call.data, call.value);
}
}

contract BridgedGovernor is UUPSUpgradeable, ILayerZeroReceiver {
address public immutable endpoint;
uint32 public immutable ownerEid;
bytes32 public immutable owner;

uint64 internal _lastNonce;

constructor(address endpoint_, uint32 ownerEid_, bytes32 owner_) {
// slither-disable-next-line missing-zero-check
endpoint = endpoint_;
ownerEid = ownerEid_;
owner = owner_;
}

function allowInitializePath(Origin calldata origin) public view onlyProxy returns (bool) {
return origin.srcEid == ownerEid && origin.sender == owner;
}

function nextNonce(uint32 srcEid, bytes32 sender)
public
view
onlyProxy
returns (uint64 nextNonce_)
{
if (srcEid == ownerEid && sender == owner) nextNonce_ = _lastNonce + 1;
}

function lzReceive(
Origin calldata origin,
bytes32, /* guid */
bytes calldata message,
address, /* executor */
bytes calldata /* extraData */
) public payable onlyProxy {
require(msg.sender == endpoint, "Must be called by the endpoint");
require(origin.srcEid == ownerEid, "Invalid message source chain");
require(origin.sender == owner, "Invalid message sender");
require(origin.nonce == _lastNonce + 1, "Invalid message nonce");
// slither-disable-next-line events-maths
_lastNonce = origin.nonce;
runCalls(abi.decode(message, (Call[])));
}

function _authorizeUpgrade(address /* newImplementation */ ) internal view override {
require(msg.sender == address(this), "Only upgradeable by self");
}
}

contract BridgedGovernorProxy is ERC1967Proxy {
constructor(address logic, Call[] memory calls) ERC1967Proxy(logic, "") {
runCalls(calls);
}
}

0 comments on commit 8c2dd91

Please sign in to comment.