Skip to content

feat: M011 CosmWasm marketplace-curation contract with unit tests#74

Open
brawlaphant wants to merge 1 commit intoregen-network:mainfrom
brawlaphant:pr/m011-cosmwasm-contract
Open

feat: M011 CosmWasm marketplace-curation contract with unit tests#74
brawlaphant wants to merge 1 commit intoregen-network:mainfrom
brawlaphant:pr/m011-cosmwasm-contract

Conversation

@brawlaphant
Copy link
Copy Markdown
Contributor

Summary

  • Implements the M011 Marketplace Curation & Quality Signals mechanism as a CosmWasm smart contract in contracts/marketplace-curation/
  • Full collection lifecycle: PROPOSED -> ACTIVE -> UNDER_REVIEW -> SUSPENDED -> CLOSED with all transitions from the SPEC state machine
  • Curation staking (bond lock/unlock), quality score submission (admin/agent), challenge mechanism with slashing and rewards, trade fee distribution to curators, featured listing detection
  • 32 unit tests covering all 9 SPEC acceptance criteria, including reference implementation test vector validation

Contract Structure

File Purpose
Cargo.toml cosmwasm-std 2.2, cw-storage-plus 2.0, cw2, thiserror 2, schemars, serde
src/lib.rs Module exports
src/error.rs ContractError enum (24 variants)
src/state.rs Storage items: Config, Collection, Challenge, QualityScore, QualityFactors
src/msg.rs InstantiateMsg, ExecuteMsg (12 variants), QueryMsg (9 endpoints)
src/contract.rs Full implementation + 32 unit tests

Key Design Decisions

  • Admin as community pool proxy: Challenge deposit forfeitures and community share of slashes are sent to the admin address (on-chain community pool distribution can be added in v2)
  • Append-only quality history: Scores are never edited, full history preserved per SPEC security invariant PR-06: Regen Commons Governance Integration #7
  • Single active challenge per collection: Prevents challenge spam while collection is under review
  • Force-close permissionless: Anyone can close a suspended collection after the top-up window expires (refunds remaining bond to curator)

Test Coverage

All 9 SPEC acceptance tests plus additional edge cases:

  1. Collection lifecycle (create -> activate -> add -> trade -> close -> refund)
  2. Quality scoring with reference implementation vector validation
  3. Challenge resolution (curator wins / challenger wins)
  4. Suspension and recovery via bond top-up
  5. Suspension and forced closure after window expiry
  6. Featured batch detection (score >= 800, in active collection)
  7. Price fairness formula validation
  8. Max collections per curator enforcement
  9. Self-challenge prevention, double challenge prevention, wrong denom, no funds, unauthorized access

Test plan

  • cargo test — 32 tests pass
  • Review SPEC alignment for state machine transitions
  • Review token flow amounts match SPEC section 7

🤖 Generated with Claude Code

Implements the Marketplace Curation & Quality Signals mechanism (M011)
as a CosmWasm smart contract. Covers the full collection lifecycle
(create, activate, add/remove batches, challenge, suspend, close),
curation staking with bond management, quality score submission,
reward distribution via trade fees, and slashing for challenger wins.
Includes 32 unit tests covering all SPEC acceptance criteria and
reference implementation test vectors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the marketplace-curation CosmWasm contract, which manages curated collections of batches, quality scores, and a challenge system. The feedback focuses on scalability concerns inherent to CosmWasm storage patterns. Specifically, storing collection members and quality history as vectors within structs or maps will lead to excessive gas costs as data grows; these should be refactored into separate maps or paginated queries. Additionally, query responses and logic for listing scores should be optimized with pagination and indexing to ensure long-term reliability.

pub bond_amount: Uint128,
pub bond_remaining: Uint128,
pub status: CollectionStatus,
pub members: Vec<String>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Storing collection members as a Vec<String> inside the Collection struct is a significant scalability risk. In CosmWasm, the entire struct must be loaded from and saved to storage during any operation that modifies the collection (e.g., adding/removing batches, resolving challenges). As the number of members grows, the gas cost will increase linearly, eventually leading to transactions failing due to the block gas limit. It is recommended to remove this field and use a separate Map<(u64, &str), Empty> to track membership, where the key is (collection_id, batch_denom).

pub const QUALITY_SCORES: Map<&str, QualityScore> = Map::new("quality_scores");

/// batch_denom -> Vec<QualityScore> (append-only history)
pub const QUALITY_HISTORY: Map<&str, Vec<QualityScore>> = Map::new("quality_history");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using a Vec to store quality history for a batch is an anti-pattern in CosmWasm for data that grows indefinitely. Since the specification requires an append-only history (Security Invariant #7), this vector will grow with every score submission. Loading the entire history to append a new score or to query it will become increasingly expensive and eventually exceed gas or memory limits. It is recommended to use a composite key to store history entries individually.

Suggested change
pub const QUALITY_HISTORY: Map<&str, Vec<QualityScore>> = Map::new("quality_history");
pub const QUALITY_HISTORY: Map<(&str, u64), QualityScore> = Map::new("quality_history");

Comment on lines +144 to +145
#[returns(QualityHistoryResponse)]
QualityHistory { batch_denom: String },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The QualityHistory query returns a full list of historical scores. Since this history is append-only and grows indefinitely, this query will eventually fail or become too expensive to execute. This endpoint should be updated to support pagination (e.g., with start_after and limit parameters) to ensure long-term reliability.


/// Return the listing score for a batch across all collections it appears in
#[returns(ListingScoreResponse)]
ListingScore { batch_denom: String },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The ListingScore query implementation should be carefully designed for scalability. If calculating the collection_count requires iterating over all collections to find where a batch is listed, the query will become increasingly slow and expensive as the number of collections grows. Consider maintaining an inverse index in the state (e.g., a Map mapping batch_denom to a count or a list of collection_ids) to optimize this lookup.

pub bond_amount: Uint128,
pub bond_remaining: Uint128,
pub status: String,
pub members: Vec<String>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Including the full members list in the CollectionResponse is problematic for scalability. If a collection contains a large number of batches, this response will become too large for the network to handle efficiently. It is better to remove this field from the general collection response and provide a separate paginated query specifically for listing the members of a collection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant