[Contract] Implement Auto-Refund Fallback Mechanism on Expired Campaigns - #562
[Contract] Implement Auto-Refund Fallback Mechanism on Expired Campaigns#562Skinny001 wants to merge 3 commits into
Conversation
…und triggers New Soroban smart contract for campaign funding with: - Campaign creation with goal amount and deadline - Permissionless contributions to active campaigns - Creator claim when goal met after deadline - Auto-refund for backers when deadline passes without meeting goal - Permissionless refund triggering (anyone can refund any backer) - 27 comprehensive unit tests covering success, failure, and edge cases - Storage TTL management following existing contract patterns
|
@Skinny001 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
Warning Review limit reached
Next review available in: 20 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds the ChangesCampaign funding contract
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Contributor
participant CampaignFundingContract
participant TokenContract
Contributor->>CampaignFundingContract: refund(campaign_id, contributor)
CampaignFundingContract->>TokenContract: transfer refund
CampaignFundingContract->>CampaignFundingContract: mark contribution refunded
CampaignFundingContract->>CampaignFundingContract: set status Expired and emit event
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (2)
contracts/campaign-funding/src/test.rs (2)
606-625: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLoosen-to-strengthen:
test_events_emittedonly checks event count, not content.
assert!(events.len() >= 2)doesn't verify which events were emitted (e.g., campaign-created vs contribution-made) or their payloads, so it wouldn't catch a regression that emits the wrong event type while preserving count.♻️ Suggested tightening
let events = env.events().all(); - assert!(events.len() >= 2); + assert!(events.len() >= 2, "expected at least a campaign-created and a contribution event"); + // Optionally assert on event topics/data to catch wrong-event-type regressions, + // e.g. matching on the contract address and topic symbol for each expected event.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@contracts/campaign-funding/src/test.rs` around lines 606 - 625, Strengthen test_events_emitted by asserting the emitted events’ topics and payloads, not only that at least two events exist. Verify the campaign-created and contribution-made events are present in the expected order and contain the relevant campaign ID, addresses, and contribution amount.
169-188: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winClarify deadline error direction or split error codes.
Error(Contract,#8)is used for both past-deadline failures (test_contribute_after_deadline) and before-deadline failures (test_claim_before_deadline,test_refund_before_deadline). If this is a single sharedDeadlineErrorvariant, production failures won’t distinguish “deadline already passed” from “deadline not reached”, so use distinct codes/variants or document the intentionally shared semantics.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@contracts/campaign-funding/src/test.rs` around lines 169 - 188, The deadline validation errors are ambiguous because error code `#8` is used for both expired and not-yet-reached campaigns. Update the relevant contract error variants and checks used by test_contribute_after_deadline, test_claim_before_deadline, and test_refund_before_deadline to return distinct codes for each direction, or explicitly document the shared DeadlineError semantics if that behavior is intentional.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@contracts/campaign-funding/src/lib.rs`:
- Around line 92-323: Add concise Rust documentation comments (`///`)
immediately before every public entrypoint in `CampaignFundingContract`:
`initialize`, `create_campaign`, `contribute`, `claim`, `refund`,
`get_campaign`, `get_contribution`, and `get_admin`. Describe each method’s
purpose and key parameters/return value where applicable, without changing
behavior.
- Around line 24-30: Assign CampaignStatus::Expired when a campaign deadline has
passed without reaching its goal. Update the relevant refund/deadline handling
flow around refund() so it persists the status transition before allowing
withdrawals, while preserving the existing eligibility checks and Active/Success
behavior.
- Around line 86-88: The fixed LEDGER_THRESHOLD and LEDGER_BUMP values do not
guarantee that entries survive until the deadline set by create_campaign. Update
create_campaign and its Campaign/Contribution TTL initialization to derive the
extension from deadline minus the current ledger time, including the required
margin, or enforce and validate a maximum deadline within the configured TTL
window before creating entries.
- Around line 160-178: Add a distinct error variant representing an expired
campaign deadline, then update contribute so its current_time >=
campaign.deadline guard panics with that variant instead of
Error::DeadlineNotReached. Preserve DeadlineNotReached for the pre-deadline
checks in claim and refund.
- Around line 95-104: Update initialize and the state-using methods
create_campaign, contribute, claim, and refund to require persisted
initialization before proceeding: detect missing admin state and return
Error::NotInitialized instead of relying on fallback values. Enforce admin
authorization on the operations intended to be admin-protected by loading the
stored admin and rejecting non-admin callers with Error::Unauthorized, while
preserving existing authentication and state-update behavior after validation.
---
Nitpick comments:
In `@contracts/campaign-funding/src/test.rs`:
- Around line 606-625: Strengthen test_events_emitted by asserting the emitted
events’ topics and payloads, not only that at least two events exist. Verify the
campaign-created and contribution-made events are present in the expected order
and contain the relevant campaign ID, addresses, and contribution amount.
- Around line 169-188: The deadline validation errors are ambiguous because
error code `#8` is used for both expired and not-yet-reached campaigns. Update the
relevant contract error variants and checks used by
test_contribute_after_deadline, test_claim_before_deadline, and
test_refund_before_deadline to return distinct codes for each direction, or
explicitly document the shared DeadlineError semantics if that behavior is
intentional.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 5d902fad-64e1-4014-b5d5-281dee026894
📒 Files selected for processing (31)
contracts/Cargo.tomlcontracts/campaign-funding/Cargo.tomlcontracts/campaign-funding/src/lib.rscontracts/campaign-funding/src/test.rscontracts/campaign-funding/test_snapshots/test/test/test_claim_already_withdrawn.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_claim_before_deadline.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_claim_goal_not_met.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_claim_success.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute_after_deadline.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute_campaign_not_found.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute_multiple_backers.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute_multiple_from_same_backer.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute_zero_amount.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_create_campaign.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_create_campaign_deadline_in_past.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_create_campaign_zero_goal.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_events_emitted.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_full_lifecycle_refund.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_full_lifecycle_success.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_get_campaign_not_found.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_get_contribution_none.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_initialize.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_re_initialize_fails.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_already_refunded.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_before_deadline.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_goal_met.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_multiple_backers.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_no_contribution.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_permissionless.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_success.1.json
| pub fn initialize(env: Env, admin: Address) { | ||
| if env.storage().instance().has(&Symbol::new(&env, "admin")) { | ||
| panic_with_error!(&env, Error::AlreadyInitialized); | ||
| } | ||
| admin.require_auth(); | ||
|
|
||
| env.storage().instance().set(&Symbol::new(&env, "admin"), &admin); | ||
| env.storage().instance().set(&Symbol::new(&env, "campaign_counter"), &0u64); | ||
| env.storage().instance().extend_ttl(LEDGER_THRESHOLD, LEDGER_BUMP); | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Files matching lib.rs under campaigns-campaign-funding:"
fd -a 'lib.rs' . | sed 's#^\./##' | rg 'campaign-funding|campaign' || true
echo
echo "git ls-files relevant:"
git ls-files | rg 'campaign-funding|campaign' || true
echo
echo "Outline:"
file="$(fd 'lib.rs' contracts/campaign-funding/src | head -n1 || true)"
if [ -n "${file:-}" ]; then
echo "file=$file"
wc -l "$file"
ast-grep outline "$file" || true
echo
echo "Lines 1-220:"
sed -n '1,220p' "$file" | nl -ba
fi
echo
echo "Search errors/usages:"
rg -n "NotInitialized|Unauthorized|initialize|campaign_counter|admin|require_auth|unwrap_or" contracts/campaign-funding/src || trueRepository: Fundable-Protocol/stellar_client_os
Length of output: 2074
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "pwd:"
pwd
echo
echo "git status:"
git status --short || true
echo
echo "top-level files:"
git ls-files | head -80 || true
echo
echo "Find campaign-funding:"
fd -a 'lib.rs' . 2>/dev/null | sed 's#^\./##' | rg '(^|/)campaign-funding($|/)|campaign' || true
echo
echo "Search local tracked files for symbols:"
rg -n "Error::NotInitialized|Error::Unauthorized|fn initialize|campaign_counter|unwrap_or|admin.require_auth|admin" . 2>/dev/null | head -200 || trueRepository: Fundable-Protocol/stellar_client_os
Length of output: 2074
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'PWD: %s\n' "$(pwd)"
printf '\nTracked candidates:\n'
git ls-files | rg '(^|/)contracts/campaign-funding/src/lib\.rs$|campaign-funding|campaign' || true
printf '\nLocal file direct check:\n'
p='contracts/campaign-funding/src/lib.rs'
if [ -f "$p" ]; then
echo "EXISTS $p"
wc -l "$p"
sed -n '1,190p' "$p" | nl -ba
else
echo "MISSING $p"
fi
printf '\nLocal symbol search:\n'
if [ -d contracts/campaign-funding/src ]; then
rg -n "Error::NotInitialized|Error::Unauthorized|fn initialize|campaign_counter|unwrap_or|admin.require_auth|admin|create_campaign|contribute|claim|refund" contracts/campaign-funding/src || true
else
echo "contracts/campaign-funding/src directory not present"
fiRepository: Fundable-Protocol/stellar_client_os
Length of output: 2074
🌐 Web query:
stellar_client_os contracts/campaign-funding/src/lib.rs initialize admin campaign_counter initialize
💡 Result:
The command you are referencing refers to initializing a Soroban smart contract, commonly found in crowdfunding DApp implementations on the Stellar network [1][2][3]. In the context of Soroban contracts (typically written in Rust), the initialize function is used to set the initial state of the contract, such as the admin address and various configuration parameters, upon deployment [2][3]. While there is no single universal codebase matching your exact string, the structure you described is common in Soroban crowdfunding projects [1][2][4]. Below is a breakdown of how these components are typically handled: 1. Initialization: The initialize function (often named init or initialize) is defined in the contract's main library file (e.g., lib.rs) and is invoked to persist critical data to the ledger [2][5][3]. 2. Storage Keys: Data like the admin account and campaign_counter are typically stored using an enum-based data key system to ensure type-safe access to contract storage [2]. 3. Campaign Counter: A campaign_counter is often used in factory-style contracts to generate deterministic IDs for new campaigns as they are deployed [5]. Example of typical initialization logic in a Soroban contract: #[contractimpl] impl CrowdfundingContract { pub fn initialize(env: Env, admin: Address, campaign_counter: u32) { // Logic to store the admin and counter in the contract's persistent storage env.storage.instance.set(&DataKey::Admin, &admin); env.storage.instance.set(&DataKey::CampaignCounter, &campaign_counter); } } To invoke this function via the Stellar CLI after deploying your contract, you would use a command similar to the following: stellar contract invoke \ --id <contract_id> \ --source <admin_key> \ --network \ -- initialize \ --admin <admin_address> \ --campaign_counter Please ensure you check the specific lib.rs file in your project or the relevant repository for the exact parameter names, as they can vary between different implementations [2][6].
Citations:
- 1: https://github.com/Crowdfunding-DApp/stellar-raise-contracts
- 2: https://github.com/0xDarioSanchez/NovaFund
- 3: https://github.com/ritik4ever/stellar-goal-vault
- 4: https://github.com/fundlr/fundlr-contracts/blob/main/README.md
- 5: https://dev.to/berkebey01/building-milestone-escrow-crowdfunding-on-soroban-5hc
- 6: https://libraries.io/npm/@rtu-bindings%2Fsoroban-groth16-verifier
🌐 Web query:
"Fundable-Protocol/stellar_client_os" "campaign-funding" "lib.rs" "campaign_counter" "unwrap_or"
💡 Result:
The repository Fundable-Protocol/stellar_client_os exists within the Fundable-Protocol organization on GitHub [1][2], but there is no widely documented or standardized campaign_funding module, lib.rs file, or campaign_counter function associated with that specific repository in public documentation. The Fundable-Protocol primarily develops decentralized finance solutions on StarkNet (using Cairo) [3] and maintains various client repositories [1]. It is common in Rust development to use the unwrap_or pattern when handling Option<T> types, such as some_variable.unwrap_or_default() or some_variable.unwrap_or(value), to provide fallback values [4][5]. If you are encountering a compiler error or logic issue related to campaign_counter and unwrap_or in your local environment, ensure that: 1. The variable campaign_counter is of type Option<T> [4][6]. 2. The argument passed to unwrap_or matches the type T of the inner value [4]. 3. You are importing the necessary modules if campaign_counter is defined in a different file within your project structure. If this refers to a private project or a specific commit, please double-check the repository path and the file structure of your local clone.
Citations:
- 1: https://github.com/Fundable-Protocol
- 2: Dashboard shows nothing when wallet disconnected - inconsistent with other pages #119
- 3: https://github.com/Fundable-Protocol/fundable
- 4: https://docs.rs/stellar-rpc-client/latest/src/stellar_rpc_client/lib.rs.html
- 5: https://docs.rs/wasi-stellar-rpc-client/latest/src/wasi_stellar_rpc_client/lib.rs.html
- 6: https://github.com/gemwalletcom/wallet/blob/f2d99035/core/crates/gem_stellar/src/rpc/client.rs
🌐 Web query:
site:github.com/Fundable-Protocol/stellar_client_os contracts/campaign-funding/src/lib.rs campaign_counter unwrap_or
💡 Result:
Based on the provided search results, there is no evidence of a file path specifically named contracts/campaign-funding/src/lib.rs containing a variable or function called campaign_counter used with the unwrap_or method. While the search results identify various crowdfunding-related projects and their directory structures—such as fundlr-contracts, substrate-recipes, and solfundme—none of these repositories match the specific file path or code pattern requested [1][2][3]. The method unwrap_or is a standard Rust idiom for handling Option types by providing a default value if the Option is None, but no specific implementation of this in relation to a campaign_counter in the specified file path was found [1][2][3].
Citations:
- 1: https://github.com/fundlr/fundlr-contracts/blob/main/README.md
- 2: https://github.com/JoshOrndorff/recipes/blob/master/pallets/simple-crowdfund/src/lib.rs
- 3: domluna/solfundme@6b99b89
Enforce admin/initialize() before using persisted state
admin and campaign_counter are only read with fallback handling, so create_campaign(), contribute(), claim(), and refund() can succeed even without initialize() and admin is never consulted for authorization. Make initialization required before using shared state, and have the admin gate the operations it’s meant to protect so Error::NotInitialized / Error::Unauthorized are not dead code.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@contracts/campaign-funding/src/lib.rs` around lines 95 - 104, Update
initialize and the state-using methods create_campaign, contribute, claim, and
refund to require persisted initialization before proceeding: detect missing
admin state and return Error::NotInitialized instead of relying on fallback
values. Enforce admin authorization on the operations intended to be
admin-protected by loading the stored admin and rejecting non-admin callers with
Error::Unauthorized, while preserving existing authentication and state-update
behavior after validation.
| pub fn contribute(env: Env, campaign_id: u64, contributor: Address, amount: i128) { | ||
| contributor.require_auth(); | ||
|
|
||
| if amount <= 0 { | ||
| panic_with_error!(&env, Error::InvalidAmount); | ||
| } | ||
|
|
||
| let mut campaign: Campaign = env.storage().persistent() | ||
| .get(&campaign_id) | ||
| .unwrap_or_else(|| panic_with_error!(&env, Error::CampaignNotFound)); | ||
|
|
||
| if campaign.status != CampaignStatus::Active { | ||
| panic_with_error!(&env, Error::CampaignNotActive); | ||
| } | ||
|
|
||
| let current_time = env.ledger().timestamp(); | ||
| if current_time >= campaign.deadline { | ||
| panic_with_error!(&env, Error::DeadlineNotReached); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Misleading error code when blocking contributions after the deadline.
At Line 176-178, current_time >= campaign.deadline (deadline has already passed) panics with Error::DeadlineNotReached — the exact opposite of what happened. Contrast with claim() (Line 226-229) and refund() (Line 265-268), where the same error is correctly used for current_time < campaign.deadline. Callers switching on error codes will get a misleading reason for the rejected contribution.
🐛 Proposed fix: add a distinct error for "deadline already passed"
GoalNotMet = 10,
AlreadyWithdrawn = 11,
AlreadyRefunded = 12,
ArithmeticOverflow = 13,
DeadlineInPast = 14,
+ DeadlinePassed = 15,
} let current_time = env.ledger().timestamp();
if current_time >= campaign.deadline {
- panic_with_error!(&env, Error::DeadlineNotReached);
+ panic_with_error!(&env, Error::DeadlinePassed);
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@contracts/campaign-funding/src/lib.rs` around lines 160 - 178, Add a distinct
error variant representing an expired campaign deadline, then update contribute
so its current_time >= campaign.deadline guard panics with that variant instead
of Error::DeadlineNotReached. Preserve DeadlineNotReached for the pre-deadline
checks in claim and refund.
1. CampaignStatus::Expired now assigned — set in refund() when deadline passes without meeting goal; consumer-facing status is accurate. 2. TTL bump scaled to campaign deadline — campaign_ttl() computes (threshold, bump) proportional to deadline - now, ensuring entries survive until the campaign ends, capped at MAX_TTL (6312000). 3. Doc comments added to all public entrypoints (initialize, create_campaign, contribute, claim, refund, get_campaign, get_contribution, get_admin). 4. Initialization enforced — require_initialized() guard at the top of every state-mutating function; Error::NotInitialized is no longer dead code. 5. Wrong error in contribute() fixed — using CampaignNotActive (Fundable-Protocol#7) instead of DeadlineNotReached (Fundable-Protocol#8) when deadline has passed. Also adds 4 new tests: expired status after refund, contribution to expired campaign forbidden, and init enforcement for create + contribute.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@contracts/campaign-funding/src/lib.rs`:
- Around line 180-182: In the campaign creation flow using Self::campaign_ttl,
also extend instance storage with the same ttl and bump via
env.storage().instance().extend_ttl(ttl, bump). Keep the existing persistent
campaign write and persistent TTL extension unchanged.
- Around line 124-133: Update create_campaign and any related deadline
validation to reject future deadlines beyond the maximum retainable TTL horizon
before writing or extending the campaign entry. Reuse campaign_ttl’s
MAX_TTL/ledger timing semantics to calculate the allowed expiry boundary, while
preserving rejection of past deadlines and ensuring accepted campaigns remain
claimable or refundable until their deadline.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 755c5676-df73-4d89-bd2a-39dc0c77f139
📒 Files selected for processing (27)
contracts/campaign-funding/src/lib.rscontracts/campaign-funding/src/test.rscontracts/campaign-funding/test_snapshots/test/test/test_claim_already_withdrawn.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_claim_before_deadline.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_claim_goal_not_met.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_claim_success.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute_after_deadline.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute_multiple_backers.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute_multiple_from_same_backer.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute_to_expired_campaign_fails.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute_without_init_fails.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_contribute_zero_amount.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_create_campaign.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_create_campaign_without_init_fails.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_events_emitted.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_full_lifecycle_refund.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_full_lifecycle_success.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_get_contribution_none.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_already_refunded.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_before_deadline.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_goal_met.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_multiple_backers.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_no_contribution.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_permissionless.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_sets_campaign_status_to_expired.1.jsoncontracts/campaign-funding/test_snapshots/test/test/test_refund_success.1.json
🚧 Files skipped from review as they are similar to previous changes (19)
- contracts/campaign-funding/test_snapshots/test/test/test_claim_before_deadline.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_contribute.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_refund_already_refunded.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_claim_already_withdrawn.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_contribute_multiple_from_same_backer.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_refund_goal_met.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_create_campaign.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_refund_permissionless.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_full_lifecycle_success.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_claim_success.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_full_lifecycle_refund.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_contribute_multiple_backers.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_events_emitted.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_claim_goal_not_met.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_contribute_after_deadline.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_refund_before_deadline.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_refund_multiple_backers.1.json
- contracts/campaign-funding/test_snapshots/test/test/test_contribute_zero_amount.1.json
- contracts/campaign-funding/src/test.rs
…tainable deadlines - Instance storage (admin, campaign_counter) now gets the same campaign- specific (ttl, bump) from campaign_ttl() at creation time, ensuring the contract stays available for the full campaign lifecycle. - create_campaign rejects deadlines beyond MAX_DEADLINE_DELTA (~365 days) with Error::DeadlineTooFar (Fundable-Protocol#15), preventing campaigns that would be archived before their deadline. - Adds test_create_campaign_deadline_too_far test.
|
@Idrhas Done |
|
@Idrhas pls check and close the PR |
|
dont forget to offramp using https://stellar.fundable.finance/offramp its fast, free and p2p rates |
1 similar comment
|
dont forget to offramp using https://stellar.fundable.finance/offramp its fast, free and p2p rates |
This pull request introduces a new smart contract for campaign-based crowdfunding on Soroban, adds it to the workspace, and provides initial test coverage for key error conditions. The main focus is on creating, funding, claiming, and refunding campaigns, with robust error handling and event emission for transparency.
close #518
New Campaign Funding Contract
campaign-fundingcontract implementing crowdfunding logic, including campaign creation, contribution, claim, and refund functions, with comprehensive error handling and event emission. (contracts/campaign-funding/src/lib.rs)Cargo.tomlfor the new contract, specifying dependencies and build configuration. (contracts/campaign-funding/Cargo.toml)contracts/Cargo.toml)Testing and Snapshots
contracts/campaign-funding/test_snapshots/test/test/test_contribute_campaign_not_found.1.json,test_create_campaign_deadline_in_past.1.json,test_create_campaign_zero_goal.1.json) [1] [2] [3]…und triggersNew Soroban smart contract for campaign funding with:
Summary by CodeRabbit
campaign_expiredevent.