bountiful ergo bounty platform - #23
Conversation
WalkthroughThe PR introduces a new Judge System feature for proposal contract management on the Ergo blockchain. It adds a new contract variant (v1_1) with dispute escalation capabilities, extends status handling to recognize "judge_system" as a distinct proposal state (value 4), reinforces platform API resilience, and updates corresponding UI and test infrastructure. Changes
Sequence DiagramsequenceDiagram
actor User
participant UI as ProposalManager UI
participant Platform as ErgoPlatform
participant Ergo as Ergo Wallet/API
participant Blockchain as Ergo Blockchain
User->>UI: Click "Send to Judge System"<br/>(Disputed Proposal)
UI->>UI: handleJudgeSystem(proposalId)
UI->>Platform: updateProposalStatus(proposalBox,<br/>status: 4 [judge_system])
Platform->>Platform: Construct transaction<br/>(copy assets, encode R8=4)
Platform->>Ergo: Sign transaction
Ergo->>Blockchain: Submit transaction
Blockchain->>Blockchain: Validate: isEscalateToJudgeSystemAction<br/>(R8==4, bounty creator auth)
Blockchain-->>Ergo: Tx confirmed
Ergo-->>Platform: Return txId
Platform->>Platform: Store txId
Platform->>Platform: Wait & reload proposals
Platform->>UI: Update status to "judge_system"
UI->>User: Display Judge panel<br/>(Approve/Reject buttons)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Areas requiring extra attention:
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/lib/ergo/actions/update_proposal_status.ts (1)
33-42: Update the JSDoc comment to reflect all status codes.The function now accepts status codes 0, 1, 2, 3, and 4, but the JSDoc comment only documents 0 and 3. Please update the comment to include all valid status values for clarity.
Apply this diff:
/** * Updates the status of a proposal. - * This is used by the bounty creator to mark a proposal as "Disputed" or to resolve a dispute. * @param proposalBox The proposal box to update. - * @param newStatus The new status code (0 for Pending, 3 for Disputed). + * @param newStatus The new status code (0: Pending, 1: Approved, 2: Rejected, 3: Disputed, 4: Judge System). * @param creatorAddress The address of the bounty creator. */ export async function updateProposalStatus( proposalBox: ProposalBox, newStatus: 0 | 1 | 2 | 3 | 4, creatorAddress: string ): Promise<string | null> {src/routes/ProposalManager.svelte (1)
841-866: Missing CSS style forstatus-judge_systembadge.The UI correctly adds the "Send to Judge System" button for disputed proposals and allows approve/reject actions for judge_system status. However, there's no CSS class
.status-judge_systemdefined in the styles section (only.status-approved,.status-rejected,.status-pending,.status-disputedexist).Add a style for the judge_system status badge:
.status-judge_system { background-color: #cce5ff; color: #004085; }
🧹 Nitpick comments (3)
test/rawEsTransform.cjs (1)
1-10: Consider adding error handling for file read failures.The implementation correctly reads and exports file content as a string for Jest. However, adding error handling around
fs.readFileSyncwould provide clearer error messages if a file is missing or unreadable.Apply this diff to add error handling:
module.exports = { process(_src, filename) { + try { const content = fs.readFileSync(filename, 'utf8'); return { code: `module.exports = ${JSON.stringify(content)};` }; + } catch (error) { + throw new Error(`Failed to read contract file ${filename}: ${error.message}`); + } } };contracts/proposal_v1_1.es (1)
21-33: Unused helper function.The
isSigmaPropEqualToBoxProphelper is defined but never used in any action predicate. Consider removing it to reduce contract complexity and script size, or document its intended use for future actions.src/lib/ergo/platform.ts (1)
66-72: Consider providing clearer user feedback when Ergo API is unavailable.The guard correctly prevents issues when the API is unavailable after Nautilus connection, but the user only sees a console warning. Consider showing an alert or a UI notification similar to other failure paths (lines 77, 80).
const ergoApi = this.getErgoApi(); if (!ergoApi) { console.warn('Connected to Nautilus, but ergo API is not available on window'); + alert('Wallet connection incomplete - Ergo API not available'); connected.set(false); return; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (12)
contracts/proposal_v1_0.es(1 hunks)contracts/proposal_v1_1.es(1 hunks)jest.config.mjs(1 hunks)package.json(1 hunks)src/lib/common/proposal.ts(1 hunks)src/lib/ergo/actions/update_proposal_status.ts(1 hunks)src/lib/ergo/platform.ts(7 hunks)src/lib/ergo/proposal_contract.ts(2 hunks)src/routes/ProposalManager.svelte(4 hunks)test/fileMock.cjs(1 hunks)test/proposal-contract.test.ts(2 hunks)test/rawEsTransform.cjs(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/lib/ergo/platform.ts (2)
src/lib/common/store.ts (2)
connected(10-10)address(8-8)src/lib/ergo/actions/approve_proposal.ts (1)
ProposalBox(26-33)
🔇 Additional comments (17)
contracts/proposal_v1_0.es (1)
23-23: LGTM!The blank line improves readability by separating variable declarations from the logic below.
jest.config.mjs (2)
11-11: LGTM!Switching to
rawEsTransform.cjsenables loading.escontract files as raw string content, which is required forfleet-sdk/compilerto compile them. This aligns with the PR's objective of fixing Windows compatibility issues where Jest previously couldn't load these files correctly.
14-14: LGTM!The
?rawquery string mapper works in conjunction with the raw ES transformer to enable explicit raw imports likeimport contract from './contract.es?raw'. This is a common pattern for loading file content as strings.package.json (1)
6-8: LGTM!Excellent fix for Windows compatibility. The
NODE_OPTIONS=...syntax doesn't work in Windows CMD/PowerShell. Calling thenodebinary explicitly with the--experimental-vm-modulesflag works cross-platform.test/proposal-contract.test.ts (2)
28-29: LGTM!Adding the
JUDGE_SYSTEMstatus with value 4 correctly extends the status model to include the new judge system phase introduced in this PR.
43-43: LGTM!Correctly verifies that proposals in the Judge System state (4) cannot be disputed, which makes sense as the Judge System is an escalation mechanism beyond the normal dispute flow.
src/lib/common/proposal.ts (1)
177-179: LGTM!The new status case correctly parses value 4 from the R8 register to the "judge_system" status string, maintaining consistency with the status model introduced across the PR.
src/lib/ergo/proposal_contract.ts (3)
7-9: LGTM!The v1_1 template import and type extension follow the established pattern for v1_0 cleanly.
22-25: LGTM!The generator function for v1_1 follows the same structure as v1_0, maintaining consistency.
32-33: LGTM!The version switcher correctly handles the new v1_1 case. The exhaustive switch with a default error throw ensures type safety.
src/routes/ProposalManager.svelte (2)
309-311: LGTM!Status parsing correctly maps R8 value "4" to "judge_system", consistent with the contract definition.
665-690: LGTM!The
handleJudgeSystemfunction follows the same pattern ashandleDisputeandhandleReject, with proper error handling and state management.contracts/proposal_v1_1.es (2)
35-48: Verify: Dispute from approved state is intentional.The
isDisputeActionallows transitioning fromisApprovedto disputed status. This is unusual since approved proposals typically represent a finalized state. Please confirm this is the intended behavior for allowing post-approval disputes.
107-120: LGTM!The action aggregation and validSetup logic correctly combine all predicates. The contract structure is clean and follows the expected pattern for Ergo contracts.
src/lib/ergo/platform.ts (3)
53-57: LGTM!The
getErgoApi()helper cleanly centralizes the Ergo API resolution logic, checking both globalergoandwindow.ergowith proper guards. This addresses the PR objective of preventing blank screens when Nautilus is unavailable.
89-91: LGTM!The wallet methods (
get_address,get_current_height,get_balance) correctly use the centralizedgetErgoApi()helper with appropriate guards and error messages.Also applies to: 100-102, 122-127
241-250: LGTM!The status parsing in
fetchProposalscorrectly handles all five status values (0-4), matching the contract definition and UI expectations.
| val isMaintenanceAction = { | ||
| allOf(Coll( | ||
| isPending, | ||
| OUTPUTS(0).R4[GroupElement].get == proposerPK, | ||
| OUTPUTS(0).R5[Coll[Byte]].get == bountyId, | ||
| OUTPUTS(0).R7[SigmaProp].get == bountyCreatorProp, | ||
| OUTPUTS(0).value == SELF.value, | ||
| OUTPUTS(0).propositionBytes == SELF.propositionBytes, | ||
| OUTPUTS(0).R8[Int].get == 0 | ||
| )) | ||
| } |
There was a problem hiding this comment.
Missing R6 (metadataJson) preservation in isMaintenanceAction.
Unlike other action predicates (isDisputeAction, isRejectAction, isApprovalAction, isEscalateToJudgeSystemAction) which all verify OUTPUTS(0).R6[Coll[Byte]].get == metadataJson, this action does not check R6. This could allow the metadata to be modified during maintenance.
Apply this diff to ensure metadata is preserved:
val isMaintenanceAction = {
allOf(Coll(
isPending,
OUTPUTS(0).R4[GroupElement].get == proposerPK,
OUTPUTS(0).R5[Coll[Byte]].get == bountyId,
+ OUTPUTS(0).R6[Coll[Byte]].get == metadataJson,
OUTPUTS(0).R7[SigmaProp].get == bountyCreatorProp,
OUTPUTS(0).value == SELF.value,
OUTPUTS(0).propositionBytes == SELF.propositionBytes,
OUTPUTS(0).R8[Int].get == 0
))
}📝 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.
| val isMaintenanceAction = { | |
| allOf(Coll( | |
| isPending, | |
| OUTPUTS(0).R4[GroupElement].get == proposerPK, | |
| OUTPUTS(0).R5[Coll[Byte]].get == bountyId, | |
| OUTPUTS(0).R7[SigmaProp].get == bountyCreatorProp, | |
| OUTPUTS(0).value == SELF.value, | |
| OUTPUTS(0).propositionBytes == SELF.propositionBytes, | |
| OUTPUTS(0).R8[Int].get == 0 | |
| )) | |
| } | |
| val isMaintenanceAction = { | |
| allOf(Coll( | |
| isPending, | |
| OUTPUTS(0).R4[GroupElement].get == proposerPK, | |
| OUTPUTS(0).R5[Coll[Byte]].get == bountyId, | |
| OUTPUTS(0).R6[Coll[Byte]].get == metadataJson, | |
| OUTPUTS(0).R7[SigmaProp].get == bountyCreatorProp, | |
| OUTPUTS(0).value == SELF.value, | |
| OUTPUTS(0).propositionBytes == SELF.propositionBytes, | |
| OUTPUTS(0).R8[Int].get == 0 | |
| )) | |
| } |
🤖 Prompt for AI Agents
In contracts/proposal_v1_1.es around lines 65 to 75, the isMaintenanceAction
predicate is missing a check to preserve R6 (metadataJson); add a clause to the
allOf that asserts OUTPUTS(0).R6[Coll[Byte]].get == metadataJson so the
maintenance action validates that metadataJson is unchanged, matching the other
action predicates.
| async updateProposalStatus(proposalBox: ProposalBox, newStatus: 0 | 1 | 2 | 3 | 4, creatorAddress: string): Promise<string | null> { | ||
| return await updateProposalStatus(proposalBox, newStatus, creatorAddress); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify updateProposalStatus action signature and ProposalBox interface
echo "=== Checking updateProposalStatus signature ==="
rg -n "updateProposalStatus" src/lib/ergo/actions/update_proposal_status.ts -A 3
echo ""
echo "=== Checking ProposalBox R8 comment ==="
rg -n "R8.*status" src/lib/ergo/actions/approve_proposal.tsRepository: StabilityNexus/Bountiful-BountyPlatform-Ergo
Length of output: 410
LGTM, but ProposalBox interface comment needs updating.
The updateProposalStatus signature is correctly expanded to accept the new judge_system status (4), and the underlying action in src/lib/ergo/actions/update_proposal_status.ts (line 40-42) properly accepts the expanded status type. However, the ProposalBox interface comment at src/lib/ergo/actions/approve_proposal.ts line 32 still lists only statuses 0-3 and must be updated to include "4: Judge System":
R8: string; // Int - status (0: Pending, 1: Approved, 2: Rejected, 3: Disputed, 4: Judge System)
🤖 Prompt for AI Agents
In src/lib/ergo/actions/approve_proposal.ts around line 32, update the
ProposalBox interface comment for the R8 field to include the new status value 4
by changing the status list to: "Int - status (0: Pending, 1: Approved, 2:
Rejected, 3: Disputed, 4: Judge System)"; ensure the comment remains on the R8
line and matches the same format as other comments.
| module.exports = { | ||
| process() { | ||
| return { | ||
| code: 'module.exports = {};' | ||
| }; | ||
| }, | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for references to fileMock.cjs in the codebase
rg "fileMock\.cjs" -nRepository: StabilityNexus/Bountiful-BountyPlatform-Ergo
Length of output: 70
🏁 Script executed:
#!/bin/bash
# Also search for variations and imports of fileMock
rg "fileMock" -n --type js --type cjs --type mjs --type jsonRepository: StabilityNexus/Bountiful-BountyPlatform-Ergo
Length of output: 115
🏁 Script executed:
#!/bin/bash
# Check the jest configuration to see what mocks are actually configured
cat -n jest.config.mjsRepository: StabilityNexus/Bountiful-BountyPlatform-Ergo
Length of output: 709
🏁 Script executed:
#!/bin/bash
# Search for moduleNameMapper or transform configurations that might reference this file
rg "moduleNameMapper|transform" jest.config.mjs -A 5 -B 5Repository: StabilityNexus/Bountiful-BountyPlatform-Ergo
Length of output: 569
Remove the unused fileMock.cjs file.
This file is not referenced anywhere in the codebase and is not configured in jest.config.mjs. The jest configuration only uses rawEsTransform.cjs for .es file transformations. Delete it to reduce clutter.
🤖 Prompt for AI Agents
In test/fileMock.cjs lines 1-7: this file is unused and not referenced by the
codebase or jest.config.mjs; delete test/fileMock.cjs from the repository to
remove dead/unused test fixture and update any tests if they depended on it (no
code changes required elsewhere since jest config only uses rawEsTransform.cjs).
Issue
Tests + contract compile were failing on Windows because Jest couldn’t load *.es?raw contract templates correctly (it treated them as non-strings), and the test script used non-Windows env syntax.
Fix
Made Jest scripts Windows-compatible.
Added a Jest transformer + mapper so *.es?raw loads as raw contract source string (so fleet-sdk/compiler can compile it).
Added wallet guards to avoid blank screen when Nautilus/window.ergo isn’t available.
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.