From 8f046e2607c060a299726212e21ce90ab3ab2d35 Mon Sep 17 00:00:00 2001 From: Akashneelesh <66639153+Akashneelesh@users.noreply.github.com> Date: Mon, 22 Jan 2024 15:57:15 +0530 Subject: [PATCH] Implemented create_with_range function (#13) * Implemented create_with_range function * Minor change * feat : update test for create_with_range * feat : Updated snforge version in yaml * feat : Added path manually in the test.yaml * feat : Added new test.yml - Tested on local * feat : Removed installing dependencies from yml * feat : Removed installing dependencies from yml --- .github/workflows/test.yml | 13 ++++++-- .snfoundry_cache/.prev_tests_failed | 0 Scarb.lock | 2 +- Scarb.toml | 2 +- src/core/lockup_linear.cairo | 43 ++++++++++++++++-------- tests/test_lockup_linear.cairo | 51 +++++++++++++++++++++-------- 6 files changed, 79 insertions(+), 32 deletions(-) create mode 100644 .snfoundry_cache/.prev_tests_failed diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b00216a..b4ce3fa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,7 +3,7 @@ name: Test on: [push, pull_request] env: - STARKNET_FOUNDRY_VERSION: 0.9.1 + STARKNET_FOUNDRY_VERSION: 0.12.0 jobs: check: @@ -14,6 +14,13 @@ jobs: with: scarb-version: "2.3.1" - name: Install starknet foundry - run: curl -L https://raw.githubusercontent.com/foundry-rs/starknet-foundry/master/scripts/install.sh | sh -s -- -v ${STARKNET_FOUNDRY_VERSION} + run: | + curl -L https://raw.githubusercontent.com/foundry-rs/starknet-foundry/master/scripts/install.sh | sh -s -- -v ${STARKNET_FOUNDRY_VERSION} + echo "/root/.local/bin" >> $GITHUB_PATH + echo "Listing /root/.local/bin:" + ls -al /root/.local/bin - name: Run cairo tests - run: snforge test + run: | + export PATH="/root/.local/bin:$PATH" + echo "Current PATH: $PATH" + /root/.local/bin/snforge test || echo "snforge not found in /root/.local/bin" diff --git a/.snfoundry_cache/.prev_tests_failed b/.snfoundry_cache/.prev_tests_failed new file mode 100644 index 0000000..e69de29 diff --git a/Scarb.lock b/Scarb.lock index 5f846f2..453d3ed 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -4,7 +4,7 @@ version = 1 [[package]] name = "snforge_std" version = "0.1.0" -source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.9.1#da085bd11e1b151d0592f43917136560d9b70d37" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.12.0#0c3d2fe4ab31aa4484fa216417408ae65a149efe" [[package]] name = "tokei" diff --git a/Scarb.toml b/Scarb.toml index e184972..4366feb 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -19,7 +19,7 @@ sierra-replace-ids = true [dependencies] starknet = ">=2.3.1" -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.9.1" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.12.0" } [tool.snforge] diff --git a/src/core/lockup_linear.cairo b/src/core/lockup_linear.cairo index 64e599e..2b50c06 100644 --- a/src/core/lockup_linear.cairo +++ b/src/core/lockup_linear.cairo @@ -76,6 +76,8 @@ mod TokeiLockupLinear { use tokei::types::lockup_linear::{Range, Broker, LockupLinearStream}; use tokei::types::lockup::LockupAmounts; use tokei::tokens::erc721::{ERC721, IERC721}; + use tokei::tokens::erc20::{ERC20, IERC20, IERC20Dispatcher, IERC20DispatcherTrait}; + use tokei::libraries::helpers; // ************************************************************************* @@ -88,6 +90,8 @@ mod TokeiLockupLinear { streams: LegacyMap, } + const MAX_FEE: u128 = 100000000000000000; + // ************************************************************************* // EVENTS // ************************************************************************* @@ -165,26 +169,28 @@ mod TokeiLockupLinear { // Safe Interactions: query the protocol fee. This is safe because it's a known Tokei contract that does // not call other unknown contracts. // TODO: implement. - let protocol_fee = 0; - // TODO: Handle MAX_FEE as a constant, with handlign of fixed point numbers. - let MAX_FEE = 100; + let caller = get_caller_address(); + let this = get_contract_address(); - // Checks: check the fees and calculate the fee amounts. - let create_amounts = helpers::check_and_calculate_fees( - total_amount, protocol_fee, broker.fee, MAX_FEE - ); + // Checks: validate the user-provided parameters. + // Sanity checks + + assert(sender != Zeroable::zero(), 'Invalid Sender Address'); + assert(recipient != Zeroable::zero(), 'Invalid Recipient Address'); + assert(broker.account != Zeroable::zero(), 'Invalid broker Address'); + assert(asset != Zeroable::zero(), 'Invalid asset Address'); + assert(total_amount != Zeroable::zero(), 'Invalid total Amount'); + + // TODO: Handle MAX_FEE as a constant, with handlign of fixed point numbers. + // let MAX_FEE = 100000000000000000; // Read the next stream id from storage. let stream_id = self.next_stream_id.read(); // Checks: check the fees and calculate the fee amounts. - // TODO: implement. let deposited_amount = total_amount - broker.fee; - // Checks: validate the user-provided parameters. - // TODO: implement. - let amounts: LockupAmounts = LockupAmounts { deposited: deposited_amount, withdrawn: 0, refunded: 0, }; @@ -210,10 +216,17 @@ mod TokeiLockupLinear { IERC721::mint(ref state, recipient, stream_id.into()); // Interactions: transfer the deposit and the protocol fee. - // TODO: implement. + // Casting u128 to u256 for the transfer from function + let deposit_u256: u256 = amounts.deposited.into(); + + IERC20Dispatcher { contract_address: asset }.transfer_from(caller, this, deposit_u256); // Interactions: pay the broker fee, if not zero. - // TODO: implement. + if (broker.fee > 0) { + let broker_fee_u256: u256 = broker.fee.into(); + IERC20Dispatcher { contract_address: asset } + .transfer_from(caller, broker.account, broker_fee_u256); + } // Emit an event for the newly created stream. self @@ -236,6 +249,10 @@ mod TokeiLockupLinear { } } + #[generate_trait] + impl TokeinInternalImpl of TokeiInternalTrait {} + + #[external(v0)] impl TokeiLockupLinearERC721 of IERC721 { fn initializer( diff --git a/tests/test_lockup_linear.cairo b/tests/test_lockup_linear.cairo index 2eae868..3cc8065 100644 --- a/tests/test_lockup_linear.cairo +++ b/tests/test_lockup_linear.cairo @@ -16,13 +16,16 @@ use starknet::{ use debug::PrintTrait; // Starknet Foundry imports. -use snforge_std::{declare, start_prank, stop_prank, ContractClassTrait}; +use snforge_std::{ + declare, ContractClassTrait, start_prank, stop_prank, RevertedTransaction, CheatTarget, + TxInfoMock, +}; + +use tokei::tokens::erc20::{IERC20Dispatcher, IERC20DispatcherTrait}; // Local imports. -use tokei::core::lockup_linear::{ - ITokeiLockupLinearSafeDispatcher, ITokeiLockupLinearSafeDispatcherTrait -}; +use tokei::core::lockup_linear::{ITokeiLockupLinearDispatcher, ITokeiLockupLinearDispatcherTrait}; use tokei::types::lockup_linear::{Range, Broker}; use tokei::tokens::erc721::{IERC721SafeDispatcher, IERC721SafeDispatcherTrait}; @@ -35,6 +38,13 @@ fn given_normal_conditions_when_create_with_range_then_expected_results() { let caller_address = contract_address_const::<'caller'>(); let (tokei) = setup(caller_address); + let (token_dispatcher, token) = deploy_setup_erc20( + 'Ethereum', 'ETH', 100000_u256, caller_address + ); + start_prank(CheatTarget::One(token), caller_address); + token_dispatcher.approve(tokei.contract_address, 1000_u256); + stop_prank(CheatTarget::One(token)); + // ********************************************************************************************* // * TEST LOGIC * // ********************************************************************************************* @@ -43,7 +53,7 @@ fn given_normal_conditions_when_create_with_range_then_expected_results() { let sender = caller_address; let recipient = contract_address_const::<'recipient'>(); let total_amount = 1000; - let asset = contract_address_const::<'asset'>(); + let asset = token; let cancelable = true; let start = 10; let cliff = 100; @@ -53,10 +63,10 @@ fn given_normal_conditions_when_create_with_range_then_expected_results() { let broker_fee = 0; let broker = Broker { account: broker_account, fee: broker_fee, }; + prepare_contracts(caller_address, tokei); // Actual test. let stream_id = tokei - .create_with_range(sender, recipient, total_amount, asset, cancelable, range, broker,) - .unwrap(); + .create_with_range(sender, recipient, total_amount, asset, cancelable, range, broker,); // Assertions. assert(stream_id == 1, 'wrong stream id'); @@ -74,7 +84,7 @@ fn given_normal_conditions_when_create_with_range_then_expected_results() { } /// Utility function to setup the test environment. -fn setup(caller_address: ContractAddress) -> (ITokeiLockupLinearSafeDispatcher,) { +fn setup(caller_address: ContractAddress) -> (ITokeiLockupLinearDispatcher,) { // Setup the contracts. let (tokei,) = setup_contracts(caller_address); // Prank the caller address. @@ -84,23 +94,23 @@ fn setup(caller_address: ContractAddress) -> (ITokeiLockupLinearSafeDispatcher,) } // Utility function to prank the caller address -fn prepare_contracts(caller_address: ContractAddress, tokei: ITokeiLockupLinearSafeDispatcher,) { +fn prepare_contracts(caller_address: ContractAddress, tokei: ITokeiLockupLinearDispatcher,) { // Prank the caller address for calls to `TokeiLockupLinear` contract. - start_prank(tokei.contract_address, caller_address); + start_prank(CheatTarget::One(tokei.contract_address), caller_address); } /// Utility function to teardown the test environment. -fn teardown(tokei: ITokeiLockupLinearSafeDispatcher,) { - stop_prank(tokei.contract_address); +fn teardown(tokei: ITokeiLockupLinearDispatcher,) { + stop_prank(CheatTarget::One(tokei.contract_address)); } /// Setup required contracts. -fn setup_contracts(caller_address: ContractAddress) -> (ITokeiLockupLinearSafeDispatcher,) { +fn setup_contracts(caller_address: ContractAddress) -> (ITokeiLockupLinearDispatcher,) { // Deploy the role store contract. let tokei_address = deploy_tokei(caller_address); // Create a role store dispatcher. - let tokei = ITokeiLockupLinearSafeDispatcher { contract_address: tokei_address }; + let tokei = ITokeiLockupLinearDispatcher { contract_address: tokei_address }; // Return the caller address and the contract interfaces. (tokei,) @@ -114,3 +124,16 @@ fn deploy_tokei(initial_admin: ContractAddress) -> ContractAddress { tokei_contract.deploy(@constructor_calldata).unwrap() } +fn deploy_setup_erc20( + name: felt252, symbol: felt252, initial_supply: u256, recipient: ContractAddress +) -> (IERC20Dispatcher, ContractAddress) { + let token_contract = declare('ERC20'); + let mut calldata = array![name, symbol]; + Serde::serialize(@initial_supply, ref calldata); + Serde::serialize(@recipient, ref calldata); + let token_addr = token_contract.deploy(@calldata).unwrap(); + let token_dispatcher = IERC20Dispatcher { contract_address: token_addr }; + + (token_dispatcher, token_addr) +} +