Skip to content

Commit 4ad19cc

Browse files
authored
Merge pull request #62 from suhas-sensei/apply-to-campaign-pool-#59
enh: apply to campaign pool with Unit tests
2 parents 4f53e0b + c7cc918 commit 4ad19cc

File tree

4 files changed

+205
-3
lines changed

4 files changed

+205
-3
lines changed

src/base/errors.cairo

+3
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@ pub mod Errors {
1010
pub const INVALID_CAMPAIGN: felt252 = 'TGN: campaign is not owner!';
1111
pub const INSUFFICIENT_BALANCE: felt252 = 'TGN: insufficient balance!';
1212
pub const TRANSFER_FAILED: felt252 = 'TGN: transfer failed!';
13+
pub const INVALID_CAMPAIGN_ADDRESS: felt252 = 'TGN: invalid campaign address!';
14+
pub const INVALID_POOL_ADDRESS: felt252 = 'TGN: invalid pool address!';
15+
pub const INVALID_AMOUNT: felt252 = 'TGN: invalid amount!';
1316
}

src/campaign_pool.cairo

+46-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod CampaignPools {
1111
syscalls::deploy_syscall, SyscallResultTrait, syscalls, class_hash::class_hash_const,
1212
storage::{Map, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess}
1313
};
14+
use tokengiver::base::errors::Errors;
1415
use tokengiver::interfaces::ICampaignPool::ICampaignPool;
1516
use tokengiver::base::types::CampaignPool;
1617
use openzeppelin::access::ownable::OwnableComponent;
@@ -206,7 +207,51 @@ mod CampaignPools {
206207
campaign_address: ContractAddress,
207208
campaign_pool_address: ContractAddress,
208209
amount: u256
209-
) {}
210+
) {
211+
// Get caller address to identify who is applying
212+
let caller = get_caller_address();
213+
214+
// Validate that the campaign exists
215+
// We can do this by attempting to read campaign data and asserting it's valid
216+
let campaign_exists = self
217+
.campaign_pool
218+
.read(campaign_address)
219+
.campaign_address != starknet::contract_address_const::<0>();
220+
assert(campaign_exists, Errors::INVALID_CAMPAIGN_ADDRESS);
221+
222+
// Validate that the campaign pool exists
223+
let pool_exists = self
224+
.campaign_pool
225+
.read(campaign_pool_address)
226+
.campaign_address != starknet::contract_address_const::<0>();
227+
assert(pool_exists, Errors::INVALID_POOL_ADDRESS);
228+
229+
// Ensure amount is valid (not zero)
230+
assert(amount > 0, Errors::INVALID_AMOUNT);
231+
232+
// Store the application in the mapping
233+
self
234+
.campaign_pool_applications
235+
.write(campaign_address, (campaign_pool_address, amount));
236+
237+
// Emit an application event
238+
self
239+
.emit(
240+
ApplicationMade {
241+
campaign_pool_address: campaign_pool_address,
242+
campaign_address: campaign_address,
243+
recipient: caller,
244+
amount: amount,
245+
block_timestamp: get_block_timestamp(),
246+
}
247+
);
248+
}
249+
fn get_campaign_application(
250+
self: @ContractState, campaign_address: ContractAddress
251+
) -> (ContractAddress, u256) {
252+
self.campaign_pool_applications.read(campaign_address)
253+
}
254+
210255
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
211256
self.ownable.assert_only_owner();
212257
self.upgradeable.upgrade(new_class_hash);

src/interfaces/ICampaignPool.cairo

+4
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,8 @@ pub trait ICampaignPool<TState> {
2626
amount: u256
2727
);
2828
fn upgrade(ref self: TState, new_class_hash: ClassHash);
29+
30+
fn get_campaign_application(
31+
self: @TState, campaign_address: ContractAddress
32+
) -> (ContractAddress, u256);
2933
}

tests/test_campaign_pool.cairo

+152-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use snforge_std::{
33
DeclareResultTrait, spy_events, EventSpyAssertionsTrait, get_class_hash
44
};
55

6+
67
use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};
78

