Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add domain routing hook #38

Merged
merged 15 commits into from
Dec 17, 2024
1 change: 1 addition & 0 deletions contracts/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ mod tests {
pub mod test_messageid_multisig;
}
pub mod hooks {
pub mod test_domain_routing_hook;
pub mod test_merkle_tree_hook;
pub mod test_protocol_fee;
}
Expand Down
145 changes: 145 additions & 0 deletions contracts/src/tests/hooks/test_domain_routing_hook.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use alexandria_bytes::{Bytes, BytesTrait, BytesStore};
use hyperlane_starknet::contracts::libs::message::{Message, MessageTrait, HYPERLANE_VERSION};
use hyperlane_starknet::interfaces::{
Types, IPostDispatchHookDispatcher, IPostDispatchHookDispatcherTrait,
IDomainRoutingHookDispatcher, IDomainRoutingHookDispatcherTrait, DomainRoutingHookConfig
};
use hyperlane_starknet::tests::setup::{setup_domain_routing_hook, setup_mock_hook, OWNER};
use openzeppelin::access::ownable::interface::{IOwnableDispatcher, IOwnableDispatcherTrait};

use snforge_std::{start_prank, CheatTarget};
use starknet::{get_caller_address, contract_address_const, ContractAddress};


#[test]
fn test_domain_routing_hook_type() {
let (routing_hook_addrs, _) = setup_domain_routing_hook();
assert_eq!(routing_hook_addrs.hook_type(), Types::ROUTING(()));
}

#[test]
fn test_supports_metadata_for_domain_routing_hook() {
let (routing_hook_addrs, _) = setup_domain_routing_hook();

let metadata = BytesTrait::new_empty();
assert_eq!(routing_hook_addrs.supports_metadata(metadata), true);
}

#[test]
fn test_domain_rounting_set_hook() {
let (_, set_routing_hook_addrs) = setup_domain_routing_hook();
let ownable = IOwnableDispatcher { contract_address: set_routing_hook_addrs.contract_address };
start_prank(CheatTarget::One(ownable.contract_address), OWNER());
let destination: u32 = 12;
let hook: ContractAddress = contract_address_const::<1>();
set_routing_hook_addrs.set_hook(destination, hook);
}

#[test]
#[should_panic(expected: ('Caller is not the owner',))]
fn test_set_hook_fails_if_not_owner() {
let (_, set_routing_hook_addrs) = setup_domain_routing_hook();
let destination: u32 = 12;
let hook: ContractAddress = contract_address_const::<1>();
set_routing_hook_addrs.set_hook(destination, hook);
}

#[test]
fn test_domain_rounting_set_hook_array() {
let (_, set_routing_hook_addrs) = setup_domain_routing_hook();
let ownable = IOwnableDispatcher { contract_address: set_routing_hook_addrs.contract_address };
start_prank(CheatTarget::One(ownable.contract_address), OWNER());
let mut hook_config_arr = ArrayTrait::<DomainRoutingHookConfig>::new();
hook_config_arr
.append(DomainRoutingHookConfig { destination: 1, hook: contract_address_const::<2>() });
hook_config_arr
.append(DomainRoutingHookConfig { destination: 2, hook: contract_address_const::<3>() });
hook_config_arr
.append(DomainRoutingHookConfig { destination: 3, hook: contract_address_const::<4>() });
set_routing_hook_addrs.set_hooks(hook_config_arr);
}


#[test]
#[should_panic(expected: ('Caller is not the owner',))]
fn test_set_hook_array_fails_if_not_owner() {
let (_, set_routing_hook_addrs) = setup_domain_routing_hook();
let mut hook_config_arr = ArrayTrait::<DomainRoutingHookConfig>::new();
hook_config_arr
.append(DomainRoutingHookConfig { destination: 1, hook: contract_address_const::<2>() });
hook_config_arr
.append(DomainRoutingHookConfig { destination: 2, hook: contract_address_const::<3>() });
set_routing_hook_addrs.set_hooks(hook_config_arr);
}


