Skip to content

feat(rs-sdk): implement getTokenPreProgrammedDistributions query#3246

Merged
lklimek merged 9 commits into
v3.1-devfrom
feat/sdk-get-token-pre-programmed-distributions
Mar 14, 2026
Merged

feat(rs-sdk): implement getTokenPreProgrammedDistributions query#3246
lklimek merged 9 commits into
v3.1-devfrom
feat/sdk-get-token-pre-programmed-distributions

Conversation

@lklimek
Copy link
Copy Markdown
Contributor

@lklimek lklimek commented Mar 13, 2026

Issue being fixed or feature implemented

Implements the missing GetTokenPreProgrammedDistributions gRPC query across the full RS-SDK stack, fixing the gRPC query coverage CI check failure.

What was done?

Checklist (per packages/rs-sdk/README.md)

  1. ✅ Protobuf messages already defined in platform.proto
  2. build.rs: Added request to VERSIONED_REQUESTS (56) and response to VERSIONED_RESPONSES (54)
  3. rs-dapi-client/transport/grpc.rs: Added impl_transport_request_grpc! macro
  4. rs-drive-proof-verifier/types.rs: New TokenPreProgrammedDistributions newtype wrapping BTreeMap<TimestampMillis, BTreeMap<Identifier, Credits>>
  5. rs-drive-proof-verifier/proof/token_pre_programmed_distributions.rs: FromProof implementation with two-layer verification (GroveDB + Tenderdash)
  6. rs-sdk/src/platform/tokens/token_pre_programmed_distributions.rs: Query + Fetch traits (per-module pattern)
  7. rs-sdk/src/mock/requests.rs: impl_mock_response!(TokenPreProgrammedDistributions)
  8. rs-sdk/src/mock/sdk.rs: load_expectations match clause
  9. rs-sdk/tests/fetch/tokens/token_pre_programmed_distributions.rs: Positive + negative tests with content assertions
  10. ✅ Test vectors generated against local devnet
  11. rs-sdk-ffi: FFI bindings rewritten to use SDK Fetch trait (was raw gRPC)
  12. rs-sdk-ffi/tests: Integration test with correct token ID and vector mapping
  13. rs-drive-abci/create_genesis_state/test/tokens.rs: Added pre-programmed distributions to TOKEN_ID_2

Additional changes

  • README.md: Updated Fetch/FetchMany checklist — standardized per-module pattern, added SDK test data section, clarified vector generation workflow
  • CLAUDE.md: Added cross-reference to RS-SDK checklist
  • Clippy fixes: Removed 5 empty lines in evonode_status.rs

Review fixes (second commit)

Applied 7 fixes from code review triage:

  • FFI test uses TOKEN_ID_2 with correct vectors, errors panic instead of being swallowed
  • None distributions return success with null data (not NotFound error)
  • u32→u16 limit uses checked conversion (u16::try_from)
  • Doc comment clarified (Credits, not TokenAmount)
  • SDK test asserts distribution content (timestamps, recipients, amounts)
  • Removed unnecessary binding in proof verifier
  • Shortened deeply-qualified path with use import

How Has This Been Tested?

  • Offline SDK tests with generated test vectors (cargo test -p dash-sdk)
  • FFI unit tests (test_null_handle, test_null_token_id)
  • FFI integration test with mock vectors
  • Test vectors generated against local devnet with SDK_TEST_DATA=true yarn start
  • cargo check passes for drive-proof-verifier, dash-sdk, rs-sdk-ffi

Breaking Changes

None. This is a new query addition — no existing APIs modified.

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 made corresponding changes to the documentation

🤖 Co-authored by Claudius the Magnificent AI Agent

Summary by CodeRabbit

  • New Features

    • Support for querying token pre‑programmed distributions with pagination and validation, surfaced across SDKs and FFI; returns compact JSON results.
  • Documentation

    • Expanded SDK docs and test-data guidance; added inline checklist references and updated vector-generation workflow.
  • Tests

    • Added integration/unit tests and test vectors for present/absent distribution scenarios; updated mocks and test harnesses.
  • Chores

    • Minor repository housekeeping (gitignore update).

Wire the getTokenPreProgrammedDistributions gRPC query across the full
RS-SDK stack: build.rs versioned messages, transport macro, FromProof
implementation, Query + Fetch traits, MockResponse, mock SDK match
clause, and FFI bindings.

Add TokenPreProgrammedDistributions newtype in the proof verifier with
FromIterator to satisfy Drive's generic verification constraints.
Create SDK test data with pre-programmed distributions for TOKEN_ID_2
(3 timestamps) and add both positive and negative offline test cases
with generated test vectors.

Fix pre-existing clippy warnings in evonode_status.rs (empty lines
after attributes). Update RS-SDK README checklist to reflect current
codebase patterns and document SDK test data configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v3.1.0 milestone Mar 13, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 13, 2026

📝 Walkthrough

Walkthrough

Adds end-to-end support for token pre-programmed distributions: new SDK query types, FFI binding and tests, gRPC registration and transport, proof-verifier logic and types, mock/test-vector additions, and genesis test-data updates.

Changes

Cohort / File(s) Summary
Docs & Guides
CLAUDE.md, packages/rs-sdk/README.md
Inserted Rust SDK checklist reference and expanded SDK test/vector and Query/Fetch implementation guidance.
gRPC Build
packages/dapi-grpc/build.rs
Registered GetTokenPreProgrammedDistributionsRequest/Response as versioned gRPC messages.
gRPC Transport
packages/rs-dapi-client/src/transport/grpc.rs
Added transport binding get_token_pre_programmed_distributions to PlatformGrpcClient.
Proof Verifier — Module & Logic
packages/rs-drive-proof-verifier/src/proof.rs, .../proof/token_pre_programmed_distributions.rs
Added token_pre_programmed_distributions module and FromProof implementation with validation, Drive verification call, and TenderDash proof verification.
Proof Verifier — Types
packages/rs-drive-proof-verifier/src/types.rs, .../types/evonode_status.rs
Added TokenPreProgrammedDistributions type plus iterator/into_inner; duplicate declaration present (needs cleanup). Minor doc/comment removals in evonode_status.
SDK Core
packages/rs-sdk/src/platform/tokens/mod.rs, .../token_pre_programmed_distributions.rs
New module and public structs TokenPreProgrammedDistributionsQuery and TokenPreProgrammedDistributionsStartAtInfo, Query and Fetch impls, and re-export of verifier type.
FFI Bindings
packages/rs-sdk-ffi/src/token/queries/mod.rs, .../pre_programmed_distributions.rs
Re-enabled FFI export and implemented dash_sdk_token_get_pre_programmed_distributions with input validation, SDK-backed async fetch, JSON result formatting, and explicit error codes.
FFI Tests
packages/rs-sdk-ffi/tests/integration_tests/token.rs
Added integration tests for the FFI function (null-handle/token checks); duplicate test blocks were introduced (needs cleanup).
SDK Tests
packages/rs-sdk/tests/fetch/tokens/..., packages/rs-sdk/tests/fetch/generated_data.rs
Added SDK fetch tests for present/absent distributions and updated TOKEN_ID_2 doc comment to note pre-programmed distributions.
Test Vectors
packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_*/*
Added test vector files (quorum pubkey/hash) for present and absent cases.
Genesis Tests
packages/rs-drive-abci/.../create_genesis_state/test/tokens.rs
Extended genesis token configuration to include a pre-programmed distribution for token 2 with nested allocation maps.
Mocks
packages/rs-sdk/src/mock/requests.rs, packages/rs-sdk/src/mock/sdk.rs
Registered TokenPreProgrammedDistributions for mock serialization and added expectation loading for the new request type.
Misc
.gitignore
Added __pycache__/ to .gitignore.
Notes
(multiple files)
Tests and types include duplicate/duplicated blocks (duplicate test cases and duplicate TokenPreProgrammedDistributions type) that require cleanup.

Sequence Diagram

sequenceDiagram
    participant Client as Client (SDK)
    participant Query as Query Builder
    participant GRPC as gRPC Transport
    participant Platform as Platform API
    participant Verifier as Proof Verifier
    participant Drive as Drive (backend)

    Client->>Query: build TokenPreProgrammedDistributionsQuery
    Query->>GRPC: send GetTokenPreProgrammedDistributionsRequest
    GRPC->>Platform: Call platform RPC (token_id, start_at, limit)
    Platform->>Drive: Drive::verify_token_pre_programmed_distributions(...)
    Drive-->>Platform: Response + metadata + TenderDash proof
    Platform-->>GRPC: forward response, metadata, proof
    GRPC->>Verifier: maybe_from_proof_with_metadata(response, metadata, proof)
    Verifier->>Verifier: validate, verify_tenderdash_proof
    Verifier-->>GRPC: TokenPreProgrammedDistributions (or None) + metadata + proof
    GRPC-->>Client: return fetch result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I hopped through code and stitched a stream of time,
Timestamps and recipients now neatly chime,
From gRPC calls to proofs that bind,
Distributions found — the ledger's kind,
A joyful hop for every token's rhyme.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(rs-sdk): implement getTokenPreProgrammedDistributions query' accurately summarizes the main change: implementing a new token query feature across the RS-SDK stack.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/sdk-get-token-pre-programmed-distributions
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 13, 2026

✅ DashSDKFFI.xcframework built for this PR.

SwiftPM (host the zip at a stable URL, then use):

.binaryTarget(
  name: "DashSDKFFI",
  url: "https://your.cdn.example/DashSDKFFI.xcframework.zip",
  checksum: "9536aa6f2e45593f24e32e3520e06921257e9a933039c603ddb3affc6dc18b8a"
)

Xcode manual integration:

  • Download 'DashSDKFFI.xcframework' artifact from the run link above.
  • Drag it into your app target (Frameworks, Libraries & Embedded Content) and set Embed & Sign.
  • If using the Swift wrapper package, point its binaryTarget to the xcframework location or add the package and place the xcframework at the expected path.

@lklimek lklimek marked this pull request as ready for review March 13, 2026 12:42
@lklimek lklimek requested a review from Copilot March 13, 2026 12:42
@lklimek
Copy link
Copy Markdown
Contributor Author

lklimek commented Mar 13, 2026

🔍 Code Review Audit — PR #3246

Reviewed by: Claude Code with a 3-agent team:

  • security-engineer (opus) — FFI safety, proof verification integrity, OWASP classification
  • project-reviewer (opus) — cross-artifact consistency, checklist adherence, naming conventions
  • developer-bilby (opus) — Rust code quality, idioms, error handling, duplication

Overall Assessment

APPROVE with minor suggestions. Clean implementation that correctly wires GetTokenPreProgrammedDistributions across all SDK layers. No CRITICAL or HIGH issues found. The implementation follows the established 14-step checklist precisely. Proof verification is two-layer (GroveDB + Tenderdash). All inputs are validated. No new dependencies introduced.

Findings Summary

ID Severity Location Description
PROJ-001 🟡 MEDIUM rs-sdk-ffi/tests/.../token.rs:179-210 FFI integration test provides zero validation — uses TOKEN_ID_0 (no distributions), no matching vector directory, and silently accepts all match arms including errors
CODE-001 🟡 MEDIUM rs-sdk-ffi/.../pre_programmed_distributions.rs:139 None distributions returned as FFIError::NotFound instead of success with no data — semantic mismatch with SDK behavior
CODE-002 🔵 LOW rs-sdk-ffi/.../pre_programmed_distributions.rs:115-137 Manual format!-based JSON construction (systemic codebase pattern)
SEC-001 🔵 LOW rs-drive-proof-verifier/.../token_pre_programmed_distributions.rs:70 u32u16 limit truncation without bounds check (systemic, 15+ locations)
CODE-003 🔵 LOW rs-sdk/.../token_pre_programmed_distributions.rs:35-37 unimplemented!() panic on prove=false (systemic, 46+ locations, currently unreachable)
DOC-001 🔵 LOW rs-drive-proof-verifier/src/types.rs:669 Doc says TokenAmount but struct uses Credits
PROJ-002 🔵 LOW rs-sdk/tests/.../token_pre_programmed_distributions.rs:34-38 SDK test asserts distribution count (3) but not content (timestamps, recipients, amounts)

Before Merge Recommendations

No blocking items.

Before Production

  • PROJ-001: Fix FFI test to use TOKEN_ID_2, add vector namespace mapping, change Err arm to panic!
  • CODE-001: Align None handling with total_supply.rs pattern (success with null data) or document the NotFound convention

Systemic Issues (not introduced by this PR)

CODE-002, SEC-001, CODE-003 are all pre-existing codebase patterns found in 15–46+ locations. Not blocking.

Positive Observations

  • ✅ Two-layer proof verification (GroveDB Merkle + Tenderdash consensus) correctly prevents forged results
  • ✅ All 7 SDK layers (build.rs, transport, proof verifier, SDK, mock, FFI, tests) consistently wired
  • ✅ Zero new dependencies — no supply chain risk
  • ✅ FFI rewrite is a significant security improvement (shared runtime, SDK Fetch with proof verification vs old raw gRPC)
  • TokenPreProgrammedDistributions newtype with correct FromIterator constraints and BTreeMap for deterministic ordering
  • ✅ README checklist improvements lower the barrier for future SDK contributors
  • ✅ Pre-existing clippy warnings fixed in touched files

Agent Stats

Agent Unique Findings Redundant Candy 🍬
security-engineer 3 3 🍬🍬🍬
project-reviewer 4 2 🍬🍬🍬🍬
developer-bilby 3 4 🍬🍬🍬

Redundancy ratio: 29% (9 redundant out of 31 raw findings → 14 after dedup)

🏆 project-reviewer wins bragging rights with 4 unique findings!

🤖 Co-authored by Claudius the Magnificent AI Agent

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Implements the missing getTokenPreProgrammedDistributions query end-to-end across the Rust SDK stack (gRPC mappings, transport, proof verification/types, SDK query API, mocks), plus FFI bindings and offline test vectors, to satisfy the gRPC query coverage CI check.

Changes:

  • Added the versioned gRPC request/response mappings and a transport client method for GetTokenPreProgrammedDistributions.
  • Introduced proof-verifier support (FromProof) and a new TokenPreProgrammedDistributions wrapper type, then exposed an SDK TokenPreProgrammedDistributionsQuery + Fetch implementation.
  • Added SDK offline tests + generated vectors, and rewired the FFI query implementation to use the SDK fetch path; updated related docs/checklists.

Reviewed changes

Copilot reviewed 21 out of 25 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
packages/dapi-grpc/build.rs Registers request/response as versioned messages for codegen/mapping.
packages/rs-dapi-client/src/transport/grpc.rs Adds gRPC transport wiring for the new RPC.
packages/rs-drive-proof-verifier/src/proof.rs Exposes the new proof module.
packages/rs-drive-proof-verifier/src/proof/token_pre_programmed_distributions.rs Implements proof verification parsing for the new query.
packages/rs-drive-proof-verifier/src/types.rs Adds TokenPreProgrammedDistributions wrapper type for SDK/proof verifier.
packages/rs-drive-proof-verifier/src/types/evonode_status.rs Minor formatting/clippy cleanup (empty lines after attributes).
packages/rs-sdk/src/platform/tokens/mod.rs Exports the new token query module.
packages/rs-sdk/src/platform/tokens/token_pre_programmed_distributions.rs Adds SDK query params + Query and Fetch wiring.
packages/rs-sdk/src/mock/requests.rs Adds mock response support for the new return type.
packages/rs-sdk/src/mock/sdk.rs Adds expectation loading for the new request type.
packages/rs-sdk/tests/fetch/generated_data.rs Documents that TOKEN_ID_2 includes pre-programmed distributions in test data.
packages/rs-sdk/tests/fetch/tokens/mod.rs Adds the new fetch test module to the suite.
packages/rs-sdk/tests/fetch/tokens/token_pre_programmed_distributions.rs Adds offline SDK tests for “present” and “absent” cases.
packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_present/.gitkeep Keeps the vectors directory tracked.
packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_present/quorum_pubkey-106-7e46a98df86489ce0e3845110ce59d247f99d4333a771efb4bc554e4575d59c9.json Adds quorum pubkey vector for “present” case.
packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_present/msg_GetTokenPreProgrammedDistributionsRequest_70451dbd24fcfcbf1831ab10f5ae02962217f1ae14791d7fbee47d4e01696b52.json Adds recorded request/response vector for “present” case.
packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_absent/.gitkeep Keeps the vectors directory tracked.
packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_absent/quorum_pubkey-106-7e46a98df86489ce0e3845110ce59d247f99d4333a771efb4bc554e4575d59c9.json Adds quorum pubkey vector for “absent” case.
packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_absent/msg_GetTokenPreProgrammedDistributionsRequest_a46a647398eedeed648eafe389472c682c883ab61c852a35528493ea2680909b.json Adds recorded request/response vector for “absent” case.
packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs Adds deterministic pre-programmed distributions to genesis test token config.
packages/rs-sdk/README.md Updates SDK checklist/docs around test data and vector generation.
packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs Implements FFI query via SDK Fetch path (rewrites prior disabled stub).
packages/rs-sdk-ffi/src/token/queries/mod.rs Exports the new FFI function.
packages/rs-sdk-ffi/tests/integration_tests/token.rs Adds an integration test exercising the new FFI query.
CLAUDE.md Links to the rs-sdk checklist reference.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/rs-drive-proof-verifier/src/proof/token_pre_programmed_distributions.rs Outdated
Comment thread packages/rs-drive-proof-verifier/src/types.rs Outdated
Comment thread packages/rs-sdk/src/platform/tokens/token_pre_programmed_distributions.rs Outdated
Comment thread packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs Outdated
Comment thread packages/rs-sdk-ffi/tests/integration_tests/token.rs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
packages/rs-sdk/tests/fetch/tokens/token_pre_programmed_distributions.rs (1)

14-15: Consider renaming tests to follow the should_ naming convention.

Per coding guidelines, test names should start with should … for descriptive naming. These are new tests, so they should follow this convention.

📝 Suggested renames
-async fn test_token_pre_programmed_distributions_present() {
+async fn should_fetch_token_pre_programmed_distributions_when_present() {
-async fn test_token_pre_programmed_distributions_absent() {
+async fn should_return_none_when_token_has_no_pre_programmed_distributions() {

Based on learnings: "Enforce descriptive naming for new tests in test files with names starting with 'should …'."

Also applies to: 42-43

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk/tests/fetch/tokens/token_pre_programmed_distributions.rs`
around lines 14 - 15, Rename the test function
test_token_pre_programmed_distributions_present to follow the "should_"
convention (e.g., should_token_pre_programmed_distributions_present) and update
any other tests mentioned (lines 42-43) to the same pattern; modify the async
test function names (the tokio test functions) and any references to those
functions in the file so the new names are used consistently (search for
test_token_pre_programmed_distributions_present and the other test identifiers
and replace them with should_... variants).
packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs (2)

166-196: Rename tests to follow 'should …' naming convention.

Per coding guidelines, new tests should have descriptive names starting with 'should …'. Based on learnings: "Enforce descriptive naming for new tests in test files with names starting with 'should …'".

♻️ Suggested renames
 #[test]
-fn test_null_handle() {
+fn should_return_error_when_handle_is_null() {
     // ...
 }

 #[test]
-fn test_null_token_id() {
+fn should_return_error_when_token_id_is_null() {
     // ...
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs` around
lines 166 - 196, Rename the two test functions to follow the "should …"
convention: change test_null_handle to a descriptive name like
should_return_error_when_handle_is_null and test_null_token_id to
should_return_error_when_token_id_is_null; update the function identifiers in
the file (the unsafe calls to dash_sdk_token_get_pre_programmed_distributions
and the assertions remain the same) and ensure any test helper calls
(create_mock_sdk_handle, destroy_mock_sdk_handle) are still referenced
correctly, then run the test suite to confirm names compile and tests pass.

115-137: Consider using serde_json for JSON construction.

Manual JSON string building is fragile and can produce malformed output if values contain unexpected characters. While Identifier uses Base58 (safe) and Credits is numeric, using serde_json::json! or serialization would be more robust and maintainable.

♻️ Suggested refactor using serde_json
+use serde_json::json;
+
 // Inside the async block:
-                let distributions_json: Vec<String> = dists
-                    .0
-                    .iter()
-                    .map(|(timestamp, recipients)| {
-                        let recipients_json: Vec<String> = recipients
-                            .iter()
-                            .map(|(recipient_id, amount)| {
-                                format!(
-                                    r#"{{"recipient_id":"{}","amount":{}}}"#,
-                                    recipient_id, amount
-                                )
-                            })
-                            .collect();
-
-                        format!(
-                            r#"{{"timestamp":{},"distributions":[{}]}}"#,
-                            timestamp,
-                            recipients_json.join(",")
-                        )
-                    })
-                    .collect();
-
-                Ok(format!("[{}]", distributions_json.join(",")))
+                let distributions_vec: Vec<_> = dists
+                    .0
+                    .iter()
+                    .map(|(timestamp, recipients)| {
+                        let recipients_vec: Vec<_> = recipients
+                            .iter()
+                            .map(|(recipient_id, amount)| {
+                                json!({
+                                    "recipient_id": recipient_id.to_string(),
+                                    "amount": amount
+                                })
+                            })
+                            .collect();
+                        json!({
+                            "timestamp": timestamp,
+                            "distributions": recipients_vec
+                        })
+                    })
+                    .collect();
+
+                Ok(serde_json::to_string(&distributions_vec)?)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs` around
lines 115 - 137, The current manual string construction of JSON from dists
(variables distributions_json, recipients_json, recipients, recipient_id,
amount) is fragile; replace it by building actual JSON values with serde_json
(either using serde_json::json! to construct nested arrays/objects or by
defining serializable structs and collecting into Vecs) and then call
serde_json::to_string(&value) to produce the final String; add the necessary
serde_json import and propagate/handle any serialization error (returning it
from the enclosing function) instead of formatting raw strings.
packages/rs-sdk-ffi/tests/integration_tests/token.rs (1)

177-210: Rename test to follow 'should …' naming convention.

Per coding guidelines, new tests should have descriptive names starting with 'should …'. Based on learnings: "Enforce descriptive naming for new tests in test files with names starting with 'should …'".

♻️ Suggested rename
-/// Test fetching pre-programmed distributions for a token
+/// Should return pre-programmed distributions for a token when available
 #[test]
-fn test_token_pre_programmed_distributions() {
+fn should_fetch_pre_programmed_distributions_for_token() {

Note: The test implementation itself looks correct - it follows the established pattern in this file, properly handles success/not-found/error cases, and uses mock vectors appropriately for offline testing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk-ffi/tests/integration_tests/token.rs` around lines 177 - 210,
Rename the test function test_token_pre_programmed_distributions to a
descriptive 'should …' name (for example
should_fetch_pre_programmed_distributions_for_token) and update the test's
internal handle name passed to create_test_sdk_handle to the new function name
string; modify the fn identifier (fn test_token_pre_programmed_distributions ->
fn should_fetch_pre_programmed_distributions_for_token) and the
create_test_sdk_handle("test_token_pre_programmed_distributions") call to use
the matching new name so the test and its handle string remain consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/rs-drive-proof-verifier/src/proof/token_pre_programmed_distributions.rs`:
- Line 70: The conversion of req_v0.limit to u16 using map(|l| l as u16) can
silently truncate values > u16::MAX; replace this with the existing
u32_to_u16_opt helper (or equivalent explicit check) to validate and return an
error on overflow instead of truncating — update the code that sets limit
(referencing req_v0.limit and the local variable limit) to call u32_to_u16_opt
and propagate the error (consistent with how GetDataContractHistoryRequest
handles u32_to_u16_opt).

---

Nitpick comments:
In `@packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs`:
- Around line 166-196: Rename the two test functions to follow the "should …"
convention: change test_null_handle to a descriptive name like
should_return_error_when_handle_is_null and test_null_token_id to
should_return_error_when_token_id_is_null; update the function identifiers in
the file (the unsafe calls to dash_sdk_token_get_pre_programmed_distributions
and the assertions remain the same) and ensure any test helper calls
(create_mock_sdk_handle, destroy_mock_sdk_handle) are still referenced
correctly, then run the test suite to confirm names compile and tests pass.
- Around line 115-137: The current manual string construction of JSON from dists
(variables distributions_json, recipients_json, recipients, recipient_id,
amount) is fragile; replace it by building actual JSON values with serde_json
(either using serde_json::json! to construct nested arrays/objects or by
defining serializable structs and collecting into Vecs) and then call
serde_json::to_string(&value) to produce the final String; add the necessary
serde_json import and propagate/handle any serialization error (returning it
from the enclosing function) instead of formatting raw strings.

In `@packages/rs-sdk-ffi/tests/integration_tests/token.rs`:
- Around line 177-210: Rename the test function
test_token_pre_programmed_distributions to a descriptive 'should …' name (for
example should_fetch_pre_programmed_distributions_for_token) and update the
test's internal handle name passed to create_test_sdk_handle to the new function
name string; modify the fn identifier (fn
test_token_pre_programmed_distributions -> fn
should_fetch_pre_programmed_distributions_for_token) and the
create_test_sdk_handle("test_token_pre_programmed_distributions") call to use
the matching new name so the test and its handle string remain consistent.

In `@packages/rs-sdk/tests/fetch/tokens/token_pre_programmed_distributions.rs`:
- Around line 14-15: Rename the test function
test_token_pre_programmed_distributions_present to follow the "should_"
convention (e.g., should_token_pre_programmed_distributions_present) and update
any other tests mentioned (lines 42-43) to the same pattern; modify the async
test function names (the tokio test functions) and any references to those
functions in the file so the new names are used consistently (search for
test_token_pre_programmed_distributions_present and the other test identifiers
and replace them with should_... variants).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9ec90002-1f5c-4a6b-9e4b-66614f570483

📥 Commits

Reviewing files that changed from the base of the PR and between 63bd81b and 25d1056.

📒 Files selected for processing (25)
  • CLAUDE.md
  • packages/dapi-grpc/build.rs
  • packages/rs-dapi-client/src/transport/grpc.rs
  • packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs
  • packages/rs-drive-proof-verifier/src/proof.rs
  • packages/rs-drive-proof-verifier/src/proof/token_pre_programmed_distributions.rs
  • packages/rs-drive-proof-verifier/src/types.rs
  • packages/rs-drive-proof-verifier/src/types/evonode_status.rs
  • packages/rs-sdk-ffi/src/token/queries/mod.rs
  • packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs
  • packages/rs-sdk-ffi/tests/integration_tests/token.rs
  • packages/rs-sdk/README.md
  • packages/rs-sdk/src/mock/requests.rs
  • packages/rs-sdk/src/mock/sdk.rs
  • packages/rs-sdk/src/platform/tokens/mod.rs
  • packages/rs-sdk/src/platform/tokens/token_pre_programmed_distributions.rs
  • packages/rs-sdk/tests/fetch/generated_data.rs
  • packages/rs-sdk/tests/fetch/tokens/mod.rs
  • packages/rs-sdk/tests/fetch/tokens/token_pre_programmed_distributions.rs
  • packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_absent/.gitkeep
  • packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_absent/msg_GetTokenPreProgrammedDistributionsRequest_a46a647398eedeed648eafe389472c682c883ab61c852a35528493ea2680909b.json
  • packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_absent/quorum_pubkey-106-7e46a98df86489ce0e3845110ce59d247f99d4333a771efb4bc554e4575d59c9.json
  • packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_present/.gitkeep
  • packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_present/msg_GetTokenPreProgrammedDistributionsRequest_70451dbd24fcfcbf1831ab10f5ae02962217f1ae14791d7fbee47d4e01696b52.json
  • packages/rs-sdk/tests/vectors/test_token_pre_programmed_distributions_present/quorum_pubkey-106-7e46a98df86489ce0e3845110ce59d247f99d4333a771efb4bc554e4575d59c9.json
💤 Files with no reviewable changes (1)
  • packages/rs-drive-proof-verifier/src/types/evonode_status.rs

Comment thread packages/rs-drive-proof-verifier/src/proof/token_pre_programmed_distributions.rs Outdated
Apply 7 triage fixes:
- PROJ-001: FFI test uses TOKEN_ID_2 with correct vectors, errors panic
- CODE-001: None distributions return success with null, not NotFound
- SEC-001: u32→u16 limit uses checked conversion
- DOC-001: Doc comment clarifies token amounts in credits
- PROJ-002: SDK test asserts distribution content, not just count
- CODE-004: Remove unnecessary binding in proof verifier
- CODE-005: Shorten deeply-qualified path with use import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs (1)

115-137: Consider using serde_json for JSON construction.

Manual JSON construction via format! works here since Base58 identifiers and integer amounts don't require escaping. However, using serde_json would provide automatic escaping guarantees and better maintainability if the data model evolves.

This is a low-priority suggestion given that the current approach is functional and matches existing patterns in the codebase.

♻️ Optional refactor using serde_json
// Alternative approach using serde_json:
let distributions_json = serde_json::to_string(
    &dists.0.iter().map(|(timestamp, recipients)| {
        serde_json::json!({
            "timestamp": timestamp,
            "distributions": recipients.iter().map(|(id, amt)| {
                serde_json::json!({
                    "recipient_id": id.to_string(),
                    "amount": amt
                })
            }).collect::<Vec<_>>()
        })
    }).collect::<Vec<_>>()
).map_err(|e| FFIError::InternalError(format!("JSON serialization failed: {}", e)))?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs` around
lines 115 - 137, The current manual JSON construction in the block that builds
distributions_json from dists (using nested format! calls) should be replaced
with serde_json serialization to ensure correct escaping and maintainability;
update the code that builds distributions_json (the closure mapping over dists.0
and the final Ok(Some(...)) to instead construct Rust structs or
serde_json::Value (e.g., mapping each (timestamp, recipients) to an object with
"timestamp" and a "distributions" array of objects with "recipient_id" and
"amount") and then call serde_json::to_string, propagating any serialization
error as an FFIError (or existing error type) to replace the current string
concatenation approach. Ensure you reference and replace distributions_json and
the final Ok(Some(...)) return so the function returns the serialized JSON
string produced by serde_json.
packages/rs-drive-proof-verifier/src/types.rs (1)

666-695: Prefer TokenAmount over Credits in this public type.

Drive::verify_token_pre_programmed_distributions() verifies token amounts, but this wrapper exposes them as dpp::fee::Credits. The aliases are compatible today, yet the public API now advertises the wrong unit and keeps the docs confusing.

♻️ Suggested shape
 pub struct TokenPreProgrammedDistributions(
-    pub BTreeMap<TimestampMillis, BTreeMap<Identifier, Credits>>,
+    pub BTreeMap<TimestampMillis, BTreeMap<Identifier, TokenAmount>>,
 );

 impl TokenPreProgrammedDistributions {
     /// Get the inner map.
-    pub fn into_inner(self) -> BTreeMap<TimestampMillis, BTreeMap<Identifier, Credits>> {
+    pub fn into_inner(self) -> BTreeMap<TimestampMillis, BTreeMap<Identifier, TokenAmount>> {
         self.0
     }
 }

-impl FromIterator<(TimestampMillis, BTreeMap<Identifier, Credits>)>
+impl FromIterator<(TimestampMillis, BTreeMap<Identifier, TokenAmount>)>
     for TokenPreProgrammedDistributions
 {
-    fn from_iter<T: IntoIterator<Item = (TimestampMillis, BTreeMap<Identifier, Credits>)>>(
+    fn from_iter<T: IntoIterator<Item = (TimestampMillis, BTreeMap<Identifier, TokenAmount>)>>(
         iter: T,
     ) -> Self {
         Self(iter.into_iter().collect())
     }
 }
use dpp::balances::credits::TokenAmount;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-drive-proof-verifier/src/types.rs` around lines 666 - 695, The
public struct TokenPreProgrammedDistributions currently exposes inner values as
Credits; change the public API to use TokenAmount instead: replace all
occurrences of Credits with dpp::balances::credits::TokenAmount in the
TokenPreProgrammedDistributions tuple type, the into_inner return type, and the
FromIterator item and generic signatures for
TokenPreProgrammedDistributions::from_iter; add the appropriate use
dpp::balances::credits::TokenAmount import and ensure any trait bounds remain
satisfied so Drive::verify_token_pre_programmed_distributions continues to work
with TokenAmount.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs`:
- Around line 166-178: The test test_null_handle creates a temporary CString and
calls as_ptr() which gets dropped immediately, risking a dangling pointer; to
fix, keep the CString alive for the duration of the call by binding it to a
local variable (e.g., let name = CString::new("test").unwrap();) and then pass
name.as_ptr() into dash_sdk_token_get_pre_programmed_distributions so the
pointer remains valid until after the function returns; apply the same pattern
for any other temporary CStrings used in that test.

---

Nitpick comments:
In `@packages/rs-drive-proof-verifier/src/types.rs`:
- Around line 666-695: The public struct TokenPreProgrammedDistributions
currently exposes inner values as Credits; change the public API to use
TokenAmount instead: replace all occurrences of Credits with
dpp::balances::credits::TokenAmount in the TokenPreProgrammedDistributions tuple
type, the into_inner return type, and the FromIterator item and generic
signatures for TokenPreProgrammedDistributions::from_iter; add the appropriate
use dpp::balances::credits::TokenAmount import and ensure any trait bounds
remain satisfied so Drive::verify_token_pre_programmed_distributions continues
to work with TokenAmount.

In `@packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs`:
- Around line 115-137: The current manual JSON construction in the block that
builds distributions_json from dists (using nested format! calls) should be
replaced with serde_json serialization to ensure correct escaping and
maintainability; update the code that builds distributions_json (the closure
mapping over dists.0 and the final Ok(Some(...)) to instead construct Rust
structs or serde_json::Value (e.g., mapping each (timestamp, recipients) to an
object with "timestamp" and a "distributions" array of objects with
"recipient_id" and "amount") and then call serde_json::to_string, propagating
any serialization error as an FFIError (or existing error type) to replace the
current string concatenation approach. Ensure you reference and replace
distributions_json and the final Ok(Some(...)) return so the function returns
the serialized JSON string produced by serde_json.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a54bfbd0-7977-49af-88ec-89c7fa4164ee

📥 Commits

Reviewing files that changed from the base of the PR and between 25d1056 and b92be4b.

📒 Files selected for processing (7)
  • .gitignore
  • packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/tokens.rs
  • packages/rs-drive-proof-verifier/src/proof/token_pre_programmed_distributions.rs
  • packages/rs-drive-proof-verifier/src/types.rs
  • packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs
  • packages/rs-sdk-ffi/tests/integration_tests/token.rs
  • packages/rs-sdk/tests/fetch/tokens/token_pre_programmed_distributions.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/rs-sdk/tests/fetch/tokens/token_pre_programmed_distributions.rs
  • packages/rs-sdk-ffi/tests/integration_tests/token.rs

- Fix incorrect base_supply values in README token test data table
- Document default-inclusive behavior for start_recipient_included
- Fix CString use-after-free in FFI unit test (bind to variable before as_ptr)
- Add content assertions to FFI integration test (array length, structure)
- Add absent distributions FFI test using TOKEN_ID_0
- Only set start_recipient_included in proto when start_recipient is present

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/rs-sdk/README.md (1)

153-160: Clarify checklist responsibility split between Step 6 and Step 8.

Step 6 already includes FetchMany, then Step 8 repeats it conditionally. Consider making Step 6 strictly Query + Fetch and leaving FetchMany only in Step 8 to avoid ambiguity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk/README.md` around lines 153 - 160, Update the README
checklist to remove ambiguity by making Step 6 require implementing the Query
trait for Request and the Fetch trait for Object (or the appropriate type) and
move any FetchMany mention solely into Step 8; reference the existing symbols
Request, Query, Fetch, FetchMany and Object and note that FetchMany
implementations (with Request as the inner Request type) belong only in Step 8,
while Step 6 remains Query+Fetch; also keep the note about adding the
implementation in a dedicated module (e.g.,
packages/rs-sdk/src/platform/tokens/my_query.rs) and leave MockResponse in Step
7 unchanged.
packages/rs-sdk-ffi/tests/integration_tests/token.rs (1)

187-187: Rename newly added tests to the required should_... style.

These are new tests under tests/ and should follow the project naming rule.

✏️ Proposed rename
-fn test_token_pre_programmed_distributions() {
+fn should_fetch_token_pre_programmed_distributions() {

-fn test_token_pre_programmed_distributions_absent() {
+fn should_return_no_token_pre_programmed_distributions_for_token_id_0() {

Based on learnings: "Enforce descriptive naming for new tests in test files with names starting with 'should …'. Do not rename existing tests that already follow a different convention."

Also applies to: 243-243

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk-ffi/tests/integration_tests/token.rs` at line 187, Rename the
newly added test functions to the project's required naming convention by
changing test_token_pre_programmed_distributions to
should_token_pre_programmed_distributions (and similarly rename the other new
test in the same file that currently uses the test_* prefix to a should_* name);
update the test function identifiers (the #[test] functions) so their names
start with "should_" and adjust any internal references or module exports that
call those functions to match the new names.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs`:
- Around line 67-93: Currently the code silently ignores a non-null
start_recipient when start_time_ms == 0; update the validation so that if
start_time_ms == 0 and start_recipient is not null you return an error
(DashSDKResult::error) with DashSDKErrorCode::InvalidParameter and a clear
message like "start_recipient provided but start_time_ms is zero" instead of
proceeding to None; keep parsing logic for start_recipient inside the
start_time_ms > 0 branch (Identifier::from_string and CStr::from_ptr usage) but
add an explicit check before constructing start_at_info to detect the invalid
combination and return the error (referencing start_time_ms, start_recipient,
start_recipient_included, and TokenPreProgrammedDistributionsStartAtInfo).

In `@packages/rs-sdk-ffi/tests/integration_tests/token.rs`:
- Around line 203-235: The test currently allows Ok(None) (no data) which masks
regressions; update the match handling so that in the “present distributions”
case the Ok(None) branch fails instead of passing. Specifically, in the test
around parse_string_result(result) (and using parse_json_result), replace the
Ok(None) arm with an assert or panic that explicitly fails (e.g.,
panic!("Expected distributions for TOKEN_ID_2, got None") or assert!(false,
...)), so a missing result causes the test to fail rather than silently succeed.

---

Nitpick comments:
In `@packages/rs-sdk-ffi/tests/integration_tests/token.rs`:
- Line 187: Rename the newly added test functions to the project's required
naming convention by changing test_token_pre_programmed_distributions to
should_token_pre_programmed_distributions (and similarly rename the other new
test in the same file that currently uses the test_* prefix to a should_* name);
update the test function identifiers (the #[test] functions) so their names
start with "should_" and adjust any internal references or module exports that
call those functions to match the new names.

In `@packages/rs-sdk/README.md`:
- Around line 153-160: Update the README checklist to remove ambiguity by making
Step 6 require implementing the Query trait for Request and the Fetch trait for
Object (or the appropriate type) and move any FetchMany mention solely into Step
8; reference the existing symbols Request, Query, Fetch, FetchMany and Object
and note that FetchMany implementations (with Request as the inner Request type)
belong only in Step 8, while Step 6 remains Query+Fetch; also keep the note
about adding the implementation in a dedicated module (e.g.,
packages/rs-sdk/src/platform/tokens/my_query.rs) and leave MockResponse in Step
7 unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 332cf6ec-a91c-4fea-9695-131baffe4653

📥 Commits

Reviewing files that changed from the base of the PR and between b92be4b and 7994ff9.

📒 Files selected for processing (5)
  • packages/rs-drive-proof-verifier/src/proof/token_pre_programmed_distributions.rs
  • packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs
  • packages/rs-sdk-ffi/tests/integration_tests/token.rs
  • packages/rs-sdk/README.md
  • packages/rs-sdk/src/platform/tokens/token_pre_programmed_distributions.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/rs-sdk/src/platform/tokens/token_pre_programmed_distributions.rs

Comment on lines +203 to +235
match parse_string_result(result) {
Ok(Some(json_str)) => {
let json = parse_json_result(&json_str).expect("valid JSON");
assert!(json.is_array(), "Expected array, got: {:?}", json);
let arr = json.as_array().unwrap();
assert_eq!(
arr.len(),
3,
"Expected 3 distribution timestamps, got {}",
arr.len()
);
// Verify at least the first entry has the expected structure.
// Test data: timestamp 1000 with 2 recipients, timestamp 5000 with 1, timestamp 10000 with 2.
let first = &arr[0];
assert!(
first.get("timestamp").is_some(),
"Entry should have a 'timestamp' field: {:?}",
first
);
assert!(
first
.get("distributions")
.map(|d| d.is_array())
.unwrap_or(false),
"Entry should have a 'distributions' array: {:?}",
first
);
}
Ok(None) => {
// Vectors not yet generated; absent distributions is also acceptable.
}
Err(e) => panic!("Unexpected error: {}", e),
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Mar 13, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make the “present distributions” test strict on expected data presence.

Ok(None) is currently accepted for TOKEN_ID_2, so this test can pass even if the fetch path regresses and returns no data. This should fail for the “present” scenario.

✅ Tighten assertion
         match parse_string_result(result) {
             Ok(Some(json_str)) => {
                 let json = parse_json_result(&json_str).expect("valid JSON");
                 assert!(json.is_array(), "Expected array, got: {:?}", json);
                 let arr = json.as_array().unwrap();
                 assert_eq!(
                     arr.len(),
                     3,
                     "Expected 3 distribution timestamps, got {}",
                     arr.len()
                 );
+                let timestamps: Vec<u64> = arr
+                    .iter()
+                    .map(|entry| {
+                        entry
+                            .get("timestamp")
+                            .and_then(|t| t.as_u64())
+                            .expect("timestamp should be a u64")
+                    })
+                    .collect();
+                assert_eq!(timestamps, vec![1000, 5000, 10000]);
                 // Verify at least the first entry has the expected structure.
                 // Test data: timestamp 1000 with 2 recipients, timestamp 5000 with 1, timestamp 10000 with 2.
                 let first = &arr[0];
                 assert!(
                     first.get("timestamp").is_some(),
                     "Entry should have a 'timestamp' field: {:?}",
                     first
                 );
                 assert!(
                     first
                         .get("distributions")
                         .map(|d| d.is_array())
                         .unwrap_or(false),
                     "Entry should have a 'distributions' array: {:?}",
                     first
                 );
             }
-            Ok(None) => {
-                // Vectors not yet generated; absent distributions is also acceptable.
-            }
+            Ok(None) => panic!("Expected distributions for TOKEN_ID_2, got none"),
             Err(e) => panic!("Unexpected error: {}", e),
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
match parse_string_result(result) {
Ok(Some(json_str)) => {
let json = parse_json_result(&json_str).expect("valid JSON");
assert!(json.is_array(), "Expected array, got: {:?}", json);
let arr = json.as_array().unwrap();
assert_eq!(
arr.len(),
3,
"Expected 3 distribution timestamps, got {}",
arr.len()
);
// Verify at least the first entry has the expected structure.
// Test data: timestamp 1000 with 2 recipients, timestamp 5000 with 1, timestamp 10000 with 2.
let first = &arr[0];
assert!(
first.get("timestamp").is_some(),
"Entry should have a 'timestamp' field: {:?}",
first
);
assert!(
first
.get("distributions")
.map(|d| d.is_array())
.unwrap_or(false),
"Entry should have a 'distributions' array: {:?}",
first
);
}
Ok(None) => {
// Vectors not yet generated; absent distributions is also acceptable.
}
Err(e) => panic!("Unexpected error: {}", e),
}
match parse_string_result(result) {
Ok(Some(json_str)) => {
let json = parse_json_result(&json_str).expect("valid JSON");
assert!(json.is_array(), "Expected array, got: {:?}", json);
let arr = json.as_array().unwrap();
assert_eq!(
arr.len(),
3,
"Expected 3 distribution timestamps, got {}",
arr.len()
);
let timestamps: Vec<u64> = arr
.iter()
.map(|entry| {
entry
.get("timestamp")
.and_then(|t| t.as_u64())
.expect("timestamp should be a u64")
})
.collect();
assert_eq!(timestamps, vec![1000, 5000, 10000]);
// Verify at least the first entry has the expected structure.
// Test data: timestamp 1000 with 2 recipients, timestamp 5000 with 1, timestamp 10000 with 2.
let first = &arr[0];
assert!(
first.get("timestamp").is_some(),
"Entry should have a 'timestamp' field: {:?}",
first
);
assert!(
first
.get("distributions")
.map(|d| d.is_array())
.unwrap_or(false),
"Entry should have a 'distributions' array: {:?}",
first
);
}
Ok(None) => panic!("Expected distributions for TOKEN_ID_2, got none"),
Err(e) => panic!("Unexpected error: {}", e),
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk-ffi/tests/integration_tests/token.rs` around lines 203 - 235,
The test currently allows Ok(None) (no data) which masks regressions; update the
match handling so that in the “present distributions” case the Ok(None) branch
fails instead of passing. Specifically, in the test around
parse_string_result(result) (and using parse_json_result), replace the Ok(None)
arm with an assert or panic that explicitly fails (e.g., panic!("Expected
distributions for TOKEN_ID_2, got None") or assert!(false, ...)), so a missing
result causes the test to fail rather than silently succeed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Claude here. Disagree with this finding — the Ok(None) arm is intentional. This is an integration test that runs against test vectors which may or may not be present depending on the SDK test data setup. Failing on None would make the test brittle and environment-dependent. The test already has a separate test_token_pre_programmed_distributions_absent that explicitly verifies the no-data case. The current pattern correctly handles both scenarios.

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.

@QuantumExplorer, understood — that's a fair point. Since the test vectors aren't guaranteed to be present in every environment, treating Ok(None) as a skip-rather-than-fail is a reasonable approach for integration tests of this kind. The separate test_token_pre_programmed_distributions_absent test already covers the explicit no-data assertion. I'll drop this suggestion.


🧠 Learnings used
Learnt from: lklimek
Repo: dashpay/platform PR: 3032
File: packages/rs-drive-abci/tests/strategy_tests/test_cases/identity_and_document_tests.rs:670-672
Timestamp: 2026-01-30T15:22:20.469Z
Learning: Enforce descriptive naming for new tests in test files with names starting with 'should …'. Do not rename existing tests that already follow a different convention. This applies to all test files under any directory named tests, for JS/JSX/TS/TSX/.RS test files as well.

@lklimek lklimek added the ready for final review Ready for the final review. If AI was involved in producing this PR, it has already had a reviewer. label Mar 13, 2026
QuantumExplorer and others added 2 commits March 14, 2026 16:01
Return InvalidParameter error instead of silently ignoring
start_recipient when start_time_ms is 0, catching caller bugs early.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs (1)

121-143: Extract distribution JSON serialization into a helper to reduce fragility.

The nested Vec<String>/join/format! chain is hard to evolve safely. A dedicated serializer helper will be easier to maintain and avoids extra intermediate allocations.

Proposed refactor
-            Some(dists) => {
-                let distributions_json: Vec<String> = dists
-                    .0
-                    .iter()
-                    .map(|(timestamp, recipients)| {
-                        let recipients_json: Vec<String> = recipients
-                            .iter()
-                            .map(|(recipient_id, amount)| {
-                                format!(
-                                    r#"{{"recipient_id":"{}","amount":{}}}"#,
-                                    recipient_id, amount
-                                )
-                            })
-                            .collect();
-
-                        format!(
-                            r#"{{"timestamp":{},"distributions":[{}]}}"#,
-                            timestamp,
-                            recipients_json.join(",")
-                        )
-                    })
-                    .collect();
-
-                Ok(Some(format!("[{}]", distributions_json.join(","))))
-            }
+            Some(dists) => Ok(Some(serialize_pre_programmed_distributions(&dists))),
             None => Ok(None),
         }
     });
fn serialize_pre_programmed_distributions(dists: &TokenPreProgrammedDistributions) -> String {
    let mut out = String::from("[");
    for (i, (timestamp, recipients)) in dists.0.iter().enumerate() {
        if i > 0 {
            out.push(',');
        }
        out.push_str(&format!(r#"{{"timestamp":{},"distributions":["#, timestamp));
        for (j, (recipient_id, amount)) in recipients.iter().enumerate() {
            if j > 0 {
                out.push(',');
            }
            out.push_str(&format!(
                r#"{{"recipient_id":"{}","amount":{}}}"#,
                recipient_id, amount
            ));
        }
        out.push_str("]}");
    }
    out.push(']');
    out
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs` around
lines 121 - 143, Extract the JSON serialization logic into a helper function
(e.g., fn serialize_pre_programmed_distributions(dists:
&TokenPreProgrammedDistributions) -> String) and replace the current nested
map/join/format! block inside the query handler with a call to that helper; the
helper should iterate dists.0 and build the JSON string incrementally (avoiding
intermediate Vec<String>) producing the same output format "[{...},{...}]" for
timestamps and their distributions, and then return
Ok(Some(serialize_pre_programmed_distributions(&dists))).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs`:
- Around line 121-143: Extract the JSON serialization logic into a helper
function (e.g., fn serialize_pre_programmed_distributions(dists:
&TokenPreProgrammedDistributions) -> String) and replace the current nested
map/join/format! block inside the query handler with a call to that helper; the
helper should iterate dists.0 and build the JSON string incrementally (avoiding
intermediate Vec<String>) producing the same output format "[{...},{...}]" for
timestamps and their distributions, and then return
Ok(Some(serialize_pre_programmed_distributions(&dists))).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f1876943-74a7-47e1-9f84-e32eb1b3fa59

📥 Commits

Reviewing files that changed from the base of the PR and between 7994ff9 and 9dfbb06.

📒 Files selected for processing (1)
  • packages/rs-sdk-ffi/src/token/queries/pre_programmed_distributions.rs

@lklimek lklimek merged commit affaa5f into v3.1-dev Mar 14, 2026
3 checks passed
@lklimek lklimek deleted the feat/sdk-get-token-pre-programmed-distributions branch March 14, 2026 10:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for final review Ready for the final review. If AI was involved in producing this PR, it has already had a reviewer.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants