diff --git a/contracts/account/Scarb.lock b/contracts/account/Scarb.lock index e136a8f..0bdf12f 100644 --- a/contracts/account/Scarb.lock +++ b/contracts/account/Scarb.lock @@ -5,9 +5,129 @@ version = 1 name = "account" version = "0.1.0" dependencies = [ + "openzeppelin", "snforge_std", ] +[[package]] +name = "openzeppelin" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:320185f3e17cf9fafda88b1ce490f5eaed0bfcc273036b56cd22ce4fb8de628f" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_governance", + "openzeppelin_introspection", + "openzeppelin_merkle_tree", + "openzeppelin_presets", + "openzeppelin_security", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_access" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:a39a4ea1582916c637bf7e3aee0832c3fe1ea3a3e39191955e8dc39d08327f9b" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_account" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:7e943a2de32ddca4d48e467e52790e380ab1f49c4daddbbbc4634dd930d0243f" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_finance" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:9fa9e91d39b6ccdfa31eef32fdc087cd06c0269cc9c6b86e32d57f5a6997d98b" +dependencies = [ + "openzeppelin_access", + "openzeppelin_token", +] + +[[package]] +name = "openzeppelin_governance" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:c05add2974b3193c3a5c022b9586a84cf98c5970cdb884dcf201c77dbe359f55" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_introspection" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:34e088ecf19e0b3012481a29f1fbb20e600540cb9a5db1c3002a97ebb7f5a32a" + +[[package]] +name = "openzeppelin_merkle_tree" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:a5341705514a3d9beeeb39cf11464111f7355be621639740d2c5006786aa63dc" + +[[package]] +name = "openzeppelin_presets" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:4eb098e2ee3ac0e67b6828115a7de62f781418beab767d4e80b54e176808369d" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_security" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:1deb811a239c4f9cc28fc302039e2ffcb19911698a8c612487207448d70d2e6e" + +[[package]] +name = "openzeppelin_token" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:33fcb84a1a76d2d3fff9302094ff564f78d45b743548fd7568c130b272473f66" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_upgrades" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:36f7a03e7e7111577916aacf31f88ad0053de20f33ee10b0ab3804849c3aa373" + +[[package]] +name = "openzeppelin_utils" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:fd348b31c4a4407add33adc3c2b8f26dca71dbd7431faaf726168f37a91db0c1" + [[package]] name = "snforge_scarb_plugin" version = "0.43.1" diff --git a/contracts/account/Scarb.toml b/contracts/account/Scarb.toml index 9f7594d..a3c1308 100644 --- a/contracts/account/Scarb.toml +++ b/contracts/account/Scarb.toml @@ -7,6 +7,7 @@ edition = "2024_07" [dependencies] starknet = "2.11.4" +openzeppelin = "1.0.0" [dev-dependencies] snforge_std = "0.43.0" diff --git a/contracts/account/src/contract/account.cairo b/contracts/account/src/contract/account.cairo new file mode 100644 index 0000000..7a5cce0 --- /dev/null +++ b/contracts/account/src/contract/account.cairo @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +// Compatible with OpenZeppelin Contracts for Cairo 2.0.0-alpha.1 + +#[starknet::contract(account)] +mod Account { + use account::interfaces::Iaccount::Iaccount; + use openzeppelin::account::AccountComponent; + use openzeppelin::account::extensions::SRC9Component; + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::upgrades::UpgradeableComponent; + use openzeppelin::upgrades::interface::IUpgradeable; + use starknet::storage::{ + Map, StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess, + StoragePointerWriteAccess, + }; + use starknet::{ClassHash, ContractAddress, get_caller_address}; + + + component!(path: AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: SRC9Component, storage: src9, event: SRC9Event); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + // External + #[abi(embed_v0)] + impl AccountMixinImpl = AccountComponent::AccountMixinImpl; + #[abi(embed_v0)] + impl OutsideExecutionV2Impl = + SRC9Component::OutsideExecutionV2Impl; + + // Internal + impl AccountInternalImpl = AccountComponent::InternalImpl; + impl OutsideExecutionInternalImpl = SRC9Component::InternalImpl; + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + account: AccountComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, + #[substorage(v0)] + src9: SRC9Component::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage, + // Custom storage for SyncPayment functionality + fiat_balance: Map<(ContractAddress, felt252), u128>, // (user, currency) => balance + token_address: Map, // symbol => token_address + default_fiat_currency: felt252, + liquidity_bridge: ContractAddress, + initialized: bool, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + SRC9Event: SRC9Component::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event, + } + + #[constructor] + fn constructor(ref self: ContractState, public_key: felt252) { + self.account.initializer(public_key); + self.src9.initializer(); + } + + // + // Upgradeable + // + + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + self.account.assert_only_self(); + self.upgradeable.upgrade(new_class_hash); + } + } + + + // contract impl + #[abi(embed_v0)] + impl AccountImpl of Iaccount { + fn increase_balance(ref self: ContractState, amount: felt252) { + assert(amount != 0, 'Amount cannot be 0'); + let caller = get_caller_address(); + let currency = self.default_fiat_currency.read(); + let current_balance = self.fiat_balance.read((caller, currency)); + + self + .fiat_balance + .write((caller, currency), current_balance + amount.try_into().unwrap()); + } + + fn get_balance(self: @ContractState) -> felt252 { + let caller = get_caller_address(); + let currency = self.default_fiat_currency.read(); + self.fiat_balance.read((caller, currency)).into() + } + } +} + diff --git a/contracts/account/src/interfaces/Iaccount.cairo b/contracts/account/src/interfaces/Iaccount.cairo new file mode 100644 index 0000000..4b4d400 --- /dev/null +++ b/contracts/account/src/interfaces/Iaccount.cairo @@ -0,0 +1,5 @@ +#[starknet::interface] +pub trait Iaccount { + fn increase_balance(ref self: TContractState, amount: felt252); + fn get_balance(self: @TContractState) -> felt252; +} diff --git a/contracts/account/src/lib.cairo b/contracts/account/src/lib.cairo index 9a718e3..a333659 100644 --- a/contracts/account/src/lib.cairo +++ b/contracts/account/src/lib.cairo @@ -1,32 +1,7 @@ -/// Interface representing `HelloContract`. -/// This interface allows modification and retrieval of the contract balance. -#[starknet::interface] -pub trait IHelloStarknet { - /// Increase contract balance. - fn increase_balance(ref self: TContractState, amount: felt252); - /// Retrieve contract balance. - fn get_balance(self: @TContractState) -> felt252; +pub mod contract { + pub mod account; } -/// Simple contract for managing balance. -#[starknet::contract] -mod HelloStarknet { - use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; - - #[storage] - struct Storage { - balance: felt252, - } - - #[abi(embed_v0)] - impl HelloStarknetImpl of super::IHelloStarknet { - fn increase_balance(ref self: ContractState, amount: felt252) { - assert(amount != 0, 'Amount cannot be 0'); - self.balance.write(self.balance.read() + amount); - } - - fn get_balance(self: @ContractState) -> felt252 { - self.balance.read() - } - } +pub mod interfaces { + pub mod Iaccount; } diff --git a/contracts/account/tests/lib.cairo b/contracts/account/tests/lib.cairo new file mode 100644 index 0000000..4c719b5 --- /dev/null +++ b/contracts/account/tests/lib.cairo @@ -0,0 +1 @@ +pub mod test_contract; diff --git a/contracts/account/tests/test_contract.cairo b/contracts/account/tests/test_contract.cairo index 07cc846..dc6e150 100644 --- a/contracts/account/tests/test_contract.cairo +++ b/contracts/account/tests/test_contract.cairo @@ -1,27 +1,49 @@ -use account::{ - IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait, IHelloStarknetSafeDispatcher, - IHelloStarknetSafeDispatcherTrait, +use account::interfaces::Iaccount::{ + IaccountDispatcher, IaccountDispatcherTrait, IaccountSafeDispatcher, + IaccountSafeDispatcherTrait, }; -use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; -use starknet::ContractAddress; +use snforge_std::{ + ContractClassTrait, DeclareResultTrait, declare, start_cheat_caller_address, + stop_cheat_caller_address, +}; +use starknet::{ContractAddress, contract_address_const}; + + +fn setup() -> (ContractAddress, ContractAddress) { + let admin_address: ContractAddress = contract_address_const::<'1'>(); + let public_key: felt252 = 'TEST_PUBLIC_KEY'; + + let declare_result = declare("Account"); + assert(declare_result.is_ok(), 'contract decleration failed'); + + let contract_class = declare_result.unwrap().contract_class(); + let mut calldata = array![public_key]; + + let deploy_result = contract_class.deploy(@calldata); + assert(deploy_result.is_ok(), 'contract deployment failed'); -fn deploy_contract(name: ByteArray) -> ContractAddress { - let contract = declare(name).unwrap().contract_class(); - let (contract_address, _) = contract.deploy(@ArrayTrait::new()).unwrap(); - contract_address + let (contract_address, _) = deploy_result.unwrap(); + + (contract_address, admin_address) } + #[test] fn test_increase_balance() { - let contract_address = deploy_contract("HelloStarknet"); + let (contract_address, admin_address_) = setup(); + let owner = contract_address_const::<'1'>(); + + let dispatcher = IaccountDispatcher { contract_address }; - let dispatcher = IHelloStarknetDispatcher { contract_address }; + start_cheat_caller_address(dispatcher.contract_address, owner); let balance_before = dispatcher.get_balance(); assert(balance_before == 0, 'Invalid balance'); dispatcher.increase_balance(42); + stop_cheat_caller_address(owner); + let balance_after = dispatcher.get_balance(); assert(balance_after == 42, 'Invalid balance'); } @@ -29,9 +51,14 @@ fn test_increase_balance() { #[test] #[feature("safe_dispatcher")] fn test_cannot_increase_balance_with_zero_value() { - let contract_address = deploy_contract("HelloStarknet"); + let (contract_address, admin_address_) = setup(); + let owner = contract_address_const::<'1'>(); + + let dispatcher = IaccountDispatcher { contract_address }; + + start_cheat_caller_address(dispatcher.contract_address, owner); - let safe_dispatcher = IHelloStarknetSafeDispatcher { contract_address }; + let safe_dispatcher = IaccountSafeDispatcher { contract_address }; let balance_before = safe_dispatcher.get_balance().unwrap(); assert(balance_before == 0, 'Invalid balance');