Skip to content

Commit

Permalink
Add AxelarBridgedGovernor
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeSandwich committed Aug 27, 2024
1 parent 2f101e0 commit d080e51
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "lib/gelato-automate"]
path = lib/gelato-automate
url = https://github.com/gelatodigital/automate
[submodule "lib/axelar-gmp-sdk-solidity"]
path = lib/axelar-gmp-sdk-solidity
url = https://github.com/axelarnetwork/axelar-gmp-sdk-solidity
1 change: 1 addition & 0 deletions lib/axelar-gmp-sdk-solidity
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ forge-std/=lib/forge-std/src/
openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/
layer-zero-v2/=lib/LayerZero-v2/
gelato-automate/=lib/gelato-automate/contracts/
axelar/=lib/axelar-gmp-sdk-solidity/contracts/
128 changes: 128 additions & 0 deletions scripts/DeployAxelarBridgedGovernor.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.20;

import {console, Script} from "forge-std/Script.sol";
import {AxelarBridgedGovernor, BridgedGovernorProxy, Call} from "src/BridgedGovernor.sol";
import {IAxelarGasService} from "axelar/interfaces/IAxelarGasService.sol";
import {IAxelarGMPGateway} from "axelar/interfaces/IAxelarGMPGateway.sol";
import {AddressToString} from "axelar/libs/AddressString.sol";

string constant SEPOLIA_CHAIN_NAME = "ethereum-sepolia";
string constant BSC_TESTNET_CHAIN_NAME = "binance";

IAxelarGMPGateway constant SEPOLIA_GATEWAY =
IAxelarGMPGateway(0xe432150cce91c13a887f7D836923d5597adD8E31);
IAxelarGasService constant BSC_TESTNET_GAS_SERVICE =
IAxelarGasService(0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6);
IAxelarGMPGateway constant BSC_TESTNET_GATEWAY =
IAxelarGMPGateway(0x4D147dCb984e6affEEC47e44293DA442580A3Ec0);

// forge script scripts/DeployAxelarBridgedGovernor.s.sol:DeployToBscTestnet $WALLET_ARGS -f "$ETH_RPC_URL"

// contract DeployToBscTestnet is Script {
// function run() public {
// address owner = vm.envOr("OWNER", msg.sender);

// require(block.chainid == 97, "Must be run on BSC testnet");
// vm.startBroadcast();
// AxelarBridgedGovernor logic =
// new AxelarBridgedGovernor(BSC_TESTNET_GATEWAY, SEPOLIA_CHAIN_NAME, owner);
// BridgedGovernorProxy governor = new BridgedGovernorProxy(address(logic), new Call[](0));
// vm.stopBroadcast();
// console.log("Deployed AxelarBridgedGovernor:", address(governor));
// }
// }


// Gateway and ddresses taken from https://docs.axelar.dev/resources/contract-addresses/testnet

// Run on BSC testnet
// forge create $WALLET_ARGS scripts/DeployAxelarBridgedGovernor.s.sol:ContractCaller \
// --constructor-args 0x4D147dCb984e6affEEC47e44293DA442580A3Ec0 0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6

// Run on Sepolia
// OWNER=0xdACfE6Bf5A06953EccC3755758C5aDFfed94e147 \
// GATEWAY=0xe432150cce91c13a887f7D836923d5597adD8E31 \
// SOURCE_CHAIN=binance \
// forge script scripts/DeployAxelarBridgedGovernor.s.sol:DeployGovernor $WALLET_ARGS -f "$ETH_RPC_URL"

// Run on BSC testnet
// cast send $WALLET_ARGS 0xdACfE6Bf5A06953EccC3755758C5aDFfed94e147 \
// 'setRecipient(string,address)' ethereum-sepolia 0x78EeC20c86e5f40Ceb1b651c38072DF528AE6407

// Run on BSC testnet
// CALLER=0xdACfE6Bf5A06953EccC3755758C5aDFfed94e147 \
// FEE=$(cast to-wei 0.00 eth) \
// NONCE=2 \
// forge script scripts/DeployAxelarBridgedGovernor.s.sol:ContractCall $WALLET_ARGS -f "$ETH_RPC_URL"

contract DeployGovernor is Script {
function run() public {
address owner = vm.envOr("OWNER", msg.sender);
IAxelarGMPGateway gateway = IAxelarGMPGateway(vm.envAddress("GATEWAY"));
string memory sourceChain = vm.envString("SOURCE_CHAIN");

vm.startBroadcast();
AxelarBridgedGovernor logic = new AxelarBridgedGovernor(gateway, sourceChain, owner);
BridgedGovernorProxy governor = new BridgedGovernorProxy(address(logic), new Call[](0));
vm.stopBroadcast();
console.log("Deployed AxelarBridgedGovernor:", address(governor));
}
}

contract ContractCaller {
address public immutable owner;
IAxelarGMPGateway public immutable gateway;
IAxelarGasService public immutable gasService;

string public destinationChain;
address public recipient;

constructor(IAxelarGMPGateway gateway_, IAxelarGasService gasService_) {
owner = msg.sender;
gateway = gateway_;
gasService = gasService_;
}

function setRecipient(string calldata destinationChain_, address recipient_) public {
require(msg.sender == owner, "Only owner");
destinationChain = destinationChain_;
recipient = recipient_;
}

function callContract(bytes calldata payload) payable public {
require(msg.sender == owner, "Only owner");
string memory recipient_ = AddressToString.toString(recipient);
if (msg.value > 0) {
gasService.payNativeGasForContractCall{value: msg.value}(
address(this), destinationChain, recipient_, payload, owner
);
}
gateway.callContract(destinationChain, recipient_, payload);
}
}


contract ContractCall is Script {
function run() public {
ContractCaller caller = ContractCaller(vm.envAddress("CALLER"));
uint256 fee = vm.envOr("FEE", uint(0));
uint256 nonce = vm.envUint("NONCE");

Call[] memory calls = new Call[](1);
calls[0] = Call({
target: 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14,
data: abi.encodeWithSignature("approve(address,uint256)", address(0x1234), 100 + nonce),
value: 0
});
// calls[0] = Call({
// target: 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14,
// data: abi.encodeWithSignature("transferFrom(address,address,uint256)",
// msg.sender, address(0xdead), 1234),
// value: 0
// });

vm.broadcast();
caller.callContract{value: fee}(abi.encode(AxelarBridgedGovernor.Message(nonce, calls)));
}
}
61 changes: 61 additions & 0 deletions src/BridgedGovernor.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.20;

import {IAxelarGMPExecutable} from "axelar/interfaces/IAxelarGMPExecutable.sol";
import {IAxelarGMPGateway} from "axelar/interfaces/IAxelarGMPGateway.sol";
import {StringToAddress} from "axelar/libs/AddressString.sol";
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";
import {ShortString, ShortStrings} from "openzeppelin-contracts/utils/ShortStrings.sol";
import {Strings} from "openzeppelin-contracts/utils/Strings.sol";

/// @notice Description of a call.
struct Call {
Expand Down Expand Up @@ -136,6 +141,62 @@ contract LZBridgedGovernor is Governor, ILayerZeroReceiver {
}
}

/// @notice The governor running calls received from its owner on another chain using Axelar.
contract AxelarBridgedGovernor is Governor, IAxelarGMPExecutable {
/// @notice The Axelar gateway used by this contract.
IAxelarGMPGateway public immutable override gateway;
/// @notice The name of the chain from which the owner is allowed to send messages.
ShortString internal immutable _ownerChain;
/// @notice The owner address which is allowed to send messages.
address public immutable owner;

/// @notice The name of the chain from which the owner is allowed to send messages.
/// @return ownerChain_ The name of the chain.
function ownerChain() public view returns (string memory ownerChain_) {
return ShortStrings.toString(_ownerChain);
}

/// @notice The message passed over the bridge to the governor to execute.
struct Message {
/// @notice The message nonce, must be equal to `nextMessageNonce` when executed.
uint256 nonce;
/// @notice The list of calls to run.
Call[] calls;
}

/// @param gateway_ The Axelar gateway used by this contract.
/// @param ownerChain_ The name of the chain from which the owner is allowed to send messages.
/// @param owner_ The owner address which is allowed to send messages.
constructor(IAxelarGMPGateway gateway_, string memory ownerChain_, address owner_) {
// slither-disable-next-line missing-zero-check
gateway = gateway_;
_ownerChain = ShortStrings.toShortString(ownerChain_);
owner = owner_;
}

/// @notice Execute the specified command sent from another chain.
/// @param commandId The identifier of the command to execute.
/// @param sourceChain The name of the source chain from where the command originated.
/// @param sender The address on the source chain that sent the command.
/// @param payload The payload of the command to be executed.
/// It must be an abi-encoded `Message`, see its documentation for more details.
function execute(
bytes32 commandId,
string calldata sourceChain,
string calldata sender,
bytes calldata payload
) public {
if (!gateway.validateContractCall(commandId, sourceChain, sender, keccak256(payload))) {
revert NotApprovedByGateway();
}
require(Strings.equal(sourceChain, ownerChain()), "Invalid message source chain");
require(StringToAddress.toAddress(sender) == owner, "Invalid message sender");

Message memory message = abi.decode(payload, (Message));
_executeMessage(message.nonce, message.calls);
}
}

/// @notice The specialized proxy for `BridgedGovernor`.
contract BridgedGovernorProxy is ERC1967Proxy {
/// @param logic The initial address of the logic for the proxy.
Expand Down

0 comments on commit d080e51

Please sign in to comment.