Skip to content

Commit

Permalink
Test Cases with hook specified (#133)
Browse files Browse the repository at this point in the history
* Mock Mailbox dispatch mirror logic of mailbox

* Add quote_dispatch entrypoint to TestIGP

* HypERC20-test: add case with hook specified

* HypERC20Collat-test: add test with hook specified

* HypERC20Lockbox-Test: add test with custom gas config & hook specified

* HypFiat-Test: add test with custom gas config & hook specified

* HypXERC20-Test: add tests with custom gas config & hook specified

* HypERC721-test: add test w/hook specified

* HypERC721Collateral-test: add test w/hook specified

* HypERC721CollateralURIStorage-test: add test w/hook specified

* HypERC721URIStorage-test: add test w/hook specified

* Add Metadata Variant to tests with hook specified

* RateLimitedTest-test-scenario-fix

---------

Co-authored-by: JordyRo1 <[email protected]>
  • Loading branch information
EgeCaner and JordyRo1 authored Feb 27, 2025
1 parent d674cf1 commit be1d4af
Show file tree
Hide file tree
Showing 14 changed files with 499 additions and 112 deletions.
2 changes: 1 addition & 1 deletion cairo/crates/contracts/tests/libs/test_rate_limited.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ fn test_ctor_should_panic_when_low_capacity() {
let rate_limited_contract = declare("MockRateLimited").unwrap();
let owner = starknet::contract_address_const::<'OWNER'>();
let mut ctor_calldata: Array<felt252> = array![];
(RateLimitedComponent::DURATION - 1).serialize(ref ctor_calldata);
(RateLimitedComponent::DURATION.into() - 1_u256).serialize(ref ctor_calldata);
owner.serialize(ref ctor_calldata);
rate_limited_contract.deploy(@ctor_calldata).unwrap();
}
Expand Down
111 changes: 34 additions & 77 deletions cairo/crates/mocks/src/mock_mailbox.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -271,12 +271,43 @@ pub mod MockMailbox {
);
self.emit(DispatchId { id });

// HOOKS

let required_hook_address = self.required_hook.read();
let required_hook = ITestPostDispatchHookDispatcher {
contract_address: self.required_hook.read()
contract_address: required_hook_address
};
let mut required_fee = required_hook
.quote_dispatch(hook_metadata.clone(), message.clone());
let hook_dispatcher = ITestPostDispatchHookDispatcher { contract_address: hook };
let default_fee = hook_dispatcher
.quote_dispatch(hook_metadata.clone(), message.clone());

assert(fee_amount >= required_fee + default_fee, Errors::NOT_ENOUGH_FEE_PROVIDED);

let caller_address = get_caller_address();
let contract_address = get_contract_address();

let token_dispatcher = ERC20ABIDispatcher { contract_address: self.eth_address.read() };
let user_balance = token_dispatcher.balanceOf(caller_address);
assert(user_balance >= required_fee + default_fee, Errors::INSUFFICIENT_BALANCE);

assert(
token_dispatcher.allowance(caller_address, contract_address) >= fee_amount,
Errors::INSUFFICIENT_ALLOWANCE
);

if (required_fee > 0) {
token_dispatcher.transferFrom(caller_address, required_hook_address, required_fee);
}

required_hook.post_dispatch(hook_metadata.clone(), message.clone());
let hook = ITestPostDispatchHookDispatcher { contract_address: hook };
hook.post_dispatch(hook_metadata, message.clone());
if (default_fee > 0) {
token_dispatcher.transferFrom(caller_address, hook, default_fee);
}

hook_dispatcher.post_dispatch(hook_metadata, message.clone());

let remote_mailbox = self.remote_mailboxes.read(destination_domain);
assert!(remote_mailbox != contract_address_const::<0>());
IMockMailboxDispatcher { contract_address: remote_mailbox }
Expand Down Expand Up @@ -539,78 +570,4 @@ pub mod MockMailbox {
}
)
}
#[generate_trait]
impl Private of PrivateTrait {
fn _dispatch(
ref self: ContractState,
_destination_domain: u32,
_recipient_address: u256,
_message_body: Bytes,
_fee_amount: u256,
_custom_hook_metadata: Option<Bytes>,
_custom_hook: Option<ContractAddress>
) -> u256 {
let hook = match _custom_hook {
Option::Some(hook) => hook,
Option::None(()) => self.default_hook.read(),
};
let hook_metadata = match _custom_hook_metadata {
Option::Some(hook_metadata) => { hook_metadata },
Option::None(()) => BytesTrait::new_empty()
};
let (id, message) = build_message(
@self, _destination_domain, _recipient_address, _message_body
);
self.latest_dispatched_id.write(id);
let current_nonce = self.nonce.read();
self.nonce.write(current_nonce + 1);
let caller: felt252 = get_caller_address().into();
self
.emit(
Dispatch {
sender: caller.into(),
destination_domain: _destination_domain,
recipient_address: _recipient_address,
message: message.clone()
}
);
self.emit(DispatchId { id: id });

// HOOKS

let required_hook_address = self.required_hook.read();
let required_hook = ITestPostDispatchHookDispatcher {
contract_address: required_hook_address
};
let mut required_fee = required_hook
.quote_dispatch(hook_metadata.clone(), message.clone());
let hook_dispatcher = ITestPostDispatchHookDispatcher { contract_address: hook };
let default_fee = hook_dispatcher
.quote_dispatch(hook_metadata.clone(), message.clone());

assert(_fee_amount >= required_fee + default_fee, Errors::NOT_ENOUGH_FEE_PROVIDED);

let caller_address = get_caller_address();
let contract_address = get_contract_address();

let token_dispatcher = ERC20ABIDispatcher { contract_address: self.eth_address.read() };
let user_balance = token_dispatcher.balanceOf(caller_address);
assert(user_balance >= required_fee + default_fee, Errors::INSUFFICIENT_BALANCE);

assert(
token_dispatcher.allowance(caller_address, contract_address) >= _fee_amount,
Errors::INSUFFICIENT_ALLOWANCE
);

if (required_fee > 0) {
token_dispatcher.transfer_from(caller_address, required_hook_address, required_fee);
}
required_hook.post_dispatch(hook_metadata.clone(), message.clone());
if (default_fee > 0) {
token_dispatcher.transfer_from(caller_address, hook, default_fee);
}
hook_dispatcher.post_dispatch(hook_metadata, message.clone());
id
}
}
}
8 changes: 8 additions & 0 deletions cairo/crates/mocks/src/test_interchain_gas_payment.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub trait ITestInterchainGasPayment<TContractState> {
fn get_default_gas_usage(self: @TContractState) -> u256;
fn gas_price(self: @TContractState) -> u256;
fn post_dispatch(ref self: TContractState, metadata: Bytes, message: Message);
fn quote_dispatch(ref self: TContractState, _metadata: Bytes, _message: Message) -> u256;
}

#[starknet::contract]
Expand All @@ -20,6 +21,8 @@ pub mod TestInterchainGasPayment {

impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;

pub const DEFAULT_GAS_LIMIT: u256 = 10_000;

#[storage]
struct Storage {
gas_price: u256,
Expand Down Expand Up @@ -54,7 +57,12 @@ pub mod TestInterchainGasPayment {
fn gas_price(self: @ContractState) -> u256 {
self.gas_price.read()
}

fn post_dispatch(ref self: ContractState, metadata: Bytes, message: Message) {}

fn quote_dispatch(ref self: ContractState, _metadata: Bytes, _message: Message) -> u256 {
self.quote_gas_payment(DEFAULT_GAS_LIMIT)
}
}

#[generate_trait]
Expand Down
24 changes: 13 additions & 11 deletions cairo/crates/token/tests/hyp_erc20/common.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -133,23 +133,24 @@ pub struct Setup {
pub igp: ITestInterchainGasPaymentDispatcher,
pub erc20_token: ITestERC20Dispatcher,
pub eth_token: MockEthDispatcher,
pub mock_mailbox_contract: ContractClass
pub mock_mailbox_contract: ContractClass,
pub test_post_dispatch_hook_contract: ContractClass
}

pub fn setup() -> Setup {
let contract = declare("TestISM").unwrap();
let (default_ism, _) = contract.deploy(@array![]).unwrap();

let contract = declare("TestPostDispatchHook").unwrap();
let (noop_hook, _) = contract.deploy(@array![]).unwrap();
let test_post_dispatch_hook_contract = declare("TestPostDispatchHook").unwrap();
let (noop_hook, _) = test_post_dispatch_hook_contract.deploy(@array![]).unwrap();
let noop_hook = ITestPostDispatchHookDispatcher { contract_address: noop_hook };

let contract = declare("Ether").unwrap();
let mut calldata: Array<felt252> = array![];
starknet::get_contract_address().serialize(ref calldata);
let (eth_address, _) = contract.deploy_at(@calldata, ETH_ADDRESS()).unwrap();
let eth_token = MockEthDispatcher { contract_address: eth_address };
eth_token.mint(ALICE(), 10 * E18);
eth_token.mint(ALICE(), 20 * E18);

let mock_mailbox_contract = declare("MockMailbox").unwrap();
let (local_mailbox, _) = mock_mailbox_contract
Expand Down Expand Up @@ -262,7 +263,8 @@ pub fn setup() -> Setup {
igp,
erc20_token,
eth_token,
mock_mailbox_contract
mock_mailbox_contract,
test_post_dispatch_hook_contract
}
}

Expand Down Expand Up @@ -406,22 +408,22 @@ pub fn perform_remote_transfer_and_gas_with_hook(
}

pub fn test_transfer_with_hook_specified(setup: @Setup, fee: u256, metadata: Bytes) {
let contract = declare("TestPostDispatchHook").unwrap();
let (hook, _) = contract.deploy(@array![]).unwrap();
let hook = ITestPostDispatchHookDispatcher { contract_address: hook };

let (hook_address, _) = setup.test_post_dispatch_hook_contract.deploy(@array![]).unwrap();
let hook = ITestPostDispatchHookDispatcher { contract_address: hook_address };
hook.set_fee(fee);

start_prank(CheatTarget::One((*setup).primary_token.contract_address), ALICE());
let primary_token = IERC20Dispatcher {
contract_address: (*setup).primary_token.contract_address
};
primary_token.approve((*setup).local_token.contract_address, TRANSFER_AMT);
stop_prank(CheatTarget::One((*setup).primary_token.contract_address));

let message_id = perform_remote_transfer_and_gas_with_hook(
setup, 0, TRANSFER_AMT, hook.contract_address, metadata
setup, fee, TRANSFER_AMT, hook.contract_address, metadata
);

let eth_dispatcher = IERC20Dispatcher { contract_address: *setup.eth_token.contract_address };
assert_eq!(eth_dispatcher.balance_of(hook_address), fee, "fee didnt transferred");
assert!(hook.message_dispatched(message_id) == true, "Hook did not dispatch");
}

Expand Down
82 changes: 78 additions & 4 deletions cairo/crates/token/tests/hyp_erc20/hyp_erc20_collateral_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use alexandria_bytes::{Bytes, BytesTrait};
use contracts::client::gas_router_component::{
GasRouterComponent::GasRouterConfig, IGasRouterDispatcher, IGasRouterDispatcherTrait
};
use contracts::hooks::libs::standard_hook_metadata::standard_hook_metadata::VARIANT;
use contracts::utils::utils::U256TryIntoContractAddress;
use core::integer::BoundedInt;
use mocks::{
Expand Down Expand Up @@ -66,7 +67,6 @@ fn perform_remote_transfer_collateral(
setup: @Setup,
collateral: @IHypERC20TestDispatcher,
msg_value: u256,
extra_gas: u256,
amount: u256,
approve: bool
) {
Expand Down Expand Up @@ -108,12 +108,65 @@ fn process_transfers_collateral(
stop_prank(CheatTarget::One((*setup).remote_token.contract_address));
}

pub fn perform_remote_transfer_collateral_and_gas_with_hook(
setup: @Setup,
collateral: @IHypERC20TestDispatcher,
msg_value: u256,
amount: u256,
hook: ContractAddress,
hook_metadata: Bytes
) -> u256 {
// Approve
start_prank(CheatTarget::One(*setup.primary_token.contract_address), ALICE());
(*setup.primary_token).approve(*collateral.contract_address, amount);
stop_prank(CheatTarget::One(*setup.primary_token.contract_address));

// Remote transfer
start_prank(CheatTarget::One(*collateral.contract_address), ALICE());
let bob_felt: felt252 = BOB().into();
let bob_address: u256 = bob_felt.into();
let message_id = (*collateral)
.transfer_remote(
DESTINATION,
bob_address,
amount,
msg_value,
Option::Some(hook_metadata),
Option::Some(hook)
);

process_transfers_collateral(setup, collateral, BOB(), amount);

let remote_token = IERC20Dispatcher {
contract_address: (*setup).remote_token.contract_address
};
assert_eq!(remote_token.balance_of(BOB()), amount);

stop_prank(CheatTarget::One(*collateral.contract_address));
message_id
}

pub fn test_transfer_collateral_with_hook_specified(
setup: @Setup, collateral: @IHypERC20TestDispatcher, fee: u256, metadata: Bytes
) {
let (hook_address, _) = setup.test_post_dispatch_hook_contract.deploy(@array![]).unwrap();
let hook = ITestPostDispatchHookDispatcher { contract_address: hook_address };
hook.set_fee(fee);

let message_id = perform_remote_transfer_collateral_and_gas_with_hook(
setup, collateral, fee, TRANSFER_AMT, hook.contract_address, metadata
);
let eth_dispatcher = IERC20Dispatcher { contract_address: *setup.eth_token.contract_address };
assert_eq!(eth_dispatcher.balance_of(hook_address), fee, "fee didnt transferred");
assert!(hook.message_dispatched(message_id) == true, "Hook did not dispatch");
}

#[test]
fn test_remote_transfer() {
let (collateral, setup) = setup_hyp_erc20_collateral();
let balance_before = collateral.balance_of(ALICE());
start_prank(CheatTarget::One(collateral.contract_address), ALICE());
perform_remote_transfer_collateral(@setup, @collateral, REQUIRED_VALUE, 0, TRANSFER_AMT, true);
perform_remote_transfer_collateral(@setup, @collateral, REQUIRED_VALUE, TRANSFER_AMT, true);
stop_prank(CheatTarget::One(collateral.contract_address));
// Check balance after transfer
assert_eq!(
Expand All @@ -128,7 +181,7 @@ fn test_remote_transfer() {
fn test_remote_transfer_invalid_allowance() {
let (collateral, setup) = setup_hyp_erc20_collateral();
start_prank(CheatTarget::One(collateral.contract_address), ALICE());
perform_remote_transfer_collateral(@setup, @collateral, REQUIRED_VALUE, 0, TRANSFER_AMT, false);
perform_remote_transfer_collateral(@setup, @collateral, REQUIRED_VALUE, TRANSFER_AMT, false);
stop_prank(CheatTarget::One(collateral.contract_address));
}

Expand All @@ -142,9 +195,10 @@ fn test_remote_transfer_with_custom_gas_config() {
collateral.set_hook(setup.igp.contract_address);
let config = array![GasRouterConfig { domain: DESTINATION, gas: GAS_LIMIT }];
collateral.set_destination_gas(Option::Some(config), Option::None, Option::None);
let gas_price = setup.igp.gas_price();
// Do a remote transfer
perform_remote_transfer_collateral(
@setup, @collateral, REQUIRED_VALUE, setup.igp.gas_price(), TRANSFER_AMT, true
@setup, @collateral, REQUIRED_VALUE + GAS_LIMIT * gas_price, TRANSFER_AMT, true
);

stop_prank(CheatTarget::One(collateral.contract_address));
Expand All @@ -154,4 +208,24 @@ fn test_remote_transfer_with_custom_gas_config() {
balance_before - TRANSFER_AMT,
"Incorrect balance after transfer"
);
let eth_dispatcher = IERC20Dispatcher { contract_address: setup.eth_token.contract_address };
assert_eq!(
eth_dispatcher.balance_of(setup.igp.contract_address),
GAS_LIMIT * gas_price,
"Gas fee didnt transferred"
);
}

#[test]
fn test_erc20_remote_transfer_collateral_with_hook_specified(mut fee: u256, metadata: u256) {
let fee = fee % (TRANSFER_AMT / 10);
let mut metadata_bytes = BytesTrait::new_empty();
metadata_bytes.append_u16(VARIANT);
metadata_bytes.append_u256(metadata);
let (collateral, setup) = setup_hyp_erc20_collateral();

let balance_before = collateral.balance_of(ALICE());
test_transfer_collateral_with_hook_specified(@setup, @collateral, fee, metadata_bytes);
let balance_after = collateral.balance_of(ALICE());
assert_eq!(balance_after, balance_before - TRANSFER_AMT);
}
Loading

0 comments on commit be1d4af

Please sign in to comment.