Skip to content

Conversation

jo-elimu
Copy link
Member

@jo-elimu jo-elimu commented Sep 17, 2025

#65

Summary by CodeRabbit

  • New Features

    • Added a QueueHandler for processing queue pairs and explicit queue navigation (front/next).
    • Per-queue approval/rejection counters and rejection-status checks.
  • Refactor

    • UI and components now use 1-based queueNumber and getLength; sponsorship entries no longer show language.
    • Distribution and sponsorship views updated to match new queue data shape.
    • Deployment journaling switched to artifact-based, transaction-centric flows.
  • Chores

    • RPC endpoints updated for mainnet and Sepolia; contracts redeployed to new addresses.

@jo-elimu jo-elimu requested a review from a team as a code owner September 17, 2025 10:55
Copy link

netlify bot commented Sep 17, 2025

Deploy Preview for web3-sponsors ready!

Name Link
🔨 Latest commit 1052543
🔍 Latest deploy log https://app.netlify.com/projects/web3-sponsors/deploys/68caba36fbe0cf0008e0c8f8
😎 Deploy Preview https://deploy-preview-92--web3-sponsors.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link

coderabbitai bot commented Sep 17, 2025

Walkthrough

Updated Hardhat RPC URLs; recompiled and replaced multiple smart-contract artifacts (SponsorshipQueue, DistributionQueue, DistributionVerifier), added a new QueueHandler artifact, updated deployed addresses and ignition journal entries, and adapted frontend components/pages to the revised ABIs and 1-based queue-numbering.
elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

Changes

Cohort / File(s) Summary
Hardhat config
backend/hardhat.config.ts
Updated RPC URLs for sepolia and mainnet to https://0xrpc.io/...; other config values unchanged.
Queue & Verifier artifacts
backend/ignition/deployments/chain-11155111/artifacts/.../DistributionQueueModule#DistributionQueue.json, .../DistributionVerifierModule#DistributionVerifier.json, .../SponsorshipQueueModule#SponsorshipQueue.json
Recompiled ABIs/bytecode. DistributionQueue: moved to index-based queue APIs (queue(uint24) → Distribution tuple), added dequeue, queueNumberFront/Next, update functions/events, removed OnlyOwner. DistributionVerifier: renamed queueIndexqueueNumber, added approvalCount, rejectionCount, isDistributionRejected. SponsorshipQueue: removed languages ctor arg, removed languageCode from Sponsorship, added dequeue, queueHandler, payDistributor, queueNumberFront/Next, renamed getters/update funcs; bytecode updated.
New QueueHandler artifact
backend/ignition/deployments/chain-11155111/artifacts/.../QueueHandlerModule#QueueHandler.json
New contract artifact: ctor (roles_, sponsorshipQueue_, distributionQueue_, distributionVerifier_), events (QueuePairProcessed, DistributionVerifierUpdated, OwnerUpdated, RolesUpdated), view getters, processQueuePair, removeRejectedDistribution, and update functions; bytecode included.
Deployed addresses & journal
backend/ignition/deployments/chain-11155111/deployed_addresses.json, .../journal.jsonl
Updated deployed addresses for SponsorshipQueue, DistributionQueue, DistributionVerifier; added QueueHandler address. Ignition journal now records granular transaction flow (NETWORK_INTERACTION_REQUEST → TRANSACTION_PREPARE_SEND → TRANSACTION_SEND → TRANSACTION_CONFIRM); SponsorshipQueue ctor args reduced.
Frontend — queue reads & numbering
frontend/src/components/Distributions.tsx, frontend/src/components/Sponsorships.tsx
Switched reads from getQueueCountgetLength; renamed queueCountqueueLength; iterate using 1-based queueNumber (i + 1) and updated prop names.
Frontend — summaries & pages (sponsorships)
frontend/src/components/SponsorshipSummary.tsx, frontend/src/pages/sponsorships/[queueNumber].tsx, frontend/src/pages/sponsorships/add.tsx
Prop renamed queueIndexqueueNumber; SponsorshipSummary adapts to new tuple shape (estimatedCost, timestamp, sponsor) — language removed; callers updated. parseEther calls use String(estimatedCost); simulation/write buttons forward estimatedCost.
Frontend — summaries & pages (distributions)
frontend/src/components/DistributionSummary.tsx, frontend/src/pages/distributions/[queueNumber].tsx, frontend/src/pages/distributions/add.tsx
Prop renamed queueIndexqueueNumber; DistributionSummary maps new Distribution tuple (languageCode, androidId, timestamp, distributor) and UI shows #{queueNumber}; callers updated to pass raw queueNumber. add forwards languageCode/androidId to write.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as Frontend UI
  participant SQ as SponsorshipQueue
  participant DQ as DistributionQueue
  participant QH as QueueHandler
  participant DV as DistributionVerifier

  rect rgba(230,240,255,0.4)
  note over UI: Reads use getLength() and queue(uint24)
  User->>UI: View queues
  UI->>SQ: getLength()
  UI->>DQ: getLength()
  UI->>SQ: queue(queueNumber)
  UI->>DQ: queue(queueNumber)
  end

  rect rgba(230,255,230,0.4)
  note over UI,QH: Queue processing via QueueHandler
  User->>UI: Trigger processQueuePair
  UI->>QH: processQueuePair()
  QH->>DQ: dequeue()  -- returns Distribution tuple
  QH->>SQ: dequeue()  -- returns Sponsorship tuple
  QH->>DV: verifyDistribution(queueNumber, approved?)
  alt approved
    DV-->>QH: DistributionApproved
    QH-->>UI: QueuePairProcessed(distribution, sponsorship)
  else rejected
    DV-->>QH: DistributionRejected
    QH->>QH: removeRejectedDistribution()
  end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • nya-elimu

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "chore(backend): deploy to sepolia" correctly references a real part of the changeset (Hardhat sepolia endpoint updates and updated deployment addresses/journal) but omits substantial ABI/artifact and frontend API changes also included in this PR, so it only partially represents the overall scope. Under the provided criteria a title that refers to a real aspect of the change is acceptable, so this qualifies as a related (partial) title.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@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 (10)
backend/hardhat.config.ts (1)

22-29: Switch to env-driven RPC with safe fallbacks; keep empty-accounts pattern only for non-prod.

Good move to 0xRPC. To reduce blast radius and keep CI happy while aligning with elimu.ai’s mission to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months, prefer env-driven URLs with 0xRPC as fallback, and avoid accidental mainnet usage with empty accounts.

Apply:

 require("dotenv").config();

