Skip to content
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
58 changes: 57 additions & 1 deletion src/issues_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,64 @@ fn test_vote_weight_overflow_fails() {

// Cast a vote with balance 501, which overflows i128::MAX when added to i128::MAX - 500
token_admin.mint(&contributor, &501);

// cast vote should return Overflow error
let res = client.try_vote_on_campaign(&campaign_id, &contributor, &true);
assert_eq!(res.unwrap_err().unwrap(), Error::Overflow);
}

// ── #360 resume_campaign admin-path coverage ──────────────────────────────────

/// Helper: set the contract into auto-paused state directly in storage.
fn set_auto_paused(env: &Env, client_address: &Address, paused: bool) {
env.as_contract(client_address, || {
env.storage().instance().set(&DataKey::AutoPaused, &paused);
});
}

#[test]
fn test_resume_by_admin() {
let (env, admin, creator, _, _, _, _, client) = setup_env();
let campaign_id = client.create_campaign(&make_campaign_params_simple(&env, &creator));

set_auto_paused(&env, &client.address, true);
assert!(client.is_paused());

// Admin (not the creator) should be able to resume.
client.resume_campaign(&campaign_id, &admin);
assert!(!client.is_paused());
}

#[test]
fn test_resume_unauthorized_fails() {
let (env, _admin, creator, _, _, _, _, client) = setup_env();
let campaign_id = client.create_campaign(&make_campaign_params_simple(&env, &creator));
let stranger = Address::generate(&env);

set_auto_paused(&env, &client.address, true);

let result = client.try_resume_campaign(&campaign_id, &stranger);
assert_eq!(result.unwrap_err().unwrap(), Error::NotAuthorized);
}

