Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
manlikeHB committed Nov 29, 2024
2 parents 8c8a0bf + bd32042 commit f071d17
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 42 deletions.
1 change: 1 addition & 0 deletions .snfoundry_cache/.prev_tests_failed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tokengiver_integrationtest::test_campaign::test_donate
1 change: 1 addition & 0 deletions src/base/errors.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ pub mod Errors {
pub const INITIALIZED: felt252 = 'TGN: already initialized!';
pub const INVALID_OWNER: felt252 = 'TGN: caller is not owner!';
pub const INVALID_CAMPAIGN: felt252 = 'TGN: campaign is not owner!';
pub const INSUFFICIENT_BALANCE: felt252 = 'TGN: insufficient balance!';
}
90 changes: 75 additions & 15 deletions src/campaign.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ mod TokengiverCampaign {
// IMPORT
// *************************************************************************
use core::traits::TryInto;
// use starknet::{ContractAddress, get_caller_address, get_block_timestamp};
use starknet::{
ContractAddress, get_caller_address, get_block_timestamp, SyscallResultTrait,
storage::{Map, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess},
syscalls::{library_call_syscall}
ContractAddress, get_caller_address, get_block_timestamp, ClassHash,
syscalls::deploy_syscall, SyscallResultTrait,
storage::{Map, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess}
};
use tokengiver::interfaces::ITokenGiverNft::{
ITokenGiverNftDispatcher, ITokenGiverNftDispatcherTrait
Expand All @@ -21,7 +20,7 @@ mod TokengiverCampaign {
use tokengiver::interfaces::IERC721::{IERC721Dispatcher, IERC721DispatcherTrait};
use tokengiver::interfaces::ICampaign::ICampaign;
use tokengiver::base::types::Campaign;
use tokengiver::base::errors::Errors::NOT_CAMPAIGN_OWNER;
use tokengiver::base::errors::Errors::{NOT_CAMPAIGN_OWNER, INSUFFICIENT_BALANCE};
use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};

#[derive(Drop, Copy, Serde, starknet::Store)]
Expand All @@ -44,6 +43,7 @@ mod TokengiverCampaign {
donation_count: Map<ContractAddress, u16>,
donation_details: Map<ContractAddress, DonationDetails>,
strk_address: ContractAddress,
token_giver_nft_class_hash: ClassHash,
}

// *************************************************************************
Expand All @@ -54,6 +54,7 @@ mod TokengiverCampaign {
pub enum Event {
CreateCampaign: CreateCampaign,
DonationCreated: DonationCreated,
DeployedTokenGiverNFT: DeployedTokenGiverNFT,
}

#[derive(Drop, starknet::Event)]
Expand All @@ -63,6 +64,14 @@ mod TokengiverCampaign {
#[key]
campaign_address: ContractAddress,
token_id: u256,
token_giverNft_contract_address: ContractAddress,
}

#[derive(Drop, starknet::Event)]
pub struct DeployedTokenGiverNFT {
pub campaign_id: u256,
pub token_giver_nft_contract_address: ContractAddress,
pub block_timestamp: u64,
}

#[derive(Drop, starknet::Event)]
Expand All @@ -80,47 +89,56 @@ mod TokengiverCampaign {
// CONSTRUCTOR
// *************************************************************************
#[constructor]
fn constructor(ref self: ContractState, strk_address: ContractAddress) {
fn constructor(
ref self: ContractState,
token_giver_nft_class_hash: ClassHash,
strk_address: ContractAddress
) {
self.token_giver_nft_class_hash.write(token_giver_nft_class_hash);
self.strk_address.write(strk_address);
}

// *************************************************************************
// EXTERNAL FUNCTIONS
// *************************************************************************
#[external(v0)]
#[abi(embed_v0)]
impl CampaignImpl of ICampaign<ContractState> {
fn create_campaign(
ref self: ContractState,
token_giverNft_contract_address: ContractAddress,
registry_hash: felt252,
implementation_hash: felt252,
salt: felt252,
recipient: ContractAddress
) -> ContractAddress {
let count: u16 = self.count.read() + 1;
ITokenGiverNftDispatcher { contract_address: token_giverNft_contract_address }
.mint_token_giver_nft(recipient);

let token_Nft_address = self.token_giver_nft_class_hash.read();
let token_giverNft_contract_address = self
.deploy_token_giver_nft(token_Nft_address, count.into());
let token_id = ITokenGiverNftDispatcher {
contract_address: token_giverNft_contract_address
}
.get_user_token_id(recipient);

let campaign_address = IRegistryLibraryDispatcher {
class_hash: registry_hash.try_into().unwrap()
}
.create_account(
implementation_hash, token_giverNft_contract_address, token_id, salt
);

let new_campaign = Campaign {
campaign_address, campaign_owner: recipient, metadata_URI: "",
};

self.campaign.write(campaign_address, new_campaign);
self.campaigns.write(count, campaign_address);
self.count.write(count);
self.emit(CreateCampaign { owner: recipient, campaign_address, token_id });
self
.emit(
CreateCampaign {
owner: recipient,
campaign_address,
token_id,
token_giverNft_contract_address
}
);
campaign_address
}

Expand Down Expand Up @@ -153,6 +171,23 @@ mod TokengiverCampaign {
self.withdrawal_balance.write(campaign_address, amount);
}

// withdraw function
fn withdraw(ref self: ContractState, campaign_address: ContractAddress, amount: u256) {
let campaign: Campaign = self.campaign.read(campaign_address);
let caller: ContractAddress = get_caller_address();

assert(caller == campaign.campaign_owner, NOT_CAMPAIGN_OWNER);

let available_balance: u256 = self.withdrawal_balance.read(campaign_address);
assert(amount <= available_balance, INSUFFICIENT_BALANCE);

let token_address = self.strk_address.read();
let token_dispatcher = IERC20Dispatcher { contract_address: token_address };
let transfer_result = token_dispatcher.transfer(caller, amount);
assert!(transfer_result, "Transfer failed");
self.withdrawal_balance.write(campaign_address, available_balance - amount);
}

// *************************************************************************
// GETTERS
// *************************************************************************
Expand Down Expand Up @@ -249,4 +284,29 @@ mod TokengiverCampaign {
);
}
}

#[generate_trait]
impl InternalImpl of InternalTrait {
fn deploy_token_giver_nft(
ref self: ContractState, token_giver_nft_class_hash: ClassHash, campaign_id: u16
) -> ContractAddress {
let mut constructor_calldata = array![];
campaign_id.serialize(ref constructor_calldata);

let (token_giver_nft_address, _) = deploy_syscall(
token_giver_nft_class_hash, 'salt'.into(), constructor_calldata.span(), false
)
.unwrap();

self
.emit(
DeployedTokenGiverNFT {
campaign_id: campaign_id.into(),
token_giver_nft_contract_address: token_giver_nft_address,
block_timestamp: get_block_timestamp()
}
);
token_giver_nft_address
}
}
}
2 changes: 1 addition & 1 deletion src/interfaces/ICampaign.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use tokengiver::base::types::Campaign;
pub trait ICampaign<TState> {
fn create_campaign(
ref self: TState,
token_giverNft_contract_address: ContractAddress,
registry_hash: felt252,
implementation_hash: felt252,
salt: felt252,
Expand All @@ -21,6 +20,7 @@ pub trait ICampaign<TState> {
fn set_available_withdrawal(ref self: TState, campaign_address: ContractAddress, amount: u256);
fn set_donations(ref self: TState, campaign_address: ContractAddress, amount: u256);
fn donate(ref self: TState, campaign_address: ContractAddress, amount: u256, token_id: u256);
fn withdraw(ref self: TState, campaign_address: ContractAddress, amount: u256);


// Getters
Expand Down
9 changes: 1 addition & 8 deletions src/presets/erc20.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,6 @@ mod MyToken {
let initial_supply = 100_000_000_u256;

self.erc20.initializer(name, symbol);
self.mint(recipient, initial_supply);
}

#[generate_trait]
impl internalImpl of InternalTrait {
fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) {
self.erc20.mint(recipient, amount);
}
self.erc20.mint(recipient, initial_supply);
}
}
35 changes: 17 additions & 18 deletions tests/test_campaign.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use snforge_std::{
declare, start_cheat_caller_address, stop_cheat_caller_address, ContractClassTrait,
DeclareResultTrait, spy_events, EventSpyAssertionsTrait,
DeclareResultTrait, spy_events, EventSpyAssertionsTrait, get_class_hash
};

use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};
Expand Down Expand Up @@ -38,8 +38,10 @@ const ADMIN: felt252 = 'ADMIN';
fn __setup__() -> (ContractAddress, ContractAddress) {
let class_hash = declare("TokengiverCampaign").unwrap().contract_class();
let strk_address = deploy_erc20();
let nft_class_hash = __declare_token_giver_NFT__();

let mut calldata = array![];
nft_class_hash.serialize(ref calldata);
strk_address.serialize(ref calldata);

let (contract_address, _) = class_hash.deploy(@calldata).unwrap();
Expand All @@ -48,14 +50,8 @@ fn __setup__() -> (ContractAddress, ContractAddress) {
}


fn __setup_token_giver_NFT__() -> ContractAddress {
// deploy events
let nft_class_hash = declare("TokenGiverNFT").unwrap().contract_class();

let mut events_constructor_calldata: Array<felt252> = array![ADMIN];
let (nft_contract_address, _) = nft_class_hash.deploy(@events_constructor_calldata).unwrap();

return (nft_contract_address);
fn __declare_token_giver_NFT__() -> ClassHash {
*declare("TokenGiverNFT").unwrap().contract_class().class_hash
}

fn deploy_erc20() -> ContractAddress {
Expand All @@ -73,46 +69,48 @@ fn deploy_erc20() -> ContractAddress {
#[fork("Mainnet")]
fn test_donate() {
let (token_giver_address, strk_address) = __setup__();
let token_giverNft_contract_address = __setup_token_giver_NFT__();
let token_giver = ICampaignDispatcher { contract_address: token_giver_address };
let strk_dispatcher = IERC20Dispatcher { contract_address: strk_address };
let random_id = 1;

let mut spy = spy_events();



//create campaign
start_cheat_caller_address(token_giver_address, RECIPIENT());


let campaign_address = token_giver
.create_campaign(
token_giverNft_contract_address,
REGISTRY_HASH(),
IMPLEMENTATION_HASH(),
SALT(),
RECIPIENT()
);
.create_campaign(REGISTRY_HASH(), IMPLEMENTATION_HASH(), SALT(), RECIPIENT());

stop_cheat_caller_address(token_giver_address);


/// Transfer STRK to Donor
start_cheat_caller_address(strk_address, OWNER());
let amount = 2000000; //
strk_dispatcher.transfer(DONOR(), amount);
assert(strk_dispatcher.balance_of(DONOR()) >= amount, 'strk bal too low');
stop_cheat_caller_address(strk_address);


// approve allowance
start_cheat_caller_address(strk_address, DONOR());
strk_dispatcher.approve(token_giver_address, amount);
stop_cheat_caller_address(strk_address);


// donate
start_cheat_caller_address(token_giver_address, DONOR());
token_giver.donate(campaign_address, amount, random_id);
stop_cheat_caller_address(token_giver_address);


assert(strk_dispatcher.balance_of(DONOR()) == 0, 'wrong balance');
assert(token_giver.get_donations(campaign_address) == amount, 'wrong donation amount');
assert(token_giver.get_donation_count(campaign_address) == 1, 'wrong donation amount');


let expected_event = Event::DonationCreated(
DonationCreated {
campaign_id: random_id,
Expand All @@ -122,5 +120,6 @@ fn test_donate() {
block_timestamp: get_block_timestamp(),
}
);

spy.assert_emitted(@array![(token_giver.contract_address, expected_event)]);
}

0 comments on commit f071d17

Please sign in to comment.