diff --git a/src/factory.cairo b/src/factory.cairo deleted file mode 100644 index 8d5d19d..0000000 --- a/src/factory.cairo +++ /dev/null @@ -1,100 +0,0 @@ -use starknet::{ContractAddress, ClassHash, EthAddress}; - -#[starknet::interface] -pub trait IFactory { - fn lens(self: @TContractState) -> ContractAddress; - fn current_account_class(self: @TContractState) -> ClassHash; - fn precalculate_starknet_address(self: @TContractState, address: EthAddress) -> ContractAddress; - fn deploy_account(self: @TContractState, address: EthAddress) -> ContractAddress; - fn upgrade_contract(self: @TContractState, new_class: ClassHash); - fn change_account_class(ref self: TContractState, new_class: ClassHash); -} - -#[starknet::contract] -pub mod Factory { - use core::option::OptionTrait; - use starknet::{ContractAddress, ClassHash, EthAddress, get_caller_address}; - use starknet::syscalls::{deploy_syscall, replace_class_syscall}; - use core::traits::{Into, TryInto}; - use openzeppelin::utils::deployments::{calculate_contract_address_from_deploy_syscall}; - use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; - - #[storage] - struct Storage { - account_class: ClassHash, - lens: ContractAddress, - dev: ContractAddress - } - - #[constructor] - fn constructor(ref self: ContractState, dev: ContractAddress) { - // todo write initial values to storage - self.dev.write(dev); - } - - #[abi(embed_v0)] - impl Factory of super::IFactory { - /// Returns lens contract address - fn lens(self: @ContractState) -> ContractAddress { - self.lens.read() - } - - /// Returns latest account class hash - fn current_account_class(self: @ContractState) -> ClassHash { - self.account_class.read() - } - - /// Precalculate starknet address derived from ethereum address. This will be users accounts - /// starknet address if deployed. - /// # Params - /// `address` - Ethereum Address that will be used to precalculate starknet account address. - /// # Returns - /// `ContractAddress` - Precalculated starknet address - fn precalculate_starknet_address( - self: @ContractState, address: EthAddress - ) -> ContractAddress { - // TODO: Tests - - let eth_address_felt: felt252 = address.into(); - calculate_contract_address_from_deploy_syscall( - eth_address_felt, - self.account_class.read(), - array![eth_address_felt].span(), - 0.try_into().unwrap() - ) - } - - // TODO: this funcation can be removed - /// Deploys new rosettanet account. Fails if account already deployed - /// # Params - /// `address` - Ethereum Address that will be used to deploy starknet account. - /// # Returns - /// `ContractAddress` - Newly deployed starknet account - fn deploy_account(self: @ContractState, address: EthAddress) -> ContractAddress { - // TODO: Tests - let eth_address_felt: felt252 = address.into(); - - let (account, _) = deploy_syscall( - self.account_class.read(), eth_address_felt, array![eth_address_felt].span(), true - ) - .unwrap(); - - // Todo: register lens if needed ?? Or we can use precalculate - account - } - - // REMOVE THIS FUNCTION AFTER DEVELOPMENT - fn upgrade_contract(self: @ContractState, new_class: ClassHash) { - assert(get_caller_address() == self.dev.read(), 'only dev'); - - replace_class_syscall(new_class).unwrap(); - } - - // REMOVE THIS FUNCTION AFTER DEVELOPMENT - fn change_account_class(ref self: ContractState, new_class: ClassHash) { - assert(get_caller_address() == self.dev.read(), 'only dev'); - - self.account_class.write(new_class); - } - } -} diff --git a/src/lens.cairo b/src/lens.cairo deleted file mode 100644 index 7bb272a..0000000 --- a/src/lens.cairo +++ /dev/null @@ -1 +0,0 @@ -mod lens_dev; diff --git a/src/lens/lens_dev.cairo b/src/lens/lens_dev.cairo deleted file mode 100644 index 240b306..0000000 --- a/src/lens/lens_dev.cairo +++ /dev/null @@ -1,104 +0,0 @@ -use starknet::{ContractAddress, EthAddress}; - -#[starknet::interface] -trait ILensDev { - fn register_address(ref self: TState, address: ContractAddress); - fn register_address_dev(ref self: TState, address: ContractAddress, eth: EthAddress); - fn get_eth_address_from_sn_address(self: @TState, sn_address: ContractAddress) -> EthAddress; - fn get_sn_address_from_eth_address(self: @TState, eth_address: EthAddress) -> ContractAddress; -} - - -#[starknet::contract] -mod LensDev { - use super::ILensDev; - use starknet::{ContractAddress, EthAddress}; - use core::poseidon::PoseidonTrait; - use core::hash::{HashStateTrait}; - use core::traits::{Into, TryInto}; - use core::num::traits::{Zero}; - use starknet::storage::{Map}; - - #[storage] - struct Storage { - eth_address_to_sn_address: Map, - sn_address_to_eth_address: Map, - } - - #[constructor] - fn constructor(ref self: ContractState,) {} - - fn convert_to_eth_address(sn_address: ContractAddress) -> EthAddress { - // Remove 252 bits to 160 bits - - // TODO: check is it higher than 160 bits. - // TODO: then remove 160 bits. - let sn_address_f252: felt252 = sn_address.into(); - - let sn_address_u256: u256 = sn_address_f252.into(); - - let (_, address) = DivRem::div_rem( - sn_address_u256, 0x10000000000000000000000000000000000000000_u256.try_into().unwrap() - ); - - address.try_into().unwrap() - } - - fn regenerate_address(eth_address: EthAddress) -> EthAddress { - let hash = PoseidonTrait::new().update(eth_address.try_into().unwrap()).finalize(); - convert_to_eth_address(hash.try_into().unwrap()) - } - - #[abi(embed_v0)] - impl Lens of ILensDev { - fn register_address(ref self: ContractState, address: ContractAddress) { - assert(self.sn_address_to_eth_address.read(address).is_zero(), 'already registered'); - - let mut regenerated_address: EthAddress = convert_to_eth_address(address); - - let mut existance = self.eth_address_to_sn_address.read(regenerated_address).is_zero(); - if (existance) { - // Register address and return - self.eth_address_to_sn_address.write(regenerated_address, address); - self.sn_address_to_eth_address.write(address, regenerated_address); - return; - } - - loop { - regenerated_address = regenerate_address(regenerated_address); - - existance = self.eth_address_to_sn_address.read(regenerated_address).is_zero(); - - if (existance) { - self.eth_address_to_sn_address.write(regenerated_address, address); - self.sn_address_to_eth_address.write(address, regenerated_address); - break; - } - } - } - - // Function just for development tests. We can match any address with any eth address we - // want. - // Helps developing. - fn register_address_dev( - ref self: ContractState, address: ContractAddress, eth: EthAddress - ) { - assert(self.sn_address_to_eth_address.read(address).is_zero(), 'already registered'); - - self.eth_address_to_sn_address.write(eth, address); - self.sn_address_to_eth_address.write(address, eth); - } - - fn get_eth_address_from_sn_address( - self: @ContractState, sn_address: ContractAddress - ) -> EthAddress { - self.sn_address_to_eth_address.read(sn_address) - } - - fn get_sn_address_from_eth_address( - self: @ContractState, eth_address: EthAddress - ) -> ContractAddress { - self.eth_address_to_sn_address.read(eth_address) - } - } -} diff --git a/src/rosettanet.cairo b/src/rosettanet.cairo index de352b6..8e41536 100644 --- a/src/rosettanet.cairo +++ b/src/rosettanet.cairo @@ -4,11 +4,13 @@ pub trait IRosettanet { // Write methods fn register_contract(ref self: TState, address: ContractAddress); // Registers existing starknet contract to registry fn deploy_account(ref self: TState, eth_address: EthAddress) -> ContractAddress; // Deploys starknet account and returns address + fn set_account_class(ref self: TState, class: ClassHash); // Sets account class, this function will be removed after stable account // Read methods fn get_starknet_address(self: @TState, eth_address: EthAddress) -> ContractAddress; fn get_ethereum_address(self: @TState, sn_address: ContractAddress) -> EthAddress; fn precalculate_starknet_account(self: @TState, eth_address: EthAddress) -> ContractAddress; fn account_class(self: @TState) -> ClassHash; + fn developer(self: @TState) -> ContractAddress; } #[starknet::contract] pub mod Rosettanet { @@ -16,7 +18,7 @@ pub mod Rosettanet { use starknet::storage::{Map}; use core::poseidon::{poseidon_hash_span}; use starknet::syscalls::{deploy_syscall}; - use starknet::{ContractAddress, EthAddress, ClassHash, get_contract_address}; + use starknet::{ContractAddress, EthAddress, ClassHash, get_contract_address, get_caller_address}; use openzeppelin::utils::deployments::{calculate_contract_address_from_deploy_syscall}; #[storage] @@ -51,6 +53,13 @@ pub mod Rosettanet { account } + + fn set_account_class(ref self: ContractState, class: ClassHash) { + assert(get_caller_address() == self.dev.read(), 'only dev'); + + self.account_class.write(class); + } + // View methods fn get_starknet_address(self: @ContractState, eth_address: EthAddress) -> ContractAddress { self.eth_to_sn.read(eth_address) @@ -73,6 +82,10 @@ pub mod Rosettanet { fn account_class(self: @ContractState) -> ClassHash { self.account_class.read() } + + fn developer(self: @ContractState) -> ContractAddress { + self.dev.read() + } } #[generate_trait] diff --git a/src/verifier.cairo b/src/verifier.cairo deleted file mode 100644 index 95adf51..0000000 --- a/src/verifier.cairo +++ /dev/null @@ -1 +0,0 @@ -mod utils; diff --git a/src/verifier/utils.cairo b/src/verifier/utils.cairo deleted file mode 100644 index 061fe30..0000000 --- a/src/verifier/utils.cairo +++ /dev/null @@ -1,28 +0,0 @@ -/// Returns starknet calldata span of felt252s -/// This function do not checks function selector -/// # Params -/// `offsets` - Calldata read offsets, parse done according to these values -/// `calldata` - Actual EVM calldata, each element presents one slot -/// # Returns -/// `Span` - Parsed and converted calldata which is going to be passed to -/// call_contract_syscall. -pub fn parse_calldata(offsets: Span, calldata: Span) -> Span {} - - -/// Finds correct selector with trial and error method -/// It tries to re-calculate ethereum function signature by trying -/// all functions from the input. -/// # Params -/// `functions` - Function names with data types of ethereum span (balanceOf(address), -/// transfer_from(address,address,uint256)) -/// `signature` - Actual ethereum function signature from calldata. -pub fn find_selector(functions: Span, signature: u16) -> felt252 {} - -/// Parse parameters bit offsets according their sizes in ethereum slot -/// It has to be called after function name is found and matched. -/// # Params -/// `function` - Ethereum function name and parameters e.g. balanceOf(address), -/// witharray(uint256[]), withtuple((uint128,uint64,uint256),address) -/// # Returns -/// `Span` - Calldata read offsets in bits -pub fn parse_calldata_offsets(function: ByteArray) -> Span {} diff --git a/tests/rosettanet_tests.cairo b/tests/rosettanet_tests.cairo new file mode 100644 index 0000000..8db5cf6 --- /dev/null +++ b/tests/rosettanet_tests.cairo @@ -0,0 +1,75 @@ +use snforge_std::{declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address, stop_cheat_caller_address}; + +use rosettacontracts::rosettanet::{ + IRosettanetDispatcher, IRosettanetDispatcherTrait +}; +use starknet::{ContractAddress, ClassHash, EthAddress}; + +fn developer() -> ContractAddress { + starknet::contract_address_const::<1>() +} + +fn eth_account() -> EthAddress { + 0x12345678.try_into().unwrap() +} + +fn declare_accounts() -> ClassHash { + let class = declare("RosettaAccount").unwrap().contract_class(); + *class.class_hash +} + +fn deploy_rosettanet() -> IRosettanetDispatcher { + let contract = declare("Rosettanet").unwrap().contract_class(); + let (contract_address, _) = contract.deploy(@array![developer().into()]).unwrap(); + IRosettanetDispatcher { contract_address } +} + +fn deploy_and_set_account() -> IRosettanetDispatcher { + let contract = declare("Rosettanet").unwrap().contract_class(); + let (contract_address, _) = contract.deploy(@array![developer().into()]).unwrap(); + let dispatcher = IRosettanetDispatcher { contract_address }; + let account_class = declare_accounts(); + + start_cheat_caller_address(dispatcher.contract_address, developer()); + dispatcher.set_account_class(account_class); + stop_cheat_caller_address(dispatcher.contract_address); + + dispatcher +} + +#[test] +fn rosettanet_deploy_initial_dev() { + let rosettanet = deploy_rosettanet(); + + assert_eq!(rosettanet.developer(), starknet::contract_address_const::<1>()); +} + +#[test] +#[should_panic(expected: 'only dev')] +fn rosettanet_non_dev_set_class() { + let rosettanet = deploy_rosettanet(); + + rosettanet.set_account_class(1.try_into().unwrap()); +} + +#[test] +fn rosettanet_set_class() { + let rosettanet = deploy_rosettanet(); + + start_cheat_caller_address(rosettanet.contract_address, developer()); + rosettanet.set_account_class(1.try_into().unwrap()); + stop_cheat_caller_address(rosettanet.contract_address); + + assert_eq!(rosettanet.account_class(), 1.try_into().unwrap()); +} + +#[test] +fn rosettanet_check_precalculate_address() { + let rosettanet = deploy_and_set_account(); + + let precalculated_address = rosettanet.precalculate_starknet_account(eth_account()); + + let deployed_account = rosettanet.deploy_account(eth_account()); + + assert_eq!(precalculated_address, deployed_account) +} \ No newline at end of file diff --git a/tests/test_data.md b/tests/test_data.md deleted file mode 100644 index bb703dc..0000000 --- a/tests/test_data.md +++ /dev/null @@ -1,5 +0,0 @@ -### Eth Account used in tests -0x11655f4Ee2A5B66F9DCbe758e9FcdCd3eBF95eE5 -### Test transaction -[ ] Transfer or approve of standard erc20 in starknet -[ ] Account contruction etc. \ No newline at end of file