feat: add update_campaign_metadata() so business can amend campaign brief before any applications - #38
Conversation
…rief before any applications Closes Ads-Bazaar#13 - Added CampaignApplicants storage key and tracking helpers - Added ApplicationsExist (22) and InvalidMetadata (23) error variants - Added CampaignMetadataUpdated event - Wired applicant tracking into apply_to_campaign() - New function only allows metadata updates when no creator has applied - Rejects empty metadata strings - Rejects updates on Cancelled/Completed campaigns - 8 comprehensive tests covering happy path, auth, pause, and edge cases
|
Merged — this one's clean, no follow-up needed. Nice work: the auth/ownership/status checks are correctly ordered, and — worth calling out specifically — the status gate (blocking Cancelled/Completed) isn't actually redundant with the applicant check even though it might look that way at first: a campaign can be cancelled while it still has zero applicants, and the applicant-check alone wouldn't catch an update attempt on that campaign. Good instinct keeping both guards. The 8 tests cover the real edge cases (idempotency-on-failure, paused, both terminal statuses, funded-but-still-zero-applicants) rather than just the happy path. One non-blocking thought for later: |
…50) storage::add_campaign_applicant rewrote an ever-growing Vec<Address> on every apply_to_campaign call just to answer a yes/no question in has_campaign_applicants. Replaces it with a u32 counter under DataKey::ApplicantCount, matching the existing approved_count pattern on Campaign, so applying costs O(1) storage-writes regardless of how many creators already applied. Adds a regression test that applies 200+ creators and asserts (via env.cost_estimate()) that the storage write cost of a later apply matches an early one, plus confirms update_campaign_metadata's lock-after-first-application behavior from #38 is unaffected. Closes #43 Verified locally in an isolated worktree before merge: - cargo fmt --all -- --check - cargo build --workspace - cargo test --workspace (64 tests) - cargo clippy --workspace --all-targets -- -D warnings - cargo build --workspace --target wasm32v1-none --release - Confirmed via git merge-tree that the merge combines cleanly with #34 and #49, which landed after this branch was cut.
Summary
Closes #13
Adds the
update_campaign_metadata()function to the campaign-escrow contract, allowing a business to update a campaign's metadata URI (e.g. IPFS brief) before any creator applies. Once a creator applies, the brief is permanently locked to protect applicant trust.Changes
New functionality
update_campaign_metadata()— Updates themetadata_urifield of a campaign. Only the campaign's business may call it.Rules enforced
NotCampaignOwnerInvalidStatusApplicationsExistnew_metadatamust be a non-empty stringInvalidMetadataContractPausedFiles changed
storage.rsCampaignApplicants(CampaignId)storage key +add_campaign_applicant()/has_campaign_applicants()helperserror.rsApplicationsExist = 22andInvalidMetadata = 23events.rsCampaignMetadataUpdatedeventlib.rsupdate_campaign_metadata(); wired applicant tracking intoapply_to_campaign()test.rstest_update_metadatamoduleTests (8 new)
update_metadata_successupdate_metadata_after_fundingnot_campaign_owner_cannot_update_metadataNotCampaignOwnerapplications_exist_blocks_metadata_updateApplicationsExistempty_metadata_rejectedInvalidMetadatacancelled_campaign_rejects_metadata_updateInvalidStatuscompleted_campaign_rejects_metadata_updateInvalidStatusmetadata_not_changed_on_failuremetadata_update_blocked_when_pausedContractPausedCI
Design notes
CampaignApplicantspersistent storage key (Vec<Address>) that is populated inapply_to_campaign(). This keeps the check O(1) from the contract's perspective without iterating over individualApplicationstorage entries.InvalidStatuserror on Cancelled/Completed rather than introducing a newCampaignClosedvariant, staying consistent with how the rest of the codebase rejects state-changing operations on closed campaigns.closes #13