Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor current_goal_state #167

Merged
merged 4 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 27 additions & 17 deletions contracts/src/fund.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ mod Fund {
up_votes: u32,
voters: LegacyMap::<ContractAddress, u32>,
goal: u256,
current_goal_state: u256,
token_address: ContractAddress,
state: u8,
}

Expand All @@ -92,15 +92,20 @@ mod Fund {
// *************************************************************************
#[constructor]
fn constructor(
ref self: ContractState, id: u128, owner: ContractAddress, name: felt252, goal: u256
ref self: ContractState,
id: u128,
owner: ContractAddress,
name: felt252,
goal: u256,
token_address: ContractAddress
) {
self.id.write(id);
self.owner.write(owner);
self.name.write(name);
self.reason.write(" ");
self.up_votes.write(FundConstants::INITIAL_UP_VOTES);
self.goal.write(goal);
self.current_goal_state.write(FundConstants::INITIAL_GOAL);
self.token_address.write(token_address);
saimeunt marked this conversation as resolved.
Show resolved Hide resolved
self.state.write(FundStates::RECOLLECTING_VOTES);
}

Expand Down Expand Up @@ -171,24 +176,25 @@ mod Fund {
self.state.read() == FundStates::RECOLLECTING_DONATIONS,
'Fund not recollecting dons!'
);
self.current_goal_state.write(self.current_goal_state.read() + strks);
if self.current_goal_state.read() >= self.goal.read() {
self.token().transfer_from(get_caller_address(), get_contract_address(), strks);
let current_balance = self.getCurrentGoalState();
if current_balance >= self.goal.read() {
self.state.write(FundStates::CLOSED);
}

// Emit receiveDonation event
self
.emit(
DonationReceived {
current_balance: self.current_goal_state.read(),
current_balance,
donated_strks: strks,
donator_address: get_caller_address(),
fund_contract_address: get_contract_address(),
}
)
}
fn getCurrentGoalState(self: @ContractState) -> u256 {
saimeunt marked this conversation as resolved.
Show resolved Hide resolved
return self.current_goal_state.read();
self.token().balance_of(get_contract_address())
}
fn setState(ref self: ContractState, state: u8) {
self.state.write(state);
Expand All @@ -206,25 +212,29 @@ mod Fund {
assert(self.state.read() == FundStates::CLOSED, 'Fund not close goal yet.');
assert(self.getCurrentGoalState() > 0, 'Fund hasnt reached its goal yet');
// Withdraw
let starknet_contract_address = contract_address_const::<
StarknetConstants::STRK_TOKEN_ADDRESS
>();
let starknet_dispatcher = IERC20Dispatcher {
contract_address: starknet_contract_address
};
let balance = starknet_dispatcher.balance_of(get_contract_address());
//TODO: Calculate balance to deposit in owner address and in fund manager address (95% and 5%), also transfer the amount to fund manager address.
starknet_dispatcher.transfer(self.getOwner(), balance);
let withdrawn_amount = self.getCurrentGoalState();
// TODO: Calculate balance to deposit in owner address and in fund manager address (95%
// and 5%), also transfer the amount to fund manager address.
self.token().transfer(self.getOwner(), withdrawn_amount);
assert(self.getCurrentGoalState() != 0, 'Fund hasnt reached its goal yet');
self.setState(4);
self
.emit(
DonationWithdraw {
owner_address: self.getOwner(),
fund_contract_address: get_contract_address(),
withdrawn_amount: balance
withdrawn_amount
}
);
}
}
// *************************************************************************
// INTERNALS
// *************************************************************************
#[generate_trait]
impl InternalImpl of InternalTrait {
fn token(self: @ContractState) -> IERC20Dispatcher {
saimeunt marked this conversation as resolved.
Show resolved Hide resolved
IERC20Dispatcher { contract_address: self.token_address.read() }
}
}
}
7 changes: 5 additions & 2 deletions contracts/src/fundManager.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use starknet::class_hash::ClassHash;

#[starknet::interface]
pub trait IFundManager<TContractState> {
fn newFund(ref self: TContractState, name: felt252, goal: u256);
fn newFund(ref self: TContractState, name: felt252, goal: u256, token_address: ContractAddress);
saimeunt marked this conversation as resolved.
Show resolved Hide resolved
fn getCurrentId(self: @TContractState) -> u128;
fn getFund(self: @TContractState, id: u128) -> ContractAddress;
fn getOwner(self: @TContractState) -> ContractAddress;
Expand Down Expand Up @@ -70,12 +70,15 @@ mod FundManager {

#[abi(embed_v0)]
impl FundManagerImpl of super::IFundManager<ContractState> {
fn newFund(ref self: ContractState, name: felt252, goal: u256) {
fn newFund(
ref self: ContractState, name: felt252, goal: u256, token_address: ContractAddress
) {
let mut call_data: Array<felt252> = array![];
Serde::serialize(@self.current_id.read(), ref call_data);
Serde::serialize(@get_caller_address(), ref call_data);
Serde::serialize(@name, ref call_data);
Serde::serialize(@goal, ref call_data);
Serde::serialize(@token_address, ref call_data);
let (new_fund_address, _) = deploy_syscall(
self.fund_class_hash.read(), 12345, call_data.span(), false
)
Expand Down
2 changes: 2 additions & 0 deletions contracts/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
pub mod constants;
// TOKEN
pub mod token;
// FUND
pub mod fund;
pub mod fundManager;
Expand Down
36 changes: 36 additions & 0 deletions contracts/src/token.cairo
saimeunt marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#[starknet::contract]
mod Token {
use openzeppelin::token::erc20::ERC20Component;
use starknet::ContractAddress;

component!(path: ERC20Component, storage: erc20, event: ERC20Event);

#[abi(embed_v0)]
impl ERC20Impl = ERC20Component::ERC20Impl<ContractState>;
impl ERC20InternalImpl = ERC20Component::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
erc20: ERC20Component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
ERC20Event: ERC20Component::Event
}

#[constructor]
fn constructor(
ref self: ContractState,
name: ByteArray,
symbol: ByteArray,
initial_supply: u256,
recipient: ContractAddress
) {
self.erc20.initializer(name, symbol);
self.erc20._mint(recipient, initial_supply);
}
}
53 changes: 39 additions & 14 deletions contracts/tests/test_fund.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,27 @@
// ***************************************************************************************
use starknet::{ContractAddress, contract_address_const};

use snforge_std::{declare, ContractClassTrait, start_cheat_caller_address_global};
use snforge_std::{
declare, ContractClassTrait, start_cheat_caller_address_global, start_cheat_caller_address,
cheat_caller_address, CheatSpan
};

use openzeppelin::utils::serde::SerializedAppend;
use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};

use gostarkme::fund::IFundDispatcher;
use gostarkme::fund::IFundDispatcherTrait;
use gostarkme::constants::{funds::{fund_manager_constants::FundManagerConstants},};

fn TOKEN_NAME() -> ByteArray {
"NAME"
}
fn TOKEN_SYMBOL() -> ByteArray {
"SYMBOL"
}
fn TOKEN_SUPPLY() -> u256 {
1000
}
fn ID() -> u128 {
1
}
Expand All @@ -32,22 +45,30 @@ fn REASON() -> ByteArray {
fn GOAL() -> u256 {
1000
}
fn _setup_() -> ContractAddress {
fn _setup_() -> (ContractAddress, ContractAddress) {
let token_contract = declare("Token").unwrap();
let mut token_calldata = array![];
token_calldata.append_serde(TOKEN_NAME());
token_calldata.append_serde(TOKEN_SYMBOL());
token_calldata.append_serde(TOKEN_SUPPLY());
token_calldata.append_serde(FUND_MANAGER());
let (token_address, _) = token_contract.deploy(@token_calldata).unwrap();
let contract = declare("Fund").unwrap();
let mut calldata: Array<felt252> = array![];
calldata.append_serde(ID());
calldata.append_serde(OWNER());
calldata.append_serde(NAME());
calldata.append_serde(GOAL());
calldata.append_serde(token_address);
let (contract_address, _) = contract.deploy(@calldata).unwrap();
contract_address
(contract_address, token_address)
}
// ***************************************************************************************
// TEST
// ***************************************************************************************
#[test]
fn test_constructor() {
let contract_address = _setup_();
let (contract_address, _) = _setup_();
let dispatcher = IFundDispatcher { contract_address };
let id = dispatcher.getId();
let owner = dispatcher.getOwner();
Expand All @@ -69,7 +90,7 @@ fn test_constructor() {

#[test]
fn test_set_name() {
let contract_address = _setup_();
let (contract_address, _) = _setup_();
let dispatcher = IFundDispatcher { contract_address };
let name = dispatcher.getName();
assert(name == NAME(), 'Invalid name');
Expand All @@ -81,7 +102,7 @@ fn test_set_name() {

#[test]
fn test_set_reason() {
let contract_address = _setup_();
let (contract_address, _) = _setup_();
let dispatcher = IFundDispatcher { contract_address };
let reason = dispatcher.getReason();
assert(reason == " ", 'Invalid reason');
Expand All @@ -93,7 +114,7 @@ fn test_set_reason() {

#[test]
fn test_set_goal() {
let contract_address = _setup_();
let (contract_address, _) = _setup_();
let dispatcher = IFundDispatcher { contract_address };
let goal = dispatcher.getGoal();
assert(goal == GOAL(), 'Invalid goal');
Expand All @@ -105,7 +126,7 @@ fn test_set_goal() {

#[test]
fn test_receive_vote_successful() {
let contract_address = _setup_();
let (contract_address, _) = _setup_();
let dispatcher = IFundDispatcher { contract_address };
dispatcher.receiveVote();
let me = dispatcher.getVoter();
Expand All @@ -118,7 +139,7 @@ fn test_receive_vote_successful() {
#[test]
#[should_panic(expected: ('User already voted!',))]
fn test_receive_vote_unsuccessful_double_vote() {
let contract_address = _setup_();
let (contract_address, _) = _setup_();
let dispatcher = IFundDispatcher { contract_address };
dispatcher.receiveVote();
let me = dispatcher.getVoter();
Expand All @@ -132,13 +153,17 @@ fn test_receive_vote_unsuccessful_double_vote() {

#[test]
fn test_receive_donation_successful() {
let contract_address = _setup_();
let (contract_address, token_address) = _setup_();
let dispatcher = IFundDispatcher { contract_address };
// Put state as recollecting dons
dispatcher.setState(2);
// Put 10 strks as goal, only fund manager
start_cheat_caller_address_global(FUND_MANAGER());
start_cheat_caller_address(contract_address, FUND_MANAGER());
dispatcher.setGoal(10);
// approve
cheat_caller_address(token_address, FUND_MANAGER(), CheatSpan::TargetCalls(1));
let token = IERC20Dispatcher { contract_address: token_address };
token.approve(contract_address, 10);
// Donate 5 strks
dispatcher.receiveDonation(5);
let current_goal_state = dispatcher.getCurrentGoalState();
Expand All @@ -152,18 +177,18 @@ fn test_receive_donation_successful() {
#[test]
#[should_panic(expected: ('Fund not recollecting dons!',))]
fn test_receive_donation_unsuccessful_wrong_state() {
let contract_address = _setup_();
let (contract_address, _) = _setup_();
let dispatcher = IFundDispatcher { contract_address };
// Put a wrong state to receive donations
dispatcher.setState(1);
// Donate
// Donate
dispatcher.receiveDonation(5);
}

#[test]
#[should_panic(expected: ("You are not the fund manager",))]
fn test_set_goal_unauthorized() {
let contract_address = _setup_();
let (contract_address, _) = _setup_();
let dispatcher = IFundDispatcher { contract_address };
// Change the goal without being the fund manager
dispatcher.setGoal(22);
Expand Down
28 changes: 23 additions & 5 deletions contracts/tests/test_fund_manager.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ use openzeppelin::utils::serde::SerializedAppend;
use gostarkme::fundManager::IFundManagerDispatcher;
use gostarkme::fundManager::IFundManagerDispatcherTrait;

fn TOKEN_NAME() -> ByteArray {
"NAME"
}
fn TOKEN_SYMBOL() -> ByteArray {
"SYMBOL"
}
fn TOKEN_SUPPLY() -> u256 {
1_000_000_000
}
fn ID() -> u128 {
1
}
Expand All @@ -33,14 +42,23 @@ fn GOAL() -> u256 {
1000
}

fn _setup_() -> (ContractAddress, ClassHash) {
fn _setup_() -> (ContractAddress, ClassHash, ContractAddress) {
// Token
let token_contract = declare("Token").unwrap();
let mut token_calldata = array![];
token_calldata.append_serde(TOKEN_NAME());
token_calldata.append_serde(TOKEN_SYMBOL());
token_calldata.append_serde(TOKEN_SUPPLY());
token_calldata.append_serde(OWNER());
let (token_address, _) = token_contract.deploy(@token_calldata).unwrap();
// Fund
let fund = declare("Fund").unwrap();
let mut fund_calldata: Array<felt252> = array![];
fund_calldata.append_serde(ID());
fund_calldata.append_serde(OWNER());
fund_calldata.append_serde(NAME());
fund_calldata.append_serde(GOAL());
fund_calldata.append_serde(token_address);
let (fund_contract_address, _) = fund.deploy(@fund_calldata).unwrap();
let fund_class_hash = get_class_hash(fund_contract_address);

Expand All @@ -50,7 +68,7 @@ fn _setup_() -> (ContractAddress, ClassHash) {
fund_manager_calldata.append_serde(fund_class_hash);
let (contract_address, _) = fund_manager.deploy(@fund_manager_calldata).unwrap();

return (contract_address, fund_class_hash,);
return (contract_address, fund_class_hash, token_address);
}

// ******************************************************************************
Expand All @@ -60,7 +78,7 @@ fn _setup_() -> (ContractAddress, ClassHash) {
#[test]
fn test_constructor() {
start_cheat_caller_address_global(OWNER());
let (contract_address, fund_class_hash) = _setup_();
let (contract_address, fund_class_hash, _) = _setup_();
let fund_manager_contract = IFundManagerDispatcher { contract_address };
let expected_fund_address = fund_manager_contract.getFundClassHash();
let owner = fund_manager_contract.getOwner();
Expand All @@ -71,9 +89,9 @@ fn test_constructor() {
#[test]
fn test_new_fund() {
start_cheat_caller_address_global(OWNER());
let (contract_address, fund_class_hash) = _setup_();
let (contract_address, fund_class_hash, token_address) = _setup_();
let fund_manager_contract = IFundManagerDispatcher { contract_address };
fund_manager_contract.newFund(NAME(), GOAL());
fund_manager_contract.newFund(NAME(), GOAL(), token_address);
let expected_fund_class_hash = get_class_hash(fund_manager_contract.getFund(1));
let current_id = fund_manager_contract.getCurrentId();
assert(expected_fund_class_hash == fund_class_hash, 'Invalid fund address');
Expand Down