From 7fdf978e1745edcf29ad16977c8806b15e50be28 Mon Sep 17 00:00:00 2001 From: Lamsy Date: Mon, 10 Jun 2024 17:03:18 +0100 Subject: [PATCH 1/5] simple_vault test implementation --- listings/applications/simple_vault/Scarb.toml | 1 + .../applications/simple_vault/src/lib.cairo | 1 + .../simple_vault/src/simple_vault.cairo | 162 ++++++++++++++++-- 3 files changed, 147 insertions(+), 17 deletions(-) diff --git a/listings/applications/simple_vault/Scarb.toml b/listings/applications/simple_vault/Scarb.toml index 346b42bb..561214d3 100644 --- a/listings/applications/simple_vault/Scarb.toml +++ b/listings/applications/simple_vault/Scarb.toml @@ -5,6 +5,7 @@ edition = '2023_11' [dependencies] starknet.workspace = true +erc20 = { path = "../erc20" } [scripts] test.workspace = true diff --git a/listings/applications/simple_vault/src/lib.cairo b/listings/applications/simple_vault/src/lib.cairo index b08098a0..b5492840 100644 --- a/listings/applications/simple_vault/src/lib.cairo +++ b/listings/applications/simple_vault/src/lib.cairo @@ -1,4 +1,5 @@ mod simple_vault; + #[cfg(test)] mod tests; diff --git a/listings/applications/simple_vault/src/simple_vault.cairo b/listings/applications/simple_vault/src/simple_vault.cairo index 22cd61b9..e8c0191b 100644 --- a/listings/applications/simple_vault/src/simple_vault.cairo +++ b/listings/applications/simple_vault/src/simple_vault.cairo @@ -4,23 +4,34 @@ use starknet::ContractAddress; // we need to have the interface of the remote ERC20 contract defined to import the Dispatcher. #[starknet::interface] pub trait IERC20 { - fn name(self: @TContractState) -> felt252; - fn symbol(self: @TContractState) -> felt252; - fn decimals(self: @TContractState) -> u8; - fn total_supply(self: @TContractState) -> u256; - fn balance_of(self: @TContractState, account: ContractAddress) -> u256; - fn allowance(self: @TContractState, owner: ContractAddress, spender: ContractAddress) -> u256; - fn transfer(ref self: TContractState, recipient: ContractAddress, amount: u256) -> bool; + fn get_name(self: @TContractState) -> felt252; + fn get_symbol(self: @TContractState) -> felt252; + fn get_decimals(self: @TContractState) -> u8; + fn get_total_supply(self: @TContractState) -> felt252; + fn balance_of(self: @TContractState, account: ContractAddress) -> felt252; + fn allowance( + self: @TContractState, owner: ContractAddress, spender: ContractAddress + ) -> felt252; + fn transfer(ref self: TContractState, recipient: ContractAddress, amount: felt252); fn transfer_from( - ref self: TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256 - ) -> bool; - fn approve(ref self: TContractState, spender: ContractAddress, amount: u256) -> bool; + ref self: TContractState, + sender: ContractAddress, + recipient: ContractAddress, + amount: felt252 + ); + fn approve(ref self: TContractState, spender: ContractAddress, amount: felt252); + fn increase_allowance(ref self: TContractState, spender: ContractAddress, added_value: felt252); + fn decrease_allowance( + ref self: TContractState, spender: ContractAddress, subtracted_value: felt252 + ); } #[starknet::interface] pub trait ISimpleVault { fn deposit(ref self: TContractState, amount: u256); fn withdraw(ref self: TContractState, shares: u256); + fn user_balance_of(ref self: TContractState, account: ContractAddress) -> u256; + fn contract_total_supply(ref self: TContractState) -> u256; } #[starknet::contract] @@ -51,11 +62,22 @@ pub mod SimpleVault { self.total_supply.write(self.total_supply.read() - shares); self.balance_of.write(from, self.balance_of.read(from) - shares); } + } #[abi(embed_v0)] impl SimpleVault of super::ISimpleVault { - fn deposit(ref self: ContractState, amount: u256) { + + fn user_balance_of(ref self: ContractState, account: ContractAddress) -> u256 { + self.balance_of.read(account) + } + + fn contract_total_supply(ref self: ContractState) -> u256 { + self.total_supply.read() + } + + + fn deposit(ref self: ContractState, amount: u256){ // a = amount // B = balance of token before deposit // T = total supply @@ -71,12 +93,15 @@ pub mod SimpleVault { if self.total_supply.read() == 0 { shares = amount; } else { - let balance = self.token.read().balance_of(this); + let balance: u256 = self.token.read().balance_of(this).try_into() + .unwrap(); shares = (amount * self.total_supply.read()) / balance; } - - PrivateFunctions::_mint(ref self, caller, shares); - self.token.read().transfer_from(caller, this, amount); + + PrivateFunctions::_mint(ref self, caller, shares); + + let amount_felt252: felt252 = amount.low.into(); + self.token.read().transfer_from(caller, this, amount_felt252); } fn withdraw(ref self: ContractState, shares: u256) { @@ -91,11 +116,114 @@ pub mod SimpleVault { let caller = get_caller_address(); let this = get_contract_address(); - let balance = self.token.read().balance_of(this); + let balance = self.user_balance_of(this); let amount = (shares * balance) / self.total_supply.read(); PrivateFunctions::_burn(ref self, caller, shares); - self.token.read().transfer(caller, amount); + let amount_felt252: felt252 = amount.low.into(); + self.token.read().transfer(caller, amount_felt252); } } } +// ANCHOR_END: simple_vault + +#[cfg(test)] +mod tests { + use core::traits::Into; + use super::{ + SimpleVault, ISimpleVaultDispatcher, ISimpleVaultDispatcherTrait, IERC20Dispatcher, + IERC20DispatcherTrait + }; + + // use erc20::token::IERC20; + use erc20::token::{IERC20DispatcherTrait as IERC20DispatcherTrait_token, + IERC20Dispatcher as IERC20Dispatcher_token + }; + + use core::num::traits::Zero; + + use starknet::testing::{set_contract_address, set_account_contract_address}; + use starknet::{ + ContractAddress, SyscallResultTrait, syscalls::deploy_syscall, get_caller_address, + contract_address_const + }; + + const token_name: felt252 = 'myToken'; + const decimals: u8 = 18; + const initial_supply: felt252 = 100000; + const symbols: felt252 = 'mtk'; + + fn deploy() -> (ISimpleVaultDispatcher, ContractAddress, IERC20Dispatcher_token) { + let _token_address: ContractAddress = contract_address_const::<'token_address'>(); + let caller = contract_address_const::<'caller'>(); + + let (token_contract_address, _) = deploy_syscall( + erc20::token::erc20::TEST_CLASS_HASH.try_into().unwrap(), + caller.into(), + array![caller.into(), 'myToken', '8', '1000'.into(), 'MYT'].span(), + false + ) + .unwrap_syscall(); + + let (contract_address, _) = deploy_syscall( + SimpleVault::TEST_CLASS_HASH.try_into().unwrap(), + 0, + array![token_contract_address.into()].span(), + false + ) + .unwrap_syscall(); + ( + ISimpleVaultDispatcher { contract_address }, + contract_address, + IERC20Dispatcher_token { contract_address: token_contract_address } + ) + } + + #[test] + fn test_deposit() { + let caller = contract_address_const::<'caller'>(); + let (dispatcher, vault_address, token_dispatcher) = deploy(); + + // Approve the vault to transfer tokens on behalf of the caller + let amount: felt252 = 10.into(); + token_dispatcher.approve(vault_address.into(), amount); + set_contract_address(caller); + + // Deposit tokens into the vault + let amount: u256 = 10.into(); + let _deposit = dispatcher.deposit(amount); + println!("deposit :{:?}", _deposit); + + // Check balances and total supply + let balance_of_caller = dispatcher.user_balance_of(caller); + let total_supply = dispatcher.contract_total_supply(); + + assert(balance_of_caller == amount, 'Deposit failed'); + assert(total_supply == amount, 'total supply mismatch'); + + } + + #[test] + fn test_deposit_withdraw() { + let caller = contract_address_const::<'caller'>(); + let (dispatcher, vault_address, token_dispatcher) = deploy(); + + // Approve the vault to transfer tokens on behalf of the caller + let amount: felt252 = 10.into(); + token_dispatcher.approve(vault_address.into(), amount); + set_contract_address(caller); + set_account_contract_address(vault_address); + + // Deposit tokens into the vault + let amount: u256 = 10.into(); + dispatcher.deposit(amount); + dispatcher.withdraw(amount); + + // Check balances of user in the vault after withdraw + let balance_of_caller = dispatcher.user_balance_of(caller); + + assert(balance_of_caller == 0.into(), 'withdraw failed'); + } + + +} \ No newline at end of file From 66aba46676c9c28d08e80b03ad5f30877ffc6560 Mon Sep 17 00:00:00 2001 From: Lamsy Date: Sat, 20 Jul 2024 00:59:16 +0100 Subject: [PATCH 2/5] Added dict cheatsheet --- .../cairo_cheatsheet/src/dict_example.cairo | 17 +++++++++++++++++ .../cairo_cheatsheet/src/lib.cairo | 1 + 2 files changed, 18 insertions(+) create mode 100644 listings/getting-started/cairo_cheatsheet/src/dict_example.cairo diff --git a/listings/getting-started/cairo_cheatsheet/src/dict_example.cairo b/listings/getting-started/cairo_cheatsheet/src/dict_example.cairo new file mode 100644 index 00000000..08e60992 --- /dev/null +++ b/listings/getting-started/cairo_cheatsheet/src/dict_example.cairo @@ -0,0 +1,17 @@ +fn dict() { + // ANCHOR: sheet + + let mut Auctions: Felt252Dict = Default::default(); + + Auctions.insert('Bola', 30); + Auctions.insert('Maria', 40); + + let bola_balance = Auctions.get('Bola'); + assert!(bola_balance == 30, "Bola balance should be 30"); + + let maria_balance = Auctions.get('Maria'); + assert!(maria_balance == 40, "Maria balance should be 40"); + +// ANCHOR_END: sheet + +} \ No newline at end of file diff --git a/listings/getting-started/cairo_cheatsheet/src/lib.cairo b/listings/getting-started/cairo_cheatsheet/src/lib.cairo index b495fd77..37dae1cd 100644 --- a/listings/getting-started/cairo_cheatsheet/src/lib.cairo +++ b/listings/getting-started/cairo_cheatsheet/src/lib.cairo @@ -9,3 +9,4 @@ mod struct_example; mod type_casting_example; mod while_let_example; mod if_let_example; +mod dict_example; \ No newline at end of file From 408514e9f446d667c53ab78554439365634dec69 Mon Sep 17 00:00:00 2001 From: Lamsy Date: Sat, 20 Jul 2024 00:59:39 +0100 Subject: [PATCH 3/5] Added dict cheatsheet --- Scarb.lock | 3 +++ src/SUMMARY.md | 1 + src/getting-started/cairo_cheatsheet/dict.md | 12 ++++++++++++ 3 files changed, 16 insertions(+) create mode 100644 src/getting-started/cairo_cheatsheet/dict.md diff --git a/Scarb.lock b/Scarb.lock index ef3d064e..b2646014 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -108,6 +108,9 @@ version = "0.1.0" [[package]] name = "simple_vault" version = "0.1.0" +dependencies = [ + "erc20", +] [[package]] name = "snforge_std" diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 10436298..ec59eda8 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -40,6 +40,7 @@ Summary - [Tuples](./getting-started/cairo_cheatsheet/tuples.md) - [Struct](./getting-started/cairo_cheatsheet/struct.md) - [Type casting](./getting-started/cairo_cheatsheet/type_casting.md) + - [Dict](./ch00/cairo_cheatsheet/dict.md) # Components diff --git a/src/getting-started/cairo_cheatsheet/dict.md b/src/getting-started/cairo_cheatsheet/dict.md new file mode 100644 index 00000000..eaa654ae --- /dev/null +++ b/src/getting-started/cairo_cheatsheet/dict.md @@ -0,0 +1,12 @@ +# Dictionary + +A dictionary is a data structure used to store key-value pairs, enabling efficient data retrieval. The keys and values in a Cairo dictionary can be of various types, including Felt252. Dictionaries provide fast access to data, as they allow for quick lookups, insertions, and deletions based on the keys.The core functionality of a `Felt252Dict` is implemented in the trait `Felt252DictTrait`, which includes all basic operations. Among them, we can find: + +- `insert(felt252, T) -> ()` to write values to a dictionary instance. +- `get(felt252) -> T` to read values from it. + +For example: + +```rust +{{#include ../../../listings/getting-started/cairo_cheatsheet/src/dict_example.cairo:sheet}} +``` From 0c68e7c0b8e61ee20b90bb330fd0416855f2f53c Mon Sep 17 00:00:00 2001 From: Lamsy Date: Mon, 22 Jul 2024 19:14:11 +0100 Subject: [PATCH 4/5] a little clean up --- .../cairo_cheatsheet/src/dict_example.cairo | 7 +++---- listings/getting-started/cairo_cheatsheet/src/lib.cairo | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/listings/getting-started/cairo_cheatsheet/src/dict_example.cairo b/listings/getting-started/cairo_cheatsheet/src/dict_example.cairo index 08e60992..13a70676 100644 --- a/listings/getting-started/cairo_cheatsheet/src/dict_example.cairo +++ b/listings/getting-started/cairo_cheatsheet/src/dict_example.cairo @@ -1,6 +1,5 @@ +// ANCHOR: sheet fn dict() { - // ANCHOR: sheet - let mut Auctions: Felt252Dict = Default::default(); Auctions.insert('Bola', 30); @@ -11,7 +10,7 @@ fn dict() { let maria_balance = Auctions.get('Maria'); assert!(maria_balance == 40, "Maria balance should be 40"); - +} // ANCHOR_END: sheet -} \ No newline at end of file + diff --git a/listings/getting-started/cairo_cheatsheet/src/lib.cairo b/listings/getting-started/cairo_cheatsheet/src/lib.cairo index 37dae1cd..246ffb4c 100644 --- a/listings/getting-started/cairo_cheatsheet/src/lib.cairo +++ b/listings/getting-started/cairo_cheatsheet/src/lib.cairo @@ -9,4 +9,4 @@ mod struct_example; mod type_casting_example; mod while_let_example; mod if_let_example; -mod dict_example; \ No newline at end of file +mod dict_example; From 0bb0fcae9a04cbf89ad552e64e0e44be998f9866 Mon Sep 17 00:00:00 2001 From: Lamsy Date: Mon, 22 Jul 2024 19:25:58 +0100 Subject: [PATCH 5/5] a little clean up --- Scarb.lock | 4 ++++ src/SUMMARY.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Scarb.lock b/Scarb.lock index c96e3249..c84915d1 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -131,6 +131,10 @@ dependencies = [ "openzeppelin", ] +[[package]] +name = "simple_storage" +version = "0.1.0" + [[package]] name = "simple_vault" version = "0.1.0" diff --git a/src/SUMMARY.md b/src/SUMMARY.md index fcdb5c87..8079da68 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -41,7 +41,7 @@ Summary - [Tuples](./getting-started/cairo_cheatsheet/tuples.md) - [Struct](./getting-started/cairo_cheatsheet/struct.md) - [Type casting](./getting-started/cairo_cheatsheet/type_casting.md) - - [Dict](./ch00/cairo_cheatsheet/dict.md) + - [Dict](./getting-started/cairo_cheatsheet/dict.md) # Components