#[test]
#[should_panic(expected: ('Destination has no hooks',))]
fn hook_not_set_for_destination_should_fail() {
let (routing_hook_addrs, set_routing_hook_addrs) = setup_domain_routing_hook();
let ownable = IOwnableDispatcher { contract_address: set_routing_hook_addrs.contract_address };
start_prank(CheatTarget::One(ownable.contract_address), OWNER());
let destination: u32 = 12;
let hook: ContractAddress = contract_address_const::<1>();
set_routing_hook_addrs.set_hook(destination, hook);

let message = Message {
version: HYPERLANE_VERSION,
nonce: 0_u32,
origin: 0_u32,
sender: contract_address_const::<0>(),
destination: destination - 1,
recipient: contract_address_const::<0>(),
body: BytesTrait::new_empty(),
};
let metadata = BytesTrait::new_empty();
routing_hook_addrs.post_dispatch(metadata, message);
}

// Note: Test fails with msg('Result::unwrap failed')
#[ignore]
#[test]
fn hook_set_for_destination_post_dispatch() {
let (routing_hook_addrs, set_routing_hook_addrs) = setup_domain_routing_hook();
let ownable = IOwnableDispatcher { contract_address: set_routing_hook_addrs.contract_address };
start_prank(CheatTarget::One(ownable.contract_address), OWNER());
let destination: u32 = 18;
let hook: ContractAddress = setup_mock_hook().contract_address;
set_routing_hook_addrs.set_hook(destination, hook);

let message = Message {
version: HYPERLANE_VERSION,
nonce: 0_u32,
origin: 0_u32,
sender: contract_address_const::<0>(),
destination: destination,
recipient: contract_address_const::<0>(),
body: BytesTrait::new_empty(),
};
let metadata = BytesTrait::new_empty();
routing_hook_addrs.post_dispatch(metadata, message);
}

// Note: Test fails with msg('Result::unwrap failed')
#[ignore]
#[test]
fn hook_set_for_destination_quote_dispatch() {
let (routing_hook_addrs, set_routing_hook_addrs) = setup_domain_routing_hook();
let ownable = IOwnableDispatcher { contract_address: set_routing_hook_addrs.contract_address };
start_prank(CheatTarget::One(ownable.contract_address), OWNER());
let destination: u32 = 18;
let hook: ContractAddress = setup_mock_hook().contract_address;
EvolveArt marked this conversation as resolved.
Show resolved Hide resolved
set_routing_hook_addrs.set_hook(destination, hook);

let message = Message {
version: HYPERLANE_VERSION,
nonce: 0_u32,
origin: 0_u32,
sender: contract_address_const::<0>(),
destination: destination,
recipient: contract_address_const::<0>(),
body: BytesTrait::new_empty(),
};
let metadata = BytesTrait::new_empty();
routing_hook_addrs.quote_dispatch(metadata, message);
}
17 changes: 16 additions & 1 deletion contracts/src/tests/setup.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use hyperlane_starknet::interfaces::{
IPostDispatchHookDispatcherTrait, IProtocolFeeDispatcherTrait, IMockValidatorAnnounceDispatcher,
ISpecifiesInterchainSecurityModuleDispatcher, ISpecifiesInterchainSecurityModuleDispatcherTrait,
IRoutingIsmDispatcher, IRoutingIsmDispatcherTrait, IDomainRoutingIsmDispatcher,
IDomainRoutingIsmDispatcherTrait
IDomainRoutingIsmDispatcherTrait, IDomainRoutingHookDispatcher,
IDomainRoutingHookDispatcherTrait
};
use openzeppelin::account::utils::signature::EthSignature;
use openzeppelin::token::erc20::interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait};
Expand Down Expand Up @@ -439,3 +440,17 @@ pub fn setup_protocol_fee() -> (
IPostDispatchHookDispatcher { contract_address: protocol_fee_addr }
)
}

pub fn setup_domain_routing_hook() -> (IPostDispatchHookDispatcher, IDomainRoutingHookDispatcher) {
let (mailbox, _, _, _) = setup();

let domain_routing_hook_class = declare("domain_routing_hook").unwrap();
let (domain_routing_hook_addrs, _) = domain_routing_hook_class
.deploy(@array![mailbox.contract_address.into(), OWNER().into()])
.unwrap();
(
IPostDispatchHookDispatcher { contract_address: domain_routing_hook_addrs },
IDomainRoutingHookDispatcher { contract_address: domain_routing_hook_addrs }
)
}