Skip to content

enh: apply to campaign pool with Unit tests #62

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
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
3 changes: 3 additions & 0 deletions src/base/errors.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ pub mod Errors {
pub const INVALID_CAMPAIGN: felt252 = 'TGN: campaign is not owner!';
pub const INSUFFICIENT_BALANCE: felt252 = 'TGN: insufficient balance!';
pub const TRANSFER_FAILED: felt252 = 'TGN: transfer failed!';
pub const INVALID_CAMPAIGN_ADDRESS: felt252 = 'TGN: invalid campaign address!';
pub const INVALID_POOL_ADDRESS: felt252 = 'TGN: invalid pool address!';
pub const INVALID_AMOUNT: felt252 = 'TGN: invalid amount!';
}
47 changes: 46 additions & 1 deletion src/campaign_pool.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod CampaignPools {
syscalls::deploy_syscall, SyscallResultTrait, syscalls, class_hash::class_hash_const,
storage::{Map, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess}
};
use tokengiver::base::errors::Errors;
use tokengiver::interfaces::ICampaignPool::ICampaignPool;
use tokengiver::base::types::CampaignPool;
use openzeppelin::access::ownable::OwnableComponent;
Expand Down Expand Up @@ -206,7 +207,51 @@ mod CampaignPools {
campaign_address: ContractAddress,
campaign_pool_address: ContractAddress,
amount: u256
) {}
) {
// Get caller address to identify who is applying
let caller = get_caller_address();

// Validate that the campaign exists
// We can do this by attempting to read campaign data and asserting it's valid
let campaign_exists = self
.campaign_pool
.read(campaign_address)
.campaign_address != starknet::contract_address_const::<0>();
assert(campaign_exists, Errors::INVALID_CAMPAIGN_ADDRESS);

// Validate that the campaign pool exists
let pool_exists = self
.campaign_pool
.read(campaign_pool_address)
.campaign_address != starknet::contract_address_const::<0>();
assert(pool_exists, Errors::INVALID_POOL_ADDRESS);

// Ensure amount is valid (not zero)
assert(amount > 0, Errors::INVALID_AMOUNT);

// Store the application in the mapping
self
.campaign_pool_applications
.write(campaign_address, (campaign_pool_address, amount));

// Emit an application event
self
.emit(
ApplicationMade {
campaign_pool_address: campaign_pool_address,
campaign_address: campaign_address,
recipient: caller,
amount: amount,
block_timestamp: get_block_timestamp(),
}
);
}
fn get_campaign_application(
self: @ContractState, campaign_address: ContractAddress
) -> (ContractAddress, u256) {
self.campaign_pool_applications.read(campaign_address)
}

fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
self.ownable.assert_only_owner();
self.upgradeable.upgrade(new_class_hash);
Expand Down
4 changes: 4 additions & 0 deletions src/interfaces/ICampaignPool.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ pub trait ICampaignPool<TState> {
amount: u256
);
fn upgrade(ref self: TState, new_class_hash: ClassHash);

fn get_campaign_application(
self: @TState, campaign_address: ContractAddress
) -> (ContractAddress, u256);
}
154 changes: 152 additions & 2 deletions tests/test_campaign_pool.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use snforge_std::{
DeclareResultTrait, spy_events, EventSpyAssertionsTrait, get_class_hash
};


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

use starknet::{ContractAddress, ClassHash, get_block_timestamp};
Expand All @@ -13,7 +14,9 @@ use tokengiver::interfaces::ICampaignPool::{
use tokengiver::interfaces::ITokenGiverNft::{
ITokenGiverNftDispatcher, ITokenGiverNftDispatcherTrait
};
use tokengiver::campaign_pool::CampaignPools::{Event, DonationMade, CreateCampaignPool};
use tokengiver::campaign_pool::CampaignPools::{
Event, DonationMade, CreateCampaignPool, ApplicationMade
};
use token_bound_accounts::interfaces::ILockable::{ILockableDispatcher, ILockableDispatcherTrait};

fn REGISTRY_HASH() -> felt252 {
Expand Down Expand Up @@ -130,7 +133,7 @@ fn test_create_campaign_pool() {


#[test]
#[fork("Mainnet")]
#[fork("SEPOLIA_LATEST")]
fn test_create_campaign_pool_event_emission() {
// Get the initial setup
let (token_giver_address, _, nft_address) = __setup__();
Expand Down Expand Up @@ -172,3 +175,150 @@ fn test_create_campaign_pool_event_emission() {
);
// spy.assert_emitted(@array![(token_giver.contract_address, expected_event)]);
}
#[test]
#[fork("SEPOLIA_LATEST")]
fn test_apply_to_campaign_pool_success() {
// Set up a campaign pool and a campaign
let (token_giver_address, strk_address, nft_address) = __setup__();
let token_giver = ICampaignPoolDispatcher { contract_address: token_giver_address };
let mut spy = spy_events();

// Create the required parameters for campaign pool
let registry_hash = REGISTRY_HASH();
let implementation_hash = IMPLEMENTATION_HASH();
let salt: felt252 = SALT();
let recipient: ContractAddress = RECIPIENT();
let campaign_id: u256 = 10;
let pool_id: u256 = 11;

// Create campaign pool
start_cheat_caller_address(token_giver_address, recipient);
let campaign_pool_address = token_giver
.create_campaign_pool(registry_hash, implementation_hash, salt, recipient, pool_id);

// Create campaign with different salt
let campaign_salt: felt252 = 'campaign_salt';
let campaign_address = token_giver
.create_campaign_pool(
registry_hash, implementation_hash, campaign_salt, recipient, campaign_id
);
stop_cheat_caller_address(token_giver_address);

// Amount to request
let amount: u256 = 100;

// Apply to campaign pool
start_cheat_caller_address(token_giver_address, recipient);
token_giver.apply_to_campaign_pool(campaign_address, campaign_pool_address, amount);
stop_cheat_caller_address(token_giver_address);

// Verify application was stored correctly
start_cheat_caller_address(token_giver_address, recipient);
let (stored_pool, stored_amount) = token_giver.get_campaign_application(campaign_address);
stop_cheat_caller_address(token_giver_address);

assert(stored_pool == campaign_pool_address, 'Wrong pool address stored');
assert(stored_amount == amount, 'Wrong amount stored');

// Check event emission
let expected_event = Event::ApplicationMade(
ApplicationMade {
campaign_pool_address: campaign_pool_address,
campaign_address: campaign_address,
recipient: recipient,
amount: amount,
block_timestamp: get_block_timestamp(),
}
);

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

#[test]
#[fork("Mainnet")]
#[should_panic(expected: ('TGN: invalid campaign address!',))]
fn test_apply_with_invalid_campaign_address() {
// Set up a campaign pool
let (token_giver_address, _, _) = __setup__();
let token_giver = ICampaignPoolDispatcher { contract_address: token_giver_address };

let registry_hash = REGISTRY_HASH();
let implementation_hash = IMPLEMENTATION_HASH();
let salt: felt252 = SALT();
let recipient: ContractAddress = RECIPIENT();
let pool_id: u256 = 12;

// Create campaign pool
start_cheat_caller_address(token_giver_address, recipient);
let campaign_pool_address = token_giver
.create_campaign_pool(registry_hash, implementation_hash, salt, recipient, pool_id);
stop_cheat_caller_address(token_giver_address);

// Use an invalid campaign address
let invalid_campaign_address: ContractAddress = starknet::contract_address_const::<0x123>();
let amount: u256 = 100;

// This should panic with "TGN: invalid campaign address!"
start_cheat_caller_address(token_giver_address, recipient);
token_giver.apply_to_campaign_pool(invalid_campaign_address, campaign_pool_address, amount);
stop_cheat_caller_address(token_giver_address);
}

#[test]
#[fork("Mainnet")]
#[should_panic(expected: ('TGN: invalid pool address!',))]
fn test_apply_with_invalid_pool_address() {
// Set up a campaign
let (token_giver_address, _, _) = __setup__();
let token_giver = ICampaignPoolDispatcher { contract_address: token_giver_address };

let registry_hash = REGISTRY_HASH();
let implementation_hash = IMPLEMENTATION_HASH();
let salt: felt252 = SALT();
let recipient: ContractAddress = RECIPIENT();
let campaign_id: u256 = 13;

// Create campaign
start_cheat_caller_address(token_giver_address, recipient);
let campaign_address = token_giver
.create_campaign_pool(registry_hash, implementation_hash, salt, recipient, campaign_id);
stop_cheat_caller_address(token_giver_address);

// Use an invalid pool address
let invalid_pool_address: ContractAddress = starknet::contract_address_const::<0x456>();
let amount: u256 = 100;

// This should panic with "TGN: invalid pool address!"
start_cheat_caller_address(token_giver_address, recipient);
token_giver.apply_to_campaign_pool(campaign_address, invalid_pool_address, amount);
stop_cheat_caller_address(token_giver_address);
}

#[test]
#[fork("Mainnet")]
#[should_panic(expected: ('TGN: invalid amount!',))]
fn test_apply_with_zero_amount() {
// Set up a campaign pool and a campaign
let (token_giver_address, _, _) = __setup__();
let token_giver = ICampaignPoolDispatcher { contract_address: token_giver_address };

let registry_hash = REGISTRY_HASH();
let implementation_hash = IMPLEMENTATION_HASH();
let salt1: felt252 = 'salt1';
let salt2: felt252 = 'salt2';
let recipient: ContractAddress = RECIPIENT();
let campaign_id: u256 = 14;
let pool_id: u256 = 15;

// Create campaign pool and campaign
start_cheat_caller_address(token_giver_address, recipient);
let campaign_pool_address = token_giver
.create_campaign_pool(registry_hash, implementation_hash, salt1, recipient, pool_id);
let campaign_address = token_giver
.create_campaign_pool(registry_hash, implementation_hash, salt2, recipient, campaign_id);

// Try to apply with zero amount, should fail
let zero_amount: u256 = 0;
token_giver.apply_to_campaign_pool(campaign_address, campaign_pool_address, zero_amount);
stop_cheat_caller_address(token_giver_address);
}
Loading