#[test]
fn test_resume_after_campaign_transfer_uses_new_creator() {
let (env, _admin, original_creator, _, _, _, _, client) = setup_env();
let campaign_id = client.create_campaign(&make_campaign_params_simple(&env, &original_creator));

let new_creator = Address::generate(&env);
client.initiate_campaign_transfer(&campaign_id, &new_creator);
client.accept_campaign_transfer(&campaign_id);

set_auto_paused(&env, &client.address, true);
assert!(client.is_paused());

// New creator can resume; original creator can no longer.
client.resume_campaign(&campaign_id, &new_creator);
assert!(!client.is_paused());

// Re-pause and verify the original creator is now rejected.
set_auto_paused(&env, &client.address, true);
let result = client.try_resume_campaign(&campaign_id, &original_creator);
assert_eq!(result.unwrap_err().unwrap(), Error::NotAuthorized);
}
13 changes: 12 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,7 @@ impl ProofOfHeart {
let old_threshold =
get_approval_threshold_bps(&env, voting::DEFAULT_APPROVAL_THRESHOLD_BPS);
let caller = admin.clone();
voting::set_params(&env, admin, min_votes_quorum, approval_threshold_bps)?;
voting::set_params(&env, min_votes_quorum, approval_threshold_bps)?;
env.events().publish(
(
soroban_sdk::Symbol::new(&env, "voting_params_updated"),
Expand Down Expand Up @@ -1910,6 +1910,17 @@ impl ProofOfHeart {
}
}

/// Returns `true` if the given campaign currently has a pending ownership transfer.
///
/// Allows off-chain tooling and the pending creator to discover transfers without
/// iterating all campaigns. Returns `false` for unknown campaign IDs.
pub fn has_pending_campaign_transfer(env: Env, campaign_id: u32) -> bool {
match get_campaign(&env, campaign_id) {
Some(c) => c.pending_creator != MaybePendingCreator::None,
None => false,
}
}

/// Initiates a transfer of campaign ownership to a new address.
///
/// # Authorization
Expand Down
5 changes: 3 additions & 2 deletions src/voting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ pub const MIN_APPROVAL_THRESHOLD_BPS: u32 = 1000;
/// * `ValidationFailed` - Quorum or threshold values are out of range.
pub fn set_params(
env: &Env,
_admin: Address,
min_votes_quorum: u32,
approval_threshold_bps: u32,
) -> Result<(), Error> {
Expand Down Expand Up @@ -90,8 +89,10 @@ pub fn cast_vote(env: &Env, campaign_id: u32, voter: Address, approve: bool) ->
}

set_has_voted(env, campaign_id, &voter);

let vote_weight = balance;
env.events()
.publish(("campaign_vote_cast", campaign_id, voter), approve);
.publish(("campaign_vote_cast", campaign_id, voter), (approve, balance, vote_weight));

Ok(())
}
Expand Down
74 changes: 52 additions & 22 deletions test_snapshots/test/test_cancel_and_refund.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,58 @@
15
]
],
[
{
"contract_data": {
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4",
"key": {
"vec": [
{
"symbol": "BlockCampaignContributionCount"
},
{
"u32": 1
}
]
},
"durability": "temporary"
}
},
[
{
"last_modified_ledger_seq": 0,
"data": {
"contract_data": {
"ext": "v0",
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4",
"key": {
"vec": [
{
"symbol": "BlockCampaignContributionCount"
},
{
"u32": 1
}
]
},
"durability": "temporary",
"val": {
"vec": [
{
"u32": 0
},
{
"u32": 2
}
]
}
}
},
"ext": "v0"
},
15
]
],
[
{
"contract_data": {
Expand Down Expand Up @@ -1338,28 +1390,6 @@
"u32": 6000
}
},
{
"key": {
"vec": [
{
"symbol": "BlockCampaignContributionCount"
},
{
"u32": 1
}
]
},
"val": {
"vec": [
{
"u32": 0
},
{
"u32": 2
}
]
}
},
{
"key": {
"vec": [
Expand Down
74 changes: 52 additions & 22 deletions test_snapshots/test/test_contribute_and_withdraw_success.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,58 @@
15
]
],
[
{
"contract_data": {
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4",
"key": {
"vec": [
{
"symbol": "BlockCampaignContributionCount"
},
{
"u32": 1
}
]
},
"durability": "temporary"
}
},
[
{
"last_modified_ledger_seq": 0,
"data": {
"contract_data": {
"ext": "v0",
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4",
"key": {
"vec": [
{
"symbol": "BlockCampaignContributionCount"
},
{
"u32": 1
}
]
},
"durability": "temporary",
"val": {
"vec": [
{
"u32": 0
},
{
"u32": 1
}
]
}
}
},
"ext": "v0"
},
15
]
],
[
{
"contract_data": {
Expand Down Expand Up @@ -1195,28 +1247,6 @@
"u32": 6000
}
},
{
"key": {
"vec": [
{
"symbol": "BlockCampaignContributionCount"
},
{
"u32": 1
}
]
},
"val": {
"vec": [
{
"u32": 0
},
{
"u32": 1
}
]
}
},
{
"key": {
"vec": [
Expand Down
74 changes: 52 additions & 22 deletions test_snapshots/test/test_failure_states.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,58 @@
15
]
],
[
{
"contract_data": {
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4",
"key": {
"vec": [
{
"symbol": "BlockCampaignContributionCount"
},
{
"u32": 1
}
]
},
"durability": "temporary"
}
},
[
{
"last_modified_ledger_seq": 0,
"data": {
"contract_data": {
"ext": "v0",
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4",
"key": {
"vec": [
{
"symbol": "BlockCampaignContributionCount"
},
{
"u32": 1
}
]
},
"durability": "temporary",
"val": {
"vec": [
{
"u32": 0
},
{
"u32": 1
}
]
}
}
},
"ext": "v0"
},
15
]
],
[
{
"contract_data": {
Expand Down Expand Up @@ -1089,28 +1141,6 @@
"u32": 6000
}
},
{
"key": {
"vec": [
{
"symbol": "BlockCampaignContributionCount"
},
{
"u32": 1
}
]
},
"val": {
"vec": [
{
"u32": 0
},
{
"u32": 1
}
]
}
},
{
"key": {
"vec": [
Expand Down
Loading
Loading