Skip to content

feat(platform)!: moderation team reward split, action counters and reason check - #4971

Merged
QuantumExplorer merged 5 commits into
v4.2-devfrom
feat/moderation-reward-split-counters-reasons
Sep 24, 2026
Merged

QuantumExplorer merged 5 commits into
v4.2-devfrom
feat/moderation-reward-split-counters-reasons

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Item 3 of the decentralized moderation teams plan (#4865), on top of seating as reads (#4952) and deletable team changes (#4967). A seated elected team could moderate but not be paid: its claim of the moderators pot was refused, nothing counted who did the work, a team change left the pot to whoever was on the team at the next claim, and the reasons a proposal lists bound nothing. Protocol version 14, tables edited in place.

What was done?

Three commits, reviewable one by one: the pot and the counts, the reason check, the docs.

1. The seated team claims the pot, split by its proposal's rewardSplit

The leader or an active member claims the moderators pot for the team (once per epoch, as before). The pot is paid out by the proposal's three percentages:

  • leader share: to the leader;
  • equal share: in equal parts to the other active members, or to the leader when it has none;
  • actions share: between the whole team, the leader included, in proportion to each one's count of bans, suspensions, warnings and document deletions since the last settle; equally between them when nobody acted.

Rounding: every share and every part of one rounds down to the credit. What is left, a few credits, stays in the pot for the next settle, the rule the equal split of a declared team already follows, so no identity is favoured by the order of the ids. A claim that would pay nobody a credit is refused (41112, as today). The arithmetic is ModerationCharterRewardSplit::payouts in rs-dpp.

Before, with a seated charter on the contract:

leader claims the moderators pot  ->  ContractFeeClaimNotAllowedError (41113)

After, 10/40/50 split, pot of 1_003 credits, leader + two members + one added member, counts leader 0, member A 2, member B 1, added 0:

leader    100             (10% of 1_003, rounded down)
member A  133 + 334       (401 / 3, and 501 * 2/3)
member B  133 + 167
added     133
left in the pot: 3

2. Action counts

Each counted action of a seated team member (ban, suspend, warn, delete a document) reads the signer's count and writes it one higher. Reversals (unban, unsuspend, clear warnings, restore) and the interim's actions before the seating count for nothing.

Where they live, and why: a subtree of the moderated contract's other tree at key 48, [64, contract id, 2, 48], identity id -> Item(u32 BE), created with an elected contract (the only time elected moderation can be declared, so never lazily).

  • Per-member items, not one packed item: the action, far more frequent than a settle, is one point read and a four-byte replacement (a 36-byte insert for the member's first action of a period). A packed item for the team would rewrite every member's count on every action.
  • A settle is one range query bounded by what the team can hold (leader + elected members + maxAddedModerators) and one delete per count. A claim reads the team once (for the claimant's membership and the split), and an addition's cap and settle share one read of the charter and its target.
  • No storage flags: the member whose action writes a count pays for it, and the settle that deletes it refunds nobody, so a settle's fee result carries no refunds for other identities.
  • Key 48, below the version item: created together with the lists, it keeps the banlist on top of the other tree for a contract keeping two or three lists, with or without removal records (with all three and no removal records the suspension list used to top it); it costs the banlist a level for a contract keeping the banlist alone, or the banlist and one other list beside removal records. Pinned by should_keep_the_banlist_on_top_of_an_elected_contracts_other_tree_with_two_or_more_lists.

Every settle deletes the counts: a claim, and the settle a team change forces.

3. A team change settles the pot first

An addedModerator or removedModerator created or deleted (after #4967 a deletion undoes an addition or a removal, so it changes the team too) pays the pot out to the team as it was before the change, by the same split, and resets the counts. It ignores the once-per-epoch claim limit and is not a claim: it writes no last claim, so the team may still claim in the same epoch. A pot too small to pay anyone is settled all the same (the counts start over).

It is an inline hook in the batch's validate_state v0, beside the cap on additions, no new processor trait: once the change passed its own state validation (and the cap), it reads the charter, the target, the pot, the team and the counts, all billed, and the batch action carries the payouts for the batch converter to write before the change. An effect, never a refusal. At most one settle per target per batch (dormant: one transition per batch at every version).

Before:

pot 100_000_000, member warned once; leader removes the member
-> the removal is written, the pot is untouched; the member is paid nothing for its work

After:

-> first: leader +10_000_000, member +90_000_000 (40% equal + the whole action share), counts reset
-> then the removal

4. The reason check

ContractModerationReason gains reason_document_id: Option<Identifier> (reasonDocumentId): the moderation charters contract's reason document the action is taken on. It is appended to the transition's reason, stored under a third tag bit (bit 2, the id right after the code; older values without the bit read as naming none), carried as field 4 of the gRPC reason and by wasm-dpp2.

A seated team's ban, suspension, warning or document deletion must name a reason its proposal lists; otherwise it is refused, paid, in a block and in the mempool, with the new ModerationReasonNotListedError (41203, StateError discriminant 150, pinned). A reason naming none is refused the same way, so a proposal listing no reason can take no such action. The proposal is read, billed, only when a reason document is named. The code field stays as it was, unchecked.

Before, seated team:

ban { reason: { text: "spam" } }                                -> accepted

After:

ban { reason: { text: "spam" } }                                -> ModerationReasonNotListedError (41203)
ban { reason: { text: "spam", reasonDocumentId: <unlisted> } }  -> 41203
ban { reason: { text: "spam", reasonDocumentId: <listed> } }    -> accepted, counted
unban { identityId }                                            -> accepted (reversals carry no reason)

Interim and declared moderators are not bound: a reason document they name is stored as written.

5. The claim's proof

The proof of a claim's execution showed the balance of every recipient, read from the contract. For a seated team the contract does not name the team (the charter contract does), so neither the prover nor a verifier holding the contract could list it. When the claimant is not among the recipients the contract names (a seated team's member), the proof now shows the pot, its last claim and the claimant's balance alone (ContractFeePot::claim_proof_identities); every other claim, an interim team's included, proves every recipient as before. Proving the seated payees would need the charter's team documents in the proof, left for a follow-up.

My calls, please confirm

  1. The leader is in the action share (its bans count like anyone's) and in the equal fallback when nobody acted; the equal share goes to the leader when it has no other member.
  2. Deletions settle too, not only creates: since feat(platform)!: deletable charter team changes and lookups on deletableDocument references (PV14) #4967 deleting an addition takes the member off and deleting a removal puts one back.
  3. A forced settle does not use up the epoch's claim and writes no last claim.
  4. Reversals are neither counted nor bound by reasons, so a team whose proposal lists no reason can still lift what the interim did.
  5. A settle refused as "nothing to claim" is only a claim that pays nobody; a claim paying some members and not others (no actions under an actions-only split) goes through.

In-place changes to shipped generations

Generation Selected by Why consensus cannot change there
batch validate_state v0 (drive-abci), the forced settle hook every protocol version It acts only on a create or delete of the moderation charters contract's addedModerator or removedModerator. That contract is in state from protocol version 14 only (genesis or the upgrade to 14), and a document transition against a contract not in state fails in the transformer before this loop, as for the cap on additions already hooked there. Every other batch leaves the settlements empty.
prove_state_transition v0 and verify_state_transition_was_executed_with_proof v0 (drive), the ContractFeeClaim arm every protocol version Only a contract fee claim (type 25) reaches the arm, a transition inactive before protocol version 14.
DriveContractModerationMethodVersions in DRIVE_CONTRACT_METHOD_VERSIONS_V1 to V3 protocol versions 1 to 13 Four new slots set to 0, like the other moderation slots there: the methods are only reached from protocol version 14 code.

Everything else is in generations no release selects: the moderation transition's and the claim's state v0, insert_contract_moderation_trees v0, the converters contract_fee_claim_transition 0, contract_user_moderation_transition 0 and documents_batch_transition 1, the moderation list storage encoding, and the transition's bincode (type 24 activates at 14).

How Has This Been Tested?

End to end in drive-abci (contract_user_moderation/tests/seated_team/pot.rs and reasons.rs), through process_raw_state_transitions and check tx:

  • should_split_a_seated_teams_claim_by_its_reward_split_rounding_each_part_down: counts 1:2:4 over a 150_000_000 action share, exact balances, 2 credits left in the pot, counts reset, last claim; a joiner the leader did not add and the interim refused.
  • should_split_the_action_share_equally_when_the_team_did_not_act
  • should_count_each_signers_actions_and_reset_the_counts_at_every_settle: ban, suspend, warn, delete count; unban, unsuspend, clear, restore and the interim do not; reset by a claim and by an addition.
  • should_settle_the_pot_to_the_team_as_it_was_before_an_addition: the added member gets nothing earned before, the settle writes no last claim and the team claims in the same epoch.
  • should_settle_the_pot_before_a_removal_and_before_a_change_is_undone: in an epoch already claimed; undoing a removal pays the leader alone, undoing an addition pays the added member first.
  • should_prove_a_seated_teams_claim_with_the_claimants_balance
  • should_refuse_a_seated_teams_action_that_names_no_listed_reason (block and mempool, no id, unlisted, nonexistent; refused actions not counted), should_let_a_team_whose_proposal_lists_no_reason_take_no_bound_action, should_not_bind_the_interim_to_a_listed_reason.

The existing seated team tests now cite a listed reason (the harness writes two reason documents and the proposal lists one).

Unit: ModerationCharterRewardSplit::payouts (9 tests: rounding, remainder, equal fallback, leader alone, foreign counts, largest pot), the counts in Drive (tree created with elected contracts only, write, read, bound, reset without refunds, estimate vs applied, banlist on top), the reason encoding with bit 2, the JSON round trip of reasonDocumentId, the discriminant pin, the converter writing the count, and a GroveDB structure fixture reaching the new node.

Run locally: cargo test -p dpp --lib moderation (98), cargo test -p drive --lib contract::moderation structure (91), cargo test -p drive-abci --lib moderation contract_fee_claim elected charter (148), earlier also batch data_contract_create genesis (973), cargo test -p drive-proof-verifier --lib contract_moderation (33), earlier also cargo test -p drive --lib contract::moderation contract::fee_pots state_transition_action structure (1233), clippy -D warnings on dpp, drive, drive-abci, drive-proof-verifier, dash-sdk, platform-version (all targets, all features) and on wasm-dpp, wasm-dpp2, wasm-sdk for wasm32, cargo check -p drive --no-default-features --features verify, and the wasm-dpp2 ContractUserModerationTransition spec (31 passing) against a rebuilt package. gRPC clients regenerated with yarn workspace @dashevo/dapi-grpc build.

Breaking Changes

Consensus-breaking at protocol version 14 (unreleased):

  • a seated team's claim of the moderators pot is accepted and split, where it was refused;
  • a team change of the charter contract moves credits from the target's pot to the team;
  • a seated team's ban, suspension, warning or deletion needs a listed reasonDocumentId (41203);
  • the moderation reason gains a field (transition bincode, storage tag bit 2, gRPC field 4);
  • an elected contract is created with one more tree ([64, id, 2, 48]), so its creation costs more;
  • the proof of a claim by a seated team's member shows the claimant's balance only.

A devnet already on protocol version 14 with an elected contract created before this change has no counts tree: the counts read as none there, so its seated team's actions go uncounted (the action share splits equally) instead of failing.

A claim of an elected contract's moderators pot reads the seated charter first whoever claims (any identity may be on a seated team), so a claim refused for a stranger pays that query too. The mempool prices a team change without the settle it forces, which only the block's state validation computes, as it does the cap on additions.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed
  • If I added or changed GroveDB structure, I described it in the area's structure.rs, regenerated grovedb-structure.json, and checked the structure viewer link posted on this pull request (described and regenerated; the viewer link is still to check)

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

PR Hygiene · 9995499

  • Bots — coderabbitai not yet · thepastaclaw not yet — /skip-bots proceeds without the ones not yet reported
  • Self-review — post /self-reviewed once the bots are done
  • Within your 5 open PRs
  • Build running
  • Approvals
    • files with no dedicated owner — you own it
    • js-wasm-sdk (packages/js-evo-sdk/src/contracts/facade.ts, packages/wasm-sdk/src/state_transitions/contract.rs) — shumkov
    • dpp — you own it
    • rs-drive-abci — you own it
    • rs-drive — you own it
    • rust-sdk (packages/rs-sdk/src/platform/transition/contract_fee_claim.rs) — lklimek or shumkov

When every box is checked the PR Hygiene check passes and this can merge.

QuantumExplorer and others added 4 commits September 24, 2026 13:34
…plit and settle it before team changes

A seated elected team claims its contract's moderators pot by its proposal's
rewardSplit: the leader share, the equal share between the other members and
the action share by each one's count of bans, suspensions, warnings and
document deletions since the last settle (equally when nobody acted). Every
part rounds down and the remainder stays in the pot.

The counts live in the moderated contract's other tree at key 48, created
with an elected contract. Every settle resets them: a claim, and the settle
an addedModerator or removedModerator create or delete forces first, which
ignores the once-per-epoch limit and writes no last claim.

The claim proof of an elected contract's moderators pot shows the
claimant's balance alone: the contract does not name a seated team.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oposal lists

A moderation reason gains reasonDocumentId, the moderation charters
contract's reason document the action is taken on: appended to the
transition's reason, tag bit 2 where it is stored, field 4 of the gRPC
reason, reasonDocumentId in wasm-dpp2.

A seated elected team's ban, suspension, warning or document deletion must
name a reason its proposal lists, or is refused, paid, in a block and in the
mempool (ModerationReasonNotListedError, 41203). A proposal with no reason
can take no such action. Reversals carry no reason and are not checked,
and the interim and declared moderators are not bound.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sons

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eward-split-counters-reasons

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 24, 2026
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

🌳 GroveDB structure

This pull request changes the described GroveDB structure. Open it in the structure viewer: new nodes glow, removed ones stay as ghosts, and the tour walks through each change.

Added (2 nodes)

  • contracts.contract.other.moderation_action_counts

Changed (1 node)

  • contracts.contract.other.banlist.identity

Compared 5bf351d9af with 9995499ffb. Updated at 2026-09-24T07:38:52.042Z

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

📖 Book Preview built successfully.

Download the preview from the workflow artifacts.
To view locally: download the artifact, unzip, and open index.html.

Updated at 2026-09-24T07:39:06.439Z

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Sep 24, 2026
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: dashpay/platform/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 89cd8631-256b-4e07-aecd-7e6304da3b1e

📥 Commits

Reviewing files that changed from the base of the PR and between 5bf351d and ed23503.

📒 Files selected for processing (83)
  • book/src/data-model/contract-moderation.md
  • book/src/error-handling/error-codes.md
  • docs/protocol/moderation-charters.md
  • packages/dapi-grpc/clients/drive/v0/nodejs/drive_pbjs.js
  • packages/dapi-grpc/clients/platform/v0/nodejs/platform_pbjs.js
  • packages/dapi-grpc/clients/platform/v0/nodejs/platform_protoc.js
  • packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h
  • packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.m
  • packages/dapi-grpc/clients/platform/v0/python/platform_pb2.py
  • packages/dapi-grpc/clients/platform/v0/web/platform_pb.d.ts
  • packages/dapi-grpc/clients/platform/v0/web/platform_pb.js
  • packages/dapi-grpc/protos/platform/v0/platform.proto
  • packages/moderation-charters-contract/README.md
  • packages/rs-dpp/src/data_contract/config/moderation/reason.rs
  • packages/rs-dpp/src/data_contract/document_type/action_fees/mod.rs
  • packages/rs-dpp/src/errors/consensus/codes.rs
  • packages/rs-dpp/src/errors/consensus/state/contract_moderation/mod.rs
  • packages/rs-dpp/src/errors/consensus/state/contract_moderation/moderation_reason_not_listed_error.rs
  • packages/rs-dpp/src/errors/consensus/state/state_error.rs
  • packages/rs-dpp/src/moderation_charter/mod.rs
  • packages/rs-dpp/src/moderation_charter/reward_split.rs
  • packages/rs-dpp/src/state_transition/state_transitions/contract/contract_user_moderation_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/contract/contract_user_moderation_transition/v0/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/common/seated_moderation_charter/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/state/v0/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/state/v0/moderators_pot_settle.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_fee_claim/state/v0/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_user_moderation/state/v0/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_user_moderation/tests.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_user_moderation/tests/seated_team.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_user_moderation/tests/seated_team/pot.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_user_moderation/tests/seated_team/reasons.rs
  • packages/rs-drive-abci/src/query/contract_moderation_queries/contract_document_removals/v0/mod.rs
  • packages/rs-drive-abci/src/query/contract_moderation_queries/contract_moderation_entries/v0/mod.rs
  • packages/rs-drive-abci/src/query/contract_moderation_queries/contract_moderation_status/v0/mod.rs
  • packages/rs-drive-abci/src/query/contract_moderation_queries/mod.rs
  • packages/rs-drive-proof-verifier/src/types/contract_moderation.rs
  • packages/rs-drive-proof-verifier/src/unproved.rs
  • packages/rs-drive/grovedb-structure.json
  • packages/rs-drive/src/drive/contract/moderation/action_count_tests.rs
  • packages/rs-drive/src/drive/contract/moderation/document_removal_tests.rs
  • packages/rs-drive/src/drive/contract/moderation/estimated_costs/mod.rs
  • packages/rs-drive/src/drive/contract/moderation/estimated_costs/v0/mod.rs
  • packages/rs-drive/src/drive/contract/moderation/fetch_contract_moderation_action_counts/mod.rs
  • packages/rs-drive/src/drive/contract/moderation/fetch_contract_moderation_action_counts/v0/mod.rs
  • packages/rs-drive/src/drive/contract/moderation/insert_contract_moderation_trees/mod.rs
  • packages/rs-drive/src/drive/contract/moderation/insert_contract_moderation_trees/v0/mod.rs
  • packages/rs-drive/src/drive/contract/moderation/mod.rs
  • packages/rs-drive/src/drive/contract/moderation/remove_contract_moderation_action_counts/mod.rs
  • packages/rs-drive/src/drive/contract/moderation/remove_contract_moderation_action_counts/v0/mod.rs
  • packages/rs-drive/src/drive/contract/moderation/set_contract_moderation_action_count/mod.rs
  • packages/rs-drive/src/drive/contract/moderation/set_contract_moderation_action_count/v0/mod.rs
  • packages/rs-drive/src/drive/contract/moderation/tests.rs
  • packages/rs-drive/src/drive/contract/moderation/types.rs
  • packages/rs-drive/src/drive/contract/paths.rs
  • packages/rs-drive/src/drive/contract/structure.rs
  • packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs
  • packages/rs-drive/src/state_transition_action/action_convert_to_operations/batch/document/documents_batch_transition.rs
  • packages/rs-drive/src/state_transition_action/action_convert_to_operations/contract/contract_fee_claim_transition.rs
  • packages/rs-drive/src/state_transition_action/action_convert_to_operations/contract/contract_user_moderation_transition.rs
  • packages/rs-drive/src/state_transition_action/batch/mod.rs
  • packages/rs-drive/src/state_transition_action/batch/v0/mod.rs
  • packages/rs-drive/src/state_transition_action/contract/contract_fee_claim/mod.rs
  • packages/rs-drive/src/state_transition_action/contract/contract_fee_claim/transformer.rs
  • packages/rs-drive/src/state_transition_action/contract/contract_fee_claim/v0/mod.rs
  • packages/rs-drive/src/state_transition_action/contract/contract_fee_claim/v0/transformer.rs
  • packages/rs-drive/src/state_transition_action/contract/contract_user_moderation/mod.rs
  • packages/rs-drive/src/state_transition_action/contract/contract_user_moderation/v0/mod.rs
  • packages/rs-drive/src/state_transition_action/contract/contract_user_moderation/v0/transformer.rs
  • packages/rs-drive/src/state_transition_action/contract/mod.rs
  • packages/rs-drive/src/state_transition_action/contract/moderators_pot_settlement.rs
  • packages/rs-drive/src/structure/tests.rs
  • packages/rs-drive/src/util/batch/drive_op_batch/contract_moderation.rs
  • packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_contract_method_versions/mod.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_contract_method_versions/v1.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_contract_method_versions/v2.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_contract_method_versions/v3.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_contract_method_versions/v4.rs
  • packages/rs-platform-version/src/version/v14.rs
  • packages/wasm-dpp/src/errors/consensus/consensus_error.rs
  • packages/wasm-dpp2/src/data_contract/transitions/user_moderation.rs
  • packages/wasm-dpp2/tests/unit/ContractUserModerationTransition.spec.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

This change adds reason-document identifiers and proposal-based reason checks for seated moderation teams. It also adds elected-team moderator-pot reward splits, per-member action counts, and settlements on claims and membership changes, with corresponding storage, API, proof, and documentation updates.

Changes

Elected moderation team rules and settlements

Layer / File(s) Summary
Reason document field and encoding
packages/rs-dpp/src/data_contract/config/moderation/reason.rs, packages/rs-drive/src/drive/contract/moderation/types.rs, packages/dapi-grpc/protos/platform/v0/platform.proto, packages/dapi-grpc/clients/..., packages/wasm-dpp2/..., packages/rs-drive-abci/src/query/..., packages/rs-drive-proof-verifier/..., packages/rs-drive/grovedb-structure.json
Moderation reasons gain an optional reason_document_id. The field is encoded in storage and protobuf, converted through WASM, and included in moderation query responses and proof decoding.
Seated-team reason enforcement
packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_user_moderation/..., packages/rs-dpp/src/errors/consensus/..., packages/wasm-dpp/src/errors/consensus/consensus_error.rs, packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_user_moderation/tests/..., book/src/error-handling/error-codes.md, docs/protocol/moderation-charters.md
Seated-team bans, suspensions, warnings, and document deletions require a reason document listed in the proposal. The new ModerationReasonNotListedError uses code 41203. Reversal and interim actions are not subject to this check.
Action-count tracking and storage
packages/rs-drive/src/drive/contract/moderation/..., packages/rs-drive/src/drive/contract/paths.rs, packages/rs-drive/src/drive/contract/structure.rs, packages/rs-drive/src/state_transition_action/contract/contract_user_moderation/..., packages/rs-drive/src/util/batch/drive_op_batch/contract_moderation.rs, packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_user_moderation/state/v0/mod.rs, packages/rs-platform-version/src/version/drive_versions/...
Elected contracts gain a key-48 tree for per-member moderation action counts. Qualifying seated-team actions update the counts. Drive adds versioned methods and operations to store, fetch, and remove them.
Reward splits, claims, and team-change settlements
packages/rs-dpp/src/moderation_charter/reward_split.rs, packages/rs-drive-abci/src/execution/validation/state_transition/common/seated_moderation_charter/mod.rs, packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_fee_claim/state/v0/mod.rs, packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/state/v0/..., packages/rs-drive/src/state_transition_action/contract/..., packages/rs-drive/src/prove/..., packages/rs-drive/src/verify/..., packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_user_moderation/tests/seated_team/pot.rs, book/src/data-model/contract-moderation.md, docs/protocol/moderation-charters.md
Seated-team pot claims use the proposal reward split and action counts. Team changes settle the pot before membership changes and reset counts. Claim and batch actions carry payouts and count resets into Drive operations; proof handling uses the claimant’s balance for elected-contract moderators-pot claims.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~60 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant FeeClaimValidation
  participant SeatedModerationCharter
  participant ModerationActionCounts
  participant ModerationCharterRewardSplit
  participant ModeratorsPotSettlement
  FeeClaimValidation->>SeatedModerationCharter: settle_moderators_pot
  SeatedModerationCharter->>ModerationActionCounts: fetch member counts
  SeatedModerationCharter->>ModerationCharterRewardSplit: calculate payouts
  SeatedModerationCharter->>ModeratorsPotSettlement: build payouts and count reset list
  ModeratorsPotSettlement->>ModerationActionCounts: remove settled counts
Loading

Suggested reviewers: shumkov, lklimek

Merge Risk: ⚪ Minimal · up to ed235

Protocol version 14 adds reward-split payouts, per-member action counts, pot settlement on team changes, and a listed-reason check for elected moderation teams. The review found no concrete defect in these paths. The reported breaking storage and encoding changes are intentional and call for a devnet reset, as the author notes.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.16% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 151 functions across 44 files. (38 skippe… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: moderation-team reward splits, action counters, and reason validation.
Full details: Docstring Coverage

Explanation

Docstring coverage is 76.16% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 151 functions across 44 files. (38 skipped: 7 unsupported, 6 too large, 25 over the file limit.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@thepastaclaw

thepastaclaw commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

🕓 Queued for automated review — 3rd in line, estimated start in ~1.4 h (commit 9995499)
Estimated review time once started: ~0.9 h (two-phase automated review; median of recent runs).

  • Request priority review — click to move this review to the front of the queue.

- An elected contract without a counts tree (stored before the counts
  existed, on a development network) reads as having no counts, and its
  team's actions go uncounted, instead of failing inside Drive.
- The claim proof shows every recipient the contract names when the
  claimant is one of them (interim claims keep full coverage), the
  claimant alone otherwise (a seated team's member).
- A seated claim reads the team once; the cap on additions and the settle
  an addition forces share one read of the charter and its target.
- Entry size estimates count the reason document id; the share reader goes
  through the proposal reader; the batch converter takes its settlements.
- SDK docs describe the seated split and the claimant-only proof.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@QuantumExplorer QuantumExplorer left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Approved

@QuantumExplorer
QuantumExplorer merged commit cea41c7 into v4.2-dev Sep 24, 2026
21 of 22 checks passed
@QuantumExplorer
QuantumExplorer deleted the feat/moderation-reward-split-counters-reasons branch September 24, 2026 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-bots Waiting for the review bots to report on this head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants