Skip to content

Commit

Permalink
Feat implement mailbox client component (#34)
Browse files Browse the repository at this point in the history
* feat: mailbox client component

* feat: merkle tree hook and mailbox client contract simplification

* fix: test adjustments

* fix: format
  • Loading branch information
JordyRo1 authored Jun 12, 2024
1 parent 926c329 commit adb7435
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 137 deletions.
138 changes: 19 additions & 119 deletions contracts/src/contracts/client/mailboxclient.cairo
Original file line number Diff line number Diff line change
@@ -1,139 +1,39 @@
#[starknet::contract]
mod mailboxclient {
use alexandria_bytes::{Bytes, BytesTrait, BytesStore};
use hyperlane_starknet::interfaces::{
IMailbox, IMailboxDispatcher, IMailboxDispatcherTrait, IInterchainSecurityModuleDispatcher,
IInterchainSecurityModuleDispatcherTrait, IMailboxClient,
use hyperlane_starknet::contracts::client::mailboxclient_component::{
MailboxclientComponent, MailboxclientComponent::MailboxClientInternalImpl
};
use openzeppelin::access::ownable::OwnableComponent;
use openzeppelin::upgrades::{interface::IUpgradeable, upgradeable::UpgradeableComponent};
use starknet::{ContractAddress, contract_address_const, ClassHash};

use openzeppelin::access::ownable::ownable::OwnableComponent::InternalTrait;
use openzeppelin::access::ownable::{OwnableComponent,};
use starknet::ContractAddress;
component!(path: MailboxclientComponent, storage: mailboxclient, event: MailboxclientEvent);
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);
#[abi(embed_v0)]
impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;
impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;
impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl<ContractState>;


#[storage]
struct Storage {
mailbox: ContractAddress,
local_domain: u32,
hook: ContractAddress,
interchain_security_module: ContractAddress,
#[substorage(v0)]
ownable: OwnableComponent::Storage,
#[substorage(v0)]
upgradeable: UpgradeableComponent::Storage,
mailboxclient: MailboxclientComponent::Storage,
}

#[constructor]
fn constructor(ref self: ContractState, _mailbox: ContractAddress, _owner: ContractAddress,) {
self.mailboxclient.initialize(_mailbox);
self.ownable.initializer(_owner);
}


#[event]
#[derive(Drop, starknet::Event)]
enum Event {
pub enum Event {
#[flat]
OwnableEvent: OwnableComponent::Event,
MailboxclientEvent: MailboxclientComponent::Event,
#[flat]
UpgradeableEvent: UpgradeableComponent::Event,
}


#[constructor]
fn constructor(ref self: ContractState, _mailbox: ContractAddress, _owner: ContractAddress) {
self.mailbox.write(_mailbox);
let mailbox = IMailboxDispatcher { contract_address: _mailbox };
let local_domain = mailbox.get_local_domain();
self.local_domain.write(local_domain);
self.ownable.initializer(_owner);
}
#[abi(embed_v0)]
impl Upgradeable of IUpgradeable<ContractState> {
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
self.ownable.assert_only_owner();
self.upgradeable._upgrade(new_class_hash);
}
OwnableEvent: OwnableComponent::Event,
}


#[abi(embed_v0)]
impl IMailboxClientImpl of IMailboxClient<ContractState> {
fn set_hook(ref self: ContractState, _hook: ContractAddress) {
self.ownable.assert_only_owner();
self.hook.write(_hook);
}

fn set_interchain_security_module(ref self: ContractState, _module: ContractAddress) {
self.ownable.assert_only_owner();
self.interchain_security_module.write(_module);
}

fn get_local_domain(self: @ContractState) -> u32 {
self.local_domain.read()
}

fn get_hook(self: @ContractState) -> ContractAddress {
self.hook.read()
}

fn get_interchain_security_module(self: @ContractState) -> ContractAddress {
self.interchain_security_module.read()
}


fn _MailboxClient_initialize(
ref self: ContractState,
_hook: ContractAddress,
_interchain_security_module: ContractAddress,
) {
self.ownable.assert_only_owner();
self.set_hook(_hook);
self.set_interchain_security_module(_interchain_security_module);
}

fn _is_latest_dispatched(self: @ContractState, _id: u256) -> bool {
let mailbox_address = self.mailbox.read();
let mailbox = IMailboxDispatcher { contract_address: mailbox_address };
mailbox.get_latest_dispatched_id() == _id
}

fn _is_delivered(self: @ContractState, _id: u256) -> bool {
let mailbox_address = self.mailbox.read();
let mailbox = IMailboxDispatcher { contract_address: mailbox_address };
mailbox.delivered(_id)
}

fn mailbox(self: @ContractState) -> ContractAddress {
self.mailbox.read()
}

fn _dispatch(
self: @ContractState,
_destination_domain: u32,
_recipient: ContractAddress,
_message_body: Bytes,
_hook_metadata: Option<Bytes>,
_hook: Option<ContractAddress>
) -> u256 {
let mailbox_address = self.mailbox.read();
let mailbox = IMailboxDispatcher { contract_address: mailbox_address };
mailbox.dispatch(_destination_domain, _recipient, _message_body, _hook_metadata, _hook)
}

fn quote_dispatch(
self: @ContractState,
_destination_domain: u32,
_recipient: ContractAddress,
_message_body: Bytes,
_hook_metadata: Option<Bytes>,
_hook: Option<ContractAddress>
) -> u256 {
let mailbox_address = self.mailbox.read();
let mailbox = IMailboxDispatcher { contract_address: mailbox_address };
mailbox
.quote_dispatch(
_destination_domain, _recipient, _message_body, _hook_metadata, _hook
)
}
}
impl MailboxclientImpl =
MailboxclientComponent::MailboxClientImpl<ContractState>;
}
120 changes: 120 additions & 0 deletions contracts/src/contracts/client/mailboxclient_component.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#[starknet::component]
pub mod MailboxclientComponent {
use alexandria_bytes::{Bytes, BytesTrait};
use hyperlane_starknet::interfaces::{
IMailboxClient, IMailboxDispatcher, IMailboxDispatcherTrait
};
use openzeppelin::access::ownable::{OwnableComponent, OwnableComponent::InternalImpl};
use openzeppelin::upgrades::{interface::IUpgradeable, upgradeable::UpgradeableComponent};
use starknet::ContractAddress;

#[storage]
struct Storage {
mailbox: IMailboxDispatcher,
local_domain: u32,
hook: ContractAddress,
interchain_security_module: ContractAddress,
}


#[embeddable_as(MailboxClientImpl)]
impl MailboxClient<
TContractState,
+HasComponent<TContractState>,
impl Owner: OwnableComponent::HasComponent<TContractState>
> of IMailboxClient<ComponentState<TContractState>> {
fn set_hook(ref self: ComponentState<TContractState>, _hook: ContractAddress) {
let ownable_comp = get_dep_component!(@self, Owner);
ownable_comp.assert_only_owner();
self.hook.write(_hook);
}

fn set_interchain_security_module(
ref self: ComponentState<TContractState>, _module: ContractAddress
) {
let ownable_comp = get_dep_component!(@self, Owner);
ownable_comp.assert_only_owner();
self.interchain_security_module.write(_module);
}

fn get_local_domain(self: @ComponentState<TContractState>) -> u32 {
self.local_domain.read()
}

fn get_hook(self: @ComponentState<TContractState>) -> ContractAddress {
self.hook.read()
}

fn get_interchain_security_module(
self: @ComponentState<TContractState>
) -> ContractAddress {
self.interchain_security_module.read()
}


fn _MailboxClient_initialize(
ref self: ComponentState<TContractState>,
_hook: ContractAddress,
_interchain_security_module: ContractAddress,
) {
let ownable_comp = get_dep_component!(@self, Owner);
ownable_comp.assert_only_owner();
self.set_hook(_hook);
self.set_interchain_security_module(_interchain_security_module);
}

fn _is_latest_dispatched(self: @ComponentState<TContractState>, _id: u256) -> bool {
self.mailbox.read().get_latest_dispatched_id() == _id
}

fn _is_delivered(self: @ComponentState<TContractState>, _id: u256) -> bool {
self.mailbox.read().delivered(_id)
}

fn mailbox(self: @ComponentState<TContractState>) -> ContractAddress {
let mailbox: IMailboxDispatcher = self.mailbox.read();
mailbox.contract_address
}

fn _dispatch(
self: @ComponentState<TContractState>,
_destination_domain: u32,
_recipient: ContractAddress,
_message_body: Bytes,
_hook_metadata: Option<Bytes>,
_hook: Option<ContractAddress>
) -> u256 {
self
.mailbox
.read()
.dispatch(_destination_domain, _recipient, _message_body, _hook_metadata, _hook)
}

fn quote_dispatch(
self: @ComponentState<TContractState>,
_destination_domain: u32,
_recipient: ContractAddress,
_message_body: Bytes,
_hook_metadata: Option<Bytes>,
_hook: Option<ContractAddress>
) -> u256 {
self
.mailbox
.read()
.quote_dispatch(
_destination_domain, _recipient, _message_body, _hook_metadata, _hook
)
}
}

#[generate_trait]
pub impl MailboxClientInternalImpl<
TContractState,
+HasComponent<TContractState>,
impl Owner: OwnableComponent::HasComponent<TContractState>
> of InternalTrait<TContractState> {
fn initialize(ref self: ComponentState<TContractState>, _mailbox: ContractAddress) {
self.mailbox.write(IMailboxDispatcher { contract_address: _mailbox });
}
}
}
45 changes: 34 additions & 11 deletions contracts/src/contracts/hooks/merkle_tree_hook.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,44 @@
pub mod merkle_tree_hook {
use alexandria_bytes::{Bytes, BytesTrait};
use alexandria_math::pow;
use hyperlane_starknet::contracts::client::mailboxclient_component::{
MailboxclientComponent, MailboxclientComponent::MailboxClientInternalImpl
};
use hyperlane_starknet::contracts::hooks::libs::standard_hook_metadata::standard_hook_metadata::{
StandardHookMetadata, VARIANT
};
use hyperlane_starknet::contracts::libs::merkle_lib::merkle_lib::{Tree, MerkleLib};
use hyperlane_starknet::contracts::libs::message::{Message, MessageTrait};
use hyperlane_starknet::interfaces::{
IMailboxClientDispatcher, IMailboxClientDispatcherTrait, Types, IMerkleTreeHook,
IPostDispatchHook
IPostDispatchHook, IMailboxClient, IMailboxDispatcher, IMailboxDispatcherTrait,
};
use hyperlane_starknet::utils::keccak256::{reverse_endianness, compute_keccak, ByteData};
use openzeppelin::access::ownable::OwnableComponent;
use openzeppelin::upgrades::{interface::IUpgradeable, upgradeable::UpgradeableComponent};
use starknet::ContractAddress;

type Index = usize;
pub const TREE_DEPTH: u32 = 32;
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);
component!(path: MailboxclientComponent, storage: mailboxclient, event: MailboxclientEvent);

#[abi(embed_v0)]
impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;
impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;
impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl<ContractState>;


#[storage]
struct Storage {
mailbox_client: ContractAddress,
tree: LegacyMap<Index, u256>,
count: u32
count: u32,
#[substorage(v0)]
mailboxclient: MailboxclientComponent::Storage,
#[substorage(v0)]
ownable: OwnableComponent::Storage,
#[substorage(v0)]
upgradeable: UpgradeableComponent::Storage,
}


Expand All @@ -35,20 +52,29 @@ pub mod merkle_tree_hook {
#[event]
#[derive(Drop, starknet::Event)]
pub enum Event {
InsertedIntoTree: InsertedIntoTree
InsertedIntoTree: InsertedIntoTree,
#[flat]
OwnableEvent: OwnableComponent::Event,
#[flat]
UpgradeableEvent: UpgradeableComponent::Event,
#[flat]
MailboxclientEvent: MailboxclientComponent::Event,
}


#[derive(starknet::Event, Drop)]
pub struct InsertedIntoTree {
pub id: u256,
pub index: u32
}

#[constructor]
fn constructor(ref self: ContractState, _mailbox_client: ContractAddress) {
self.mailbox_client.write(_mailbox_client);
fn constructor(ref self: ContractState, _mailbox: ContractAddress, _owner: ContractAddress,) {
self.mailboxclient.initialize(_mailbox);
self.ownable.initializer(_owner);
}


#[abi(embed_v0)]
impl IMerkleTreeHookImpl of IMerkleTreeHook<ContractState> {
fn count(self: @ContractState) -> u32 {
Expand Down Expand Up @@ -92,10 +118,7 @@ pub mod merkle_tree_hook {
impl InternalImpl of InternalTrait {
fn _post_dispatch(ref self: ContractState, _metadata: Bytes, _message: Message) {
let (id, _) = MessageTrait::format_message(_message);
let mailbox_client = IMailboxClientDispatcher {
contract_address: self.mailbox_client.read()
};
assert(mailbox_client._is_latest_dispatched(id), Errors::MESSAGE_NOT_DISPATCHING);
assert(self.mailboxclient._is_latest_dispatched(id), Errors::MESSAGE_NOT_DISPATCHING);
let index = self.count();
self._insert(id);
self.emit(InsertedIntoTree { id, index });
Expand Down
1 change: 1 addition & 0 deletions contracts/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod contracts {
}
pub mod client {
pub mod mailboxclient;
pub mod mailboxclient_component;
pub mod router;
}
pub mod mocks {
Expand Down
Loading

0 comments on commit adb7435

Please sign in to comment.