-const privateKeys = process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
+const privateKeys = process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [];
+const sepoliaUrl = process.env.SEPOLIA_RPC_URL ?? "https://0xrpc.io/sep";
+const mainnetUrl = process.env.MAINNET_RPC_URL ?? "https://0xrpc.io/eth";

 ...
   networks: {
     sepolia: { // Chain ID 11155111
-      url: "https://0xrpc.io/sep",
+      url: sepoliaUrl,
       accounts: privateKeys
     },
     mainnet: { // Chain ID 1
-      url: "https://0xrpc.io/eth",
+      url: mainnetUrl,
       accounts: privateKeys
     }
   },

Confirm:

  • PRIVATE_KEY is unset in CI runs that only lint/compile.
  • When deploying to mainnet, PRIVATE_KEY is present and points to a funded address.
backend/ignition/deployments/chain-11155111/journal.jsonl (1)

3-8: Constructor arg change on SponsorshipQueue—confirm units and front-end expectations.

constructorArgs: 100000000000000 suggests 0.0001 ETH (1e14 wei). Verify the UI/QueueHandler uses the same unit and does not assume gwei/ether.

Add a unit test asserting expected value in wei and end-to-end flow through QueueHandler.

backend/README.md (2)

31-33: Great: pre-deploy artifact cleanup. Add a caution.

Add a one-liner warning that this purges only cached deployment artifacts, not source code, to avoid accidental path edits.


45-47: Mirror the Sepolia cleanup note for mainnet and require explicit consent.

Consider adding a short “Are you sure?” prompt (via a wrapper script) before mainnet cleanup/deploy to prevent accidental mainnet redeploys.

backend/ignition/deployments/chain-11155111/artifacts/DistributionVerifierModule#DistributionVerifier.json (2)

82-99: New counters: approvalCount/rejectionCount—expose in frontend.

Surface these read-only endpoints in the UI to improve moderator visibility.


21-35: queueIndex → queueNumber rename—update all callers.

Events and function params switched to queueNumber. Ensure off-chain code, tests, and subgraphs use the new name.

#!/bin/bash
# Find old API usages across repo
rg -nP -C2 '\bqueueIndex\b|isDistributionApproved\s*\(|verifyDistribution\s*\(|getQueueCount\b|languages\s*\(\)' -g '!**/node_modules/**'
backend/ignition/deployments/chain-11155111/artifacts/QueueHandlerModule#QueueHandler.json (1)

209-220: updateDistributionVerifier event + method—wire hot-swap logic.

Confirm QueueHandler validates the new verifier address (non-zero, code size > 0) and emits DistributionVerifierUpdated.

backend/ignition/deployments/chain-11155111/artifacts/DistributionQueueModule#DistributionQueue.json (1)

235-260: Expose queueNumberFront/Next—validate monotonicity and overflow.

Using uint24 is fine; add tests for wrap-around (very long run) and for length computation using Next−Front.

backend/ignition/deployments/chain-11155111/artifacts/SponsorshipQueueModule#SponsorshipQueue.json (2)

18-21: Orphaned error: InvalidLanguageCode remains but no language input.

This error seems unused now—remove to avoid confusion and reduce bytecode.


245-269: New queueNumberFront/Next—align with DistributionQueue semantics.

Add cross-contract tests: enqueue on SponsorshipQueue then process via QueueHandler to confirm indices move in lockstep with DistributionQueue.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 645c9b1 and 58269f6.

