Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ url = https://github.com/foundry-rs/forge-std
branch = "v1.3.0"
path = lib/osx
url = https://github.com/aragon/osx
[submodule "lib/optimism"]
path = lib/optimism
url = https://github.com/ethereum-optimism/optimism
1 change: 1 addition & 0 deletions lib/optimism
Submodule optimism added at 566244
4 changes: 4 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ solidity-bytes-utils/=lib/solidity-bytes-utils/
@execution-chain/=src/execution-chain/
@utils/=src/utils/
@helpers/=test/helpers/

@eth-optimism/contracts-bedrock/=lib/optimism/packages/contracts-bedrock/

@hashi/contracts/=src/hashi/
107 changes: 22 additions & 85 deletions src/execution-chain/crosschain/ActionRelay.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,115 +2,52 @@
pragma solidity ^0.8.0;

import {IDAO} from "@aragon/osx/core/dao/IDAO.sol";
import {MessagingFee, MessagingReceipt} from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {OptionsBuilder} from "@lz-oapp/libs/OptionsBuilder.sol";

import {OAppSenderUpgradeable, MessagingFee} from "@oapp-upgradeable/aragon-oapp/OAppSenderUpgradeable.sol";
import {bytes32ToAddress} from "@utils/converters.sol";
import {DaoAuthorizableUpgradeable} from "@aragon/osx/core/plugin/dao-authorizable/DaoAuthorizableUpgradeable.sol";

/// @title ActionRelay
/// @author Aragon
/// @notice A LayerZero-compatible OApp that allows for sending arbitrary action data across chains.
contract ActionRelay is OAppSenderUpgradeable, UUPSUpgradeable {
using OptionsBuilder for bytes;
using SafeCast for uint256;

/// @notice A contract that allows for sending arbitrary action data across chains using Hashi pull flow.
contract ActionRelay is UUPSUpgradeable, DaoAuthorizableUpgradeable {
/// @notice Holders of this role are allowed to relay actions to another chain.
bytes32 public constant XCHAIN_ACTION_RELAYER_ID = keccak256("XCHAIN_ACTION_RELAYER");

/// @notice Additional Layer Zero params required to send a cross chain message.
/// @param dstEid The LayerZero endpoint ID of the execution chain.
/// @param gasLimit The additional gas needed on the execution chain to process the message, surplus will be refunded.
/// @param fee The messaging fee required to send the message, this is sent to LayerZero.
/// @param options Additional options required to send the message, these are encoded as bytes.
struct LzSendParams {
uint32 dstEid;
uint128 gasLimit;
MessagingFee fee;
bytes options;
}
/// @notice Holders of this role are allowed to upgrade the contract
bytes32 public constant OAPP_ADMINISTRATOR_ID = keccak256("OAPP_ADMINISTRATOR_ID");

/// @notice Variable used to ensure commitment uniqueness
uint256 private _nonce;

/// @notice Emitted when actions have been successfully relayed to another chain.
/// @param callId A unique identifier for the relayed actions, such as a proposal ID.
/// @param destinationEid The LayerZero endpoint ID of the destination chain.
event ActionsRelayed(
uint256 indexed callId,
uint256 indexed destinationEid,
MessagingReceipt receipt
);
/// @param destinationChainId The destination chain ID.
/// @param commitment The commitment of the message to execute on the destination chain.
event ActionsRelayed(uint256 indexed callId, uint256 indexed destinationChainId, bytes32 commitment);

constructor() {
_disableInitializers();
}

/// @notice Initialize the OApp with the LayerZero endpoint and DAO.
/// @param _lzEndpoint The LayerZero endpoint address on this chain.
/// @param _dao The DAO address, will be the delegate for this OApp.
function initialize(address _lzEndpoint, address _dao) external initializer {
__OAppCore_init({_endpoint: _lzEndpoint, _dao: _dao});
}

/// @notice The refund address will receive extra gas on the destination chain.
/// @param _dstEid The layerZero endpoint ID of the destination chain.
/// @dev Encoded as a 256bit integer in case we want to change the implementation to a different chain Id.
/// @return The address that will receive the refund. By default this is the LayerZero peer address.
/// which should implement a sweep function to recover the funds.
function refundAddress(uint256 _dstEid) public view virtual returns (address) {
return bytes32ToAddress(peers[_dstEid.toUint32()]);
}

/// @notice Quote the messaging fee required to relay actions to another chain.
/// @param _callId The unique identifier for the relayed actions, such as a proposal ID.
/// @param _actions The actions to relay to the destination chain, including value, target and calldata.
/// @param _allowFailureMap A bitmap of actions that are allowed to fail.
/// @param _dstEid The LayerZero endpoint ID of the destination chain.
/// @param _gasLimit The additional gas needed on the destination chain to process the message, surplus will be refunded.
function quote(
uint256 _callId,
IDAO.Action[] memory _actions,
uint256 _allowFailureMap,
uint32 _dstEid,
uint128 _gasLimit
) external view returns (LzSendParams memory params) {
bytes memory message = abi.encode(_callId, _actions, _allowFailureMap);
bytes memory options = OptionsBuilder.newOptions().addExecutorLzReceiveOption({
_gas: _gasLimit,
_value: 0
});
MessagingFee memory fee = _quote({
_dstEid: _dstEid,
_message: message,
_options: options,
_payInLzToken: false
});
return LzSendParams({dstEid: _dstEid, gasLimit: _gasLimit, fee: fee, options: options});
}
function initialize() external initializer {}

/// @notice Relay actions to another chain. Requires the sender to be authorized and the peer OApp to be set.
/// @param _callId The unique identifier for the relayed actions, such as a proposal ID.
/// @param _actions The actions to relay to the destination chain, including value, target and calldata.
/// @param _allowFailureMap A bitmap of actions that are allowed to fail.
/// @param _params Additional Layer Zero params required to send a cross chain message, use the `quote` function to get these.
/// @param _destinationChainId The destination chain ID.
function relayActions(
uint256 _callId,
IDAO.Action[] memory _actions,
uint256 _allowFailureMap,
LzSendParams memory _params
) external payable auth(XCHAIN_ACTION_RELAYER_ID) returns (MessagingReceipt memory receipt) {
bytes memory message = abi.encode(_callId, _actions, _allowFailureMap);

receipt = _lzSend({
_dstEid: _params.dstEid,
_message: message,
_options: _params.options,
_fee: _params.fee,
_refundAddress: refundAddress(_params.dstEid)
});

emit ActionsRelayed(_callId, _params.dstEid, receipt);
uint256 _destinationChainId

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To confirm my understanding of Hashi: the destinationChainId is it:

  • The chainId for the specific protocol
  • A chainId used by Hashi that reconciles to the specific chainId for the xchain protocol

) external payable auth(XCHAIN_ACTION_RELAYER_ID) returns (bytes32 commitment) {
bytes memory message =
abi.encode(block.chainid, _destinationChainId, msg.sender, _nonce, _callId, _actions, _allowFailureMap);
commitment = keccak256(message);
unchecked {
++_nonce;
}
emit ActionsRelayed(_callId, _destinationChainId, commitment);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the nonce be in the event sig?

}

/// @notice Returns the address of the implementation contract in the [proxy storage slot](https://eips.ethereum.org/EIPS/eip-1967) slot the [UUPS proxy](https://eips.ethereum.org/EIPS/eip-1822) is pointing to.
Expand Down
Loading