diff --git a/cairo/src/contracts/client/gas_router_component.cairo b/cairo/src/contracts/client/gas_router_component.cairo index 75934f1..ea54944 100644 --- a/cairo/src/contracts/client/gas_router_component.cairo +++ b/cairo/src/contracts/client/gas_router_component.cairo @@ -95,11 +95,6 @@ pub mod GasRouterComponent { impl Router: RouterComponent::HasComponent, +Drop > of InternalTrait { - fn initialize(ref self: ComponentState, mailbox: ContractAddress) { - let mut router_comp = get_dep_component_mut!(ref self, Router); - router_comp.initialize(mailbox); - } - fn _Gas_router_hook_metadata( self: @ComponentState, destination: u32 ) -> Bytes { diff --git a/cairo/src/contracts/client/mailboxclient.cairo b/cairo/src/contracts/client/mailboxclient.cairo index 358fc16..d804f42 100644 --- a/cairo/src/contracts/client/mailboxclient.cairo +++ b/cairo/src/contracts/client/mailboxclient.cairo @@ -16,6 +16,10 @@ mod mailboxClientProxy { impl OwnableInternalImpl = OwnableComponent::InternalImpl; impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + #[abi(embed_v0)] + impl MailboxclientImpl = + MailboxclientComponent::MailboxClientImpl; + #[storage] struct Storage { #[substorage(v0)] @@ -27,12 +31,19 @@ mod mailboxClientProxy { } #[constructor] - fn constructor(ref self: ContractState, _mailbox: ContractAddress, _owner: ContractAddress,) { - self.mailboxclient.initialize(_mailbox); + fn constructor( + ref self: ContractState, + _mailbox: ContractAddress, + _owner: ContractAddress, + _hook: ContractAddress, + _interchain_security_module: ContractAddress + ) { self.ownable.initializer(_owner); + self + .mailboxclient + .initialize(_mailbox, Option::Some(_hook), Option::Some(_interchain_security_module)); } - #[event] #[derive(Drop, starknet::Event)] pub enum Event { @@ -56,7 +67,4 @@ mod mailboxClientProxy { self.upgradeable.upgrade(new_class_hash); } } - #[abi(embed_v0)] - impl MailboxclientImpl = - MailboxclientComponent::MailboxClientImpl; } diff --git a/cairo/src/contracts/client/mailboxclient_component.cairo b/cairo/src/contracts/client/mailboxclient_component.cairo index 30f7735..09538e8 100644 --- a/cairo/src/contracts/client/mailboxclient_component.cairo +++ b/cairo/src/contracts/client/mailboxclient_component.cairo @@ -84,29 +84,6 @@ pub mod MailboxclientComponent { self.interchain_security_module.read() } - - /// Initializes the mailbox client configuration. - /// Dev: callable only by the admin - /// - /// # Arguments - /// - /// * - `_hook` the hook contract address to set - /// * - `_interchain_security_module`- the ISM contract address - fn _MailboxClient_initialize( - ref self: ComponentState, - _hook: ContractAddress, - _interchain_security_module: ContractAddress, - _owner: ContractAddress - ) { - let ownable_comp_read = get_dep_component!(@self, Owner); - ownable_comp_read.assert_only_owner(); - self.set_hook(_hook); - self.set_interchain_security_module(_interchain_security_module); - - let mut ownable_comp_write = get_dep_component_mut!(ref self, Owner); - ownable_comp_write.transfer_ownership(_owner); - } - /// Determines if a message associated to a given id is the mailbox's latest dispatched /// /// # Arguments @@ -219,10 +196,21 @@ pub mod MailboxclientComponent { /// # Arguments /// /// * - `_mailbox` - mailbox contract address - fn initialize(ref self: ComponentState, _mailbox: ContractAddress) { + fn initialize( + ref self: ComponentState, + _mailbox: ContractAddress, + _hook: Option, + _interchain_security_module: Option, + ) { let mailbox = IMailboxDispatcher { contract_address: _mailbox }; self.mailbox.write(mailbox); self.local_domain.write(mailbox.get_local_domain()); + if let Option::Some(hook) = _hook { + self.hook.write(hook); + } + if let Option::Some(ism) = _interchain_security_module { + self.interchain_security_module.write(ism); + } } } } diff --git a/cairo/src/contracts/client/router_component.cairo b/cairo/src/contracts/client/router_component.cairo index 24b3fbf..1dec7b7 100644 --- a/cairo/src/contracts/client/router_component.cairo +++ b/cairo/src/contracts/client/router_component.cairo @@ -3,12 +3,10 @@ use starknet::ContractAddress; #[starknet::interface] pub trait IRouter { - fn enroll_remote_router(ref self: TState, domain: u32, router: Option); + fn enroll_remote_router(ref self: TState, domain: u32, router: u256); fn enroll_remote_routers(ref self: TState, domains: Array, addresses: Array); fn unenroll_remote_router(ref self: TState, domain: u32); - fn unenroll_remote_routers( - ref self: TState, domains: Array, addresses: Option> - ); + fn unenroll_remote_routers(ref self: TState, domains: Array); // fn handle(ref self: TState, origin: u32, sender: u256, message: Bytes); fn domains(self: @TState) -> Array; fn routers(self: @TState, domain: u32) -> u256; @@ -17,10 +15,10 @@ pub trait IRouter { #[starknet::component] pub mod RouterComponent { use alexandria_bytes::Bytes; - use alexandria_storage::{List, ListTrait}; use hyperlane_starknet::contracts::client::mailboxclient_component::{ MailboxclientComponent, MailboxclientComponent::MailboxClientInternalImpl }; + use hyperlane_starknet::contracts::libs::enumerable_map::{EnumerableMap, EnumerableMapTrait}; use hyperlane_starknet::interfaces::{ IMailboxClient, IMailboxDispatcher, IMailboxDispatcherTrait }; @@ -31,7 +29,7 @@ pub mod RouterComponent { #[storage] struct Storage { - routers: List, + routers: EnumerableMap, gas_router: ContractAddress, } @@ -50,18 +48,11 @@ pub mod RouterComponent { +Drop > of super::IRouter> { fn enroll_remote_router( - ref self: ComponentState, domain: u32, router: Option + ref self: ComponentState, domain: u32, router: u256 ) { let ownable_comp = get_dep_component!(@self, Owner); ownable_comp.assert_only_owner(); - - match router { - Option::Some(router) => { self._enroll_remote_router(domain, router); }, - Option::None => { - let router = self.routers.read().get(domain).expect('DOMAIN_NOT_FOUND'); - self._enroll_remote_router(domain, router.unwrap()); - } - } + self._enroll_remote_router(domain, router); } fn enroll_remote_routers( @@ -89,31 +80,12 @@ pub mod RouterComponent { self._unenroll_remote_router(domain); } - fn unenroll_remote_routers( - ref self: ComponentState, - domains: Array, - addresses: Option> - ) { + fn unenroll_remote_routers(ref self: ComponentState, domains: Array,) { let domains_len = domains.len(); - match addresses { - Option::Some(addresses) => { - if addresses.len() != domains_len { - panic!("Addresses array length must match domains array length"); - } - - let mut i = 0; - while i < domains_len { - self._unenroll_remote_router(*domains.at(i)); - i += 1; - } - }, - Option::None => { - let mut i = 0; - while i < domains_len { - self._unenroll_remote_router(*domains.at(i)); - i += 1; - } - } + let mut i = 0; + while i < domains_len { + self._unenroll_remote_router(*domains.at(i)); + i += 1; } } @@ -127,23 +99,11 @@ pub mod RouterComponent { // } fn domains(self: @ComponentState) -> Array { - let mut keys: Array = array![]; - let routers = self.routers.read().array().expect('ROUTERS_EMPTY'); - - let mut i = 0; - let len = routers.len(); - while i < len { - let element = *routers.at(i); - if element != 0 { - keys.append(i); - } - i += 1; - }; - keys + self.routers.read().keys() } fn routers(self: @ComponentState, domain: u32) -> u256 { - self.routers.read().get(domain).expect('DOMAIN_NOT_FOUND').unwrap() + self.routers.read().get(domain) } } @@ -154,11 +114,6 @@ pub mod RouterComponent { +Drop, impl MailBoxClient: MailboxclientComponent::HasComponent > of InternalTrait { - fn initialize(ref self: ComponentState, _mailbox: ContractAddress) { - let mut mailbox_comp = get_dep_component_mut!(ref self, MailBoxClient); - mailbox_comp.initialize(_mailbox); - } - // TODO: review later once we have a clear idea of how to handle virtual functions // fn _handle( // ref self: ComponentState, origin: u32, sender: u256, message: Bytes @@ -178,29 +133,26 @@ pub mod RouterComponent { fn _unenroll_remote_router(ref self: ComponentState, domain: u32) { let mut routers = self.routers.read(); - - let _ = routers.get(domain).expect('DOMAIN_NOT_FOUND'); - - let _ = routers.set(domain, 0); + routers.remove(domain); } fn _is_remote_router( self: @ComponentState, domain: u32, address: u256 ) -> bool { let routers = self.routers.read(); - let router = routers.get(domain).expect('DOMAIN_NOT_FOUND'); - router.unwrap() == address + let router = routers.get(domain); + router == address } fn _must_have_remote_router(self: @ComponentState, domain: u32) -> u256 { let routers = self.routers.read(); - let router = routers.get(domain).expect('DOMAIN_NOT_FOUND'); + let router = routers.get(domain); - if router.is_none() { + if router == 0 { Err::domain_not_found(domain); } - router.unwrap() + router } fn _Router_dispatch( diff --git a/cairo/src/contracts/hooks/merkle_tree_hook.cairo b/cairo/src/contracts/hooks/merkle_tree_hook.cairo index 1e8b88c..d9d08f4 100644 --- a/cairo/src/contracts/hooks/merkle_tree_hook.cairo +++ b/cairo/src/contracts/hooks/merkle_tree_hook.cairo @@ -79,7 +79,9 @@ pub mod merkle_tree_hook { /// * `_mailbox` - The mailbox to be associated to the mailbox client #[constructor] fn constructor(ref self: ContractState, _mailbox: ContractAddress, _owner: ContractAddress) { - self.mailboxclient.initialize(_mailbox); + self + .mailboxclient + .initialize(_mailbox, Option::::None, Option::::None); self.ownable.initializer(_owner); } diff --git a/cairo/src/contracts/isms/multisig/validator_announce.cairo b/cairo/src/contracts/isms/multisig/validator_announce.cairo index 840c08c..a6ed147 100644 --- a/cairo/src/contracts/isms/multisig/validator_announce.cairo +++ b/cairo/src/contracts/isms/multisig/validator_announce.cairo @@ -71,7 +71,7 @@ pub mod validator_announce { #[constructor] fn constructor(ref self: ContractState, _mailbox: ContractAddress, _owner: ContractAddress) { - self.mailboxclient.initialize(_mailbox); + self.mailboxclient.initialize(_mailbox, Option::None, Option::None); self.ownable.initializer(_owner); } diff --git a/cairo/src/contracts/isms/routing/default_fallback_routing_ism.cairo b/cairo/src/contracts/isms/routing/default_fallback_routing_ism.cairo index 04340d4..b50235b 100644 --- a/cairo/src/contracts/isms/routing/default_fallback_routing_ism.cairo +++ b/cairo/src/contracts/isms/routing/default_fallback_routing_ism.cairo @@ -61,7 +61,7 @@ pub mod default_fallback_routing_ism { #[constructor] fn constructor(ref self: ContractState, _owner: ContractAddress, _mailbox: ContractAddress) { self.ownable.initializer(_owner); - self.mailboxclient.initialize(_mailbox); + self.mailboxclient.initialize(_mailbox, Option::None, Option::None); } #[abi(embed_v0)] diff --git a/cairo/src/contracts/libs/enumerable_map.cairo b/cairo/src/contracts/libs/enumerable_map.cairo new file mode 100644 index 0000000..abd1d8e --- /dev/null +++ b/cairo/src/contracts/libs/enumerable_map.cairo @@ -0,0 +1,295 @@ +use core::hash::{HashStateTrait}; +use core::num::traits::Zero; +use core::pedersen::PedersenTrait; +use core::poseidon::poseidon_hash_span; +use starknet::storage_access::{ + StorageBaseAddress, storage_address_from_base, storage_base_address_from_felt252 +}; +use starknet::{Store, SyscallResultTrait, SyscallResult}; +pub mod Err { + pub const NOT_IMPLEMENTED: felt252 = 'Not implemented!'; + pub const INDEX_OUT_OF_BOUNDS: felt252 = 'Index out of bounds!'; + pub const KEY_DOES_NOT_EXIST: felt252 = 'Key does not exist!'; +} + +// Enumerable map +// struct EnumerableMap { +// values: Map +// keys: List +// positions: Map +// } +#[derive(Copy, Drop)] +pub struct EnumerableMap { + address_domain: u32, + base: StorageBaseAddress +} + +pub impl EnumerableMapStore< + K, V, +Store, +Drop, +Store, +Drop +> of Store> { + #[inline(always)] + fn read(address_domain: u32, base: StorageBaseAddress) -> SyscallResult> { + SyscallResult::Ok(EnumerableMap:: { address_domain, base }) + } + + #[inline(always)] + fn write( + address_domain: u32, base: StorageBaseAddress, value: EnumerableMap + ) -> SyscallResult<()> { + SyscallResult::Err(array![Err::NOT_IMPLEMENTED]) + } + + #[inline(always)] + fn read_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8 + ) -> SyscallResult> { + SyscallResult::Err(array![Err::NOT_IMPLEMENTED]) + } + #[inline(always)] + fn write_at_offset( + address_domain: u32, base: StorageBaseAddress, offset: u8, value: EnumerableMap + ) -> SyscallResult<()> { + SyscallResult::Err(array![Err::NOT_IMPLEMENTED]) + } + #[inline(always)] + fn size() -> u8 { + // 0 was selected because the read method doesn't actually read from storage + 0_u8 + } +} + +pub trait EnumerableMapTrait { + fn get(self: @EnumerableMap, key: K) -> V; + fn set(ref self: EnumerableMap, key: K, val: V) -> (); + fn len(self: @EnumerableMap) -> u32; + fn contains(self: @EnumerableMap, key: K) -> bool; + fn remove(ref self: EnumerableMap, key: K) -> bool; + fn at(self: @EnumerableMap, index: u32) -> (K, V); + fn keys(self: @EnumerableMap) -> Array; +} + +pub impl EnumerableMapImpl< + K, + V, + +Drop, + +Drop, + +Store, + +Store, + +Copy, + +Copy, + +Zero, + +Zero, + +Serde, +> of EnumerableMapTrait { + fn get(self: @EnumerableMap, key: K) -> V { + let value = EnumerableMapInternalTrait::::values_mapping_read(self, key); + assert(value.is_non_zero() || self.contains(key), Err::KEY_DOES_NOT_EXIST); + value + } + + fn set(ref self: EnumerableMap, key: K, val: V) { + let is_exists = self.contains(key); + + EnumerableMapInternalTrait::::values_mapping_write(ref self, key, val); + if !is_exists { + // appends 'key' to array and updates 'position' mapping + EnumerableMapInternalTrait::::array_append(ref self, key); + } + } + + fn len(self: @EnumerableMap) -> u32 { + Store::::read(*self.address_domain, *self.base).unwrap_syscall() + } + + fn contains(self: @EnumerableMap, key: K) -> bool { + EnumerableMapInternalTrait::::positions_mapping_read(self, key) != 0 + } + + fn remove(ref self: EnumerableMap, key: K) -> bool { + if !self.contains(key) { + return false; + } + let index = EnumerableMapInternalImpl::::positions_mapping_read(@self, key) - 1; + // Deletes `key` from 'values' mapping + EnumerableMapInternalTrait::::values_mapping_write(ref self, key, Zero::::zero()); + // Deletes `key`` from 'array' and 'positions' mapping + EnumerableMapInternalTrait::::array_remove(ref self, index) + } + + fn at(self: @EnumerableMap, index: u32) -> (K, V) { + assert(index < self.len(), Err::INDEX_OUT_OF_BOUNDS); + let key = EnumerableMapInternalTrait::::array_read(self, index); + let val = EnumerableMapInternalTrait::::values_mapping_read(self, key); + (key, val) + } + + fn keys(self: @EnumerableMap) -> Array { + let mut i = 0; + let len = self.len(); + let mut keys = array![]; + while i < len { + let key = EnumerableMapInternalTrait::::array_read(self, i); + keys.append(key); + i += 1; + }; + keys + } +} + +trait EnumerableMapInternalTrait { + fn values_mapping_write(ref self: EnumerableMap, key: K, val: V); + fn values_mapping_read(self: @EnumerableMap, key: K) -> V; + fn positions_mapping_write(ref self: EnumerableMap, key: K, val: u32); + fn positions_mapping_read(self: @EnumerableMap, key: K) -> u32; + fn update_array_len(ref self: EnumerableMap, new_len: u32); + fn array_append(ref self: EnumerableMap, key: K); + fn array_remove(ref self: EnumerableMap, index: u32) -> bool; + fn array_read(self: @EnumerableMap, index: u32) -> K; + fn array_write(ref self: EnumerableMap, index: u32, val: K); +} + +impl EnumerableMapInternalImpl< + K, + V, + +Drop, + +Drop, + +Store, + +Store, + +Copy, + +Copy, + +Zero, + +Zero, + +Serde, +> of EnumerableMapInternalTrait { + fn values_mapping_write(ref self: EnumerableMap, key: K, val: V) { + let storage_base_felt: felt252 = storage_address_from_base(self.base).into(); + let mut storage_address_val = PedersenTrait::new(storage_base_felt).update('values'); + let mut serialized_key: Array = array![]; + key.serialize(ref serialized_key); + let mut i = 0; + let len = serialized_key.len(); + while i < len { + storage_address_val = storage_address_val.update(*serialized_key.at(i)); + i += 1; + }; + let storage_address_val_felt = storage_address_val.finalize(); + Store::< + V + >::write( + self.address_domain, storage_base_address_from_felt252(storage_address_val_felt), val + ) + .unwrap_syscall(); + } + + fn values_mapping_read(self: @EnumerableMap, key: K) -> V { + let storage_base_felt: felt252 = storage_address_from_base(*self.base).into(); + let mut storage_address_val = PedersenTrait::new(storage_base_felt).update('values'); + let mut serialized_key: Array = array![]; + key.serialize(ref serialized_key); + let mut i = 0; + let len = serialized_key.len(); + while i < len { + storage_address_val = storage_address_val.update(*serialized_key.at(i)); + i += 1; + }; + let storage_address_val_felt = storage_address_val.finalize(); + Store::< + V + >::read(*self.address_domain, storage_base_address_from_felt252(storage_address_val_felt)) + .unwrap_syscall() + } + + fn positions_mapping_write(ref self: EnumerableMap, key: K, val: u32) { + let storage_base_felt: felt252 = storage_address_from_base(self.base).into(); + let mut storage_address_val = PedersenTrait::new(storage_base_felt).update('positions'); + let mut serialized_key: Array = array![]; + key.serialize(ref serialized_key); + let mut i = 0; + let len = serialized_key.len(); + while i < len { + storage_address_val = storage_address_val.update(*serialized_key.at(i)); + i += 1; + }; + let storage_address_val_felt = storage_address_val.finalize(); + Store::< + u32 + >::write( + self.address_domain, storage_base_address_from_felt252(storage_address_val_felt), val + ) + .unwrap_syscall(); + } + + fn positions_mapping_read(self: @EnumerableMap, key: K) -> u32 { + let storage_base_felt: felt252 = storage_address_from_base(*self.base).into(); + let mut storage_address_val = PedersenTrait::new(storage_base_felt).update('positions'); + let mut serialized_key: Array = array![]; + key.serialize(ref serialized_key); + let mut i = 0; + let len = serialized_key.len(); + while i < len { + storage_address_val = storage_address_val.update(*serialized_key.at(i)); + i += 1; + }; + let storage_address_val_felt = storage_address_val.finalize(); + Store::< + u32 + >::read(*self.address_domain, storage_base_address_from_felt252(storage_address_val_felt)) + .unwrap_syscall() + } + + fn update_array_len(ref self: EnumerableMap, new_len: u32) { + Store::::write(self.address_domain, self.base, new_len).unwrap_syscall(); + } + + fn array_append(ref self: EnumerableMap, key: K) { + let len = Store::::read(self.address_domain, self.base).unwrap_syscall(); + self.array_write(len, key); + self.update_array_len(len + 1); + self.positions_mapping_write(key, len + 1); + } + + fn array_remove(ref self: EnumerableMap, index: u32) -> bool { + let len = Store::::read(self.address_domain, self.base).unwrap_syscall(); + if index >= len { + return false; + } + let element = self.array_read(index); + // Remove `element` from `positions` mapping + self.positions_mapping_write(element, 0); + // if element is not the last element, swap with last element and clear the last index + if index != len - 1 { + let last_element = self.array_read(len - 1); + // Updates the position of `last_element` in 'positions' mapping + self.positions_mapping_write(last_element, index + 1); + // Moves last element into 'index' and remove the last element + self.array_write(index, last_element); + // Deletes the last element from array + self.array_write(len - 1, Zero::::zero()); + } + // Decrease the array length + self.update_array_len(len - 1); + true + } + + fn array_read(self: @EnumerableMap, index: u32) -> K { + let storage_base_felt: felt252 = storage_address_from_base(*self.base).into(); + let storage_address_felt = poseidon_hash_span( + array![storage_base_felt, index.into()].span() + ); + Store::< + K + >::read(*self.address_domain, storage_base_address_from_felt252(storage_address_felt)) + .unwrap_syscall() + } + + fn array_write(ref self: EnumerableMap, index: u32, val: K) { + let storage_base_felt: felt252 = storage_address_from_base(self.base).into(); + let storage_address_felt = poseidon_hash_span( + array![storage_base_felt, index.into()].span() + ); + Store::< + K + >::write(self.address_domain, storage_base_address_from_felt252(storage_address_felt), val) + .unwrap_syscall(); + } +} diff --git a/cairo/src/contracts/mailbox.cairo b/cairo/src/contracts/mailbox.cairo index 756e9e5..5168d58 100644 --- a/cairo/src/contracts/mailbox.cairo +++ b/cairo/src/contracts/mailbox.cairo @@ -255,7 +255,7 @@ pub mod mailbox { Option::Some(hook_metadata) => { let mut sanitized_bytes_metadata = BytesTrait::new_empty(); sanitized_bytes_metadata.concat(@hook_metadata); - assert( + assert( // what does this exactly checks sanitized_bytes_metadata == hook_metadata, Errors::SIZE_DOES_NOT_MATCH_METADATA ); diff --git a/cairo/src/contracts/mocks/enumerable_map_holder.cairo b/cairo/src/contracts/mocks/enumerable_map_holder.cairo new file mode 100644 index 0000000..370d16d --- /dev/null +++ b/cairo/src/contracts/mocks/enumerable_map_holder.cairo @@ -0,0 +1,52 @@ +#[starknet::interface] +pub trait IEnumerableMapHolder { + fn do_get_len(self: @TContractState) -> u32; + fn do_set_key(ref self: TContractState, key: u32, value: u256); + fn do_get_value(self: @TContractState, key: u32) -> u256; + fn do_contains(self: @TContractState, key: u32) -> bool; + fn do_remove(ref self: TContractState, key: u32) -> bool; + fn do_at(self: @TContractState, index: u32) -> (u32, u256); + fn do_get_keys(self: @TContractState) -> Array; +} + +#[starknet::contract] +pub mod EnumerableMapHolder { + use hyperlane_starknet::contracts::libs::enumerable_map::{EnumerableMap, EnumerableMapTrait}; + + #[storage] + struct Storage { + routers: EnumerableMap + } + + #[abi(embed_v0)] + impl Holder of super::IEnumerableMapHolder { + fn do_get_len(self: @ContractState) -> u32 { + let routers = self.routers.read(); + routers.len() + } + fn do_set_key(ref self: ContractState, key: u32, value: u256) { + let mut routers = self.routers.read(); + routers.set(key, value); + } + fn do_get_value(self: @ContractState, key: u32) -> u256 { + let routers = self.routers.read(); + routers.get(key) + } + fn do_contains(self: @ContractState, key: u32) -> bool { + let routers = self.routers.read(); + routers.contains(key) + } + fn do_remove(ref self: ContractState, key: u32) -> bool { + let mut routers = self.routers.read(); + routers.remove(key) + } + fn do_at(self: @ContractState, index: u32) -> (u32, u256) { + let routers = self.routers.read(); + routers.at(index) + } + fn do_get_keys(self: @ContractState) -> Array { + let routers = self.routers.read(); + routers.keys() + } + } +} diff --git a/cairo/src/contracts/token/components/fast_token_router.cairo b/cairo/src/contracts/token/components/fast_token_router.cairo index 66abcd5..39d1a96 100644 --- a/cairo/src/contracts/token/components/fast_token_router.cairo +++ b/cairo/src/contracts/token/components/fast_token_router.cairo @@ -17,9 +17,9 @@ pub trait IFastTokenRouter { value: u256 ) -> u256; } - +// TODO: Implement hooks logic to have virtual functions #[starknet::component] -pub mod FastTokenRouter { +pub mod FastTokenRouterComponent { use alexandria_bytes::{Bytes, BytesTrait}; use hyperlane_starknet::contracts::client::gas_router_component::{ GasRouterComponent, GasRouterComponent::GasRouterInternalImpl @@ -36,6 +36,7 @@ pub mod FastTokenRouter { TokenRouterComponent, TokenRouterComponent::TokenRouterInternalImpl, TokenRouterComponent::TokenRouterHooksTrait }; + use hyperlane_starknet::utils::utils::U256TryIntoContractAddress; use openzeppelin::access::ownable::{ OwnableComponent, OwnableComponent::InternalImpl as OwnableInternalImpl }; @@ -47,12 +48,22 @@ pub mod FastTokenRouter { filled_fast_transfers: LegacyMap, } + pub trait FastTokenRouterHooksTrait { + fn fast_transfer_to_hook( + ref self: ComponentState, recipient: u256, amount: u256 + ); + fn fast_receive_from_hook( + ref self: ComponentState, sender: ContractAddress, amount: u256 + ); + } + #[embeddable_as(FastTokenRouterImpl)] impl FastTokenRouter< TContractState, +HasComponent, +Drop, +TokenRouterHooksTrait, + impl FTRHooks: FastTokenRouterHooksTrait, impl MailBoxClient: MailboxclientComponent::HasComponent, impl Router: RouterComponent::HasComponent, impl Owner: OwnableComponent::HasComponent, @@ -80,8 +91,8 @@ pub mod FastTokenRouter { let caller = starknet::get_caller_address(); self.filled_fast_transfers.write(filled_fast_transfer_key, caller); - self._fast_recieve_from(caller, amount - fast_fee); - self._fast_transfer_to(recipient, amount - fast_fee); + FTRHooks::fast_receive_from_hook(ref self, caller, amount - fast_fee); + FTRHooks::fast_transfer_to_hook(ref self, recipient, amount - fast_fee); } fn fast_transfer_remote( @@ -119,22 +130,19 @@ pub mod FastTokenRouter { } #[generate_trait] - impl InternalImpl< + pub impl InternalImpl< TContractState, +HasComponent, +Drop, +TokenRouterHooksTrait, + impl FTRHooks: FastTokenRouterHooksTrait, impl MailBoxClient: MailboxclientComponent::HasComponent, impl Router: RouterComponent::HasComponent, impl Owner: OwnableComponent::HasComponent, impl GasRouter: GasRouterComponent::HasComponent, impl TokenRouter: TokenRouterComponent::HasComponent, > of InternalTrait { - fn initialize(ref self: ComponentState, mailbox: ContractAddress) { - let mut token_router_comp = get_dep_component_mut!(ref self, TokenRouter); - token_router_comp.initialize(mailbox); - } - + // all needs to support override fn _handle(ref self: ComponentState, origin: u32, message: Bytes) { let mut token_router_comp = get_dep_component_mut!(ref self, TokenRouter); @@ -157,17 +165,9 @@ pub mod FastTokenRouter { ) { let token_recipient = self._get_token_recipient(recipient, amount, origin, metadata); - self._fast_transfer_to(token_recipient, amount); + FTRHooks::fast_transfer_to_hook(ref self, token_recipient, amount); } - fn _fast_transfer_to( - ref self: ComponentState, recipient: u256, amount: u256 - ) {} - - fn _fast_recieve_from( - ref self: ComponentState, sender: ContractAddress, amount: u256 - ) {} - fn _get_token_recipient( self: @ComponentState, recipient: u256, @@ -222,10 +222,26 @@ pub mod FastTokenRouter { fast_fee: u256, fast_transfer_id: u256 ) -> Bytes { - self._fast_recieve_from(starknet::get_caller_address(), amount); + FTRHooks::fast_receive_from_hook(ref self, starknet::get_caller_address(), amount); BytesTrait::new( 4, array![fast_fee.low, fast_fee.high, fast_transfer_id.low, fast_transfer_id.high] ) } } } + + +pub impl FastTokenRouterHooksImpl< + TContractState +> of FastTokenRouterComponent::FastTokenRouterHooksTrait { + fn fast_transfer_to_hook( + ref self: FastTokenRouterComponent::ComponentState, + recipient: u256, + amount: u256 + ) {} + fn fast_receive_from_hook( + ref self: FastTokenRouterComponent::ComponentState, + sender: starknet::ContractAddress, + amount: u256 + ) {} +} diff --git a/cairo/src/contracts/token/components/hyp_erc20_collateral_component.cairo b/cairo/src/contracts/token/components/hyp_erc20_collateral_component.cairo new file mode 100644 index 0000000..ec9dd10 --- /dev/null +++ b/cairo/src/contracts/token/components/hyp_erc20_collateral_component.cairo @@ -0,0 +1,87 @@ +use starknet::ContractAddress; + +#[starknet::interface] +pub trait IHypErc20Collateral { + fn balance_of(self: @TState, account: ContractAddress) -> u256; +} + +#[starknet::component] +pub mod HypErc20CollateralComponent { + use alexandria_bytes::{Bytes, BytesTrait}; + use hyperlane_starknet::contracts::client::gas_router_component::{ + GasRouterComponent, + GasRouterComponent::{GasRouterInternalImpl, InternalTrait as GasRouterInternalTrait} + }; + use hyperlane_starknet::contracts::client::mailboxclient_component::{ + MailboxclientComponent, MailboxclientComponent::MailboxClientImpl + }; + use hyperlane_starknet::contracts::client::router_component::{ + RouterComponent, + RouterComponent::{InternalTrait as RouterInternalTrait, RouterComponentInternalImpl} + }; + use hyperlane_starknet::contracts::token::components::token_message::TokenMessageTrait; + use hyperlane_starknet::contracts::token::components::token_router::{ + TokenRouterComponent, TokenRouterComponent::TokenRouterInternalImpl + }; + use hyperlane_starknet::interfaces::IMailboxClient; + use hyperlane_starknet::utils::utils::{U256TryIntoContractAddress}; + + use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::token::erc20::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; + use starknet::ContractAddress; + + #[storage] + struct Storage { + wrapped_token: ERC20ABIDispatcher + } + + #[embeddable_as(HypErc20CollateralImpl)] + impl HypeErc20CollateralComponentImpl< + TContractState, + +HasComponent, + +Drop, + +MailboxclientComponent::HasComponent, + +RouterComponent::HasComponent, + +OwnableComponent::HasComponent, + +GasRouterComponent::HasComponent, + +TokenRouterComponent::HasComponent, + > of super::IHypErc20Collateral> { + fn balance_of(self: @ComponentState, account: ContractAddress) -> u256 { + self.wrapped_token.read().balance_of(account) + } + } + + #[generate_trait] + pub impl InternalImpl< + TContractState, + +HasComponent, + +Drop, + impl Mailboxclient: MailboxclientComponent::HasComponent, + +RouterComponent::HasComponent, + +OwnableComponent::HasComponent, + +GasRouterComponent::HasComponent, + +TokenRouterComponent::HasComponent + > of InternalTrait { + fn initialize(ref self: ComponentState, wrapped_token: ContractAddress,) { + self.wrapped_token.write(ERC20ABIDispatcher { contract_address: wrapped_token }); + } + + fn _transfer_from_sender(ref self: ComponentState, amount: u256) -> Bytes { + self + .wrapped_token + .read() + .transfer_from( + starknet::get_caller_address(), starknet::get_contract_address(), amount + ); + + BytesTrait::new_empty() + } + + fn _transfer_to(ref self: ComponentState, recipient: u256, amount: u256) { + self + .wrapped_token + .read() + .transfer(recipient.try_into().expect('u256 to ContractAddress failed'), amount); + } + } +} diff --git a/cairo/src/contracts/token/components/hyp_erc20_component.cairo b/cairo/src/contracts/token/components/hyp_erc20_component.cairo new file mode 100644 index 0000000..7337541 --- /dev/null +++ b/cairo/src/contracts/token/components/hyp_erc20_component.cairo @@ -0,0 +1,89 @@ +use starknet::ContractAddress; + +#[starknet::interface] +pub trait IHypErc20 { + fn decimals(self: @TState) -> u8; +} + +#[starknet::component] +pub mod HypErc20Component { + use alexandria_bytes::{Bytes, BytesTrait}; + use hyperlane_starknet::contracts::client::gas_router_component::{ + GasRouterComponent, + GasRouterComponent::{GasRouterInternalImpl, InternalTrait as GasRouterInternalTrait} + }; + use hyperlane_starknet::contracts::client::mailboxclient_component::{ + MailboxclientComponent, MailboxclientComponent::MailboxClientImpl + }; + use hyperlane_starknet::contracts::client::router_component::{ + RouterComponent, + RouterComponent::{InternalTrait as RouterInternalTrait, RouterComponentInternalImpl} + }; + use hyperlane_starknet::contracts::token::components::token_message::TokenMessageTrait; + use hyperlane_starknet::contracts::token::components::token_router::{ + TokenRouterComponent, TokenRouterComponent::TokenRouterInternalImpl + }; + use hyperlane_starknet::contracts::token::interfaces::imessage_recipient::IMessageRecipient; + use hyperlane_starknet::interfaces::IMailboxClient; + use hyperlane_starknet::utils::utils::{U256TryIntoContractAddress}; + use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::token::erc20::ERC20Component::{ + InternalImpl as ERC20InternalImpl, ERC20HooksTrait + }; + use openzeppelin::token::erc20::ERC20Component; + + use starknet::ContractAddress; + + #[storage] + struct Storage { + decimals: u8, + } + + #[embeddable_as(HypeErc20Impl)] + impl HypErc20Impl< + TContractState, + +HasComponent, + +Drop, + +MailboxclientComponent::HasComponent, + +RouterComponent::HasComponent, + +OwnableComponent::HasComponent, + +GasRouterComponent::HasComponent, + +TokenRouterComponent::HasComponent, + +ERC20HooksTrait, + impl ERC20: ERC20Component::HasComponent + > of super::IHypErc20> { + fn decimals(self: @ComponentState) -> u8 { + self.decimals.read() + } + } + + #[generate_trait] + pub impl InternalImpl< + TContractState, + +HasComponent, + +Drop, + +MailboxclientComponent::HasComponent, + +RouterComponent::HasComponent, + +OwnableComponent::HasComponent, + +GasRouterComponent::HasComponent, + +TokenRouterComponent::HasComponent, + +ERC20HooksTrait, + impl ERC20: ERC20Component::HasComponent + > of InternalTrait { + fn initialize(ref self: ComponentState, decimals: u8) { + self.decimals.write(decimals); + } + + fn _transfer_from_sender(ref self: ComponentState, amount: u256) -> Bytes { + let mut erc20 = get_dep_component_mut!(ref self, ERC20); + erc20.burn(starknet::get_caller_address(), amount); + BytesTrait::new_empty() + } + + fn _transfer_to(ref self: ComponentState, recipient: u256, amount: u256) { + let mut erc20 = get_dep_component_mut!(ref self, ERC20); + + erc20.mint(recipient.try_into().expect('u256 to ContractAddress failed'), amount); + } + } +} diff --git a/cairo/src/contracts/token/components/hyp_erc721_collateral_component.cairo b/cairo/src/contracts/token/components/hyp_erc721_collateral_component.cairo index 48d80cf..a96f1ef 100644 --- a/cairo/src/contracts/token/components/hyp_erc721_collateral_component.cairo +++ b/cairo/src/contracts/token/components/hyp_erc721_collateral_component.cairo @@ -2,12 +2,6 @@ use starknet::ContractAddress; #[starknet::interface] pub trait IHypErc721Collateral { - fn initialize( - ref self: TState, - hook: ContractAddress, - interchain_security_module: ContractAddress, - owner: ContractAddress - ); fn owner_of(self: @TState, token_id: u256) -> ContractAddress; fn balance_of(self: @TState, account: ContractAddress) -> u256; } @@ -38,16 +32,6 @@ pub mod HypErc721CollateralComponent { +OwnableComponent::HasComponent, impl Mailboxclient: MailboxclientComponent::HasComponent, > of super::IHypErc721Collateral> { - fn initialize( - ref self: ComponentState, - hook: ContractAddress, - interchain_security_module: ContractAddress, - owner: ContractAddress - ) { - let mut mailboxclient_comp = get_dep_component_mut!(ref self, Mailboxclient); - mailboxclient_comp._MailboxClient_initialize(hook, interchain_security_module, owner); - } - fn owner_of(self: @ComponentState, token_id: u256) -> ContractAddress { self.wrapped_token.read().owner_of(token_id) } diff --git a/cairo/src/contracts/token/components/hyp_erc721_component.cairo b/cairo/src/contracts/token/components/hyp_erc721_component.cairo index e47c98a..9111ab2 100644 --- a/cairo/src/contracts/token/components/hyp_erc721_component.cairo +++ b/cairo/src/contracts/token/components/hyp_erc721_component.cairo @@ -2,15 +2,7 @@ use starknet::{ContractAddress, ClassHash}; #[starknet::interface] pub trait IHypErc721 { - fn initialize( - ref self: TState, - mint_amount: u256, - name: ByteArray, - symbol: ByteArray, - hook: ContractAddress, - interchain_security_module: ContractAddress, - owner: ContractAddress - ); + fn initialize(ref self: TState, mint_amount: u256, name: ByteArray, symbol: ByteArray,); } #[starknet::component] @@ -52,12 +44,7 @@ pub mod HypErc721Component { mint_amount: u256, name: ByteArray, symbol: ByteArray, - hook: ContractAddress, - interchain_security_module: ContractAddress, - owner: ContractAddress ) { - let mut mailboxclient_comp = get_dep_component_mut!(ref self, Mailboxclient); - mailboxclient_comp._MailboxClient_initialize(hook, interchain_security_module, owner); let mut erc721_comp = get_dep_component_mut!(ref self, ERC721); erc721_comp.initializer(name, symbol, ""); diff --git a/cairo/src/contracts/token/components/hyp_native_component.cairo b/cairo/src/contracts/token/components/hyp_native_component.cairo index aa47617..14633e7 100644 --- a/cairo/src/contracts/token/components/hyp_native_component.cairo +++ b/cairo/src/contracts/token/components/hyp_native_component.cairo @@ -2,15 +2,6 @@ use starknet::ContractAddress; #[starknet::interface] pub trait IHypNative { - fn initialize( - ref self: TState, - hook: ContractAddress, - interchain_security_module: ContractAddress, - owner: ContractAddress - ); - fn transfer_remote( - ref self: TState, destination: u32, recipient: u256, amount: u256, mgs_value: u256 - ) -> u256; // fn balance_of(self: @TState, account: ContractAddress) -> u256; fn receive(ref self: TState, amount: u256); } @@ -19,29 +10,34 @@ pub trait IHypNative { pub mod HypNativeComponent { use alexandria_bytes::{Bytes, BytesTrait}; use hyperlane_starknet::contracts::client::gas_router_component::{ - GasRouterComponent, GasRouterComponent::GasRouterInternalImpl, + GasRouterComponent, + GasRouterComponent::{GasRouterInternalImpl, InternalTrait as GasRouterInternalTrait} }; use hyperlane_starknet::contracts::client::mailboxclient_component::{ - MailboxclientComponent, MailboxclientComponent::MailboxClientInternalImpl, - MailboxclientComponent::MailboxClient + MailboxclientComponent, MailboxclientComponent::MailboxClientImpl }; - use hyperlane_starknet::contracts::client::router_component::{RouterComponent,}; + use hyperlane_starknet::contracts::client::router_component::{ + RouterComponent, + RouterComponent::{InternalTrait as RouterInternalTrait, RouterComponentInternalImpl} + }; + use hyperlane_starknet::contracts::token::components::token_message::TokenMessageTrait; use hyperlane_starknet::contracts::token::components::token_router::{ TokenRouterComponent, TokenRouterComponent::TokenRouterInternalImpl, - TokenRouterComponent::TokenRouterHooksTrait + TokenRouterComponent::TokenRouterHooksTrait, ITokenRouter }; + use hyperlane_starknet::contracts::token::interfaces::imessage_recipient::IMessageRecipient; use openzeppelin::access::ownable::{ OwnableComponent, OwnableComponent::InternalImpl, OwnableComponent::OwnableImpl }; use openzeppelin::token::erc20::{ - interface::{IERC20, IERC20Dispatcher, IERC20DispatcherTrait}, ERC20Component + interface::{IERC20, ERC20ABIDispatcher, ERC20ABIDispatcherTrait}, ERC20Component }; use starknet::ContractAddress; #[storage] struct Storage { - eth_token: IERC20Dispatcher, + eth_token: ERC20ABIDispatcher, } #[event] @@ -69,37 +65,66 @@ pub mod HypNativeComponent { impl Mailboxclient: MailboxclientComponent::HasComponent, impl TokenRouter: TokenRouterComponent::HasComponent, > of super::IHypNative> { - fn initialize( - ref self: ComponentState, - hook: ContractAddress, - interchain_security_module: ContractAddress, - owner: ContractAddress - ) { - let mut mailboxclient_comp = get_dep_component_mut!(ref self, Mailboxclient); - mailboxclient_comp._MailboxClient_initialize(hook, interchain_security_module, owner); - } + fn receive(ref self: ComponentState, amount: u256) { + self.eth_token.read().transfer(starknet::get_contract_address(), amount); + self.emit(Donation { sender: starknet::get_caller_address(), amount }); + } + } + /// get the overrides for transfers again + #[embeddable_as(TokenRouterImpl)] + impl TokenRouter< + TContractState, + +HasComponent, + +Drop, + impl MailboxClient: MailboxclientComponent::HasComponent, + impl Router: RouterComponent::HasComponent, + +OwnableComponent::HasComponent, + impl GasRouter: GasRouterComponent::HasComponent, + impl TokenRouterComp: TokenRouterComponent::HasComponent, + impl ERC20: ERC20Component::HasComponent + > of ITokenRouter> { fn transfer_remote( ref self: ComponentState, destination: u32, recipient: u256, - amount: u256, - mgs_value: u256 + amount_or_id: u256, + value: u256, + hook_metadata: Option, + hook: Option ) -> u256 { - assert!(mgs_value >= amount, "Native: amount exceeds msg.value"); - let hook_payment = mgs_value - amount; + assert!(value >= amount_or_id, "Native: amount exceeds msg.value"); + let hook_payment = value - amount_or_id; - let mut token_router_comp = get_dep_component_mut!(ref self, TokenRouter); + let mut token_router_comp = get_dep_component_mut!(ref self, TokenRouterComp); token_router_comp - ._transfer_remote( - destination, recipient, amount, hook_payment, Option::None, Option::None + .transfer_remote( + destination, recipient, amount_or_id, hook_payment, Option::None, Option::None ) } + } + #[generate_trait] + pub impl HypNativeInternalImpl< + TContractState, + +HasComponent, + +Drop, + +OwnableComponent::HasComponent, + +RouterComponent::HasComponent, + +GasRouterComponent::HasComponent, + +ERC20Component::HasComponent, + impl Mailboxclient: MailboxclientComponent::HasComponent, + impl TokenRouter: TokenRouterComponent::HasComponent, + > of InternalTrait { + fn _transfer_from_sender(ref self: ComponentState, amount: u256) -> Bytes { + BytesTrait::new_empty() + } - fn receive(ref self: ComponentState, amount: u256) { - self.eth_token.read().transfer(starknet::get_contract_address(), amount); - - self.emit(Donation { sender: starknet::get_caller_address(), amount }); + fn _transfer_to(ref self: ComponentState, recipient: u256, amount: u256) { + let contract_address = starknet::get_contract_address(); // this address or eth address + let erc20_dispatcher = ERC20ABIDispatcher { contract_address }; + let recipient_felt: felt252 = recipient.try_into().expect('u256 to felt failed'); + let recipient: ContractAddress = recipient_felt.try_into().unwrap(); + erc20_dispatcher.transfer(recipient, amount); } } } diff --git a/cairo/src/contracts/token/components/token_router.cairo b/cairo/src/contracts/token/components/token_router.cairo index 07af810..77e019f 100644 --- a/cairo/src/contracts/token/components/token_router.cairo +++ b/cairo/src/contracts/token/components/token_router.cairo @@ -1,4 +1,4 @@ -use alexandria_bytes::Bytes; +use alexandria_bytes::{Bytes, BytesTrait}; use starknet::ContractAddress; #[starknet::interface] @@ -127,11 +127,6 @@ pub mod TokenRouterComponent { impl GasRouter: GasRouterComponent::HasComponent, impl Hooks: TokenRouterHooksTrait > of InternalTrait { - fn initialize(ref self: ComponentState, mailbox: ContractAddress) { - let mut gas_router_comp = get_dep_component_mut!(ref self, GasRouter); - gas_router_comp.initialize(mailbox); - } - fn _transfer_remote( ref self: ComponentState, destination: u32, diff --git a/cairo/src/contracts/token/extensions/fast_hyp_erc20.cairo b/cairo/src/contracts/token/extensions/fast_hyp_erc20.cairo index ac1040e..b5e5f30 100644 --- a/cairo/src/contracts/token/extensions/fast_hyp_erc20.cairo +++ b/cairo/src/contracts/token/extensions/fast_hyp_erc20.cairo @@ -1,33 +1,199 @@ -#[starknet::interface] -pub trait IFastHypERC20Collateral { - fn initialize(ref self: TState); - fn balance_of(self: @TState) -> u256; -} - #[starknet::contract] -pub mod FastHypERC20Collateral { +pub mod FastHypERC20 { + use alexandria_bytes::Bytes; + use hyperlane_starknet::contracts::client::gas_router_component::GasRouterComponent; + use hyperlane_starknet::contracts::client::mailboxclient_component::MailboxclientComponent; + use hyperlane_starknet::contracts::client::router_component::RouterComponent; + use hyperlane_starknet::contracts::token::components::{ + hyp_erc20_component::HypErc20Component, token_message::TokenMessageTrait, + token_router::{TokenRouterComponent, TokenRouterComponent::TokenRouterHooksTrait}, + fast_token_router::{ + FastTokenRouterComponent, FastTokenRouterComponent::FastTokenRouterHooksTrait + } + }; + use hyperlane_starknet::utils::utils::U256TryIntoContractAddress; + use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use openzeppelin::upgrades::interface::IUpgradeable; + use openzeppelin::upgrades::upgradeable::UpgradeableComponent; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: MailboxclientComponent, storage: mailbox, event: MailBoxClientEvent); + component!(path: RouterComponent, storage: router, event: RouterEvent); + component!(path: GasRouterComponent, storage: gas_router, event: GasRouterEvent); + component!(path: TokenRouterComponent, storage: token_router, event: TokenRouterEvent); + component!( + path: FastTokenRouterComponent, storage: fast_token_router, event: FastTokenRouterEvent + ); + component!(path: HypErc20Component, storage: hyp_erc20, event: HypErc20Event); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + // Ownable + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + // MailboxClient + #[abi(embed_v0)] + impl MailboxClientImpl = + MailboxclientComponent::MailboxClientImpl; + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; + // Router + #[abi(embed_v0)] + impl RouterImpl = RouterComponent::RouterImpl; + // GasRouter + #[abi(embed_v0)] + impl GasRouterImpl = GasRouterComponent::GasRouterImpl; + // ERC20 + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + // HypERC20 + #[abi(embed_v0)] + impl HypErc20Impl = HypErc20Component::HypeErc20Impl; + impl HypErc20InternalImpl = HypErc20Component::InternalImpl; + // TokenRouter + #[abi(embed_v0)] + impl TokenRouterImpl = TokenRouterComponent::TokenRouterImpl; + // FastTokenRouter + #[abi(embed_v0)] + impl FastTokenRouterImpl = + FastTokenRouterComponent::FastTokenRouterImpl; + impl FastTokenRouterInternalImpl = FastTokenRouterComponent::InternalImpl; + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + #[storage] struct Storage { - wrapped_token: u256, - mailbox: u256, + #[substorage(v0)] + hyp_erc20: HypErc20Component::Storage, + #[substorage(v0)] + erc20: ERC20Component::Storage, + #[substorage(v0)] + mailbox: MailboxclientComponent::Storage, + #[substorage(v0)] + token_router: TokenRouterComponent::Storage, + #[substorage(v0)] + fast_token_router: FastTokenRouterComponent::Storage, + #[substorage(v0)] + gas_router: GasRouterComponent::Storage, + #[substorage(v0)] + router: RouterComponent::Storage, + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + HypErc20Event: HypErc20Component::Event, + #[flat] + ERC20Event: ERC20Component::Event, + #[flat] + MailBoxClientEvent: MailboxclientComponent::Event, + #[flat] + GasRouterEvent: GasRouterComponent::Event, + #[flat] + RouterEvent: RouterComponent::Event, + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + TokenRouterEvent: TokenRouterComponent::Event, + #[flat] + FastTokenRouterEvent: FastTokenRouterComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event } - fn constructor() {} + #[constructor] + fn constructor( + ref self: ContractState, + decimals: u8, + mailbox: ContractAddress, + total_supply: u256, + name: ByteArray, + symbol: ByteArray, + hook: ContractAddress, + interchain_security_module: ContractAddress, + owner: ContractAddress + ) { + self.ownable.initializer(owner); + self.hyp_erc20.initialize(decimals); + self + .mailbox + .initialize(mailbox, Option::Some(hook), Option::Some(interchain_security_module)); + self.erc20.initializer(name, symbol); + self.erc20.mint(starknet::get_caller_address(), total_supply); + } - impl FastHypERC20CollateralImpl of super::IFastHypERC20Collateral { - fn initialize(ref self: ContractState) {} + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: core::starknet::ClassHash) { + self.ownable.assert_only_owner(); + self.upgradeable.upgrade(new_class_hash); + } + } - fn balance_of(self: @ContractState) -> u256 { - 0 + pub impl TokenRouterHooksImpl of TokenRouterHooksTrait { + fn transfer_from_sender_hook( + ref self: TokenRouterComponent::ComponentState, amount_or_id: u256 + ) -> Bytes { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + contract_state.hyp_erc20._transfer_from_sender(amount_or_id) + } + // need to get aroun with extra parameter origin custom handle calls this + // should this override this interface or be seperate function + fn transfer_to_hook( + ref self: TokenRouterComponent::ComponentState, + recipient: u256, + amount_or_id: u256, + metadata: Bytes, + //origin: u32 + ) { + let origin = 0; //Dummy origin + let contract_state = TokenRouterComponent::HasComponent::get_contract(@self); + let token_recipient = contract_state + .fast_token_router + ._get_token_recipient(recipient, amount_or_id, origin, metadata); + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + contract_state.fast_token_router.fast_transfer_to_hook(token_recipient, amount_or_id); } } + pub impl FastTokenRouterHooksImpl of FastTokenRouterHooksTrait { + fn fast_transfer_to_hook( + ref self: FastTokenRouterComponent::ComponentState, + recipient: u256, + amount: u256 + ) { + let mut contract_state = FastTokenRouterComponent::HasComponent::get_contract_mut( + ref self + ); + contract_state + .erc20 + .mint(recipient.try_into().expect('u256 to ContractAddress failed'), amount); + } + fn fast_receive_from_hook( + ref self: FastTokenRouterComponent::ComponentState, + sender: ContractAddress, + amount: u256 + ) { + let mut contract_state = FastTokenRouterComponent::HasComponent::get_contract_mut( + ref self + ); + contract_state.erc20.burn(sender, amount); + } + } + // TODO: turn thtis into implementation of messagereceivertrait #[generate_trait] impl InternalImpl of InternalTrait { - fn handle(ref self: ContractState, origin: u32, sender: u256, message: u256) {} - - fn fast_transfer_to(ref self: ContractState, recipient: u256, amount: u256) {} - - fn fast_receive_from(ref self: ContractState, sender: u256, amount: u256) {} + fn _handle(ref self: ContractState, origin: u32, message: Bytes) { + self.fast_token_router._handle(origin, message); + } } } diff --git a/cairo/src/contracts/token/extensions/fast_hyp_erc20_collateral.cairo b/cairo/src/contracts/token/extensions/fast_hyp_erc20_collateral.cairo index 38ec83a..3997dde 100644 --- a/cairo/src/contracts/token/extensions/fast_hyp_erc20_collateral.cairo +++ b/cairo/src/contracts/token/extensions/fast_hyp_erc20_collateral.cairo @@ -1,32 +1,205 @@ #[starknet::interface] pub trait IFastHypERC20 { - fn initialize(ref self: TState); - fn balance_of(self: @TState, account: u256) -> u256; + fn balance_of(self: @TState, account: starknet::ContractAddress) -> u256; } -// Since this contract inherits form HyerErc20, to avoid having it as component, -// we need to reimplement all the methods of the IHyerErc20 trait. #[starknet::contract] -pub mod FastHypERC20 { +pub mod FastHypERC20Collateral { + use alexandria_bytes::Bytes; + use hyperlane_starknet::contracts::client::gas_router_component::GasRouterComponent; + use hyperlane_starknet::contracts::client::mailboxclient_component::MailboxclientComponent; + use hyperlane_starknet::contracts::client::router_component::RouterComponent; + use hyperlane_starknet::contracts::token::components::{ + hyp_erc20_collateral_component::HypErc20CollateralComponent, + token_message::TokenMessageTrait, + token_router::{TokenRouterComponent, TokenRouterComponent::TokenRouterHooksTrait}, + fast_token_router::{ + FastTokenRouterComponent, FastTokenRouterComponent::FastTokenRouterHooksTrait + } + }; + use hyperlane_starknet::utils::utils::U256TryIntoContractAddress; + use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::token::erc20::interface::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; + use openzeppelin::upgrades::interface::IUpgradeable; + use openzeppelin::upgrades::upgradeable::UpgradeableComponent; + use starknet::ContractAddress; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: MailboxclientComponent, storage: mailbox, event: MailBoxClientEvent); + component!(path: RouterComponent, storage: router, event: RouterEvent); + component!(path: GasRouterComponent, storage: gas_router, event: GasRouterEvent); + component!(path: TokenRouterComponent, storage: token_router, event: TokenRouterEvent); + component!( + path: FastTokenRouterComponent, storage: fast_token_router, event: FastTokenRouterEvent + ); + component!( + path: HypErc20CollateralComponent, storage: collateral, event: HypErc20CollateralEvent + ); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + // Ownable + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + // MailboxClient + #[abi(embed_v0)] + impl MailboxClientImpl = + MailboxclientComponent::MailboxClientImpl; + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; + // Router + #[abi(embed_v0)] + impl RouterImpl = RouterComponent::RouterImpl; + // GasRouter + #[abi(embed_v0)] + impl GasRouterImpl = GasRouterComponent::GasRouterImpl; + // HypERC20Collateral + #[abi(embed_v0)] + impl HypErc20CollateralImpl = + HypErc20CollateralComponent::HypErc20CollateralImpl; + impl HypErc20CollateralInternalImpl = HypErc20CollateralComponent::InternalImpl; + // TokenRouter + impl TokenRouterInternalImpl = TokenRouterComponent::TokenRouterInternalImpl; + // FastTokenRouter + #[abi(embed_v0)] + impl FastTokenRouterImpl = + FastTokenRouterComponent::FastTokenRouterImpl; + impl FastTokenRouterInternalImpl = FastTokenRouterComponent::InternalImpl; + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + #[storage] - struct Storage {} + struct Storage { + #[substorage(v0)] + collateral: HypErc20CollateralComponent::Storage, + #[substorage(v0)] + mailbox: MailboxclientComponent::Storage, + #[substorage(v0)] + token_router: TokenRouterComponent::Storage, + #[substorage(v0)] + fast_token_router: FastTokenRouterComponent::Storage, + #[substorage(v0)] + gas_router: GasRouterComponent::Storage, + #[substorage(v0)] + router: RouterComponent::Storage, + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage + } - fn constructor() {} + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + HypErc20CollateralEvent: HypErc20CollateralComponent::Event, + #[flat] + MailBoxClientEvent: MailboxclientComponent::Event, + #[flat] + GasRouterEvent: GasRouterComponent::Event, + #[flat] + RouterEvent: RouterComponent::Event, + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + TokenRouterEvent: TokenRouterComponent::Event, + #[flat] + FastTokenRouterEvent: FastTokenRouterComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + mailbox: ContractAddress, + wrapped_token: ContractAddress, + hook: ContractAddress, + interchain_security_module: ContractAddress, + owner: ContractAddress + ) { + self.ownable.initializer(owner); + self + .mailbox + .initialize(mailbox, Option::Some(hook), Option::Some(interchain_security_module)); + self.collateral.initialize(wrapped_token); + } impl FastHypERC20Impl of super::IFastHypERC20 { - fn initialize(ref self: ContractState) {} + fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { + self.collateral.balance_of(account) + } + } - fn balance_of(self: @ContractState, account: u256) -> u256 { - 0 + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: core::starknet::ClassHash) { + self.ownable.assert_only_owner(); + self.upgradeable.upgrade(new_class_hash); } } - #[generate_trait] - impl InternalImpl of InternalTrait { - fn handle(ref self: ContractState, origin: u32, sender: u256, message: u256) {} + pub impl TokenRouterHooksImpl of TokenRouterHooksTrait { + fn transfer_from_sender_hook( + ref self: TokenRouterComponent::ComponentState, amount_or_id: u256 + ) -> Bytes { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + contract_state.collateral._transfer_from_sender(amount_or_id) + } - fn fast_transfer_to(ref self: ContractState, recipient: u256, amount: u256) {} + // should this override this interface or be seperate function. has extra origin parameter + fn transfer_to_hook( + ref self: TokenRouterComponent::ComponentState, + recipient: u256, + amount_or_id: u256, + metadata: Bytes, + //origin: u32 + ) { + let origin = 0; //Dummy origin + let contract_state = TokenRouterComponent::HasComponent::get_contract(@self); + let token_recipient = contract_state + .fast_token_router + ._get_token_recipient(recipient, amount_or_id, origin, metadata); + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + contract_state.fast_token_router.fast_transfer_to_hook(token_recipient, amount_or_id); + } + } - fn fast_receive_from(ref self: ContractState, sender: u256, amount: u256) {} + pub impl FastTokenRouterHooksImpl of FastTokenRouterHooksTrait { + fn fast_transfer_to_hook( + ref self: FastTokenRouterComponent::ComponentState, + recipient: u256, + amount: u256 + ) { + let mut contract_state = FastTokenRouterComponent::HasComponent::get_contract_mut( + ref self + ); + contract_state + .collateral + .wrapped_token + .read() + .transfer(recipient.try_into().expect('u256 to ContractAddress failed'), amount); + } + fn fast_receive_from_hook( + ref self: FastTokenRouterComponent::ComponentState, + sender: ContractAddress, + amount: u256 + ) { + let mut contract_state = FastTokenRouterComponent::HasComponent::get_contract_mut( + ref self + ); + contract_state + .collateral + .wrapped_token + .read() + .transfer_from(sender, starknet::get_contract_address(), amount); + } + } + // TODO: This should override the _handle at Router + #[generate_trait] + impl InternalImpl of InternalTrait { + fn _handle(ref self: ContractState, origin: u32, message: Bytes) { + self.fast_token_router._handle(origin, message); + } } } diff --git a/cairo/src/contracts/token/extensions/hyp_erc721_URI_collateral.cairo b/cairo/src/contracts/token/extensions/hyp_erc721_URI_collateral.cairo index ba7f93f..a938cf5 100644 --- a/cairo/src/contracts/token/extensions/hyp_erc721_URI_collateral.cairo +++ b/cairo/src/contracts/token/extensions/hyp_erc721_URI_collateral.cairo @@ -54,6 +54,11 @@ pub mod HypERC721URICollateral { #[abi(embed_v0)] impl MailboxClientImpl = MailboxclientComponent::MailboxClientImpl; + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; + // Ownable + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; #[storage] @@ -92,8 +97,14 @@ pub mod HypERC721URICollateral { } #[constructor] - fn constructor(ref self: ContractState, erc721: ContractAddress, mailbox: ContractAddress) { - self.token_router.initialize(mailbox); + fn constructor( + ref self: ContractState, + erc721: ContractAddress, + mailbox: ContractAddress, + owner: ContractAddress + ) { + self.ownable.initializer(owner); + self.mailboxclient.initialize(mailbox, Option::None, Option::None); self .hyp_erc721_collateral diff --git a/cairo/src/contracts/token/extensions/hyp_fiat_token.cairo b/cairo/src/contracts/token/extensions/hyp_fiat_token.cairo index 86ca0b2..6a3a306 100644 --- a/cairo/src/contracts/token/extensions/hyp_fiat_token.cairo +++ b/cairo/src/contracts/token/extensions/hyp_fiat_token.cairo @@ -1,33 +1,151 @@ -#[starknet::interface] -pub trait IHypFiatToken { - fn initialize(ref self: TState); - fn balance_of(self: @TState) -> u256; -} - #[starknet::contract] pub mod HypFiatToken { + use alexandria_bytes::{Bytes, BytesTrait}; + use hyperlane_starknet::contracts::client::gas_router_component::GasRouterComponent; + use hyperlane_starknet::contracts::client::mailboxclient_component::MailboxclientComponent; + use hyperlane_starknet::contracts::client::router_component::RouterComponent; + use hyperlane_starknet::contracts::token::components::{ + hyp_erc20_collateral_component::HypErc20CollateralComponent, + token_message::TokenMessageTrait, + token_router::{TokenRouterComponent, TokenRouterComponent::TokenRouterHooksTrait}, + }; + use hyperlane_starknet::contracts::token::interfaces::ifiat_token::{ + IFiatTokenDispatcher, IFiatTokenDispatcherTrait + }; + use hyperlane_starknet::contracts::token::interfaces::imessage_recipient::IMessageRecipient; + use hyperlane_starknet::utils::utils::U256TryIntoContractAddress; + use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::token::erc20::interface::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; + use openzeppelin::upgrades::interface::IUpgradeable; + use openzeppelin::upgrades::upgradeable::UpgradeableComponent; + use starknet::ContractAddress; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: MailboxclientComponent, storage: mailbox, event: MailBoxClientEvent); + component!(path: RouterComponent, storage: router, event: RouterEvent); + component!(path: GasRouterComponent, storage: gas_router, event: GasRouterEvent); + component!(path: TokenRouterComponent, storage: token_router, event: TokenRouterEvent); + component!( + path: HypErc20CollateralComponent, storage: collateral, event: HypErc20CollateralEvent + ); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + + // Ownable + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + // MailboxClient + #[abi(embed_v0)] + impl MailboxClientImpl = + MailboxclientComponent::MailboxClientImpl; + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; + + // Router + #[abi(embed_v0)] + impl RouterImpl = RouterComponent::RouterImpl; + impl RouterInternalImpl = RouterComponent::RouterComponentInternalImpl; + // GasRouter + #[abi(embed_v0)] + impl GasRouterImpl = GasRouterComponent::GasRouterImpl; + impl GasRouterInternalImpl = GasRouterComponent::GasRouterInternalImpl; + // HypERC20Collateral + #[abi(embed_v0)] + impl HypErc20CollateralImpl = + HypErc20CollateralComponent::HypErc20CollateralImpl; + impl HypErc20CollateralInternalImpl = HypErc20CollateralComponent::InternalImpl; + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + #[storage] struct Storage { - fiat_token: u256, - mailbox: u256, + #[substorage(v0)] + collateral: HypErc20CollateralComponent::Storage, + #[substorage(v0)] + mailbox: MailboxclientComponent::Storage, + #[substorage(v0)] + token_router: TokenRouterComponent::Storage, + #[substorage(v0)] + gas_router: GasRouterComponent::Storage, + #[substorage(v0)] + router: RouterComponent::Storage, + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage } - fn constructor() {} + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + HypErc20CollateralEvent: HypErc20CollateralComponent::Event, + #[flat] + MailBoxClientEvent: MailboxclientComponent::Event, + #[flat] + GasRouterEvent: GasRouterComponent::Event, + #[flat] + RouterEvent: RouterComponent::Event, + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + TokenRouterEvent: TokenRouterComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event + } - impl HypFiatTokenImpl of super::IHypFiatToken { - fn initialize(ref self: ContractState) {} + #[constructor] + fn constructor( + ref self: ContractState, + mailbox: ContractAddress, + wrapped_token: ContractAddress, + hook: ContractAddress, + interchain_security_module: ContractAddress, + owner: ContractAddress + ) { + self.ownable.initializer(owner); + self + .mailbox + .initialize(mailbox, Option::Some(hook), Option::Some(interchain_security_module)); + self.collateral.initialize(wrapped_token); + } - fn balance_of(self: @ContractState) -> u256 { - 0 + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: starknet::ClassHash) { + self.ownable.assert_only_owner(); + self.upgradeable.upgrade(new_class_hash); } } - #[generate_trait] - impl InternalImpl of InternalTrait { - fn transfer_from_sender(ref self: ContractState, amount: u256) -> u256 { - 0 + impl TokenRouterHooksImpl of TokenRouterHooksTrait { + fn transfer_from_sender_hook( + ref self: TokenRouterComponent::ComponentState, amount_or_id: u256 + ) -> Bytes { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + let metadata = contract_state.collateral._transfer_from_sender(amount_or_id); + let token: ERC20ABIDispatcher = contract_state.collateral.wrapped_token.read(); + IFiatTokenDispatcher { contract_address: token.contract_address }.burn(amount_or_id); + metadata } - fn transfer_to(ref self: ContractState, recipient: u256, amount: u256, metadata: u256) {} + fn transfer_to_hook( + ref self: TokenRouterComponent::ComponentState, + recipient: u256, + amount_or_id: u256, + metadata: Bytes + ) { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + let token: ERC20ABIDispatcher = contract_state.collateral.wrapped_token.read(); + assert!( + IFiatTokenDispatcher { contract_address: token.contract_address } + .mint( + recipient.try_into().expect('u256 to ContractAddress failed'), amount_or_id + ), + "FiatToken mint failed" + ); + } } } + diff --git a/cairo/src/contracts/token/extensions/hyp_native_scaled.cairo b/cairo/src/contracts/token/extensions/hyp_native_scaled.cairo index a50a50d..2a98e99 100644 --- a/cairo/src/contracts/token/extensions/hyp_native_scaled.cairo +++ b/cairo/src/contracts/token/extensions/hyp_native_scaled.cairo @@ -1,27 +1,24 @@ -// #[starknet::interface] -// pub trait IHypNativeScaled { -// fn initialize(ref self: TState); -// fn transfer_remote(self: @TState, destination: u32, recipient: u256, amount: u256) -> u256; -// fn balance_of(self: @TState, account: u256) -> u256; -// } - #[starknet::contract] pub mod HypNativeScaled { - use alexandria_bytes::bytes::{BytesTrait, Bytes}; + use alexandria_bytes::{Bytes, BytesTrait}; use hyperlane_starknet::contracts::client::gas_router_component::GasRouterComponent; use hyperlane_starknet::contracts::client::mailboxclient_component::MailboxclientComponent; use hyperlane_starknet::contracts::client::router_component::RouterComponent; use hyperlane_starknet::contracts::token::components::hyp_native_component::{ - HypNativeComponent, IHypNative + HypNativeComponent }; + use hyperlane_starknet::contracts::token::components::token_message::TokenMessageTrait; use hyperlane_starknet::contracts::token::components::token_router::{ - TokenRouterComponent, TokenRouterComponent::TokenRouterHooksTrait + TokenRouterComponent, ITokenRouter, TokenRouterComponent::TokenRouterHooksTrait }; + use hyperlane_starknet::contracts::token::interfaces::imessage_recipient::IMessageRecipient; use openzeppelin::access::ownable::OwnableComponent; use openzeppelin::token::erc20::{ - interface::{IERC20Dispatcher, IERC20DispatcherTrait}, ERC20Component, ERC20HooksEmptyImpl + interface::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}, ERC20Component, + ERC20HooksEmptyImpl }; - use openzeppelin::token::erc721::{interface::IERC721Dispatcher, ERC721Component}; + use openzeppelin::upgrades::interface::IUpgradeable; + use openzeppelin::upgrades::upgradeable::UpgradeableComponent; use starknet::ContractAddress; component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); @@ -30,36 +27,48 @@ pub mod HypNativeScaled { component!(path: RouterComponent, storage: router, event: RouterEvent); component!(path: GasRouterComponent, storage: gas_router, event: GasRouterEvent); component!(path: HypNativeComponent, storage: hyp_native, event: HypNativeEvent); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); component!(path: ERC20Component, storage: erc20, event: ERC20Event); + // ERC20 + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; - // HypERC721 + // Ownable + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + // HypNative + #[abi(embed_v0)] impl HypNativeImpl = HypNativeComponent::HypNativeImpl; - - // TokenRouter - impl TokenRouterInternalImpl = TokenRouterComponent::TokenRouterInternalImpl; - + #[abi(embed_v0)] + impl HypNativeTokenRouterImpl = + HypNativeComponent::TokenRouterImpl; + impl HypNativeInternalImpl = HypNativeComponent::HypNativeInternalImpl; // GasRouter #[abi(embed_v0)] impl GasRouterImpl = GasRouterComponent::GasRouterImpl; - + impl GasRouterInternalImpl = GasRouterComponent::GasRouterInternalImpl; // Router #[abi(embed_v0)] impl RouterImpl = RouterComponent::RouterImpl; - + impl RouterInternalImpl = RouterComponent::RouterComponentInternalImpl; // MailboxClient #[abi(embed_v0)] impl MailboxClientImpl = MailboxclientComponent::MailboxClientImpl; - - // ERC20 - #[abi(embed_v0)] - impl ERC20Impl = ERC20Component::ERC20Impl; - + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; + // TokenRouter + impl TokenRouterImpl = TokenRouterComponent::TokenRouterInternalImpl; + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; #[storage] struct Storage { scale: u256, #[substorage(v0)] + erc20: ERC20Component::Storage, + #[substorage(v0)] ownable: OwnableComponent::Storage, #[substorage(v0)] token_router: TokenRouterComponent::Storage, @@ -72,9 +81,7 @@ pub mod HypNativeScaled { #[substorage(v0)] hyp_native: HypNativeComponent::Storage, #[substorage(v0)] - erc20: ERC20Component::Storage, - #[substorage(v0)] - erc721: ERC721Component::Storage + upgradeable: UpgradeableComponent::Storage } #[event] @@ -93,47 +100,47 @@ pub mod HypNativeScaled { #[flat] HypNativeEvent: HypNativeComponent::Event, #[flat] + UpgradeableEvent: UpgradeableComponent::Event, + #[flat] ERC20Event: ERC20Component::Event, } #[constructor] - fn constructor(ref self: ContractState, scale: u256, mailbox: ContractAddress) { + fn constructor( + ref self: ContractState, owner: ContractAddress, scale: u256, mailbox: ContractAddress + ) { + self.mailboxclient.initialize(mailbox, Option::None, Option::None); + self.ownable.initializer(owner); self.scale.write(scale); - self.token_router.initialize(mailbox); } #[abi(embed_v0)] - impl HypNativeScaledImpl of IHypNative { - fn initialize( - ref self: ContractState, - hook: ContractAddress, - interchain_security_module: ContractAddress, - owner: ContractAddress - ) { - self.hyp_native.initialize(hook, interchain_security_module, owner); + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: starknet::ClassHash) { + self.ownable.assert_only_owner(); + self.upgradeable.upgrade(new_class_hash); } - + } + #[embeddable_as(TokenRouterImpl)] + impl TokenRouter of ITokenRouter { fn transfer_remote( ref self: ContractState, destination: u32, recipient: u256, - amount: u256, - mgs_value: u256 + amount_or_id: u256, + value: u256, + hook_metadata: Option, + hook: Option ) -> u256 { - let hook_payment = mgs_value - amount; - let scaled_amount = amount / self.scale.read(); + let hook_payment = value - amount_or_id; + let scaled_amount = amount_or_id / self.scale.read(); self .token_router ._transfer_remote( destination, recipient, scaled_amount, hook_payment, Option::None, Option::None ) } - - fn receive(ref self: ContractState, amount: u256) { - self.hyp_native.receive(amount); - } } - impl TokenRouterHooksImpl of TokenRouterHooksTrait { fn transfer_from_sender_hook( ref self: TokenRouterComponent::ComponentState, amount_or_id: u256 @@ -147,11 +154,10 @@ pub mod HypNativeScaled { amount_or_id: u256, metadata: Bytes ) { - let contract_address = starknet::get_contract_address(); - let erc20_dispatcher = IERC20Dispatcher { contract_address }; - let recipient_felt: felt252 = recipient.try_into().expect('u256 to felt failed'); - let recipient: ContractAddress = recipient_felt.try_into().unwrap(); - erc20_dispatcher.transfer(recipient, amount_or_id); + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + + let scaled_amount = amount_or_id * contract_state.scale.read(); + contract_state.hyp_native._transfer_to(recipient, scaled_amount); } } } diff --git a/cairo/src/contracts/token/extensions/hyp_xerc20.cairo b/cairo/src/contracts/token/extensions/hyp_xerc20.cairo index acfa803..12a4137 100644 --- a/cairo/src/contracts/token/extensions/hyp_xerc20.cairo +++ b/cairo/src/contracts/token/extensions/hyp_xerc20.cairo @@ -1,35 +1,156 @@ -#[starknet::interface] -pub trait IHypXERC20 { - fn initialize(ref self: TState); - fn balance_of(self: @TState) -> u256; -} - #[starknet::contract] pub mod HypXERC20 { + use alexandria_bytes::{Bytes, BytesTrait}; + use hyperlane_starknet::contracts::client::gas_router_component::GasRouterComponent; + use hyperlane_starknet::contracts::client::mailboxclient_component::MailboxclientComponent; + use hyperlane_starknet::contracts::client::router_component::RouterComponent; + use hyperlane_starknet::contracts::token::components::hyp_erc20_collateral_component::HypErc20CollateralComponent; + + use hyperlane_starknet::contracts::token::components::token_message::TokenMessageTrait; + use hyperlane_starknet::contracts::token::components::token_router::{ + TokenRouterComponent, TokenRouterComponent::TokenRouterHooksTrait + }; + use hyperlane_starknet::contracts::token::interfaces::imessage_recipient::IMessageRecipient; + + use hyperlane_starknet::contracts::token::interfaces::ixerc20::{ + IXERC20Dispatcher, IXERC20DispatcherTrait + }; + use hyperlane_starknet::utils::utils::U256TryIntoContractAddress; + use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::token::erc20::interface::{ + IERC20, ERC20ABIDispatcher, ERC20ABIDispatcherTrait + }; + use openzeppelin::upgrades::interface::IUpgradeable; + use openzeppelin::upgrades::upgradeable::UpgradeableComponent; + use starknet::ContractAddress; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: MailboxclientComponent, storage: mailbox, event: MailBoxClientEvent); + component!(path: RouterComponent, storage: router, event: RouterEvent); + component!(path: GasRouterComponent, storage: gas_router, event: GasRouterEvent); + component!(path: TokenRouterComponent, storage: token_router, event: TokenRouterEvent); + component!( + path: HypErc20CollateralComponent, + storage: hyp_erc20_collateral, + event: HypErc20CollateralEvent + ); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + + // Ownable + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + // MailboxClient + #[abi(embed_v0)] + impl MailboxClientImpl = + MailboxclientComponent::MailboxClientImpl; + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; + // Router + #[abi(embed_v0)] + impl RouterImpl = RouterComponent::RouterImpl; + impl RouterInternalImpl = RouterComponent::RouterComponentInternalImpl; + // GasRouter + #[abi(embed_v0)] + impl GasRouterImpl = GasRouterComponent::GasRouterImpl; + impl GasRouterInternalImpl = GasRouterComponent::GasRouterInternalImpl; + // HypERC20Collateral + #[abi(embed_v0)] + impl HypErc20CollateralImpl = + HypErc20CollateralComponent::HypErc20CollateralImpl; + impl HypErc20CollateralInternalImpl = HypErc20CollateralComponent::InternalImpl; + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + #[storage] struct Storage { - xerc20: u256, - mailbox: u256, + #[substorage(v0)] + hyp_erc20_collateral: HypErc20CollateralComponent::Storage, + #[substorage(v0)] + mailbox: MailboxclientComponent::Storage, + #[substorage(v0)] + token_router: TokenRouterComponent::Storage, + #[substorage(v0)] + gas_router: GasRouterComponent::Storage, + #[substorage(v0)] + router: RouterComponent::Storage, + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage } - fn constructor() {} + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + HypErc20CollateralEvent: HypErc20CollateralComponent::Event, + #[flat] + MailBoxClientEvent: MailboxclientComponent::Event, + #[flat] + GasRouterEvent: GasRouterComponent::Event, + #[flat] + RouterEvent: RouterComponent::Event, + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + TokenRouterEvent: TokenRouterComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event + } - impl HypXERC20Impl of super::IHypXERC20 { - fn initialize(ref self: ContractState) {} + #[constructor] + fn constructor( + ref self: ContractState, + mailbox: ContractAddress, + wrapped_token: ContractAddress, + owner: ContractAddress, + hook: ContractAddress, + interchain_security_module: ContractAddress + ) { + self.ownable.initializer(owner); + self + .mailbox + .initialize(mailbox, Option::Some(hook), Option::Some(interchain_security_module)); + self.hyp_erc20_collateral.initialize(wrapped_token); + } - fn balance_of(self: @ContractState) -> u256 { - 0 + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: starknet::ClassHash) { + self.ownable.assert_only_owner(); + self.upgradeable.upgrade(new_class_hash); } } - #[generate_trait] - impl InternalImpl of InternalTrait { - fn transfer_from_sender(ref self: ContractState, amount_or_id: u256) -> u256 { - 0 + impl TokenRouterHooksImpl of TokenRouterHooksTrait { + fn transfer_from_sender_hook( + ref self: TokenRouterComponent::ComponentState, amount_or_id: u256 + ) -> Bytes { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + let token: ERC20ABIDispatcher = contract_state + .hyp_erc20_collateral + .wrapped_token + .read(); + IXERC20Dispatcher { contract_address: token.contract_address } + .burn(starknet::get_caller_address(), amount_or_id); + BytesTrait::new_empty() } - fn transfer_to( - ref self: ContractState, recipient: u256, amount_or_id: u256, metadata: u256 - ) {} + fn transfer_to_hook( + ref self: TokenRouterComponent::ComponentState, + recipient: u256, + amount_or_id: u256, + metadata: Bytes + ) { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + let token: ERC20ABIDispatcher = contract_state + .hyp_erc20_collateral + .wrapped_token + .read(); + IXERC20Dispatcher { contract_address: token.contract_address } + .mint(recipient.try_into().unwrap(), amount_or_id); + } } } diff --git a/cairo/src/contracts/token/extensions/hyp_xerc20_lockbox.cairo b/cairo/src/contracts/token/extensions/hyp_xerc20_lockbox.cairo index 480000a..b84f4f7 100644 --- a/cairo/src/contracts/token/extensions/hyp_xerc20_lockbox.cairo +++ b/cairo/src/contracts/token/extensions/hyp_xerc20_lockbox.cairo @@ -1,38 +1,180 @@ #[starknet::interface] pub trait IHypXERC20Lockbox { - fn initialize(ref self: TState, hook: u256, ism: u256, owner: u256); fn approve_lockbox(ref self: TState); - fn balance_of(self: @TState) -> u256; } #[starknet::contract] pub mod HypXERC20Lockbox { + use alexandria_bytes::{Bytes, BytesTrait}; + use core::integer::BoundedInt; + use hyperlane_starknet::contracts::client::gas_router_component::GasRouterComponent; + use hyperlane_starknet::contracts::client::mailboxclient_component::MailboxclientComponent; + use hyperlane_starknet::contracts::client::router_component::RouterComponent; + use hyperlane_starknet::contracts::token::components::{ + hyp_erc20_collateral_component::HypErc20CollateralComponent, + token_message::TokenMessageTrait, + token_router::{TokenRouterComponent, TokenRouterComponent::TokenRouterHooksTrait}, + }; + use hyperlane_starknet::contracts::token::interfaces::imessage_recipient::IMessageRecipient; + use hyperlane_starknet::contracts::token::interfaces::ixerc20::{ + IXERC20Dispatcher, IXERC20DispatcherTrait + }; + use hyperlane_starknet::contracts::token::interfaces::ixerc20_lockbox::{ + IXERC20LockboxDispatcher, IXERC20LockboxDispatcherTrait + }; + use hyperlane_starknet::utils::utils::U256TryIntoContractAddress; + use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::token::erc20::interface::{ + IERC20, ERC20ABIDispatcher, ERC20ABIDispatcherTrait + }; + use openzeppelin::upgrades::interface::IUpgradeable; + use openzeppelin::upgrades::upgradeable::UpgradeableComponent; + use starknet::ContractAddress; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: MailboxclientComponent, storage: mailbox, event: MailBoxClientEvent); + component!(path: RouterComponent, storage: router, event: RouterEvent); + component!(path: GasRouterComponent, storage: gas_router, event: GasRouterEvent); + component!(path: TokenRouterComponent, storage: token_router, event: TokenRouterEvent); + component!( + path: HypErc20CollateralComponent, storage: collateral, event: HypErc20CollateralEvent + ); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + + // Ownable + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + // MailboxClient + #[abi(embed_v0)] + impl MailboxClientImpl = + MailboxclientComponent::MailboxClientImpl; + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; + // Router + #[abi(embed_v0)] + impl RouterImpl = RouterComponent::RouterImpl; + impl RouterInternalImpl = RouterComponent::RouterComponentInternalImpl; + // GasRouter + #[abi(embed_v0)] + impl GasRouterImpl = GasRouterComponent::GasRouterImpl; + impl GasRouterInternalImpl = GasRouterComponent::GasRouterInternalImpl; + // HypERC20Collateral + #[abi(embed_v0)] + impl HypErc20CollateralImpl = + HypErc20CollateralComponent::HypErc20CollateralImpl; + impl HypErc20CollateralInternalImpl = HypErc20CollateralComponent::InternalImpl; + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[storage] struct Storage { - lockbox: u256, - xerc20: u256, - wrapped_token: u256, - mailbox: u256, + #[substorage(v0)] + collateral: HypErc20CollateralComponent::Storage, + #[substorage(v0)] + mailbox: MailboxclientComponent::Storage, + #[substorage(v0)] + token_router: TokenRouterComponent::Storage, + #[substorage(v0)] + gas_router: GasRouterComponent::Storage, + #[substorage(v0)] + router: RouterComponent::Storage, + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage, + lockbox: IXERC20LockboxDispatcher, + xerc20: IXERC20Dispatcher, } - fn constructor() {} + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + HypErc20CollateralEvent: HypErc20CollateralComponent::Event, + #[flat] + MailBoxClientEvent: MailboxclientComponent::Event, + #[flat] + GasRouterEvent: GasRouterComponent::Event, + #[flat] + RouterEvent: RouterComponent::Event, + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + TokenRouterEvent: TokenRouterComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event + } - impl HypXERC20LockboxImpl of super::IHypXERC20Lockbox { - fn initialize(ref self: ContractState, hook: u256, ism: u256, owner: u256) {} + #[constructor] + fn constructor( + ref self: ContractState, + mailbox: ContractAddress, + lockbox: ContractAddress, + owner: ContractAddress, + hook: ContractAddress, + interchain_security_module: ContractAddress + ) { + self.ownable.initializer(owner); + self + .mailbox + .initialize(mailbox, Option::Some(hook), Option::Some(interchain_security_module)); + let lockbox_dispatcher = IXERC20LockboxDispatcher { contract_address: lockbox }; + self.collateral.initialize(lockbox_dispatcher.erc20()); + let xerc20 = lockbox_dispatcher.xerc20(); + self.xerc20.write(IXERC20Dispatcher { contract_address: xerc20 }); + self.approve_lockbox(); + } - fn approve_lockbox(ref self: ContractState) {} + impl HypXERC20LockboxImpl of super::IHypXERC20Lockbox { + fn approve_lockbox(ref self: ContractState) { + let lockbox_address = self.lockbox.read().contract_address; + assert!( + self.collateral.wrapped_token.read().approve(lockbox_address, BoundedInt::max()), + "erc20 lockbox approve failed" + ); + assert!( + ERC20ABIDispatcher { contract_address: self.xerc20.read().contract_address } + .approve(lockbox_address, BoundedInt::max()), + "xerc20 lockbox approve failed" + ); + } + } - fn balance_of(self: @ContractState) -> u256 { - 0 + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: starknet::ClassHash) { + self.ownable.assert_only_owner(); + self.upgradeable.upgrade(new_class_hash); } } - #[generate_trait] - impl InternalImpl of InternalTrait { - fn transfer_from_sender(ref self: ContractState, amount: u256) -> u256 { - 0 + impl TokenRouterHooksImpl of TokenRouterHooksTrait { + fn transfer_from_sender_hook( + ref self: TokenRouterComponent::ComponentState, amount_or_id: u256 + ) -> Bytes { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + contract_state.collateral._transfer_from_sender(amount_or_id); + + contract_state.lockbox.read().deposit(amount_or_id); + + contract_state.xerc20.read().burn(starknet::get_contract_address(), amount_or_id); + BytesTrait::new_empty() } - fn transfer_to(ref self: ContractState, recipient: u256, amount: u256, metadata: u256) {} + fn transfer_to_hook( + ref self: TokenRouterComponent::ComponentState, + recipient: u256, + amount_or_id: u256, + metadata: Bytes + ) { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + + contract_state.xerc20.read().mint(starknet::get_contract_address(), amount_or_id); + contract_state.lockbox.read().withdraw_to(recipient, amount_or_id); + } } } + diff --git a/cairo/src/contracts/token/hyp_erc20.cairo b/cairo/src/contracts/token/hyp_erc20.cairo index 77b9f14..76d9d4a 100644 --- a/cairo/src/contracts/token/hyp_erc20.cairo +++ b/cairo/src/contracts/token/hyp_erc20.cairo @@ -1,33 +1,149 @@ -#[starknet::interface] -pub trait IHypErc20 { - fn initialize(ref self: TState); - fn decimals(self: @TState) -> u8; - fn balance_of(self: @TState) -> u256; -} +use starknet::ContractAddress; #[starknet::contract] pub mod HypErc20 { - #[storage] - struct Storage {} + use alexandria_bytes::{Bytes, BytesTrait}; + use hyperlane_starknet::contracts::client::gas_router_component::GasRouterComponent; + use hyperlane_starknet::contracts::client::mailboxclient_component::MailboxclientComponent; + use hyperlane_starknet::contracts::client::router_component::RouterComponent; + use hyperlane_starknet::contracts::token::components::{ + hyp_erc20_component::HypErc20Component, token_message::TokenMessageTrait, + token_router::{ + TokenRouterComponent, ITokenRouter, TokenRouterComponent::TokenRouterHooksTrait + } + }; + use hyperlane_starknet::contracts::token::interfaces::imessage_recipient::IMessageRecipient; + use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use openzeppelin::upgrades::interface::IUpgradeable; + use openzeppelin::upgrades::upgradeable::UpgradeableComponent; + use starknet::ContractAddress; - fn constructor() {} + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: MailboxclientComponent, storage: mailbox, event: MailBoxClientEvent); + component!(path: RouterComponent, storage: router, event: RouterEvent); + component!(path: GasRouterComponent, storage: gas_router, event: GasRouterEvent); + component!(path: TokenRouterComponent, storage: token_router, event: TokenRouterEvent); + component!(path: HypErc20Component, storage: hyp_erc20, event: HypErc20Event); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); - impl HypErc20Impl of super::IHypErc20 { - fn initialize(ref self: ContractState) {} + // Ownable + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + // MailboxClient + #[abi(embed_v0)] + impl MailboxClientImpl = + MailboxclientComponent::MailboxClientImpl; + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; + // Router + #[abi(embed_v0)] + impl RouterImpl = RouterComponent::RouterImpl; + // GasRouter + #[abi(embed_v0)] + impl GasRouterImpl = GasRouterComponent::GasRouterImpl; + // ERC20 + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + // HypERC20 + #[abi(embed_v0)] + impl HypErc20Impl = HypErc20Component::HypeErc20Impl; + impl HypErc20InternalImpl = HypErc20Component::InternalImpl; + // TokenRouter + #[abi(embed_v0)] + impl TokenRouterImpl = TokenRouterComponent::TokenRouterImpl; + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; - fn decimals(self: @ContractState) -> u8 { - 0 - } + #[storage] + struct Storage { + #[substorage(v0)] + hyp_erc20: HypErc20Component::Storage, + #[substorage(v0)] + erc20: ERC20Component::Storage, + #[substorage(v0)] + mailbox: MailboxclientComponent::Storage, + #[substorage(v0)] + token_router: TokenRouterComponent::Storage, + #[substorage(v0)] + gas_router: GasRouterComponent::Storage, + #[substorage(v0)] + router: RouterComponent::Storage, + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage + } - fn balance_of(self: @ContractState) -> u256 { - 0 + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + HypErc20Event: HypErc20Component::Event, + #[flat] + ERC20Event: ERC20Component::Event, + #[flat] + MailBoxClientEvent: MailboxclientComponent::Event, + #[flat] + GasRouterEvent: GasRouterComponent::Event, + #[flat] + RouterEvent: RouterComponent::Event, + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + TokenRouterEvent: TokenRouterComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + decimals: u8, + mailbox: ContractAddress, + total_supply: u256, + name: ByteArray, + symbol: ByteArray, + hook: ContractAddress, + interchain_security_module: ContractAddress, + owner: ContractAddress + ) { + self.ownable.initializer(owner); + self + .mailbox + .initialize(mailbox, Option::Some(hook), Option::Some(interchain_security_module)); + self.hyp_erc20.initialize(decimals); + self.erc20.initializer(name, symbol); + self.erc20.mint(starknet::get_caller_address(), total_supply); + } + + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: starknet::ClassHash) { + self.ownable.assert_only_owner(); + self.upgradeable.upgrade(new_class_hash); } } - #[generate_trait] - impl InternaImpl of InternalTrait { - fn transfer_from_sender(ref self: ContractState) {} + impl TokenRouterHooksImpl of TokenRouterHooksTrait { + fn transfer_from_sender_hook( + ref self: TokenRouterComponent::ComponentState, amount_or_id: u256 + ) -> Bytes { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + contract_state.hyp_erc20._transfer_from_sender(amount_or_id) + } - fn transfer_to_recipient(ref self: ContractState) {} + fn transfer_to_hook( + ref self: TokenRouterComponent::ComponentState, + recipient: u256, + amount_or_id: u256, + metadata: Bytes + ) { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + contract_state.hyp_erc20._transfer_to(recipient, amount_or_id) + } } } diff --git a/cairo/src/contracts/token/hyp_erc20_collateral.cairo b/cairo/src/contracts/token/hyp_erc20_collateral.cairo index 66dda91..7ef087b 100644 --- a/cairo/src/contracts/token/hyp_erc20_collateral.cairo +++ b/cairo/src/contracts/token/hyp_erc20_collateral.cairo @@ -1,28 +1,129 @@ -#[starknet::interface] -pub trait IHypErc20Collateral { - fn initialize(ref self: TState); - fn balance_of(self: @TState) -> u256; -} - #[starknet::contract] -pub mod HypErc20 { +pub mod HypErc20Collateral { + use alexandria_bytes::{Bytes, BytesTrait}; + use hyperlane_starknet::contracts::client::gas_router_component::GasRouterComponent; + use hyperlane_starknet::contracts::client::mailboxclient_component::MailboxclientComponent; + use hyperlane_starknet::contracts::client::router_component::RouterComponent; + use hyperlane_starknet::contracts::token::components::{ + token_router::{TokenRouterComponent, TokenRouterComponent::TokenRouterHooksTrait}, + hyp_erc20_collateral_component::HypErc20CollateralComponent + }; + use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::upgrades::interface::IUpgradeable; + use openzeppelin::upgrades::upgradeable::UpgradeableComponent; + use starknet::ContractAddress; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: MailboxclientComponent, storage: mailbox, event: MailBoxClientEvent); + component!(path: RouterComponent, storage: router, event: RouterEvent); + component!(path: GasRouterComponent, storage: gas_router, event: GasRouterEvent); + component!(path: TokenRouterComponent, storage: token_router, event: TokenRouterEvent); + component!( + path: HypErc20CollateralComponent, storage: collateral, event: HypErc20CollateralEvent + ); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + + // Ownable + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + // MailboxClient + #[abi(embed_v0)] + impl MailboxClientImpl = + MailboxclientComponent::MailboxClientImpl; + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; + + // Router + #[abi(embed_v0)] + impl RouterImpl = RouterComponent::RouterImpl; + // GasRouter + #[abi(embed_v0)] + impl GasRouterImpl = GasRouterComponent::GasRouterImpl; + // HypERC20Collateral + #[abi(embed_v0)] + impl HypErc20CollateralImpl = + HypErc20CollateralComponent::HypErc20CollateralImpl; + impl HypErc20CollateralInternalImpl = HypErc20CollateralComponent::InternalImpl; + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + #[storage] - struct Storage {} + struct Storage { + #[substorage(v0)] + collateral: HypErc20CollateralComponent::Storage, + #[substorage(v0)] + mailbox: MailboxclientComponent::Storage, + #[substorage(v0)] + token_router: TokenRouterComponent::Storage, + #[substorage(v0)] + gas_router: GasRouterComponent::Storage, + #[substorage(v0)] + router: RouterComponent::Storage, + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage + } - fn constructor() {} + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + HypErc20CollateralEvent: HypErc20CollateralComponent::Event, + #[flat] + MailBoxClientEvent: MailboxclientComponent::Event, + #[flat] + GasRouterEvent: GasRouterComponent::Event, + #[flat] + RouterEvent: RouterComponent::Event, + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + TokenRouterEvent: TokenRouterComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event + } - impl HypErc20CollateralImpl of super::IHypErc20Collateral { - fn initialize(ref self: ContractState) {} + #[constructor] + fn constructor( + ref self: ContractState, + mailbox: ContractAddress, + wrapped_token: ContractAddress, + owner: ContractAddress, + hook: Option, + interchain_security_module: Option + ) { + self.ownable.initializer(owner); + self.mailbox.initialize(mailbox, hook, interchain_security_module); + self.collateral.initialize(wrapped_token); + } - fn balance_of(self: @ContractState) -> u256 { - 0 + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: starknet::ClassHash) { + self.ownable.assert_only_owner(); + self.upgradeable.upgrade(new_class_hash); } } - #[generate_trait] - impl InternaImpl of InternalTrait { - fn transfer_from_sender(ref self: ContractState) {} + impl TokenRouterHooksImpl of TokenRouterHooksTrait { + fn transfer_from_sender_hook( + ref self: TokenRouterComponent::ComponentState, amount_or_id: u256 + ) -> Bytes { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + contract_state.collateral._transfer_from_sender(amount_or_id) + } - fn transfer_to(ref self: ContractState) {} + fn transfer_to_hook( + ref self: TokenRouterComponent::ComponentState, + recipient: u256, + amount_or_id: u256, + metadata: Bytes + ) { + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + contract_state.collateral._transfer_to(recipient, amount_or_id) + } } } diff --git a/cairo/src/contracts/token/hyp_erc721.cairo b/cairo/src/contracts/token/hyp_erc721.cairo index 63eb3df..44efb4d 100644 --- a/cairo/src/contracts/token/hyp_erc721.cairo +++ b/cairo/src/contracts/token/hyp_erc721.cairo @@ -25,7 +25,7 @@ pub mod HypErc721 { use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; use openzeppelin::upgrades::{interface::IUpgradeable, upgradeable::UpgradeableComponent}; use starknet::ContractAddress; - + // also needs {https://github.com/OpenZeppelin/cairo-contracts/blob/main/packages/token/src/erc721/extensions/erc721_enumerable/erc721_enumerable.cairo} component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); component!(path: HypErc721Component, storage: hyp_erc721, event: HypErc721Event); @@ -65,6 +65,8 @@ pub mod HypErc721 { #[abi(embed_v0)] impl MailboxClientImpl = MailboxclientComponent::MailboxClientImpl; + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; #[storage] struct Storage { @@ -111,8 +113,15 @@ pub mod HypErc721 { GasRouterEvent: GasRouterComponent::Event } - fn constructor(ref self: ContractState, mailbox: ContractAddress) { - self.token_router.initialize(mailbox); + fn constructor( + ref self: ContractState, + mailbox: ContractAddress, + name: ByteArray, + symbol: ByteArray, + mint_amount: u256 + ) { + self.mailboxclient.initialize(mailbox, Option::None, Option::None); + self.hyp_erc721.initialize(mint_amount, name, symbol); } #[abi(embed_v0)] diff --git a/cairo/src/contracts/token/hyp_erc721_collateral.cairo b/cairo/src/contracts/token/hyp_erc721_collateral.cairo index eaa558d..8eb3fde 100644 --- a/cairo/src/contracts/token/hyp_erc721_collateral.cairo +++ b/cairo/src/contracts/token/hyp_erc721_collateral.cairo @@ -5,7 +5,7 @@ pub mod HypErc721Collateral { use hyperlane_starknet::contracts::client::mailboxclient_component::MailboxclientComponent; use hyperlane_starknet::contracts::client::router_component::RouterComponent; use hyperlane_starknet::contracts::token::components::hyp_erc721_collateral_component::{ - HypErc721CollateralComponent, IHypErc721Collateral + HypErc721CollateralComponent }; use hyperlane_starknet::contracts::token::components::token_router::{ TokenRouterComponent, TokenRouterComponent::TokenRouterHooksTrait @@ -47,6 +47,8 @@ pub mod HypErc721Collateral { #[abi(embed_v0)] impl MailboxClientImpl = MailboxclientComponent::MailboxClientImpl; + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; #[storage] struct Storage { @@ -84,7 +86,7 @@ pub mod HypErc721Collateral { #[constructor] fn constructor(ref self: ContractState, erc721: ContractAddress, mailbox: ContractAddress) { - self.token_router.initialize(mailbox); + self.mailboxclient.initialize(mailbox, Option::None, Option::None); self.wrapped_token.write(ERC721ABIDispatcher { contract_address: erc721 }); } diff --git a/cairo/src/contracts/token/hyp_native.cairo b/cairo/src/contracts/token/hyp_native.cairo index 0aaf4d8..2fdaef7 100644 --- a/cairo/src/contracts/token/hyp_native.cairo +++ b/cairo/src/contracts/token/hyp_native.cairo @@ -25,9 +25,13 @@ pub mod HypNative { component!(path: HypNativeComponent, storage: hyp_native, event: HypNativeEvent); component!(path: ERC20Component, storage: erc20, event: ERC20Event); - // HypERC721 + #[abi(embed_v0)] impl HypNativeImpl = HypNativeComponent::HypNativeImpl; + #[abi(embed_v0)] + impl HypNativeTokenRouterImpl = + HypNativeComponent::TokenRouterImpl; + impl HypNativeInternalImpl = HypNativeComponent::HypNativeInternalImpl; // TokenRouter impl TokenRouterInternalImpl = TokenRouterComponent::TokenRouterInternalImpl; @@ -44,7 +48,8 @@ pub mod HypNative { #[abi(embed_v0)] impl MailboxClientImpl = MailboxclientComponent::MailboxClientImpl; - + impl MailboxClientInternalImpl = + MailboxclientComponent::MailboxClientInternalImpl; // ERC20 #[abi(embed_v0)] impl ERC20Impl = ERC20Component::ERC20Impl; @@ -91,14 +96,15 @@ pub mod HypNative { #[constructor] fn constructor(ref self: ContractState, mailbox: ContractAddress) { - self.token_router.initialize(mailbox); + self.mailboxclient.initialize(mailbox, Option::None, Option::None); } impl TokenRouterHooksImpl of TokenRouterHooksTrait { fn transfer_from_sender_hook( ref self: TokenRouterComponent::ComponentState, amount_or_id: u256 ) -> Bytes { - BytesTrait::new_empty() + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + contract_state.hyp_native._transfer_from_sender(amount_or_id) } fn transfer_to_hook( @@ -107,11 +113,8 @@ pub mod HypNative { amount_or_id: u256, metadata: Bytes ) { - let contract_address = starknet::get_contract_address(); - let erc20_dispatcher = IERC20Dispatcher { contract_address }; - let recipient_felt: felt252 = recipient.try_into().expect('u256 to felt failed'); - let recipient: ContractAddress = recipient_felt.try_into().unwrap(); - erc20_dispatcher.transfer(recipient, amount_or_id); + let mut contract_state = TokenRouterComponent::HasComponent::get_contract_mut(ref self); + contract_state.hyp_native._transfer_to(recipient, amount_or_id); } } } diff --git a/cairo/src/contracts/token/interfaces/ifiat_token.cairo b/cairo/src/contracts/token/interfaces/ifiat_token.cairo index a6d0117..48693f6 100644 --- a/cairo/src/contracts/token/interfaces/ifiat_token.cairo +++ b/cairo/src/contracts/token/interfaces/ifiat_token.cairo @@ -1,5 +1,5 @@ #[starknet::interface] pub trait IFiatToken { fn burn(ref self: TState, amount: u256); - fn mint(ref self: TState, amount: u256); + fn mint(ref self: TState, to: starknet::ContractAddress, amount: u256) -> bool; } diff --git a/cairo/src/contracts/token/interfaces/ixerc20.cairo b/cairo/src/contracts/token/interfaces/ixerc20.cairo index cbd0111..9067b3a 100644 --- a/cairo/src/contracts/token/interfaces/ixerc20.cairo +++ b/cairo/src/contracts/token/interfaces/ixerc20.cairo @@ -1,7 +1,7 @@ #[starknet::interface] pub trait IXERC20 { - fn mint(ref self: TState, user: u256, amount: u256); - fn burn(ref self: TState, user: u256, amount: u256); + fn mint(ref self: TState, user: starknet::ContractAddress, amount: u256); + fn burn(ref self: TState, user: starknet::ContractAddress, amount: u256); fn set_limits(ref self: TState, bridge: u256, minting_limit: u256, burning_limit: u256); fn owner(self: @TState) -> u256; fn burning_current_limit_of(self: @TState, bridge: u256) -> u256; diff --git a/cairo/src/contracts/token/interfaces/ixerc20_lockbox.cairo b/cairo/src/contracts/token/interfaces/ixerc20_lockbox.cairo index 7b88e1e..1ff9d2a 100644 --- a/cairo/src/contracts/token/interfaces/ixerc20_lockbox.cairo +++ b/cairo/src/contracts/token/interfaces/ixerc20_lockbox.cairo @@ -1,7 +1,7 @@ #[starknet::interface] pub trait IXERC20Lockbox { - fn xerc20(self: @TState) -> u256; - fn erc20(self: @TState) -> u256; + fn xerc20(self: @TState) -> starknet::ContractAddress; + fn erc20(self: @TState) -> starknet::ContractAddress; fn deposit(ref self: TState, amount: u256); fn deposit_to(ref self: TState, user: u256, amount: u256); fn deposit_native_to(ref self: TState, user: u256); diff --git a/cairo/src/interfaces.cairo b/cairo/src/interfaces.cairo index dd9f109..9d4b83f 100644 --- a/cairo/src/interfaces.cairo +++ b/cairo/src/interfaces.cairo @@ -145,13 +145,6 @@ pub trait IMailboxClient { fn set_interchain_security_module(ref self: TContractState, _module: ContractAddress); - fn _MailboxClient_initialize( - ref self: TContractState, - _hook: ContractAddress, - _interchain_security_module: ContractAddress, - _owner: ContractAddress - ); - fn get_hook(self: @TContractState) -> ContractAddress; fn get_local_domain(self: @TContractState) -> u32; diff --git a/cairo/src/lib.cairo b/cairo/src/lib.cairo index e35f0d1..867768d 100644 --- a/cairo/src/lib.cairo +++ b/cairo/src/lib.cairo @@ -4,6 +4,7 @@ mod contracts { pub mod libs { pub mod aggregation_ism_metadata; pub mod checkpoint_lib; + pub mod enumerable_map; pub mod message; pub mod multisig { pub mod merkleroot_ism_metadata; @@ -24,6 +25,7 @@ mod contracts { pub mod router_component; } pub mod mocks { + pub mod enumerable_map_holder; pub mod fee_hook; pub mod fee_token; pub mod hook; @@ -60,6 +62,8 @@ mod contracts { } pub mod components { pub mod fast_token_router; + pub mod hyp_erc20_collateral_component; + pub mod hyp_erc20_component; pub mod hyp_erc721_collateral_component; pub mod hyp_erc721_component; pub mod hyp_native_component; @@ -121,4 +125,7 @@ mod tests { pub mod hyp_xerc20_test; } } + pub mod libs { + pub mod test_enumerable_map; + } } diff --git a/cairo/src/tests/libs/test_enumerable_map.cairo b/cairo/src/tests/libs/test_enumerable_map.cairo new file mode 100644 index 0000000..aa512e3 --- /dev/null +++ b/cairo/src/tests/libs/test_enumerable_map.cairo @@ -0,0 +1,112 @@ +use hyperlane_starknet::contracts::libs::enumerable_map::{EnumerableMapTrait}; +use hyperlane_starknet::contracts::mocks::enumerable_map_holder::{ + IEnumerableMapHolderDispatcher, IEnumerableMapHolderDispatcherTrait +}; +use snforge_std::{declare, ContractClassTrait}; +use starknet::{ClassHash, ContractAddress}; + + +fn setup() -> IEnumerableMapHolderDispatcher { + let contract = declare("EnumerableMapHolder").unwrap(); + let (contract_address, _) = contract.deploy(@array![]).unwrap(); + IEnumerableMapHolderDispatcher { contract_address } +} + +#[test] +fn test_initialize_empty_map() { + let contract = setup(); + assert_eq!(contract.do_get_len(), 0, "EnumerableMap is not empty"); +} + +#[test] +fn test_fuzz_set(key: u32, val: u256) { + let mut contract = setup(); + assert_eq!(contract.do_get_len(), 0, "EnumerableMap is not empty"); + contract.do_set_key(key, val); + // check len increased + assert_eq!(contract.do_get_len(), 1, "EnumerableMap is empty"); + // check value stored in 'values' map correctly and test get method + assert_eq!(contract.do_get_value(key), val, "Value not stored properly"); + // check value key correctly stored in keys array and test at method + let (_key, _value) = contract.do_at(0); + assert_eq!(key, key, "Key mismatch"); + assert_eq!(_value, val, "Value mismatch"); + // check if its been correctly setted in 'positions' mapping + assert!(contract.do_contains(key), "Key not registered to positions mapping"); +} + +#[test] +fn test_fuzz_contains(key: u32, val: u256, should_contain: u8) { + let mut contract = setup(); + let should_contain: bool = should_contain % 2 == 1; + if should_contain { + contract.do_set_key(key, val); + } + assert_eq!(contract.do_contains(key), should_contain); +} + +#[test] +fn test_fuzz_should_remove(key: u32, val: u256) { + let mut contract = setup(); + contract.do_set_key(key, val); + // check len increased + assert_eq!(contract.do_get_len(), 1, "EnumerableMap is empty"); + // check value stored in 'values' map correctly + assert_eq!(contract.do_get_value(key), val, "Value not stored properly"); + // check value key correctly stored in keys array + let (_key, _value) = contract.do_at(0); + assert_eq!(key, key, "Key mismatch"); + assert_eq!(_value, val, "Value mismatch"); + // check if its been correctly setted in 'positions' mapping + assert!(contract.do_contains(key), "Key not registered to positions mapping"); + assert!(contract.do_remove(key), "Failed to remove element"); + // check len decreased + assert_eq!(contract.do_get_len(), 0, "EnumerableMap len not decreased"); + // check if its been correctly removed in 'positions' mapping + assert!(!contract.do_contains(key), "Key not removed from positions mapping"); +} + +#[test] +fn test_fuzz_get_keys( + mut key1: u32, mut key2: u32, mut key3: u32, val1: u256, val2: u256, val3: u256 +) { + if key1 == key2 { + key2 += 1; + } + if key1 == key3 { + key3 += 1; + } + if key2 == key3 { + key3 += 1; + } + let keys_to_add: Span = array![key1, key2, key3].span(); + let values_to_add: Span = array![val1, val2, val3].span(); + let mut contract = setup(); + let mut i = 0; + let len = keys_to_add.len(); + while i < len { + contract.do_set_key(*keys_to_add.at(i), *values_to_add.at(i)); + i += 1; + }; + assert_eq!(contract.do_get_len(), len, "Length mismatch"); + let keys = contract.do_get_keys(); + let mut i = 0; + while i < len { + assert_eq!(*keys.at(i), *keys_to_add.at(i), "key mismatch"); + i += 1; + }; + + // remove the middle elem and get again + contract.do_remove(key2); + assert!(!contract.do_contains(key2), "Key2 not removed from positions mapping"); + let expected_keys: Span = array![key1, key3].span(); + assert_eq!(contract.do_get_len(), expected_keys.len(), "Length mismatch"); + + let keys = contract.do_get_keys(); + let len = keys.len(); + let mut i = 0; + while i < len { + assert_eq!(*keys.at(i), *expected_keys.at(i), "key mismatch"); + i += 1; + }; +}