📒 Files selected for processing (8)
  • backend/README.md (2 hunks)
  • backend/hardhat.config.ts (1 hunks)
  • backend/ignition/deployments/chain-11155111/artifacts/DistributionQueueModule#DistributionQueue.json (5 hunks)
  • backend/ignition/deployments/chain-11155111/artifacts/DistributionVerifierModule#DistributionVerifier.json (7 hunks)
  • backend/ignition/deployments/chain-11155111/artifacts/QueueHandlerModule#QueueHandler.json (1 hunks)
  • backend/ignition/deployments/chain-11155111/artifacts/SponsorshipQueueModule#SponsorshipQueue.json (10 hunks)
  • backend/ignition/deployments/chain-11155111/deployed_addresses.json (1 hunks)
  • backend/ignition/deployments/chain-11155111/journal.jsonl (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-08-12T06:22:30.517Z
Learnt from: jo-elimu
PR: elimu-ai/web3-sponsors#74
File: backend/ignition/modules/SponsorshipQueue.ts:9-17
Timestamp: 2025-08-12T06:22:30.517Z
Learning: The web3-sponsors project has not deployed to Ethereum Mainnet yet. The placeholder "0x addr mainnet" in backend/ignition/modules/SponsorshipQueue.ts is intentional and appropriate for the current deployment state.

Applied to files:

  • backend/README.md
  • backend/ignition/deployments/chain-11155111/deployed_addresses.json
📚 Learning: 2024-10-20T07:26:44.861Z
Learnt from: jo-elimu
PR: elimu-ai/web3-sponsors#0
File: :0-0
Timestamp: 2024-10-20T07:26:44.861Z
Learning: User `jo-elimu` resolved an issue with missing `WALLET_KEY` by using an empty array `[]` in the `networks` configuration in `hardhat.config.ts` to avoid errors during the `npx hardhat clean` command. This is not suitable for production environments where real transactions occur.

Applied to files:

  • backend/hardhat.config.ts
📚 Learning: 2025-08-22T12:52:32.267Z
Learnt from: jo-elimu
PR: elimu-ai/web3-sponsors#81
File: backend/contracts/DistributionQueue.sol:0-0
Timestamp: 2025-08-22T12:52:32.267Z
Learning: In DistributionQueue.sol, the queue uses a mapping-based implementation where the first item is stored at index 0. The enqueue() function writes to queue[queueNumberNext] before incrementing queueNumberNext, and dequeue() reads from queue[queueNumberFront] before incrementing queueNumberFront. This creates a proper FIFO queue where items are enqueued starting at index 0.

Applied to files:

  • backend/ignition/deployments/chain-11155111/artifacts/DistributionQueueModule#DistributionQueue.json
📚 Learning: 2025-08-22T12:52:32.267Z
Learnt from: jo-elimu
PR: elimu-ai/web3-sponsors#81
File: backend/contracts/DistributionQueue.sol:0-0
Timestamp: 2025-08-22T12:52:32.267Z
Learning: In DistributionQueue.sol, the queue uses a mapping-based implementation where the first item is stored at index 0. The enqueue() function writes to queue[queueNumberNext] before incrementing queueNumberNext, and dequeue() reads from queue[queueNumberFront] before incrementing queueNumberFront. This creates a proper FIFO queue where items are enqueued starting at index 0, and the implementation is correct as-is.

Applied to files:

  • backend/ignition/deployments/chain-11155111/artifacts/DistributionQueueModule#DistributionQueue.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build (windows-latest, 20.x)
  • GitHub Check: build (ubuntu-latest, 20.x)
  • GitHub Check: build (ubuntu-latest, 18.x)
  • GitHub Check: build (windows-latest, 18.x)
🔇 Additional comments (6)
backend/ignition/deployments/chain-11155111/journal.jsonl (1)

21-26: Deployment flow OK; sanity-check fees/nonces.

Nonces 27–30 and very low maxFeePerGas values are recorded. Confirm these are from Sepolia at the deployment time and not mocked, to avoid misleading future operators.

backend/ignition/deployments/chain-11155111/artifacts/DistributionQueueModule#DistributionQueue.json (1)

114-148: dequeue now returns a Distribution tuple—API break.

Ensure all consumers migrated from prior getters to dequeue() and handle empty-queue errors properly.

backend/ignition/deployments/chain-11155111/artifacts/SponsorshipQueueModule#SponsorshipQueue.json (2)

9-16: Constructor simplified—double-check that Languages dependency removal is intentional.

If language validation moved elsewhere, great; otherwise, confirm no missing checks.


92-127: ```shell
#!/bin/bash
set -euo pipefail

echo "PWD: $(pwd)"
echo "Git HEAD (if available):"
git rev-parse --abbrev-ref HEAD 2>/dev/null || true

echo
echo "1) Searching for occurrences of 'addSponsorship'..."
rg -n --hidden -S "addSponsorship" -g '!/node_modules/' -g '!/dist/' -g '!/.git/' -C5 || true

echo
echo "2) Searching for Solidity function declarations 'function addSponsorship'..."
rg -n --hidden -S --type-add 'sol:*.sol' -t sol "function addSponsorship" -g '!/node_modules/' -g '!/dist/' -g '!/.git/' -C10 || true

echo
echo "3) Searching for struct/type 'Sponsorship' and 'dequeue' usages..."
rg -n --hidden -S "struct Sponsorship" -g '!/node_modules/' -C5 || true
rg -n --hidden -S "\bdequeue\b" -g '!/node_modules/' -C5 || true

echo
echo "4) Show the deployment artifact mentioned in the review (if present)..."
artifact="backend/ignition/deployments/chain-11155111/artifacts/SponsorshipQueueModule#SponsorshipQueue.json"
if [ -f "$artifact" ]; then
echo "== Found artifact: $artifact =="
sed -n '1,240p' "$artifact" || true
else
echo "Artifact not found at: $artifact"
echo "List matching artifact files under backend/ignition/deployments (if any):"
find backend/ignition/deployments -maxdepth 6 -type f -name 'Sponsorship' -print 2>/dev/null || true
fi

echo
echo "5) If any .sol file containing 'addSponsorship' was found, print its surrounding lines (up to 120 lines)."
matches=$(rg -n --hidden -S --type-add 'sol:.sol' -t sol "addSponsorship" -g '!/node_modules/' -g '!/.git/' --no-line-number || true)
if [ -n "$matches" ]; then
echo "Printing matched files and contexts:"
rg -n --hidden -S --type-add 'sol:
.sol' -t sol "addSponsorship" -g '!/node_modules/' -g '!/.git/' -C60 || true
else
echo "No direct Solidity matches for 'addSponsorship' found."
fi

echo
echo "Script completed."


</blockquote></details>
<details>
<summary>backend/ignition/deployments/chain-11155111/deployed_addresses.json (1)</summary><blockquote>

`2-6`: **Addresses updated; add lightweight validation before publishing.**

Looks fine. Add a quick check to ensure all addresses are valid EOA/contract addresses and deployed code size > 0.


```shell
#!/bin/bash
# Validate Sepolia addresses have code
set -euo pipefail
ADDRS=(
  0x646A5550af30f5340AF218C9aE375A4C7Bb740BF
  0xD7744d3Cc62748fcDc8f53e202C804488D9e85A8
  0xC3313d6BB50C081AE07B6B097700eC2ED0568377
  0x0B96769D277D8b7EaED38d2e269101615fB3e237
)
for a in "${ADDRS[@]}"; do
  echo -n "$a: "
  cast code --rpc-url "${SEPOLIA_RPC_URL:-https://0xrpc.io/sep}" "$a" | awk '{print (length($0)>2)?"OK:code":"ERR:no code"}'
done
backend/ignition/deployments/chain-11155111/artifacts/QueueHandlerModule#QueueHandler.json (1)

169-181: ```shell
#!/bin/bash
set -euo pipefail
echo "PWD: $(pwd)"
echo
echo "Find occurrences of the function names and contract name in the repo..."
rg -n --hidden --no-ignore -S "processQueuePair|removeRejectedDistribution|processQueuePair\s*(|removeRejectedDistribution\s*(|contract\s+QueueHandler" -g '!/node_modules/' -g '!/dist/' -g '!/build/' || true

ART="backend/ignition/deployments/chain-11155111/artifacts/QueueHandlerModule#QueueHandler.json"
echo
if [ -f "$ART" ]; then
echo "Artifact found: $ART"
echo
echo "Showing lines 150-200 from artifact (to include the snippet lines referenced in the review):"
sed -n '150,200p' "$ART" || true
echo
echo "Showing ABI entries for the two target functions (if present):"
jq -c '.abi[] | select(.type=="function" and (.name=="processQueuePair" or .name=="removeRejectedDistribution"))' "$ART" 2>/dev/null || true
else
echo "Artifact not found at $ART"
fi

echo
echo "Search for function definitions with context (to locate Solidity source):"
rg -n --hidden --no-ignore -S -C3 "function\s+processQueuePair|function\s+removeRejectedDistribution" -g '!/node_modules/' -g '!/dist/' -g '!/build/' || true

echo
echo "Search for common access-control and reentrancy patterns (onlyOwner, onlyRole, hasRole, nonReentrant, ReentrancyGuard):"
rg -n --hidden --no-ignore -S "onlyOwner|onlyRole|hasRole|nonReentrant|ReentrancyGuard" -g '!/node_modules/' -g '!/dist/' -g '!/build/' || true


</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines 168 to +201
"inputs": [
{
"internalType": "uint256",
"internalType": "address",
"name": "distributor",
"type": "address"
},
{
"components": [
{
"internalType": "uint256",
"name": "estimatedCost",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
},
{
"internalType": "address",
"name": "sponsor",
"type": "address"
}
],
"internalType": "struct Sponsorship",
"name": "sponsorship",
"type": "tuple"
}
],
"name": "payDistributor",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

payDistributor(distributor, Sponsorship) risks mismatch.

Ensure the function verifies the tuple matches the current front-of-queue item (not caller-provided arbitrary data) before paying, and uses nonReentrant + pull pattern if feasible.

🤖 Prompt for AI Agents
In
backend/ignition/deployments/chain-11155111/artifacts/SponsorshipQueueModule#SponsorshipQueue.json
around lines 168-201, the payDistributor(distributor, Sponsorship) ABI indicates
the function accepts a caller-supplied tuple which risks paying based on
arbitrary data; change the implementation to read the current front-of-queue
sponsorship from contract storage and verify the tuple fields (estimatedCost,
timestamp, sponsor) match that stored item instead of trusting the
caller-provided struct, refuse to proceed if they differ, and then mark the item
as paid/removed; additionally, protect the function with a nonReentrant modifier
(or equivalent reentrancy guard) and switch to a pull-payment pattern (record
payable balances for distributors and let them withdraw) rather than sending
funds directly within payDistributor to mitigate reentrancy and gas/transfer
failures.

Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
frontend/src/pages/sponsorships/add.tsx (2)

71-81: Send ETH when simulating addSponsorship + pin Sepolia (11155111)

File: frontend/src/pages/sponsorships/add.tsx (around lines 71–81)

  • ABI shows addSponsorship is payable (stateMutability = "payable") — current simulation sending 0 ETH will revert. Pass the sponsorship amount (in wei) via the value option to useSimulateContract.
  • Pin chainId to 11155111 to avoid accidental mainnet/wrong‑chain calls.
  • Add a runtime guard for a missing deployment address (disable/hide button if deploymentAddress is falsy).

Apply (example):

   const { isPending, isError, error, isSuccess } = useSimulateContract({
     abi,
     address: deploymentAddress,
-    functionName: "addSponsorship"
+    functionName: "addSponsorship",
+    chainId: 11155111,
+    // pass sponsorship amount in wei (BigInt or ethers.parseEther(amountEth))
+    value: sponsorshipValueWei
   })

elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.


100-121: Button says “Send 0.0001 ETH” but transaction sends 0 ETH — fix the mismatch

elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

File: frontend/src/pages/sponsorships/add.tsx
Lines: 100-121

Pick ONE:

  • Option A (no payment now): update label (and pin chainId).
   const { writeContract } = useWriteContract();
   return (
     <button 
       className="mt-4 p-8 text-2xl bg-purple-200 dark:bg-purple-950 rounded-lg border-purple-400 border-r-4 border-b-4 hover:border-r-8 hover:border-b-8 hover:-translate-y-1 active:border-r-2 active:border-b-2"
       onClick={() =>
         writeContract({
           abi,
           address: deploymentAddress,
-          functionName: "addSponsorship"
+          functionName: "addSponsorship",
+          chainId: 11155111
         })
       }
     >
-      Send 0.0001 ETH ⟠
+      Add Sponsorship 💜
     </button>
  • Option B (still payable): re-add value consistently to simulate + write.
   const { isPending, isError, error, isSuccess } = useSimulateContract({
     abi,
     address: deploymentAddress,
-    functionName: "addSponsorship",
-    chainId: 11155111
+    functionName: "addSponsorship",
+    value: parseEther("0.0001"),
+    chainId: 11155111
   })
         writeContract({
           abi,
           address: deploymentAddress,
-          functionName: "addSponsorship",
-          chainId: 11155111
+          functionName: "addSponsorship",
+          value: parseEther("0.0001"),
+          chainId: 11155111
         })
🧹 Nitpick comments (2)
frontend/src/components/Sponsorships.tsx (1)

35-37: Minor: strict equality and cleaner list construction

Use === and avoid filling an array only to ignore the element.

-    if (queueCount == 0) {
+    if (queueCount === 0) {
       return <div>None yet</div>;
     }
@@
-            {Array(queueCount).fill(1).map((el, i) =>
+            {[...Array(queueCount).keys()].map((i) =>
                 <Link key={i} href={`/sponsorships/${i + 1}`}>
                   <div className="skew-y-3 p-4 text-2xl bg-purple-200 dark:bg-purple-950 rounded-lg border-purple-400 border-r-4 border-b-4 hover:border-r-8 hover:border-b-8 hover:-translate-y-1">
                     <SponsorshipSummary queueIndex={i} />
                   </div>
                 </Link>
             )}

Quick check: confirm the route is 1‑based (i+1) while queueIndex is 0‑based by design. If not intentional, align both.

Also applies to: 41-47

frontend/src/components/Distributions.tsx (1)

35-37: Minor: prefer === and a simpler index list

Tighten equality and remove the unused el.

-    if (queueCount == 0) {
+    if (queueCount === 0) {
       return <div>None yet</div>;
     }
@@
-            {Array(queueCount).fill(1).map((el, i) =>
+            {[...Array(queueCount).keys()].map((i) =>
                 <Link key={i} href={`/distributions/${i + 1}`}>
                   <div className="skew-y-3 p-4 text-2xl bg-indigo-200 dark:bg-indigo-950 rounded-lg border-indigo-400 border-r-4 border-b-4 hover:border-r-8 hover:border-b-8 hover:-translate-y-1">
                     <DistributionSummary queueIndex={i} />
                   </div>
                 </Link>
             )}

Also confirm 1‑based route (i+1) vs 0‑based queueIndex is intentional.

Also applies to: 41-47

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58269f6 and e7ee86b.

📒 Files selected for processing (3)
  • frontend/src/components/Distributions.tsx (1 hunks)
  • frontend/src/components/Sponsorships.tsx (1 hunks)
  • frontend/src/pages/sponsorships/add.tsx (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: jo-elimu
PR: elimu-ai/web3-sponsors#74
File: backend/ignition/modules/SponsorshipQueue.ts:9-17
Timestamp: 2025-08-12T06:22:30.517Z
Learning: The web3-sponsors project has not deployed to Ethereum Mainnet yet. The placeholder "0x addr mainnet" in backend/ignition/modules/SponsorshipQueue.ts is intentional and appropriate for the current deployment state.
📚 Learning: 2025-08-12T06:22:30.517Z
Learnt from: jo-elimu
PR: elimu-ai/web3-sponsors#74
File: backend/ignition/modules/SponsorshipQueue.ts:9-17
Timestamp: 2025-08-12T06:22:30.517Z
Learning: The web3-sponsors project has not deployed to Ethereum Mainnet yet. The placeholder "0x addr mainnet" in backend/ignition/modules/SponsorshipQueue.ts is intentional and appropriate for the current deployment state.

Applied to files:

  • frontend/src/pages/sponsorships/add.tsx
🧬 Code graph analysis (1)
frontend/src/pages/sponsorships/add.tsx (1)
frontend/src/pages/distributions/add.tsx (2)
  • SimulateContractButton (109-141)
  • WriteContractButton (143-165)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Redirect rules - web3-sponsors
  • GitHub Check: Header rules - web3-sponsors
  • GitHub Check: Pages changed - web3-sponsors
  • GitHub Check: build (windows-latest, 20.x)
  • GitHub Check: build (ubuntu-latest, 18.x)
  • GitHub Check: build (windows-latest, 18.x)
  • GitHub Check: build (ubuntu-latest, 20.x)
  • GitHub Check: build (windows-latest, 18.x)
🔇 Additional comments (2)
frontend/src/components/Sponsorships.tsx (1)

15-19: Pin chainId=11155111 for deterministic reads; verify no stale getQueueCount calls remain

  • Add chainId to the useReadContract call in frontend/src/components/Sponsorships.tsx:
   const { isLoading, isError, error, data } = useReadContract({
     abi,
     address: deploymentAddress,
-    functionName: "getLength"
+    functionName: "getLength",
+    chainId: 11155111,
   });
  • Repo search for '\bgetQueueCount\b' returned no matches — unable to confirm absence of stale calls; re-run the search or manually verify the codebase.

elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

frontend/src/components/Distributions.tsx (1)

15-19: Pin chainId=11155111 for Sepolia reads in frontend/src/components/Distributions.tsx

Same as Sponsorships — pin the chain for Sepolia reads and verify there are no remaining getQueueCount references in distributions paths.

   const { isLoading, isError, error, data } = useReadContract({
     abi,
     address: deploymentAddress,
-    functionName: "getLength"
+    functionName: "getLength",
+    chainId: 11155111,
   });

Verification: rg produced no matches; manual confirmation required.

elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

Copy link

@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 (6)
frontend/src/components/SponsorshipSummary.tsx (5)

15-20: Guard the read until the route param is valid (avoid NaN/undefined invocation).

On first render, router.query.queueNumber is often undefined → Number(...) becomes NaN. Calling useReadContract with NaN can error. Gate the query.

   const { isLoading, isError, error, data } = useReadContract({
     abi,
     address: deploymentAddress,
     functionName: "queue",
-    args: [queueNumber]
+    args: [BigInt(queueNumber - 1)],
+    // wagmi v2 style; confirm your version
+    query: { enabled: Number.isInteger(queueNumber) && queueNumber > 0 }
   });

If you’re on wagmi v1, switch to the equivalent enabled flag for useContractRead.

Would you like me to adjust this to your exact wagmi version once you confirm it?


8-12: Type the props and drop any.

Make the intent explicit and catch invalid inputs early.

-export default function SponsorshipSummary({ queueNumber }: any) {
+export default function SponsorshipSummary({ queueNumber }: { queueNumber: number }) {

34-37: Avoid re-wrapping BigInt and destructure with types for safety.

sponsorship[0] is already bigint from viem; BigInt(…) around a bigint is unnecessary and can bite during refactors. Destructure with explicit tuple typing.

-    const sponsorship: any = data;
-    const estimatedCost = BigInt(sponsorship[0]);
-    const timestamp = Number(sponsorship[1]);
-    const sponsor = sponsorship[2];
+    const [estimatedCost, timestampBn, sponsor] =
+      data as readonly [bigint, bigint, Address];
+    const timestamp = Number(timestampBn);

41-43: Tiny nit: compute Date once to avoid duplicate work.

Precompute and reuse.

-                {new Date(timestamp * 1_000).toISOString().substring(0,10)} {new Date(timestamp * 1_000).toISOString().substring(11,16)}
+                {(() => {
+                  const d = new Date(timestamp * 1_000);
+                  const iso = d.toISOString();
+                  return `${iso.substring(0,10)} ${iso.substring(11,16)}`;
+                })()}

9-24: Trim debug logs or gate behind an env flag.

Spammy console output in production hurts UX. Consider a small debug util gated by NEXT_PUBLIC_DEBUG.

If helpful, I can add a debug() helper and replace these calls.

frontend/src/pages/sponsorships/[queueNumber].tsx (1)

31-31: Validate route param before rendering and keep 1‑based UX consistent.

Ensure the param is present and valid (positive integer). This prevents NaN from propagating and aligns with the component’s 1‑based prop.

-          <SponsorshipSummary queueNumber={queueNumber} />
+          <SponsorshipSummary queueNumber={queueNumber} />

Add guards above (outside this hunk):

// after const router = useRouter();
const queueNumber = Number(router.query.queueNumber);
const isValid = Number.isInteger(queueNumber) && queueNumber > 0;
if (!router.isReady) return null; // or <LoadingIndicator />
if (!isValid) return <div className="mt-8 p-4">Invalid queue number.</div>;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7ee86b and fad7a0d.

📒 Files selected for processing (4)
  • frontend/src/components/SponsorshipSummary.tsx (2 hunks)
  • frontend/src/components/Sponsorships.tsx (2 hunks)
  • frontend/src/pages/sponsorships/[queueNumber].tsx (1 hunks)
  • frontend/src/pages/sponsorships/add.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/components/Sponsorships.tsx
  • frontend/src/pages/sponsorships/add.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: jo-elimu
PR: elimu-ai/web3-sponsors#74
File: backend/ignition/modules/SponsorshipQueue.ts:9-17
Timestamp: 2025-08-12T06:22:30.517Z
Learning: The web3-sponsors project has not deployed to Ethereum Mainnet yet. The placeholder "0x addr mainnet" in backend/ignition/modules/SponsorshipQueue.ts is intentional and appropriate for the current deployment state.
📚 Learning: 2025-08-12T06:22:30.517Z
Learnt from: jo-elimu
PR: elimu-ai/web3-sponsors#74
File: backend/ignition/modules/SponsorshipQueue.ts:9-17
Timestamp: 2025-08-12T06:22:30.517Z
Learning: The web3-sponsors project has not deployed to Ethereum Mainnet yet. The placeholder "0x addr mainnet" in backend/ignition/modules/SponsorshipQueue.ts is intentional and appropriate for the current deployment state.

Applied to files:

  • frontend/src/components/SponsorshipSummary.tsx
🧬 Code graph analysis (1)
frontend/src/pages/sponsorships/[queueNumber].tsx (1)
frontend/src/components/SponsorshipSummary.tsx (1)
  • SponsorshipSummary (8-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - web3-sponsors
  • GitHub Check: Header rules - web3-sponsors
  • GitHub Check: Pages changed - web3-sponsors
  • GitHub Check: build (windows-latest, 18.x)
  • GitHub Check: build (windows-latest, 20.x)
🔇 Additional comments (2)
frontend/src/components/SponsorshipSummary.tsx (1)

15-20: Confirmed: ABI tuple order matches (estimatedCost, timestamp, sponsor).
Verified queue() ABI in backend/ignition/deployments/chain-11155111/artifacts/SponsorshipQueueModule#SponsorshipQueue.json — outputs are [estimatedCost, timestamp, sponsor]; no change required.
elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

frontend/src/pages/sponsorships/[queueNumber].tsx (1)

31-31: Confirm 1‑based queueNumber usage in SponsorshipSummary and callers

If SponsorshipSummary now expects a 1‑based queueNumber for UX, verify all call sites (e.g., Sponsorships.tsx) pass i + 1 and that SponsorshipSummary converts to 0‑based only for contract reads. Automated search failed here (rg reported unrecognized tsx); please run locally: rg -nC2 'SponsorshipSummary' and check indexing at each call site.

elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

Comment on lines +19 to 20
args: [queueNumber]
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix off‑by‑one: contract index vs. human display is inconsistent (likely wrong item fetched).

The page now passes a 1‑based queueNumber from the route, but:

  • You call the contract with args: [queueNumber] (1‑based),
  • Then display Queue number: #{queueNumber + 1} (effectively 2‑based).

This will read the wrong queue entry on-chain and display an incorrect number. Align to a single convention: treat props as 1‑based for UX, convert to 0‑based only for the contract call.

Apply:

-        functionName: "queue",
-        args: [queueNumber]
+        functionName: "queue",
+        args: [BigInt(queueNumber - 1)]
-            Queue number: #{queueNumber + 1}
+            Queue number: #{queueNumber}

Context: Getting this right is key for trust—elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

Also applies to: 40-41

🤖 Prompt for AI Agents
In frontend/src/components/SponsorshipSummary.tsx around lines 19-20 (and also
apply the same fix at lines 40-41): the component is using a 1-based queueNumber
from the route but calling the contract with the raw 1-based value and then
displaying queueNumber + 1, causing an off-by-one error and wrong on-chain read;
convert the UX 1-based prop to 0-based only for the contract call (pass
queueNumber - 1 in args) and stop adding 1 when rendering the displayed "Queue
number" (render queueNumber directly), and make the same change for the other
occurrence on lines 40-41 so both contract calls and displays follow the single
1-based-for-UI / 0-based-for-contract convention.

Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/distributions/add.tsx (1)

5-5: Guard against wrong-chain writes (import hooks)

You’re using Sepolia artifacts (chain 11155111). Add chain guards to prevent wrong‑network submissions.

Apply this diff to extend the wagmi imports:

-import { useAccount, useSimulateContract, useWriteContract } from "wagmi";
+import { useAccount, useSimulateContract, useWriteContract, useChainId, useSwitchChain } from "wagmi";
🧹 Nitpick comments (14)
frontend/src/pages/distributions/add.tsx (10)

15-19: Add chain hooks in component scope

Retrieve chain info and a switch helper.

Apply this diff:

   const { address, isConnecting, isReconnecting } = useAccount();
+  const chainId = useChainId();
+  const { switchChain } = useSwitchChain();

45-54: Gate UI on Sepolia; offer a “Switch to Sepolia” action

Prevents user confusion and failed txs when connected to the wrong network.

Apply this diff:

-          {!address ? (
+          {!address ? (
             <button disabled={true} className="p-8 text-2xl text-zinc-400 bg-zinc-300 rounded-lg">
               <div className="text-6xl rotate-12 mb-4">☝🏽</div>
               Connect wallet first
             </button>
-          ) : (
+          ) : chainId !== 11155111 ? (
+            <button
+              className="p-8 text-2xl bg-amber-200 dark:bg-amber-900 rounded-lg border-amber-400 border-r-4 border-b-4 hover:border-r-8 hover:border-b-8 hover:-translate-y-1 active:border-r-2 active:border-b-2"
+              onClick={() => switchChain({ chainId: 11155111 })}
+            >
+              Switch to Sepolia (11155111)
+            </button>
+          ) : (
             <InputAndroidId />
           )}

109-114: Simulate with the actual account

Including the caller account makes the simulation reflect permissions/allowances precisely.

Apply this diff:

 export function SimulateContractButton({ languageCode, androidId}: any) {
   console.debug("SimulateContractButton");
 
+  const { address } = useAccount();
   const deploymentAddress: Address = deployed_addresses["DistributionQueueModule#DistributionQueue"] as `0x${string}`;
   console.debug("deploymentAddress:", deploymentAddress);

10-10: Type import for cleaner handler signatures

Avoid any by importing the event type.

Apply this diff:

-import { useState } from "react";
+import { useState, type ChangeEvent } from "react";

65-68: Type the onChange handlers (drop any)

Improves IntelliSense and prevents accidental misuse.

Apply this diff:

-  const handleLanguageCodeChange = (event: any) => {
+  const handleLanguageCodeChange = (event: ChangeEvent<HTMLSelectElement>) => {
     console.debug('handleLanguageCodeChange');
     setLanguageCode(event.target.value);
   }
 ...
-  const handleAndroidIdChange = (event: any) => {
+  const handleAndroidIdChange = (event: ChangeEvent<HTMLInputElement>) => {
     console.debug('handleAndroidIdChange');
     setAndroidId(event.target.value);
   }

Also applies to: 72-75


98-104: Stronger input validation (regex) and stricter checks

Validate format, not just length. Android ID is typically hex; language codes are 3 letters.

Apply this diff:

-      {((languageCode.length != 3) || (androidId.length != 16)) ? (
+      {!isValid ? (
         <button disabled={true} className="mt-4 p-8 text-2xl text-zinc-400 bg-zinc-300 rounded-lg">
           Add Distribution 📦
         </button>
       ) : (
         <SimulateContractButton languageCode={languageCode} androidId={androidId} />
       )}

Add this just before the return in InputAndroidId:

const isValid =
  /^[A-Z]{3}$/.test(languageCode.trim()) &&
  /^[0-9a-fA-F]{16}$/.test(androidId.trim());

136-138: Surface clearer error details from simulation

Show shortMessage or message when available for actionable feedback.

Apply this diff:

-  if (isError) {
-    return <ErrorIndicator description={error.name} />
-  }
+  if (isError) {
+    // wagmi BaseError often includes a helpful shortMessage
+    const desc =
+      (error as any)?.shortMessage ||
+      (error as any)?.message ||
+      error.name;
+    return <ErrorIndicator description={desc} />
+  }

149-164: Disable “Add Distribution” button while sending; optional tx feedback

Prevents duplicate clicks and improves UX. If desired, wire a receipt toast after writeContract.

Apply this diff:

-  const { writeContract } = useWriteContract();
+  const { writeContract, isPending } = useWriteContract();
   return (
     <button 
-      className="mt-4 p-8 text-2xl bg-indigo-200 dark:bg-indigo-950 rounded-lg border-indigo-400 border-r-4 border-b-4 hover:border-r-8 hover:border-b-8 hover:-translate-y-1 active:border-r-2 active:border-b-2"
+      disabled={isPending}
+      className="mt-4 p-8 text-2xl bg-indigo-200 dark:bg-indigo-950 rounded-lg border-indigo-400 border-r-4 border-b-4 hover:border-r-8 hover:border-b-8 hover:-translate-y-1 active:border-r-2 active:border-b-2 disabled:opacity-60"
       onClick={() =>
         writeContract({
           abi,
           address: deploymentAddress,
           functionName: "addDistribution",
           args: [languageCode, androidId]
         })
       }
     >
-      Add Distribution 📦
+      {isPending ? "Sending..." : "Add Distribution 📦"}
     </button>

80-89: Add labels/aria for a11y

Associate labels with the select/input for keyboard and screen-reader users.

Example:

<label className="block mb-2 text-sm" htmlFor="lang">Language</label>
<select id="lang" aria-label="Language" ... />

<label className="block mt-4 mb-2 text-sm" htmlFor="androidId">Android ID</label>
<input id="androidId" aria-label="Android ID" ... />

Also applies to: 91-96


115-120: Pass account into useSimulateContract; ABI verified (2 string inputs)

ABI for addDistribution is (languageCode: string, androidId: string) — args are correct; add the connected EOA to the simulate call.

File: frontend/src/pages/distributions/add.tsx (lines 115–120)

  const { isPending, isError, error, isSuccess } = useSimulateContract({
    abi,
    address: deploymentAddress,
    functionName: "addDistribution",
-    args: [languageCode, androidId]
+    account: address as Address,
+    args: [languageCode, androidId]
  })

elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

frontend/src/components/DistributionSummary.tsx (4)

8-12: Tighten types for safer props

Use an explicit props type to prevent accidental non‑integers making it into the hook.

-export default function DistributionSummary({ queueNumber }: any) {
+type Props = { queueNumber: number };
+export default function DistributionSummary({ queueNumber }: Props) {

35-38: Validate tuple field order against ABI and type the result

Ensure the tuple order matches the contract (languageCode, androidId, timestamp, distributor). Typing this eliminates silent misreads if the ABI changes.

-    const distribution: any = data;
-    const languageCode = distribution[0];
-    const androidId = distribution[1];
-    const timestamp = Number(distribution[2]);
-    const distributor = distribution[3];
+    type Distribution = readonly [string, string, bigint, Address];
+    const [languageCode, androidId, ts, distributor] = data as Distribution;
+    const timestamp = Number(ts);

46-53: Handle empty/uninitialized slots to avoid “1970‑01‑01”

Uninitialized queue entries return zeroed structs; rendering them shows epoch time and empty strings. Add a lightweight guard.

-            <div className="mt-2">
-                {new Date(timestamp * 1_000).toISOString().substring(0,10)} {new Date(timestamp * 1_000).toISOString().substring(11,16)}
-            </div>
+            {timestamp > 0 ? (
+              <div className="mt-2">
+                {new Date(timestamp * 1_000).toISOString().substring(0,10)} {new Date(timestamp * 1_000).toISOString().substring(11,16)}
+              </div>
+            ) : (
+              <div className="mt-2 text-zinc-500">Empty slot</div>
+            )}

11-11: Optional: gate debug logs behind env flag

Reduce noisy logs in production builds.

if (process.env.NEXT_PUBLIC_DEBUG === "true") {
  console.debug("queueNumber:", queueNumber);
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fad7a0d and 16608f1.

📒 Files selected for processing (5)
  • frontend/src/components/DistributionSummary.tsx (2 hunks)
  • frontend/src/components/Distributions.tsx (2 hunks)
  • frontend/src/components/SponsorshipSummary.tsx (2 hunks)
  • frontend/src/pages/distributions/[queueNumber].tsx (1 hunks)
  • frontend/src/pages/distributions/add.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/components/Distributions.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/SponsorshipSummary.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-22T12:52:32.267Z
Learnt from: jo-elimu
PR: elimu-ai/web3-sponsors#81
File: backend/contracts/DistributionQueue.sol:0-0
Timestamp: 2025-08-22T12:52:32.267Z
Learning: In DistributionQueue.sol, the queue uses a mapping-based implementation where the first item is stored at index 0. The enqueue() function writes to queue[queueNumberNext] before incrementing queueNumberNext, and dequeue() reads from queue[queueNumberFront] before incrementing queueNumberFront. This creates a proper FIFO queue where items are enqueued starting at index 0, and the implementation is correct as-is.

Applied to files:

  • frontend/src/pages/distributions/[queueNumber].tsx
  • frontend/src/components/DistributionSummary.tsx
📚 Learning: 2025-08-22T12:52:32.267Z
Learnt from: jo-elimu
PR: elimu-ai/web3-sponsors#81
File: backend/contracts/DistributionQueue.sol:0-0
Timestamp: 2025-08-22T12:52:32.267Z
Learning: In DistributionQueue.sol, the queue uses a mapping-based implementation where the first item is stored at index 0. The enqueue() function writes to queue[queueNumberNext] before incrementing queueNumberNext, and dequeue() reads from queue[queueNumberFront] before incrementing queueNumberFront. This creates a proper FIFO queue where items are enqueued starting at index 0.

Applied to files:

  • frontend/src/pages/distributions/[queueNumber].tsx
  • frontend/src/components/DistributionSummary.tsx
🧬 Code graph analysis (2)
frontend/src/pages/distributions/add.tsx (1)
frontend/src/pages/sponsorships/add.tsx (1)
  • WriteContractButton (101-123)
frontend/src/pages/distributions/[queueNumber].tsx (1)
frontend/src/components/DistributionSummary.tsx (1)
  • DistributionSummary (8-56)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: build (windows-latest, 18.x)
  • GitHub Check: build (ubuntu-latest, 20.x)
  • GitHub Check: build (windows-latest, 20.x)
  • GitHub Check: build (ubuntu-latest, 18.x)
  • GitHub Check: build (windows-latest, 20.x)
  • GitHub Check: build (windows-latest, 18.x)
  • GitHub Check: Redirect rules - web3-sponsors
  • GitHub Check: Header rules - web3-sponsors
  • GitHub Check: Pages changed - web3-sponsors
🔇 Additional comments (2)
frontend/src/pages/distributions/add.tsx (1)

140-140: LGTM: props now correctly forwarded to the write step

Forwarding languageCode and androidId ensures parity between simulation and the actual on-chain call. This tightens the Add Distribution flow and helps us ship reliable UX toward our mission: elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

frontend/src/components/DistributionSummary.tsx (1)

19-20: Incorrect off‑by‑one — contract is 1‑based; no UI index subtraction required.

Backend (backend/contracts/DistributionQueue.sol) initializes queueNumberFront = 1 and queueNumberNext = 1 and uses those keys; frontend (frontend/src/components/DistributionSummary.tsx) passes args: [queueNumber] and Distributions passes i + 1 — do not convert to 0‑based. Optional: gate the contract read until queueNumber is an integer >= 1 to avoid invalid reads.

elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

Likely an incorrect or invalid review comment.


<div className="mt-8 p-4 text-2xl bg-zinc-50 dark:bg-zinc-900 rounded-lg">
<DistributionSummary queueIndex={queueIndex} />
<DistributionSummary queueNumber={queueNumber} />
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Guard rendering and validate route param before hitting the chain

Passing queueNumber straight through is fine only if the child converts 1‑based UI → 0‑based contract index. Also prevent NaN/invalid values during the initial render (Next.js router not ready) to avoid spurious contract reads.

Apply this minimal guard at the call site:

-          <DistributionSummary queueNumber={queueNumber} />
+          {Number.isInteger(queueNumber) && queueNumber >= 1 ? (
+            <DistributionSummary queueNumber={queueNumber} />
+          ) : null}

Optional follow‑up (helps our mission’s reliability): run this to ensure no stale queueIndex props remain:

Eliminating off‑by‑one pitfalls here keeps the Distributions UI trustworthy—important as elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.


🏁 Script executed:

#!/bin/bash
# Find any remaining queueIndex usages
rg -n --type=ts --type=tsx -C1 '\bqueueIndex\b'

Length of output: 27


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "1) locate files named [queueNumber].tsx"
find . -type f -name '[queueNumber].tsx' -print || true
echo "----"
echo "2) if found, print first 200 lines"
for f in $(find . -type f -name '[queueNumber].tsx' -print); do
  echo ">>> $f"
  sed -n '1,200p' "$f"
done
echo "----"
echo "3) search for queueIndex occurrences (no --type flags)"
rg -n -C1 '\bqueueIndex\b' || true
echo "----"
echo "4) search for DistributionSummary usages"
rg -n -C3 'DistributionSummary' || true
echo "----"
echo "5) list files in frontend/src/pages/distributions (if present)"
ls -la frontend/src/pages/distributions 2>/dev/null || true

Length of output: 3717


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "=== pages/distributions/[queueNumber].tsx ==="
sed -n '1,240p' frontend/src/pages/distributions/[queueNumber].tsx || true
echo
echo "=== components/DistributionSummary.tsx ==="
sed -n '1,240p' frontend/src/components/DistributionSummary.tsx || true
echo
echo "=== search for queueIndex occurrences ==="
rg -n '\bqueueIndex\b' -S || true
echo
echo "=== search for queueNumber occurrences ==="
rg -n '\bqueueNumber\b' -S || true
echo
echo "=== search for router.query references ==="
rg -n "router\.query" -S || true
echo
echo "=== search for parseInt or Number around queueNumber ==="
rg -n -C1 "parseInt|Number\(" -S || true

Length of output: 9672


Guard route param before calling DistributionSummary

router.query can be undefined on initial render; Number(router.query.queueNumber) can be NaN — validate before passing to DistributionSummary (it calls useReadContract with args: [queueNumber]).

-          <DistributionSummary queueNumber={queueNumber} />
+          {Number.isInteger(queueNumber) && queueNumber >= 1 ? (
+            <DistributionSummary queueNumber={queueNumber} />
+          ) : null}

Also apply the same guard in frontend/src/pages/sponsorships/[queueNumber].tsx.

elimu.ai's mission is to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

📝 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
<DistributionSummary queueNumber={queueNumber} />
{Number.isInteger(queueNumber) && queueNumber >= 1 ? (
<DistributionSummary queueNumber={queueNumber} />
) : null}
🤖 Prompt for AI Agents
In frontend/src/pages/distributions/[queueNumber].tsx around line 31,
router.query.queueNumber may be undefined or non-numeric on initial render so
Number(router.query.queueNumber) can yield NaN; guard and validate the param
before passing it to <DistributionSummary /> (which ultimately calls
useReadContract). Fix by extracting queueNumber from router.query, check that it
exists and that Number(queueNumber) is a finite integer (or use parseInt +
isFinite) and only render <DistributionSummary queueNumber={...}/> when valid;
otherwise render a loading/null fallback or redirect. Apply the same guard and
pattern in frontend/src/pages/sponsorships/[queueNumber].tsx.

Copy link

@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: 0

🧹 Nitpick comments (2)
frontend/src/components/Distributions.tsx (2)

33-36: Safely normalize contract return to number; use strict equality.

Prevents accidental NaN and avoids loose equality.

-    const queueLength = Number(data);
-    console.debug("queueLength:", queueLength);
-    if (queueLength == 0) {
+    const queueLength = typeof data === "bigint" ? Number(data) : Number(data ?? 0);
+    console.debug("queueLength:", queueLength);
+    if (queueLength === 0) {

41-47: Prefer Array.from and stable keys; keep 1‑based queueNumber.

More idiomatic, avoids an unnecessary fill, and keys won’t collide.

-            {Array(queueLength).fill(1).map((el, i) =>
-                <Link key={i} href={`/distributions/${i + 1}`}>
-                    <div className="skew-y-3 p-4 text-2xl bg-indigo-200 dark:bg-indigo-950 rounded-lg border-indigo-400 border-r-4 border-b-4 hover:border-r-8 hover:border-b-8 hover:-translate-y-1">
-                        <DistributionSummary queueNumber={i + 1} />
-                    </div>
-                </Link>
-            )}
+            {Array.from({ length: queueLength }, (_, i) => i + 1).map((queueNumber) => (
+                <Link key={queueNumber} href={`/distributions/${queueNumber}`}>
+                    <div className="skew-y-3 p-4 text-2xl bg-indigo-200 dark:bg-indigo-950 rounded-lg border-indigo-400 border-r-4 border-b-4 hover:border-r-8 hover:border-b-8 hover:-translate-y-1">
+                        <DistributionSummary queueNumber={queueNumber} />
+                    </div>
+                </Link>
+            ))}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16608f1 and 1052543.

📒 Files selected for processing (2)
  • frontend/src/components/Distributions.tsx (2 hunks)
  • frontend/src/components/Sponsorships.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/Sponsorships.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/components/Distributions.tsx (1)
frontend/src/components/DistributionSummary.tsx (1)
  • DistributionSummary (8-56)
🔇 Additional comments (1)
frontend/src/components/Distributions.tsx (1)

16-19: Approve — ABI exposes getLength; no stale getQueueCount in frontend.

backend/ignition/deployments/chain-11155111/artifacts/*SponsorshipQueue.json and *DistributionQueue.json contain "getLength"; no matches for getQueueCount in frontend; DistributionSummary passes queueNumber as the queue arg. Aligning this keeps us moving fast toward elimu.ai's mission: to build innovative learning software that empowers out-of-school children to teach themselves basic reading📖, writing✍🏽 and math🔢 within 6 months.

@nya-elimu nya-elimu merged commit 011bb03 into elimu-ai:main Sep 18, 2025
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants