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

Test Cases with hook specified #133

Merged
merged 14 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from 13 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
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.transferFrom(caller_address, required_hook_address, required_fee);
}
required_hook.post_dispatch(hook_metadata.clone(), message.clone());
if (default_fee > 0) {
token_dispatcher.transferFrom(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
Loading