diff --git a/contracts/account/src/base/errors.cairo b/contracts/account/src/base/errors.cairo index a960f7d..832d5eb 100644 --- a/contracts/account/src/base/errors.cairo +++ b/contracts/account/src/base/errors.cairo @@ -2,4 +2,4 @@ pub mod AccountErrors { pub const CANNOT_BE_ADDR_ZERO: felt252 = 'Address cannot be Zero'; pub const AMOUNT_CANNOT_BE_ZERO: felt252 = 'Amount cannot be Zero'; pub const CURRENCY_IS_REQUIRED: felt252 = 'Currency cannot be Zero'; -} \ No newline at end of file +} diff --git a/contracts/account/src/contract/account.cairo b/contracts/account/src/contract/account.cairo index b7dbdca..98e36f6 100644 --- a/contracts/account/src/contract/account.cairo +++ b/contracts/account/src/contract/account.cairo @@ -3,13 +3,16 @@ #[starknet::contract(account)] mod Account { - use core::num::traits::Zero; - use account::interfaces::Iaccount::Iaccount; - use account::interfaces::ILiquidityBridge::{ILiquidityBridge, ILiquidityBridgeDispatcher, ILiquidityBridgeDispatcherTrait}; use account::base::errors::AccountErrors; + use account::interfaces::ILiquidityBridge::{ + ILiquidityBridge, ILiquidityBridgeDispatcher, ILiquidityBridgeDispatcherTrait, + }; + use account::interfaces::Iaccount::Iaccount; + use core::num::traits::Zero; use openzeppelin::account::AccountComponent; use openzeppelin::account::extensions::SRC9Component; use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use openzeppelin::upgrades::UpgradeableComponent; use openzeppelin::upgrades::interface::IUpgradeable; use starknet::storage::{ @@ -17,7 +20,6 @@ mod Account { StoragePointerWriteAccess, }; use starknet::{ClassHash, ContractAddress, get_caller_address, get_contract_address}; - use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; component!(path: AccountComponent, storage: account, event: AccountEvent); @@ -54,7 +56,7 @@ mod Account { liquidity_bridge: ContractAddress, initialized: bool, public_key: felt252, - approved_tokens: Map, // symbol => token_address + approved_tokens: Map // symbol => token_address } #[event] @@ -69,7 +71,9 @@ mod Account { #[flat] UpgradeableEvent: UpgradeableComponent::Event, TokenApproved: TokenApproved, - PaymentMade: PaymentMade + PaymentMade: PaymentMade, + FiatDeposit: FiatDeposit, + FiatWithdrawal: FiatWithdrawal, } #[derive(Drop, starknet::Event)] @@ -89,6 +93,22 @@ mod Account { pub used_bridge: bool, } + + #[derive(Drop, starknet::Event)] + pub struct FiatDeposit { + pub user: ContractAddress, + pub currency: felt252, + pub amount: u128, + } + + #[derive(Drop, starknet::Event)] + pub struct FiatWithdrawal { + pub account_address: ContractAddress, + pub currency: felt252, + pub amount: u128, + pub recipient: ContractAddress, + } + #[constructor] fn constructor(ref self: ContractState, public_key: felt252) { self.account.initializer(public_key); @@ -210,7 +230,7 @@ mod Account { } fn initialize( - ref self: ContractState, public_key: felt252, liquidity_bridge: ContractAddress + ref self: ContractState, public_key: felt252, liquidity_bridge: ContractAddress, ) { assert(!self.initialized.read(), 'Already initialized'); self.public_key.write(public_key); @@ -222,7 +242,8 @@ mod Account { self.account.assert_only_self(); assert(self.initialized.read(), 'Not initialized'); assert( - self.approved_tokens.read(symbol) == 0.try_into().unwrap(), 'Token already approved' + self.approved_tokens.read(symbol) == 0.try_into().unwrap(), + 'Token already approved', ); assert(token_address != 0.try_into().unwrap(), 'Token address cannot be 0'); @@ -241,7 +262,7 @@ mod Account { symbol, token_address, amount: 10000000000000000000, - } + }, ); } @@ -261,8 +282,55 @@ mod Account { self.initialized.read() } - fn get_fiat_balance(self: @ContractState, user: ContractAddress, currency: felt252) -> u128 { - 123456789 + // fn get_fiat_balance(self: @ContractState, user: ContractAddress, currency: felt252) -> + // u128 { + // 123456789 + // } + + fn get_fiat_balance( + self: @ContractState, user: ContractAddress, currency: felt252, + ) -> u128 { + self.fiat_balance.read((user, currency)) + } + + + fn deposit_fiat(ref self: ContractState, currency: felt252, amount: u128) { + self.account.assert_only_self(); + assert(!amount.is_zero(), AccountErrors::AMOUNT_CANNOT_BE_ZERO); + assert(!currency.is_zero(), AccountErrors::CURRENCY_IS_REQUIRED); + let account_address = get_contract_address(); + let current_balance = self.fiat_balance.read((account_address, currency)); + self.fiat_balance.write((account_address, currency), current_balance + amount); + self + .emit( + FiatDeposit { + user: account_address, currency, amount: current_balance + amount, + }, + ); + } + + fn withdraw_fiat( + ref self: ContractState, currency: felt252, amount: u128, recipient: ContractAddress, + ) { + self.account.assert_only_self(); + assert(!amount.is_zero(), AccountErrors::AMOUNT_CANNOT_BE_ZERO); + assert(!currency.is_zero(), AccountErrors::CURRENCY_IS_REQUIRED); + assert(!recipient.is_zero(), AccountErrors::CANNOT_BE_ADDR_ZERO); + + let account_address = get_contract_address(); + let current_balance = self.fiat_balance.read((account_address, currency)); + assert(current_balance >= amount, 'Insufficient balance'); + + self.fiat_balance.write((account_address, currency), current_balance - amount); + self + .emit( + FiatWithdrawal { + account_address, + currency, + amount: current_balance - amount, + recipient: recipient, + }, + ); } } } diff --git a/contracts/account/src/interfaces/Iaccount.cairo b/contracts/account/src/interfaces/Iaccount.cairo index 41f2e4d..55155f9 100644 --- a/contracts/account/src/interfaces/Iaccount.cairo +++ b/contracts/account/src/interfaces/Iaccount.cairo @@ -17,4 +17,8 @@ pub trait Iaccount { use_liquidity_bridge: bool, ) -> bool; fn get_fiat_balance(self: @TContractState, user: ContractAddress, currency: felt252) -> u128; + fn deposit_fiat(ref self: TContractState, currency: felt252, amount: u128); + fn withdraw_fiat( + ref self: TContractState, currency: felt252, amount: u128, recipient: ContractAddress, + ); } diff --git a/contracts/account/src/lib.cairo b/contracts/account/src/lib.cairo index f737179..243e41f 100644 --- a/contracts/account/src/lib.cairo +++ b/contracts/account/src/lib.cairo @@ -3,8 +3,8 @@ pub mod contract { } pub mod interfaces { - pub mod Iaccount; pub mod ILiquidityBridge; + pub mod Iaccount; } diff --git a/contracts/account/tests/lib.cairo b/contracts/account/tests/lib.cairo index 5c58933..9647297 100644 --- a/contracts/account/tests/lib.cairo +++ b/contracts/account/tests/lib.cairo @@ -1,2 +1,2 @@ -pub mod test_contract; pub mod test_account; +pub mod test_contract; diff --git a/contracts/account/tests/test_account.cairo b/contracts/account/tests/test_account.cairo index 4bb11d2..334b9be 100644 --- a/contracts/account/tests/test_account.cairo +++ b/contracts/account/tests/test_account.cairo @@ -48,7 +48,7 @@ fn test_approve_token() { let dispatcher = IaccountDispatcher { contract_address }; let token_address: ContractAddress = contract_address_const::< - 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d + 0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d, >(); start_cheat_caller_address(contract_address, contract_address); @@ -62,3 +62,96 @@ fn test_approve_token() { let approved_token = dispatcher.get_approved_token('STRK'); assert(approved_token == token_address, 'Invalid approved token'); } + + +#[test] +fn test_deposit_fiat() { + let (contract_address, _) = setup(); + let dispatcher = IaccountDispatcher { contract_address }; + let currency = 'USD'; + let amount: u128 = 1000; + + start_cheat_caller_address(contract_address, contract_address); + dispatcher.initialize(1, 2.try_into().unwrap()); + + dispatcher.deposit_fiat(currency, amount); + + let balance = dispatcher.get_fiat_balance(contract_address, currency); + assert(balance == amount.into(), 'Bal equal to deposited amount'); + + stop_cheat_caller_address(contract_address); +} + + +#[test] +#[should_panic(expected: ('Insufficient balance',))] +fn test_withdraw_fiat_insufficient_balance() { + let (contract_address, _) = setup(); + let dispatcher = IaccountDispatcher { contract_address }; + let currency = 'USD'; + let recipient: ContractAddress = contract_address_const::<'2'>(); + + start_cheat_caller_address(contract_address, contract_address); + dispatcher.initialize(1, 2.try_into().unwrap()); + + dispatcher.withdraw_fiat(currency, 1000, recipient); + + stop_cheat_caller_address(contract_address); +} + + +#[test] +fn test_multiple_deposits() { + let (contract_address, _) = setup(); + let dispatcher = IaccountDispatcher { contract_address }; + let currency = 'USD'; + let amount1: u128 = 1000; + let amount2: u128 = 500; + + start_cheat_caller_address(contract_address, contract_address); + dispatcher.initialize(1, 2.try_into().unwrap()); + + dispatcher.deposit_fiat(currency, amount1); + // Second deposit + dispatcher.deposit_fiat(currency, amount2); + + let balance = dispatcher.get_fiat_balance(contract_address, currency); + assert(balance == (amount1 + amount2).into(), 'Bal equ sum of both deposits'); + + stop_cheat_caller_address(contract_address); +} + +#[test] +fn test_different_currencies() { + let (contract_address, _) = setup(); + let dispatcher = IaccountDispatcher { contract_address }; + let usd_amount: u128 = 1000; + let eur_amount: u128 = 500; + + start_cheat_caller_address(contract_address, contract_address); + dispatcher.initialize(1, 2.try_into().unwrap()); + + dispatcher.deposit_fiat('USD', usd_amount); + dispatcher.deposit_fiat('EUR', eur_amount); + + let usd_balance = dispatcher.get_fiat_balance(contract_address, 'USD'); + let eur_balance = dispatcher.get_fiat_balance(contract_address, 'EUR'); + + assert(usd_balance == usd_amount.into(), 'USD balance incorrect'); + assert(eur_balance == eur_amount.into(), 'EUR balance incorrect'); + + stop_cheat_caller_address(contract_address); +} + +#[test] +#[should_panic(expected: ('Amount cannot be Zero',))] +fn test_deposit_zero_amount() { + let (contract_address, _) = setup(); + let dispatcher = IaccountDispatcher { contract_address }; + + start_cheat_caller_address(contract_address, contract_address); + dispatcher.initialize(1, 2.try_into().unwrap()); + + dispatcher.deposit_fiat('USD', 0); +} +