feat(shade): implement campaign categories and tagging system (#352)#394
Open
goodness-cpu wants to merge 2 commits into
Open
feat(shade): implement campaign categories and tagging system (#352)#394goodness-cpu wants to merge 2 commits into
goodness-cpu wants to merge 2 commits into
Conversation
…rotocol#352) Adds an admin-managed CampaignCategory registry, a merchant-curated CampaignTag vocabulary, and merchant-owned Campaign records that reference exactly one active category and any number of tags. All write paths enforce require_auth() and the appropriate role-based check; records are stored in additive DataKey variants so storage layout remains backwards compatible. Highlights: - Add DataKey variants CampaignCategory, CampaignCategoryName, CampaignTag, CampaignTagName, Campaign, CategoryCampaigns, TagCampaigns, MerchantCampaigns, CampaignTagList plus matching CampaignCategory, CampaignTag, Campaign, CampaignFilter structs to types.rs. - Add ContractError variants 55-65 (CampaignCategoryNotFound .. CampaignExpired) to errors.rs. - Add 8 new contractevent structs in events.rs: CampaignCategoryCreated/Updated, CampaignTagCreated, CampaignCreated/Updated/StatusChanged, CampaignTagAdded/Removed, CampaignContribution. - New components/campaigns.rs with reentrancy-guarded writes, unique-tagging via name index, reverse indices for get_campaigns_by_category/by_tag/by_merchant, distinct errors for inactive vs expired campaigns. - Extend ShadeTrait and Shade impl with 14 new methods covering admin category mgmt, admin/merchant tag creation, merchant-only campaign CRUD, contribution recording, and filtered queries. - New tests/test_campaigns.rs covers admin and merchant auth paths, duplicate name rejection, length validation, deadline handling, category-active enforcement, ownership checks, contribution accumulation, inactive/expired rejection, and the full filter matrix. Closes ShadeProtocol#352
|
@goodness-cpu 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! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #352
Summary
Implements the campaign categories and tagging system requested in #352. The feature is added to the
shadecontract alongside the existing merchant / invoice / subscription / ticketing modules and is fully backwards-compatible — only newDataKeyvariants, new struct types, new error variants, new event types, and new trait methods are introduced.What ships
id,name,description,active,timestamp). Each campaign must reference one active category.id,name,creator,timestamp) created by any registered merchant or the admin. Tags are platform-wide and reusable across campaigns.id,merchant_id,merchant,title,description,category_id,tags: Vec<u64>,goal_amount,token,deadline,raised_amount,active,created_at) that attaches exactly one active category and any number of tags.get_campaigns(filter)with optionalcategory_id,tag_id,merchant_id, andis_activeselectors. Backed by reverse indices (CategoryCampaigns,TagCampaigns,MerchantCampaigns,CampaignTagList) so lookups by category, by tag, and by merchant are O(n) scans over the relevant subset rather than full-table scans.record_campaign_contributionaccumulatesraised_amountand emits an event with the new ratio againstgoal_amount. The contract does not move tokens; campaign payments can be attached to any rail while on-chain metadata + totals remain authoritative.Security model
require_auth()on the calling principal.core::assert_admin.merchant::is_merchant || core address match).merchantAddress (nomerchant_idguessing).pausable_component::assert_not_paused) gates every state-changing entry point.reentrancy::enter / exit) wraps every write that mutates more than one storage key.CampaignInactive = #63) versus "deadline passed" (CampaignExpired = #65) so off-chain indexers can render the right UX.Events
Eight new
#[contractevent]structs inevents.rs:CampaignCategoryCreated—category_id,admin,name,description,timestamp.CampaignCategoryUpdated—category_id,admin,name,description,active,timestamp.CampaignTagCreated—tag_id,creator,name,timestamp.CampaignCreated—campaign_id,merchant,merchant_id,title,description,category_id,tags,goal_amount,token,deadline,timestamp.CampaignUpdated—campaign_id,merchant,title,description,timestamp.CampaignStatusChanged—campaign_id,merchant,active,timestamp.CampaignTagAdded/CampaignTagRemoved—campaign_id,merchant,tag_id,timestamp.CampaignContribution—campaign_id,contributor,amount,raised_amount,goal_amount,timestamp.Every event ships full structural metadata (ids, addresses, amounts, category/tag ids, ledger timestamp) so off-chain UIs / indexers can subscribe without follow-up lookups.
Storage layout
Twelve new
DataKeyvariants appended intypes.rswithout renumbering or re-using existing discriminants; the existing enum goes up to #54 and the new variants occupy #55-#65. Four new structs (CampaignCategory,CampaignTag,Campaign,CampaignFilter) are added alongside. Reverse indices are kept in lockstep on every add/remove:Public API additions
ShadeTrait(andShadeimpl) gain 14 new methods:All write methods are gated by
pausable_component::assert_not_paused. All merchant-facing methods callmerchant_addr.require_auth()and verify ownership against the storedCampaign.merchantAddress.Tests
A new
tests/test_campaigns.rsadds ~20 cases covering:0amount, deactivated-campaign (#63), and expired-deadline (#65) rejection paths.(category ∧ tag)intersection, and across all campaigns.Diff footprint
contracts/shade/src/types.rscontracts/shade/src/errors.rscontracts/shade/src/events.rscontracts/shade/src/components/mod.rscontracts/shade/src/components/campaigns.rscontracts/shade/src/interface.rscontracts/shade/src/shade.rscontracts/shade/src/tests/mod.rscontracts/shade/src/tests/test_campaigns.rsNotes for reviewers
DataKeyvariants, struct fields, error discriminants, or trait methods were renamed or repurposed. The existing enum'srepr(u32)is preserved.cargo/rustc/stellarwere not available in the sandbox where this PR was authored; finalcargo test -p shadeandstellar contract buildverification should be run from the standard dev image (make test).Campaign.tagsis currently stored both on theCampaignstruct and in theCampaignTagListreverse index. Dropping the field shaves rent at the cost of a tiny read-time recompute; planned as a follow-up to keep this diff surgical.Validation commands
Closes #352