diff --git a/apps/blockchain/starknet/src/bridge.cairo b/apps/blockchain/starknet/src/bridge.cairo index 2478e2e1..6ece571d 100644 --- a/apps/blockchain/starknet/src/bridge.cairo +++ b/apps/blockchain/starknet/src/bridge.cairo @@ -15,13 +15,12 @@ mod bridge { use starknet::eth_address::EthAddressZeroable; use openzeppelin::access::ownable::OwnableComponent; - use openzeppelin::access::ownable::interface::{ - IOwnableDispatcher, IOwnableDispatcherTrait - }; + use openzeppelin::access::ownable::interface::{IOwnableDispatcher, IOwnableDispatcherTrait}; - use starklane::interfaces::{IStarklane, - IUpgradeable, IUpgradeableDispatcher, IUpgradeableDispatcherTrait, - IStarklaneCollectionAdmin}; + use starklane::interfaces::{ + IStarklane, IUpgradeable, IUpgradeableDispatcher, IUpgradeableDispatcherTrait, + IStarklaneCollectionAdmin + }; // events use starklane::interfaces::{ DepositRequestInitiated, WithdrawRequestCompleted, CollectionDeployedFromL1, @@ -30,19 +29,15 @@ mod bridge { }; use starklane::request::{ - Request, - compute_request_header_v1, - compute_request_hash, - collection_type_from_header, + Request, compute_request_header_v1, compute_request_hash, collection_type_from_header, }; use starklane::token::{ interfaces::{ - IERC721Dispatcher, IERC721DispatcherTrait, - IERC721BridgeableDispatcher, IERC721BridgeableDispatcherTrait, + IERC721Dispatcher, IERC721DispatcherTrait, IERC721BridgeableDispatcher, + IERC721BridgeableDispatcherTrait, }, collection_manager::{ - CollectionType, - deploy_erc721_bridgeable, verify_collection_address, erc721_metadata, + CollectionType, deploy_erc721_bridgeable, verify_collection_address, erc721_metadata, }, }; @@ -51,7 +46,8 @@ mod bridge { component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); #[abi(embed_v0)] - impl OwnableTwoStepMixinImpl = OwnableComponent::OwnableTwoStepMixinImpl; + impl OwnableTwoStepMixinImpl = + OwnableComponent::OwnableTwoStepMixinImpl; impl OwnableInternalImpl = OwnableComponent::InternalImpl; @@ -68,17 +64,13 @@ mod bridge { // Registry of escrowed token for collections. // <(collection_l2_address, token_id), original_depositor_l2_address> escrow: LegacyMap::<(ContractAddress, u256), ContractAddress>, - // White list enabled flag white_list_enabled: bool, - // Registry of whitelisted collections white_listed_list: LegacyMap::, white_listed_head: ContractAddress, - // Bridge enabled flag enabled: bool, - #[substorage(v0)] ownable: OwnableComponent::Storage, } @@ -127,14 +119,9 @@ mod bridge { /// to be more flexible? And the first felt25 being the header /// defines how the deserialization takes place? #[l1_handler] - fn withdraw_auto_from_l1( - ref self: ContractState, - from_address: felt252, - req: Request - ) { + fn withdraw_auto_from_l1(ref self: ContractState, from_address: felt252, req: Request) { ensure_is_enabled(@self); - assert(self.bridge_l1_address.read().into() == from_address, - 'Invalid L1 msg sender'); + assert(self.bridge_l1_address.read().into() == from_address, 'Invalid L1 msg sender'); // TODO: recompute HASH to ensure data are not altered. // TODO: Validate all fields the request (cf. FSM). @@ -159,26 +146,32 @@ mod bridge { if is_escrowed { IERC721Dispatcher { contract_address: collection_l2 } - .transfer_from(from, to, token_id); + .transfer_from(from, to, token_id); + + let null_value = starknet::contract_address_const::<0>(); + self.escrow.write((collection_l2, token_id), null_value); } else { if (req.uris.len() != 0) { let token_uri = req.uris[i]; IERC721BridgeableDispatcher { contract_address: collection_l2 } - .mint_from_bridge_uri(to, token_id, token_uri.clone()); + .mint_from_bridge_uri(to, token_id, token_uri.clone()); } else { IERC721BridgeableDispatcher { contract_address: collection_l2 } - .mint_from_bridge(to, token_id); + .mint_from_bridge(to, token_id); } } i += 1; }; - self.emit(WithdrawRequestCompleted { - hash: req.hash, - block_timestamp: starknet::info::get_block_timestamp(), - req_content: req - }); + self + .emit( + WithdrawRequestCompleted { + hash: req.hash, + block_timestamp: starknet::info::get_block_timestamp(), + req_content: req + } + ); } #[abi(embed_v0)] @@ -188,10 +181,12 @@ mod bridge { match starknet::replace_class_syscall(class_hash) { Result::Ok(_) => { - self.emit(ReplacedClassHash { - contract: starknet::get_contract_address(), - class: class_hash, - }) + self + .emit( + ReplacedClassHash { + contract: starknet::get_contract_address(), class: class_hash, + } + ) }, Result::Err(revert_reason) => panic(revert_reason), }; @@ -200,7 +195,6 @@ mod bridge { #[abi(embed_v0)] impl BridgeImpl of IStarklane { - fn get_l1_collection_address(self: @ContractState, address: ContractAddress) -> EthAddress { self.l2_to_l1_addresses.read(address) } @@ -237,8 +231,10 @@ mod bridge { /// * `collection_l2` - Address of the collection on L2. /// * `owner_l1` - Address of the owner on L1. /// * `tokens_ids` - Tokens to be bridged on L1. - /// * `use_withdraw_auto` - Tokens are automatically withdrawn on L1 using Starklane indexer. - /// * `use_deposit_burn_auto` - Tokens are automatically burnt on L2 once transferred using Starklane indexer. + /// * `use_withdraw_auto` - Tokens are automatically withdrawn on L1 using Starklane + /// indexer. + /// * `use_deposit_burn_auto` - Tokens are automatically burnt on L2 once transferred using + /// Starklane indexer. /// /// TODO: add new_owners, values and uris. /// TODO: better to use a struct than too much arguments? (DepositParams/DepositInputs/???) @@ -295,17 +291,17 @@ mod bridge { let mut buf: Array = array![]; req.serialize(ref buf); - starknet::send_message_to_l1_syscall( - self.bridge_l1_address.read().into(), - buf.span(), - ) + starknet::send_message_to_l1_syscall(self.bridge_l1_address.read().into(), buf.span(),) .unwrap_syscall(); - - self.emit(DepositRequestInitiated { - hash: req.hash, - block_timestamp: starknet::info::get_block_timestamp(), - req_content: req - }); + + self + .emit( + DepositRequestInitiated { + hash: req.hash, + block_timestamp: starknet::info::get_block_timestamp(), + req_content: req + } + ); } fn enable_white_list(ref self: ContractState, enable: bool) { @@ -318,13 +314,12 @@ mod bridge { self.white_list_enabled.read() } - fn white_list_collection(ref self: ContractState, collection: ContractAddress, enabled: bool) { + fn white_list_collection( + ref self: ContractState, collection: ContractAddress, enabled: bool + ) { ensure_is_admin(@self); _white_list_collection(ref self, collection, enabled); - self.emit(CollectionWhiteListUpdated { - collection, - enabled, - }); + self.emit(CollectionWhiteListUpdated { collection, enabled, }); } fn is_white_listed(self: @ContractState, collection: ContractAddress) -> bool { @@ -352,16 +347,16 @@ mod bridge { fn enable(ref self: ContractState, enable: bool) { ensure_is_admin(@self); self.enabled.write(enable); - self.emit(BridgeEnabled { - enable: enable - }); + self.emit(BridgeEnabled { enable: enable }); } fn is_enabled(self: @ContractState) -> bool { self.enabled.read() } - fn set_l1_l2_collection_mapping(ref self: ContractState, collection_l1: EthAddress, collection_l2: ContractAddress) { + fn set_l1_l2_collection_mapping( + ref self: ContractState, collection_l1: EthAddress, collection_l2: ContractAddress + ) { ensure_is_admin(@self); self.l1_to_l2_addresses.write(collection_l1, collection_l2); self.l2_to_l1_addresses.write(collection_l2, collection_l1); @@ -371,17 +366,19 @@ mod bridge { #[abi(embed_v0)] impl BridgeCollectionAdminImpl of IStarklaneCollectionAdmin { - fn collection_upgrade(ref self: ContractState, collection: ContractAddress, class_hash: ClassHash) { + fn collection_upgrade( + ref self: ContractState, collection: ContractAddress, class_hash: ClassHash + ) { ensure_is_admin(@self); - IUpgradeableDispatcher { contract_address: collection } - .upgrade(class_hash); + IUpgradeableDispatcher { contract_address: collection }.upgrade(class_hash); } // transfer owner of the given collection to the given address - fn collection_transfer_ownership(ref self: ContractState, collection: ContractAddress, new_owner: ContractAddress) { + fn collection_transfer_ownership( + ref self: ContractState, collection: ContractAddress, new_owner: ContractAddress + ) { ensure_is_admin(@self); - IOwnableDispatcher { contract_address: collection } - .transfer_ownership(new_owner); + IOwnableDispatcher { contract_address: collection }.transfer_ownership(new_owner); } } @@ -431,7 +428,6 @@ mod bridge { /// /// * `req` - Request for bridging assets. fn ensure_erc721_deployment(ref self: ContractState, req: @Request) -> ContractAddress { - let l1_req: EthAddress = *req.collection_l1; let l2_req: ContractAddress = *req.collection_l2; @@ -476,21 +472,21 @@ mod bridge { let (already_white_listed, _) = self.white_listed_list.read(l2_addr_from_deploy); if already_white_listed != true { _white_list_collection(ref self, l2_addr_from_deploy, true); - self.emit(CollectionWhiteListUpdated { - collection: l2_addr_from_deploy, - enabled: true, - }); + self + .emit( + CollectionWhiteListUpdated { collection: l2_addr_from_deploy, enabled: true, } + ); } l2_addr_from_deploy } fn _is_white_listed(self: @ContractState, collection: ContractAddress) -> bool { - let enabled = self.white_list_enabled.read(); - if (enabled) { - let (ret, _) = self.white_listed_list.read(collection); - return ret; - } - true + let enabled = self.white_list_enabled.read(); + if (enabled) { + let (ret, _) = self.white_listed_list.read(collection); + return ret; + } + true } fn _white_list_collection(ref self: ContractState, collection: ContractAddress, enabled: bool) { @@ -517,7 +513,7 @@ mod bridge { prev = next; }; self.white_listed_list.write(prev, (true, collection)); - } else { + } else { // change head if prev == collection { let (_, next) = self.white_listed_list.read(prev); diff --git a/apps/blockchain/starknet/src/interfaces.cairo b/apps/blockchain/starknet/src/interfaces.cairo index 0302a9ab..1cd3f4bd 100644 --- a/apps/blockchain/starknet/src/interfaces.cairo +++ b/apps/blockchain/starknet/src/interfaces.cairo @@ -4,7 +4,6 @@ use starklane::request::Request; #[starknet::interface] trait IStarklane { - fn deposit_tokens( ref self: T, salt: felt252, @@ -33,7 +32,9 @@ trait IStarklane { fn enable(ref self: T, enable: bool); fn is_enabled(self: @T) -> bool; - fn set_l1_l2_collection_mapping(ref self: T , collection_l1: EthAddress, collection_l2: ContractAddress); + fn set_l1_l2_collection_mapping( + ref self: T, collection_l1: EthAddress, collection_l2: ContractAddress + ); } /// Upgradeable contract. @@ -48,7 +49,9 @@ trait IStarklaneCollectionAdmin { fn collection_upgrade(ref self: T, collection: ContractAddress, class_hash: ClassHash); // transfer owner of the given collection to the given address - fn collection_transfer_ownership(ref self: T, collection: ContractAddress, new_owner: ContractAddress); + fn collection_transfer_ownership( + ref self: T, collection: ContractAddress, new_owner: ContractAddress + ); } ////////////////////////// diff --git a/apps/blockchain/starknet/src/tests/bridge_t.cairo b/apps/blockchain/starknet/src/tests/bridge_t.cairo index 0cf22a77..4549db0f 100644 --- a/apps/blockchain/starknet/src/tests/bridge_t.cairo +++ b/apps/blockchain/starknet/src/tests/bridge_t.cairo @@ -1,11 +1,7 @@ #[cfg(test)] mod tests { use snforge_std::{ - cheatcodes::{ - events::EventFetcher, - events::EventAssertions, - l1_handler::L1HandlerTrait - }, + cheatcodes::{events::EventFetcher, events::EventAssertions, l1_handler::L1HandlerTrait}, event_name_hash, }; @@ -20,25 +16,29 @@ mod tests { use openzeppelin::access::ownable::interface::{ IOwnableTwoStepDispatcher, IOwnableTwoStepDispatcherTrait }; - + use starknet::{ContractAddress, ClassHash, EthAddress, class_hash_const}; use starklane::{ request::{Request, compute_request_hash}, - interfaces::{IStarklaneDispatcher, IStarklaneDispatcherTrait, - IUpgradeableDispatcher, IUpgradeableDispatcherTrait, - IStarklaneCollectionAdminDispatcher, IStarklaneCollectionAdminDispatcherTrait, - }, + interfaces::{ + IStarklaneDispatcher, IStarklaneDispatcherTrait, IUpgradeableDispatcher, + IUpgradeableDispatcherTrait, IStarklaneCollectionAdminDispatcher, + IStarklaneCollectionAdminDispatcherTrait, + }, }; use starklane::token::{ interfaces::{ - IERC721Dispatcher, IERC721DispatcherTrait, - IERC721BridgeableDispatcher, IERC721BridgeableDispatcherTrait, - IERC721MintableDispatcher, IERC721MintableDispatcherTrait, + IERC721Dispatcher, IERC721DispatcherTrait, IERC721BridgeableDispatcher, + IERC721BridgeableDispatcherTrait, IERC721MintableDispatcher, + IERC721MintableDispatcherTrait, }, }; use starklane::bridge::bridge; - use snforge_std::{declare, ContractClass, ContractClassTrait, start_prank, stop_prank, CheatTarget, L1Handler, get_class_hash, spy_events, SpyOn}; + use snforge_std::{ + declare, ContractClass, ContractClassTrait, start_prank, stop_prank, CheatTarget, L1Handler, + get_class_hash, spy_events, SpyOn, load, map_entry_address, + }; #[derive(Drop)] struct BridgeDeployedConfig { @@ -102,27 +102,23 @@ mod tests { let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let collection_l2: ContractAddress = 0x0.try_into().unwrap(); let mut req = setup_request( - collection_l1, - collection_l2, - owner_l1, - owner_l2, - "name", - "symbol", - "base_uri" + collection_l1, collection_l2, owner_l1, owner_l2, "name", "symbol", "base_uri" ); req.ids = ids; let mut buf = array![]; req.serialize(ref buf); - + let mut l1_handler = L1Handler { contract_address: bridge_address, // selector: 0x03593216f3a8b22f4cf375e5486e3d13bfde9d0f26976d20ac6f653c73f7e507, - function_selector: selector!("withdraw_auto_from_l1"), + function_selector: selector!("withdraw_auto_from_l1"), from_address: bridge_l1.into(), payload: buf.span() }; @@ -166,14 +162,12 @@ mod tests { let COLLECTION_OWNER = starknet::contract_address_const::<'collection owner'>(); let OWNER_L1 = EthAddress { address: 'owner_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let erc721b_address = deploy_erc721b( - erc721b_contract_class, - "everai", - "DUO", - bridge_address, - COLLECTION_OWNER + erc721b_contract_class, "everai", "DUO", bridge_address, COLLECTION_OWNER ); let erc721 = IERC721Dispatcher { contract_address: erc721b_address }; @@ -188,13 +182,7 @@ mod tests { let mut spy = spy_events(SpyOn::One(bridge_address)); start_prank(CheatTarget::One(bridge_address), COLLECTION_OWNER); - bridge.deposit_tokens( - 0x123, - erc721b_address, - OWNER_L1, - array![0, 1].span(), - false, - false); + bridge.deposit_tokens(0x123, erc721b_address, OWNER_L1, array![0, 1].span(), false, false); stop_prank(CheatTarget::One(bridge_address)); assert!(erc721.owner_of(0) == bridge_address, "Wrong owner after deposit"); @@ -204,23 +192,24 @@ mod tests { let (from, event) = spy.events.at(0); assert!(from == @bridge_address, "Emitted from wrong address"); assert_eq!(event.keys.len(), 4, "There should be four keys"); - assert!(event.keys.at(0) == @event_name_hash('DepositRequestInitiated'), "Wrong event name"); + assert!( + event.keys.at(0) == @event_name_hash('DepositRequestInitiated'), "Wrong event name" + ); assert_eq!(req_hash.low, (*event.keys.at(1)).try_into().unwrap(), "Wrong req hash"); assert_eq!(req_hash.high, (*event.keys.at(2)).try_into().unwrap(), "Wrong req hash"); let timestamp: u64 = (*event.keys.at(3)).try_into().unwrap(); assert_eq!(timestamp, starknet::info::get_block_timestamp(), "Wrong timestamp key"); - } - fn setup_request(collection_l1: EthAddress, - collection_l2: ContractAddress, - owner_l1: EthAddress, - owner_l2: ContractAddress, - name: ByteArray, - symbol: ByteArray, - base_uri: ByteArray, - ) -> Request { - + fn setup_request( + collection_l1: EthAddress, + collection_l2: ContractAddress, + owner_l1: EthAddress, + owner_l2: ContractAddress, + name: ByteArray, + symbol: ByteArray, + base_uri: ByteArray, + ) -> Request { let ids: Span = array![].span(); let values: Span = array![].span(); let uris: Span = array![].span(); @@ -253,7 +242,9 @@ mod tests { let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; let OWNER_L2 = starknet::contract_address_const::<'owner_l2'>(); - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let collection_l1: EthAddress = 0xe0c.try_into().unwrap(); let collection_l2: ContractAddress = 0x0.try_into().unwrap(); @@ -262,22 +253,16 @@ mod tests { let ids: Span = array![0_u256, 1_u256].span(); let mut req = setup_request( - collection_l1, - collection_l2, - owner_l1, - owner_l2, - "name", - "symbol", - "base_uri" + collection_l1, collection_l2, owner_l1, owner_l2, "name", "symbol", "base_uri" ); req.ids = ids; let mut buf = array![]; req.serialize(ref buf); - + let mut l1_handler = L1Handler { contract_address: bridge_address, // selector: 0x03593216f3a8b22f4cf375e5486e3d13bfde9d0f26976d20ac6f653c73f7e507, - function_selector: selector!("withdraw_auto_from_l1"), + function_selector: selector!("withdraw_auto_from_l1"), from_address: BRIDGE_L1.into(), payload: buf.span() }; @@ -286,7 +271,6 @@ mod tests { l1_handler.execute().unwrap(); let bridge = IStarklaneDispatcher { contract_address: bridge_address }; - // Deserialize the request and check some expected values. let mut sp = buf.span(); let req = Serde::::deserialize(ref sp).unwrap(); @@ -298,18 +282,21 @@ mod tests { assert!(erc721.owner_of(0) == OWNER_L2, "Wrong owner after req"); assert!(erc721.owner_of(1) == OWNER_L2, "Wrong owner after req"); - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::WithdrawRequestCompleted( - bridge::WithdrawRequestCompleted { - hash: req.hash, - block_timestamp: starknet::info::get_block_timestamp(), - req_content: req, - } - ) - - )]); + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::WithdrawRequestCompleted( + bridge::WithdrawRequestCompleted { + hash: req.hash, + block_timestamp: starknet::info::get_block_timestamp(), + req_content: req, + } + ) + ) + ] + ); } #[test] @@ -321,7 +308,9 @@ mod tests { let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; let OWNER_L2 = starknet::contract_address_const::<'owner_l2'>(); - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let collection_l1: EthAddress = 0xe0c.try_into().unwrap(); let collection_l2: ContractAddress = 0x0.try_into().unwrap(); @@ -331,23 +320,17 @@ mod tests { let uris: Span = array!["tokenA", "tokenB"].span(); let mut req = setup_request( - collection_l1, - collection_l2, - owner_l1, - owner_l2, - "name", - "symbol", - "base_uri" + collection_l1, collection_l2, owner_l1, owner_l2, "name", "symbol", "base_uri" ); req.ids = ids; req.uris = uris; let mut buf = array![]; req.serialize(ref buf); - + let mut l1_handler = L1Handler { contract_address: bridge_address, // selector: 0x03593216f3a8b22f4cf375e5486e3d13bfde9d0f26976d20ac6f653c73f7e507, - function_selector: selector!("withdraw_auto_from_l1"), + function_selector: selector!("withdraw_auto_from_l1"), from_address: BRIDGE_L1.into(), payload: buf.span() }; @@ -367,22 +350,25 @@ mod tests { assert!(erc721.owner_of(0) == OWNER_L2, "Wrong owner after req"); assert!(erc721.owner_of(1) == OWNER_L2, "Wrong owner after req"); - + assert!(erc721.token_uri(0) == "tokenA", "Wrong token uri"); assert!(erc721.token_uri(1) == "tokenB", "Wrong token uri"); - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::WithdrawRequestCompleted( - bridge::WithdrawRequestCompleted { - hash: req.hash, - block_timestamp: starknet::info::get_block_timestamp(), - req_content: req, - } - ) - - )]); + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::WithdrawRequestCompleted( + bridge::WithdrawRequestCompleted { + hash: req.hash, + block_timestamp: starknet::info::get_block_timestamp(), + req_content: req, + } + ) + ) + ] + ); } #[test] @@ -395,14 +381,12 @@ mod tests { let COLLECTION_OWNER = starknet::contract_address_const::<'collection owner'>(); let OWNER_L1 = EthAddress { address: 'owner_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let erc721b_address = deploy_erc721b( - erc721b_contract_class, - "everai", - "DUO", - bridge_address, - COLLECTION_OWNER + erc721b_contract_class, "everai", "DUO", bridge_address, COLLECTION_OWNER ); let erc721 = IERC721Dispatcher { contract_address: erc721b_address }; @@ -413,20 +397,14 @@ mod tests { start_prank(CheatTarget::One(erc721b_address), COLLECTION_OWNER); erc721.set_approval_for_all(bridge_address, true); stop_prank(CheatTarget::One(erc721b_address)); - + let mut spy = spy_events(SpyOn::One(bridge_address)); start_prank(CheatTarget::One(bridge_address), COLLECTION_OWNER); - bridge.deposit_tokens( - 0x123, - erc721b_address, - OWNER_L1, - array![0, 1].span(), - false, - false); + bridge.deposit_tokens(0x123, erc721b_address, OWNER_L1, array![0, 1].span(), false, false); stop_prank(CheatTarget::One(bridge_address)); let owner_l2 = starknet::contract_address_const::<'owner_l2'>(); - let collection_l1 = EthAddress { address: 0x4269}; + let collection_l1 = EthAddress { address: 0x4269 }; start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.set_l1_l2_collection_mapping(collection_l1, erc721b_address); @@ -445,11 +423,11 @@ mod tests { let mut l1_handler = L1Handler { contract_address: bridge_address, // selector: 0x03593216f3a8b22f4cf375e5486e3d13bfde9d0f26976d20ac6f653c73f7e507, - function_selector: selector!("withdraw_auto_from_l1"), + function_selector: selector!("withdraw_auto_from_l1"), from_address: BRIDGE_L1.into(), payload: buf.span() }; - + l1_handler.execute().unwrap(); assert!(erc721.owner_of(0) == owner_l2, "Wrong owner"); @@ -464,7 +442,9 @@ mod tests { let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; let OWNER_L2 = starknet::contract_address_const::<'owner_l2'>(); - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let collection_l1: EthAddress = 0xe0c.try_into().unwrap(); let collection_l2: ContractAddress = 0x0.try_into().unwrap(); @@ -473,13 +453,7 @@ mod tests { let ids: Span = array![0_u256, 1_u256].span(); let mut req = setup_request( - collection_l1, - collection_l2, - owner_l1, - owner_l2, - "name", - "symbol", - "base_uri" + collection_l1, collection_l2, owner_l1, owner_l2, "name", "symbol", "base_uri" ); req.ids = ids; let mut buf = array![]; @@ -493,7 +467,7 @@ mod tests { let mut l1_handler = L1Handler { contract_address: bridge_address, // selector: 0x03593216f3a8b22f4cf375e5486e3d13bfde9d0f26976d20ac6f653c73f7e507, - function_selector: selector!("withdraw_auto_from_l1"), + function_selector: selector!("withdraw_auto_from_l1"), from_address: BRIDGE_L1.into(), payload: buf.span() }; @@ -514,7 +488,9 @@ mod tests { assert!(bridge.is_white_listed(deployed_address), "Collection shall be whitelisted"); let white_listed = bridge.get_white_listed_collections(); - assert_eq!(*white_listed.at(0), deployed_address, "Collection whitelisted shall be in list"); + assert_eq!( + *white_listed.at(0), deployed_address, "Collection whitelisted shall be in list" + ); } #[test] @@ -528,14 +504,12 @@ mod tests { let COLLECTION_OWNER = starknet::contract_address_const::<'collection owner'>(); let OWNER_L1 = EthAddress { address: 'owner_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let erc721b_address = deploy_erc721b( - erc721b_contract_class, - "everai", - "DUO", - bridge_address, - COLLECTION_OWNER + erc721b_contract_class, "everai", "DUO", bridge_address, COLLECTION_OWNER ); let erc721 = IERC721Dispatcher { contract_address: erc721b_address }; @@ -552,13 +526,7 @@ mod tests { stop_prank(CheatTarget::One(erc721b_address)); start_prank(CheatTarget::One(bridge_address), COLLECTION_OWNER); - bridge.deposit_tokens( - 0x123, - erc721b_address, - OWNER_L1, - array![0, 1].span(), - false, - false); + bridge.deposit_tokens(0x123, erc721b_address, OWNER_L1, array![0, 1].span(), false, false); stop_prank(CheatTarget::One(bridge_address)); } @@ -572,14 +540,12 @@ mod tests { let COLLECTION_OWNER = starknet::contract_address_const::<'collection owner'>(); let OWNER_L1 = EthAddress { address: 'owner_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let erc721b_address = deploy_erc721b( - erc721b_contract_class, - "everai", - "DUO", - bridge_address, - COLLECTION_OWNER + erc721b_contract_class, "everai", "DUO", bridge_address, COLLECTION_OWNER ); let erc721 = IERC721Dispatcher { contract_address: erc721b_address }; @@ -597,13 +563,7 @@ mod tests { stop_prank(CheatTarget::One(erc721b_address)); start_prank(CheatTarget::One(bridge_address), COLLECTION_OWNER); - bridge.deposit_tokens( - 0x123, - erc721b_address, - OWNER_L1, - array![0, 1].span(), - false, - false); + bridge.deposit_tokens(0x123, erc721b_address, OWNER_L1, array![0, 1].span(), false, false); stop_prank(CheatTarget::One(bridge_address)); } @@ -614,13 +574,17 @@ mod tests { let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let bridge = IStarklaneDispatcher { contract_address: bridge_address }; - + start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.enable_white_list(true); stop_prank(CheatTarget::One(bridge_address)); - assert!(bridge.get_white_listed_collections().is_empty(), "White list shall be empty by default"); + assert!( + bridge.get_white_listed_collections().is_empty(), "White list shall be empty by default" + ); } #[test] @@ -630,13 +594,15 @@ mod tests { let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let bridge = IStarklaneDispatcher { contract_address: bridge_address }; - + start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.enable_white_list(true); stop_prank(CheatTarget::One(bridge_address)); - + let collection1 = starknet::contract_address_const::<'collection1'>(); let collection2 = starknet::contract_address_const::<'collection2'>(); let collection3 = starknet::contract_address_const::<'collection3'>(); @@ -644,7 +610,7 @@ mod tests { start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.white_list_collection(collection1, true); stop_prank(CheatTarget::One(bridge_address)); - + let white_listed = bridge.get_white_listed_collections(); assert_eq!(white_listed.len(), 1, "White list shall contain 1 element"); assert_eq!(*white_listed.at(0), collection1, "Wrong collection address in white list"); @@ -684,19 +650,21 @@ mod tests { let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let bridge = IStarklaneDispatcher { contract_address: bridge_address }; - + start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.enable_white_list(true); stop_prank(CheatTarget::One(bridge_address)); - + let collection1 = starknet::contract_address_const::<'collection1'>(); let collection2 = starknet::contract_address_const::<'collection2'>(); let collection3 = starknet::contract_address_const::<'collection3'>(); let collection4 = starknet::contract_address_const::<'collection4'>(); let collection5 = starknet::contract_address_const::<'collection5'>(); - + start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.white_list_collection(collection1, true); bridge.white_list_collection(collection2, true); @@ -704,7 +672,7 @@ mod tests { bridge.white_list_collection(collection4, true); bridge.white_list_collection(collection5, true); stop_prank(CheatTarget::One(bridge_address)); - + let white_listed = bridge.get_white_listed_collections(); assert_eq!(white_listed.len(), 5, "White list shall contain 5 elements"); assert_eq!(*white_listed.at(0), collection1, "Wrong collection address in white list"); @@ -734,18 +702,20 @@ mod tests { assert!(!bridge.is_white_listed(collection3), "Collection1 should not be whitelisted"); assert!(bridge.is_white_listed(collection4), "Collection1 should be whitelisted"); assert!(bridge.is_white_listed(collection5), "Collection1 should be whitelisted"); - - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::CollectionWhiteListUpdated( - bridge::CollectionWhiteListUpdated { - collection: collection3, - enabled: false, - } - ) - ) - ]); + + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::CollectionWhiteListUpdated( + bridge::CollectionWhiteListUpdated { + collection: collection3, enabled: false, + } + ) + ) + ] + ); start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.white_list_collection(collection1, false); @@ -759,32 +729,33 @@ mod tests { assert!(!bridge.is_white_listed(collection1), "Collection1 should not be whitelisted"); assert!(!bridge.is_white_listed(collection4), "Collection1 should not be whitelisted"); - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::CollectionWhiteListUpdated( - bridge::CollectionWhiteListUpdated { - collection: collection1, - enabled: false, - } - ) - ), - ( - bridge_address, - bridge::Event::CollectionWhiteListUpdated( - bridge::CollectionWhiteListUpdated { - collection: collection4, - enabled: false, - } - ) - ) - ]); + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::CollectionWhiteListUpdated( + bridge::CollectionWhiteListUpdated { + collection: collection1, enabled: false, + } + ) + ), + ( + bridge_address, + bridge::Event::CollectionWhiteListUpdated( + bridge::CollectionWhiteListUpdated { + collection: collection4, enabled: false, + } + ) + ) + ] + ); start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.white_list_collection(collection3, true); bridge.white_list_collection(collection2, false); stop_prank(CheatTarget::One(bridge_address)); - + let white_listed = bridge.get_white_listed_collections(); assert_eq!(white_listed.len(), 2, "White list shall contain 2 elements"); assert_eq!(*white_listed.at(0), collection5, "Wrong collection address in white list"); @@ -793,32 +764,33 @@ mod tests { assert!(!bridge.is_white_listed(collection2), "Collection1 should not be whitelisted"); assert!(bridge.is_white_listed(collection3), "Collection1 should be whitelisted"); - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::CollectionWhiteListUpdated( - bridge::CollectionWhiteListUpdated { - collection: collection3, - enabled: true, - } - ) - ), - ( - bridge_address, - bridge::Event::CollectionWhiteListUpdated( - bridge::CollectionWhiteListUpdated { - collection: collection2, - enabled: false, - } - ) - ) - ]); + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::CollectionWhiteListUpdated( + bridge::CollectionWhiteListUpdated { + collection: collection3, enabled: true, + } + ) + ), + ( + bridge_address, + bridge::Event::CollectionWhiteListUpdated( + bridge::CollectionWhiteListUpdated { + collection: collection2, enabled: false, + } + ) + ) + ] + ); start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.white_list_collection(collection3, false); bridge.white_list_collection(collection2, true); stop_prank(CheatTarget::One(bridge_address)); - + let white_listed = bridge.get_white_listed_collections(); assert_eq!(white_listed.len(), 2, "White list shall contain 2 elements"); assert_eq!(*white_listed.at(0), collection5, "Wrong collection address in white list"); @@ -831,7 +803,7 @@ mod tests { bridge.white_list_collection(collection5, false); bridge.white_list_collection(collection2, false); stop_prank(CheatTarget::One(bridge_address)); - + let white_listed = bridge.get_white_listed_collections(); assert!(white_listed.is_empty(), "White list shall be empty"); assert!(!bridge.is_white_listed(collection1), "Collection1 should not be whitelisted"); @@ -848,9 +820,11 @@ mod tests { let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let bridge = IStarklaneDispatcher { contract_address: bridge_address }; - + let collection1 = starknet::contract_address_const::<'collection1'>(); let collection2 = starknet::contract_address_const::<'collection2'>(); let _collection3 = starknet::contract_address_const::<'collection3'>(); @@ -862,43 +836,45 @@ mod tests { start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.white_list_collection(collection1, true); stop_prank(CheatTarget::One(bridge_address)); - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::CollectionWhiteListUpdated( - bridge::CollectionWhiteListUpdated { - collection: collection1, - enabled: true, - } - ) - ) - ]); + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::CollectionWhiteListUpdated( + bridge::CollectionWhiteListUpdated { + collection: collection1, enabled: true, + } + ) + ) + ] + ); start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.white_list_collection(collection2, true); bridge.white_list_collection(collection1, false); stop_prank(CheatTarget::One(bridge_address)); - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::CollectionWhiteListUpdated( - bridge::CollectionWhiteListUpdated { - collection: collection2, - enabled: true, - } - ) - ), - ( - bridge_address, - bridge::Event::CollectionWhiteListUpdated( - bridge::CollectionWhiteListUpdated { - collection: collection1, - enabled: false, - } - ) - ) - ]); - + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::CollectionWhiteListUpdated( + bridge::CollectionWhiteListUpdated { + collection: collection2, enabled: true, + } + ) + ), + ( + bridge_address, + bridge::Event::CollectionWhiteListUpdated( + bridge::CollectionWhiteListUpdated { + collection: collection1, enabled: false, + } + ) + ) + ] + ); } #[test] @@ -912,14 +888,12 @@ mod tests { let COLLECTION_OWNER = starknet::contract_address_const::<'collection owner'>(); let OWNER_L1 = EthAddress { address: 'owner_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let erc721b_address = deploy_erc721b( - erc721b_contract_class, - "everai", - "DUO", - bridge_address, - COLLECTION_OWNER + erc721b_contract_class, "everai", "DUO", bridge_address, COLLECTION_OWNER ); mint_range(erc721b_address, COLLECTION_OWNER, COLLECTION_OWNER, 0, 10); @@ -929,15 +903,8 @@ mod tests { bridge.enable(false); stop_prank(CheatTarget::One(bridge_address)); - start_prank(CheatTarget::One(bridge_address), COLLECTION_OWNER); - bridge.deposit_tokens( - 0x123, - erc721b_address, - OWNER_L1, - array![0, 1].span(), - false, - false); + bridge.deposit_tokens(0x123, erc721b_address, OWNER_L1, array![0, 1].span(), false, false); stop_prank(CheatTarget::One(bridge_address)); } @@ -949,14 +916,16 @@ mod tests { let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); - let bridge = IStarklaneDispatcher { contract_address: bridge_address}; + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); + let bridge = IStarklaneDispatcher { contract_address: bridge_address }; start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.enable(true); assert!(bridge.is_enabled()); bridge.enable(false); assert!(!bridge.is_enabled()); - stop_prank(CheatTarget::One(bridge_address)); + stop_prank(CheatTarget::One(bridge_address)); } #[test] @@ -969,38 +938,48 @@ mod tests { let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; let alice = starknet::contract_address_const::<'alice'>(); - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); start_prank(CheatTarget::One(bridge_address), alice); IStarklaneDispatcher { contract_address: bridge_address }.enable(true); - stop_prank(CheatTarget::One(bridge_address)); + stop_prank(CheatTarget::One(bridge_address)); } #[test] fn upgrade_as_admin() { - let erc721b_contract_class = declare("erc721_bridgeable"); + let erc721b_contract_class = declare("erc721_bridgeable"); let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let mut spy = spy_events(SpyOn::One(bridge_address)); start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); - IUpgradeableDispatcher { contract_address: bridge_address}.upgrade(erc721b_contract_class.class_hash); + IUpgradeableDispatcher { contract_address: bridge_address } + .upgrade(erc721b_contract_class.class_hash); stop_prank(CheatTarget::One(bridge_address)); - assert!(get_class_hash(bridge_address) == erc721b_contract_class.class_hash, "Incorrect class hash upgrade"); - - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::ReplacedClassHash( - bridge::ReplacedClassHash { - contract: bridge_address, - class: erc721b_contract_class.class_hash, - } - ) - ) - ]); + assert!( + get_class_hash(bridge_address) == erc721b_contract_class.class_hash, + "Incorrect class hash upgrade" + ); + + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::ReplacedClassHash( + bridge::ReplacedClassHash { + contract: bridge_address, class: erc721b_contract_class.class_hash, + } + ) + ) + ] + ); assert(spy.events.len() == 0, 'There should be no events'); } @@ -1011,13 +990,15 @@ mod tests { let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let bridge = IUpgradeableDispatcher { contract_address: bridge_address }; let alice = starknet::contract_address_const::<'alice'>(); start_prank(CheatTarget::One(bridge_address), alice); bridge.upgrade(erc721b_contract_class.class_hash); - stop_prank(CheatTarget::One(bridge_address)); + stop_prank(CheatTarget::One(bridge_address)); } #[test] @@ -1027,8 +1008,10 @@ mod tests { let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; let ALICE = starknet::contract_address_const::<'alice'>(); - let contract_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); - let ownable = IOwnableTwoStepDispatcher { contract_address}; + let contract_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); + let ownable = IOwnableTwoStepDispatcher { contract_address }; assert_eq!(ownable.owner(), BRIDGE_ADMIN, "bad owner"); start_prank(CheatTarget::One(contract_address), BRIDGE_ADMIN); @@ -1051,8 +1034,10 @@ mod tests { let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; let ALICE = starknet::contract_address_const::<'alice'>(); - let contract_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); - let ownable = IOwnableTwoStepDispatcher { contract_address}; + let contract_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); + let ownable = IOwnableTwoStepDispatcher { contract_address }; assert_eq!(ownable.owner(), BRIDGE_ADMIN, "bad owner"); start_prank(CheatTarget::One(contract_address), ALICE); @@ -1068,11 +1053,9 @@ mod tests { let COLLECTION_L1: EthAddress = 0xe0c.try_into().unwrap(); let ids: Span = array![0_u256, 1_u256].span(); - let bridge_deployed_config = deploy_bridge_and_create_collection(BRIDGE_L1, - COLLECTION_L1, - OWNER_L1, - OWNER_L2, - ids); + let bridge_deployed_config = deploy_bridge_and_create_collection( + BRIDGE_L1, COLLECTION_L1, OWNER_L1, OWNER_L2, ids + ); let BRIDGE_ADMIN = bridge_deployed_config.admin; let bridge_address = bridge_deployed_config.bridge; let erc721b_class = bridge_deployed_config.erc721_class; @@ -1084,9 +1067,13 @@ mod tests { assert!(get_class_hash(deployed_address) == erc721b_class, "Expected class hash"); start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); - IStarklaneCollectionAdminDispatcher { contract_address: bridge_address}.collection_upgrade(deployed_address, get_class_hash(bridge_address)); + IStarklaneCollectionAdminDispatcher { contract_address: bridge_address } + .collection_upgrade(deployed_address, get_class_hash(bridge_address)); stop_prank(CheatTarget::One(bridge_address)); - assert!(get_class_hash(deployed_address) == get_class_hash(bridge_address), "Class hash not updated"); + assert!( + get_class_hash(deployed_address) == get_class_hash(bridge_address), + "Class hash not updated" + ); } @@ -1098,11 +1085,9 @@ mod tests { let COLLECTION_L1: EthAddress = 0xe0c.try_into().unwrap(); let ids: Span = array![0_u256, 1_u256].span(); - let bridge_deployed_config = deploy_bridge_and_create_collection(BRIDGE_L1, - COLLECTION_L1, - OWNER_L1, - OWNER_L2, - ids); + let bridge_deployed_config = deploy_bridge_and_create_collection( + BRIDGE_L1, COLLECTION_L1, OWNER_L1, OWNER_L2, ids + ); let BRIDGE_ADMIN = bridge_deployed_config.admin; let bridge_address = bridge_deployed_config.bridge; let erc721b = bridge_deployed_config.erc721; @@ -1111,9 +1096,10 @@ mod tests { assert_eq!(ownable.owner(), bridge_address, "Wrong owner"); let ALICE = starknet::contract_address_const::<'alice'>(); - + start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); - IStarklaneCollectionAdminDispatcher { contract_address: bridge_address}.collection_transfer_ownership(erc721b, ALICE); + IStarklaneCollectionAdminDispatcher { contract_address: bridge_address } + .collection_transfer_ownership(erc721b, ALICE); stop_prank(CheatTarget::One(bridge_address)); assert_eq!(ownable.owner(), bridge_address, "Wrong owner"); @@ -1134,22 +1120,20 @@ mod tests { let COLLECTION_L1: EthAddress = 0xe0c.try_into().unwrap(); let ids: Span = array![0_u256, 1_u256].span(); - let bridge_deployed_config = deploy_bridge_and_create_collection(BRIDGE_L1, - COLLECTION_L1, - OWNER_L1, - OWNER_L2, - ids); + let bridge_deployed_config = deploy_bridge_and_create_collection( + BRIDGE_L1, COLLECTION_L1, OWNER_L1, OWNER_L2, ids + ); let bridge_address = bridge_deployed_config.bridge; let erc721b = bridge_deployed_config.erc721; - let ALICE = starknet::contract_address_const::<'alice'>(); - + start_prank(CheatTarget::One(bridge_address), OWNER_L2); - IStarklaneCollectionAdminDispatcher { contract_address: bridge_address}.collection_transfer_ownership(erc721b, ALICE); + IStarklaneCollectionAdminDispatcher { contract_address: bridge_address } + .collection_transfer_ownership(erc721b, ALICE); stop_prank(CheatTarget::One(bridge_address)); } - + #[test] fn enable_white_list() { let erc721b_contract_class = declare("erc721_bridgeable"); @@ -1157,7 +1141,9 @@ mod tests { let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let bridge = IStarklaneDispatcher { contract_address: bridge_address }; assert_eq!(bridge.is_white_list_enabled(), false); @@ -1166,16 +1152,15 @@ mod tests { start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.enable_white_list(true); stop_prank(CheatTarget::One(bridge_address)); - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::WhiteListEnabled( - bridge::WhiteListEnabled { - enabled: true, - } - ) - ) - ]); + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::WhiteListEnabled(bridge::WhiteListEnabled { enabled: true, }) + ) + ] + ); assert_eq!(bridge.is_white_list_enabled(), true); } @@ -1188,7 +1173,9 @@ mod tests { let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; let NEW_BRIDGE_L1 = EthAddress { address: 'new_starklane_l1' }; - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let bridge = IStarklaneDispatcher { contract_address: bridge_address }; assert_eq!(bridge.get_bridge_l1_addr(), BRIDGE_L1); @@ -1197,16 +1184,17 @@ mod tests { start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.set_bridge_l1_addr(NEW_BRIDGE_L1); stop_prank(CheatTarget::One(bridge_address)); - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::BridgeL1AddressUpdated( - bridge::BridgeL1AddressUpdated { - address: NEW_BRIDGE_L1, - } - ) - ) - ]); + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::BridgeL1AddressUpdated( + bridge::BridgeL1AddressUpdated { address: NEW_BRIDGE_L1, } + ) + ) + ] + ); assert_eq!(bridge.get_bridge_l1_addr(), NEW_BRIDGE_L1); } @@ -1219,7 +1207,9 @@ mod tests { let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; let ERC721_CLASS_HASH = class_hash_const::<'ERC721_CLASS_HASH'>(); - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let bridge = IStarklaneDispatcher { contract_address: bridge_address }; assert_eq!(bridge.get_erc721_class_hash(), erc721b_contract_class.class_hash); @@ -1228,16 +1218,17 @@ mod tests { start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.set_erc721_class_hash(ERC721_CLASS_HASH); stop_prank(CheatTarget::One(bridge_address)); - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::ERC721ClassHashUpdated( - bridge::ERC721ClassHashUpdated { - class_hash: ERC721_CLASS_HASH, - } - ) - ) - ]); + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::ERC721ClassHashUpdated( + bridge::ERC721ClassHashUpdated { class_hash: ERC721_CLASS_HASH, } + ) + ) + ] + ); assert_eq!(bridge.get_erc721_class_hash(), ERC721_CLASS_HASH); } @@ -1250,17 +1241,15 @@ mod tests { let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; let COLLECTION_OWNER = starknet::contract_address_const::<'collection owner'>(); - let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash); + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); let bridge = IStarklaneDispatcher { contract_address: bridge_address }; - let collection_l1 = EthAddress { address: 0x4269}; + let collection_l1 = EthAddress { address: 0x4269 }; let erc721b_address = deploy_erc721b( - erc721b_contract_class, - "everai", - "DUO", - bridge_address, - COLLECTION_OWNER + erc721b_contract_class, "everai", "DUO", bridge_address, COLLECTION_OWNER ); assert_eq!(bridge.get_erc721_class_hash(), erc721b_contract_class.class_hash); @@ -1269,19 +1258,90 @@ mod tests { start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); bridge.set_l1_l2_collection_mapping(collection_l1, erc721b_address); stop_prank(CheatTarget::One(bridge_address)); - spy.assert_emitted(@array![ - ( - bridge_address, - bridge::Event::L1L2CollectionMappingUpdated( - bridge::L1L2CollectionMappingUpdated { - collection_l1, - collection_l2: erc721b_address - } - ) - ) - ]); + spy + .assert_emitted( + @array![ + ( + bridge_address, + bridge::Event::L1L2CollectionMappingUpdated( + bridge::L1L2CollectionMappingUpdated { + collection_l1, collection_l2: erc721b_address + } + ) + ) + ] + ); assert_eq!(bridge.get_l1_collection_address(erc721b_address), collection_l1); assert_eq!(bridge.get_l2_collection_address(collection_l1), erc721b_address); } + + #[test] + fn test_escrow_clearing_after_withdrawal() { + // Setup + let erc721b_contract_class = declare("erc721_bridgeable"); + let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>(); + let BRIDGE_L1 = EthAddress { address: 'starklane_l1' }; + let COLLECTION_OWNER = starknet::contract_address_const::<'collection owner'>(); + let OWNER_L1 = EthAddress { address: 'owner_l1' }; + let OWNER_L2 = starknet::contract_address_const::<'owner_l2'>(); + + // Deploy bridge and ERC721 contract + let bridge_address = deploy_starklane( + BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash + ); + let erc721b_address = deploy_erc721b( + erc721b_contract_class, "TestToken", "TT", bridge_address, COLLECTION_OWNER + ); + + let bridge = IStarklaneDispatcher { contract_address: bridge_address }; + let erc721 = IERC721Dispatcher { contract_address: erc721b_address }; + + // Mint a token and deposit it to the bridge + mint_range(erc721b_address, COLLECTION_OWNER, COLLECTION_OWNER, 0, 1); + start_prank(CheatTarget::One(erc721b_address), COLLECTION_OWNER); + erc721.set_approval_for_all(bridge_address, true); + stop_prank(CheatTarget::One(erc721b_address)); + + start_prank(CheatTarget::One(bridge_address), COLLECTION_OWNER); + bridge.deposit_tokens(0x123, erc721b_address, OWNER_L1, array![0].span(), false, false); + stop_prank(CheatTarget::One(bridge_address)); + + // Verify token is in escrow + assert_eq!(erc721.owner_of(0), bridge_address, "Token should be in escrow"); + + // Prepare withdrawal request + let collection_l1: EthAddress = 0xe0c.try_into().unwrap(); + let mut req = setup_request( + collection_l1, erc721b_address, OWNER_L1, OWNER_L2, "TestToken", "TT", "base_uri" + ); + req.ids = array![0].span(); + let mut buf = array![]; + req.serialize(ref buf); + + start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN); + bridge.set_l1_l2_collection_mapping(collection_l1, erc721b_address); + stop_prank(CheatTarget::One(bridge_address)); + + // Execute withdrawal + let mut l1_handler = L1Handler { + contract_address: bridge_address, + function_selector: selector!("withdraw_auto_from_l1"), + from_address: BRIDGE_L1.into(), + payload: buf.span() + }; + l1_handler.execute().unwrap(); + + // Verify token is transferred to OWNER_L2 + assert_eq!(erc721.owner_of(0), OWNER_L2, "Token should be transferred to OWNER_L2"); + + // Verify escrow is cleared + let escrowed = load( + bridge_address, + map_entry_address(selector!("escrow"), array![erc721b_address.into(), 0, 0].span()), + 1 + ); + let is_escrowed = escrowed.len() == 1 && (*escrowed[0]).is_non_zero(); + assert!(!is_escrowed, "Escrow should be cleared after withdrawal"); + } }