89
use starknet::{ContractAddress, ClassHash, get_block_timestamp};
@@ -13,7 +14,9 @@ use tokengiver::interfaces::ICampaignPool::{
1314
use tokengiver::interfaces::ITokenGiverNft::{
1415
ITokenGiverNftDispatcher, ITokenGiverNftDispatcherTrait
1516
};
16-
use tokengiver::campaign_pool::CampaignPools::{Event, DonationMade, CreateCampaignPool};
17+
use tokengiver::campaign_pool::CampaignPools::{
18+
Event, DonationMade, CreateCampaignPool, ApplicationMade
19+
};
1720
use token_bound_accounts::interfaces::ILockable::{ILockableDispatcher, ILockableDispatcherTrait};
1821

1922
fn REGISTRY_HASH() -> felt252 {
@@ -130,7 +133,7 @@ fn test_create_campaign_pool() {
130133

131134

132135
#[test]
133-
#[fork("Mainnet")]
136+
#[fork("SEPOLIA_LATEST")]
134137
fn test_create_campaign_pool_event_emission() {
135138
// Get the initial setup
136139
let (token_giver_address, _, nft_address) = __setup__();
@@ -172,3 +175,150 @@ fn test_create_campaign_pool_event_emission() {
172175
);
173176
// spy.assert_emitted(@array![(token_giver.contract_address, expected_event)]);
174177
}
178+
#[test]
179+
#[fork("SEPOLIA_LATEST")]
180+
fn test_apply_to_campaign_pool_success() {
181+
// Set up a campaign pool and a campaign
182+
let (token_giver_address, strk_address, nft_address) = __setup__();
183+
let token_giver = ICampaignPoolDispatcher { contract_address: token_giver_address };
184+
let mut spy = spy_events();
185+
186+
// Create the required parameters for campaign pool
187+
let registry_hash = REGISTRY_HASH();
188+
let implementation_hash = IMPLEMENTATION_HASH();
189+
let salt: felt252 = SALT();
190+
let recipient: ContractAddress = RECIPIENT();
191+
let campaign_id: u256 = 10;
192+
let pool_id: u256 = 11;
193+
194+
// Create campaign pool
195+
start_cheat_caller_address(token_giver_address, recipient);
196+
let campaign_pool_address = token_giver
197+
.create_campaign_pool(registry_hash, implementation_hash, salt, recipient, pool_id);
198+
199+
// Create campaign with different salt
200+
let campaign_salt: felt252 = 'campaign_salt';
201+
let campaign_address = token_giver
202+
.create_campaign_pool(
203+
registry_hash, implementation_hash, campaign_salt, recipient, campaign_id
204+
);
205+
stop_cheat_caller_address(token_giver_address);
206+
207+
// Amount to request
208+
let amount: u256 = 100;
209+
210+
// Apply to campaign pool
211+
start_cheat_caller_address(token_giver_address, recipient);
212+
token_giver.apply_to_campaign_pool(campaign_address, campaign_pool_address, amount);
213+
stop_cheat_caller_address(token_giver_address);
214+
215+
// Verify application was stored correctly
216+
start_cheat_caller_address(token_giver_address, recipient);
217+
let (stored_pool, stored_amount) = token_giver.get_campaign_application(campaign_address);
218+
stop_cheat_caller_address(token_giver_address);
219+
220+
assert(stored_pool == campaign_pool_address, 'Wrong pool address stored');
221+
assert(stored_amount == amount, 'Wrong amount stored');
222+
223+
// Check event emission
224+
let expected_event = Event::ApplicationMade(
225+
ApplicationMade {
226+
campaign_pool_address: campaign_pool_address,
227+
campaign_address: campaign_address,
228+
recipient: recipient,
229+
amount: amount,
230+
block_timestamp: get_block_timestamp(),
231+
}
232+
);
233+
234+
spy.assert_emitted(@array![(token_giver.contract_address, expected_event)]);
235+
}
236+
237+
#[test]
238+
#[fork("Mainnet")]
239+
#[should_panic(expected: ('TGN: invalid campaign address!',))]
240+
fn test_apply_with_invalid_campaign_address() {
241+
// Set up a campaign pool
242+
let (token_giver_address, _, _) = __setup__();
243+
let token_giver = ICampaignPoolDispatcher { contract_address: token_giver_address };
244+
245+
let registry_hash = REGISTRY_HASH();
246+
let implementation_hash = IMPLEMENTATION_HASH();
247+
let salt: felt252 = SALT();
248+
let recipient: ContractAddress = RECIPIENT();
249+
let pool_id: u256 = 12;
250+
251+
// Create campaign pool
252+
start_cheat_caller_address(token_giver_address, recipient);
253+
let campaign_pool_address = token_giver
254+
.create_campaign_pool(registry_hash, implementation_hash, salt, recipient, pool_id);
255+
stop_cheat_caller_address(token_giver_address);
256+
257+
// Use an invalid campaign address
258+
let invalid_campaign_address: ContractAddress = starknet::contract_address_const::<0x123>();
259+
let amount: u256 = 100;
260+
261+
// This should panic with "TGN: invalid campaign address!"
262+
start_cheat_caller_address(token_giver_address, recipient);
263+
token_giver.apply_to_campaign_pool(invalid_campaign_address, campaign_pool_address, amount);
264+
stop_cheat_caller_address(token_giver_address);
265+
}
266+
267+
#[test]
268+
#[fork("Mainnet")]
269+
#[should_panic(expected: ('TGN: invalid pool address!',))]
270+
fn test_apply_with_invalid_pool_address() {
271+
// Set up a campaign
272+
let (token_giver_address, _, _) = __setup__();
273+
let token_giver = ICampaignPoolDispatcher { contract_address: token_giver_address };
274+
275+
let registry_hash = REGISTRY_HASH();
276+
let implementation_hash = IMPLEMENTATION_HASH();
277+
let salt: felt252 = SALT();
278+
let recipient: ContractAddress = RECIPIENT();
279+
let campaign_id: u256 = 13;
280+
281+
// Create campaign
282+
start_cheat_caller_address(token_giver_address, recipient);
283+
let campaign_address = token_giver
284+
.create_campaign_pool(registry_hash, implementation_hash, salt, recipient, campaign_id);
285+
stop_cheat_caller_address(token_giver_address);
286+
287+
// Use an invalid pool address
288+
let invalid_pool_address: ContractAddress = starknet::contract_address_const::<0x456>();
289+
let amount: u256 = 100;
290+
291+
// This should panic with "TGN: invalid pool address!"
292+
start_cheat_caller_address(token_giver_address, recipient);
293+
token_giver.apply_to_campaign_pool(campaign_address, invalid_pool_address, amount);
294+
stop_cheat_caller_address(token_giver_address);
295+
}
296+
297+
#[test]
298+
#[fork("Mainnet")]
299+
#[should_panic(expected: ('TGN: invalid amount!',))]
300+
fn test_apply_with_zero_amount() {
301+
// Set up a campaign pool and a campaign
302+
let (token_giver_address, _, _) = __setup__();
303+
let token_giver = ICampaignPoolDispatcher { contract_address: token_giver_address };
304+
305+
let registry_hash = REGISTRY_HASH();
306+
let implementation_hash = IMPLEMENTATION_HASH();
307+
let salt1: felt252 = 'salt1';
308+
let salt2: felt252 = 'salt2';
309+
let recipient: ContractAddress = RECIPIENT();
310+
let campaign_id: u256 = 14;
311+
let pool_id: u256 = 15;
312+
313+
// Create campaign pool and campaign
314+
start_cheat_caller_address(token_giver_address, recipient);
315+
let campaign_pool_address = token_giver
316+
.create_campaign_pool(registry_hash, implementation_hash, salt1, recipient, pool_id);
317+
let campaign_address = token_giver
318+
.create_campaign_pool(registry_hash, implementation_hash, salt2, recipient, campaign_id);
319+
320+
// Try to apply with zero amount, should fail
321+
let zero_amount: u256 = 0;
322+
token_giver.apply_to_campaign_pool(campaign_address, campaign_pool_address, zero_amount);
323+
stop_cheat_caller_address(token_giver_address);
324+
}

0 commit comments

Comments
 (0)