Skip to content

Fix pot application flow for DAOs#457

Open
akaia-shadowfox wants to merge 4 commits intostagingfrom
fix/165-apply-as-a-dao
Open

Fix pot application flow for DAOs#457
akaia-shadowfox wants to merge 4 commits intostagingfrom
fix/165-apply-as-a-dao

Conversation

@akaia-shadowfox
Copy link
Collaborator

@akaia-shadowfox akaia-shadowfox commented Sep 30, 2025

Summary by CodeRabbit

  • New Features

    • Apply to a Pot directly or via DAO proposal with automated deposit & gas calculation.
    • New proposal submission helper and a unified apply flow.
    • New utilities for encoding arguments to Base64 and UTF-8-safe strings.
  • UI

    • Pot Application modal now shows “Apply to {Pot Name}” with clearer labels, submit states, and success/error toasts.
    • “Donate to list” button renamed to “Donate to Projects.”
  • Refactor

    • Simplified Pot application form API and modal props; submission delegated to a single handler.

@akaia-shadowfox akaia-shadowfox self-assigned this Sep 30, 2025
@vercel
Copy link

vercel bot commented Sep 30, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
potlock-next-app Ready Ready Preview Comment Oct 1, 2025 3:38pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 30, 2025

Walkthrough

Adds a Pot apply flow: new pot client apply(...) and calculateCallDeposit util, DAO proposal submission support, base64 JSON helpers, refactored Pot application UI/hooks/effects to use applyToPot with toast feedback, and minor encoding and label updates across related features.

Changes

Cohort / File(s) Summary
Pot contract client + utils
src/common/contracts/core/pot/client.ts, src/common/contracts/core/pot/index.ts, src/common/contracts/core/pot/utils.ts
New ApplyArgs type and apply(...) contract call added; calculateCallDeposit util computes deposits (handles "apply"); ONE_TGAS.mul(100) used for gas; index exports potContractUtils.
Sputnik DAO v2 client + types
src/common/contracts/sputnikdao2/client.ts, src/common/contracts/sputnikdao2/types.ts
Added AddProposalArgs type and add_proposal(...) function (accepts proposalBond, callbackUrl, returns ProposalId); changed ActionCall.gas type to IndivisibleUnits; added ProposalInput type.
Common lib utilities
src/common/lib/string.ts, src/common/lib/object.ts
Added utf8StringToBase64 and objectToBase64Json helpers for UTF‑8 → Base64 JSON encoding; used by other modules.
Pot application flow (UI, hooks, effects, layout)
src/features/pot-application/components/PotApplicationModal.tsx, src/features/pot-application/hooks/forms.ts, src/features/pot-application/model/effects.ts, src/layout/pot/components/layout.tsx
Modal props refactored to use potConfig and callbacks; usePotApplicationForm now accepts PotApplicationFormParams and delegates submission to new applyToPot; applyToPot branches between direct pot.apply and DAO add_proposal; layout integrates toast feedback and updated modal usage.
Encoding updates in other features
src/features/matching-pool-contribution/hooks/forms.ts, src/features/profile-configuration/models/effects.ts
Replaced Buffer-based JSON→base64 encoding with objectToBase64Json for DAO/transaction args.
Minor UI copy tweak
src/features/donation/components/user-entrypoints.tsx
Button label changed from "Donate to list" to "Donate to Projects".

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as PotApplicationModal
  participant Hook as usePotApplicationForm
  participant Effect as applyToPot
  participant Pot as potContractClient
  participant DAO as sputnikdao2.client

  User->>UI: Submit application
  UI->>Hook: onSubmit(message)
  Hook->>Effect: applyToPot({ applicantAccountId, asDao, message, potConfig })
  alt Direct application (asDao = false)
    Effect->>Pot: apply({ potId, args:{message}, deposit, gas, callbackUrl })
    Pot-->>Effect: Application result
  else DAO proposal (asDao = true)
    Effect->>DAO: get_policy(daoAccountId)
    DAO-->>Effect: policy
    Effect->>DAO: add_proposal({ proposal:{ description, kind: FunctionCall(method:"apply", args: base64Json, deposit, gas) }, proposalBond, callbackUrl })
    DAO-->>Effect: ProposalId
  end
  Effect-->>Hook: success / failure
  Hook-->>UI: invoke onSuccess / onFailure
  UI-->>User: show toast (success / error)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

Thump-thump! I press “Apply,” paws light,
I base64 my message for the night.
If DAO, I draft a proposal tight,
Else hop direct with gas set right.
Toasts bloom warm — the rabbit’s delight. 🥕

Pre-merge checks and finishing touches

✅ 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 “Fix pot application flow for DAOs” succinctly captures the primary change in this pull request, which centers on updating the application logic and user interface to support DAO-based submissions and proposals within the pot application workflow. It clearly conveys the intent of the branch without extraneous details or ambiguity.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/165-apply-as-a-dao

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.

@akaia-shadowfox akaia-shadowfox marked this pull request as ready for review October 1, 2025 15:02
@akaia-shadowfox akaia-shadowfox removed the request for review from Ebube111 October 1, 2025 15:02
Copy link
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: 3

🧹 Nitpick comments (1)
src/common/lib/object.ts (1)

58-59: Add error handling for JSON serialization.

JSON.stringify can throw when encountering circular references or non-serializable values (e.g., functions, symbols). Consider wrapping the serialization in try-catch or documenting the expectation that inputs must be serializable.

Apply this diff to add error handling:

-export const objectToBase64Json = (obj: object | Record<string, unknown>): string =>
-  utf8StringToBase64(JSON.stringify(obj));
+export const objectToBase64Json = (obj: object | Record<string, unknown>): string => {
+  try {
+    return utf8StringToBase64(JSON.stringify(obj));
+  } catch (error) {
+    throw new Error(`Failed to serialize object to base64 JSON: ${error}`);
+  }
+};
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3f4d3f and 641303d.

📒 Files selected for processing (14)
  • src/common/contracts/core/pot/client.ts (3 hunks)
  • src/common/contracts/core/pot/index.ts (1 hunks)
  • src/common/contracts/core/pot/utils.ts (1 hunks)
  • src/common/contracts/sputnikdao2/client.ts (2 hunks)
  • src/common/contracts/sputnikdao2/types.ts (2 hunks)
  • src/common/lib/object.ts (2 hunks)
  • src/common/lib/string.ts (1 hunks)
  • src/features/donation/components/user-entrypoints.tsx (1 hunks)
  • src/features/matching-pool-contribution/hooks/forms.ts (2 hunks)
  • src/features/pot-application/components/PotApplicationModal.tsx (1 hunks)
  • src/features/pot-application/hooks/forms.ts (1 hunks)
  • src/features/pot-application/model/effects.ts (1 hunks)
  • src/features/profile-configuration/models/effects.ts (2 hunks)
  • src/layout/pot/components/layout.tsx (5 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-02-14T13:55:21.902Z
Learnt from: carina-akaia
PR: PotLock/potlock-nextjs-app#341
File: src/entities/campaign/hooks/forms.ts:94-116
Timestamp: 2025-02-14T13:55:21.902Z
Learning: The toast component from shadcn/ui is imported from "@/hooks/use-toast". For success messages, use a simple toast with description. For error states, use the "destructive" variant.

Applied to files:

  • src/layout/pot/components/layout.tsx
🧬 Code graph analysis (12)
src/features/donation/components/user-entrypoints.tsx (1)
src/common/ui/layout/components/atoms/button.tsx (1)
  • Button (111-111)
src/features/profile-configuration/models/effects.ts (1)
src/common/lib/object.ts (1)
  • objectToBase64Json (58-59)
src/features/matching-pool-contribution/hooks/forms.ts (1)
src/common/lib/object.ts (1)
  • objectToBase64Json (58-59)
src/features/pot-application/model/effects.ts (5)
src/common/contracts/core/pot/client.ts (1)
  • ApplyArgs (112-114)
src/common/api/indexer/internal/client.generated.ts (1)
  • Pot (857-977)
src/common/contracts/sputnikdao2/types.ts (1)
  • ActionCall (81-91)
src/common/lib/object.ts (1)
  • objectToBase64Json (58-59)
src/common/constants.ts (1)
  • ONE_TGAS (152-152)
src/common/contracts/core/pot/client.ts (4)
src/common/api/indexer/types.ts (1)
  • ByPotId (11-13)
src/common/contracts/core/pot/interfaces.ts (1)
  • Application (16-23)
src/common/contracts/core/pot/utils.ts (1)
  • calculateCallDeposit (14-30)
src/common/constants.ts (1)
  • ONE_TGAS (152-152)
src/common/lib/object.ts (1)
src/common/lib/string.ts (1)
  • utf8StringToBase64 (39-40)
src/common/contracts/sputnikdao2/client.ts (3)
src/common/contracts/sputnikdao2/types.ts (2)
  • ProposalInput (152-155)
  • ProposalId (189-189)
src/common/types.ts (1)
  • IndivisibleUnits (92-92)
src/common/constants.ts (1)
  • FULL_TGAS (145-145)
src/common/contracts/core/pot/utils.ts (4)
src/common/contracts/core/pot/client.ts (1)
  • ApplyArgs (112-114)
src/common/types.ts (1)
  • IndivisibleUnits (92-92)
src/common/lib/format.ts (1)
  • parseNearAmount (7-7)
src/common/constants.ts (1)
  • ONE_HUNDREDTH_NEAR (141-141)
src/layout/pot/components/layout.tsx (2)
src/layout/pot/hooks/tab-navigation.ts (1)
  • usePotLayoutTabNavigation (157-225)
src/common/ui/layout/components/templates/page-with-banner.tsx (1)
  • PageWithBanner (8-12)
src/features/pot-application/hooks/forms.ts (3)
src/common/types.ts (1)
  • AccountId (45-45)
src/common/api/indexer/internal/client.generated.ts (1)
  • Pot (857-977)
src/features/pot-application/model/effects.ts (1)
  • applyToPot (15-57)
src/common/contracts/sputnikdao2/types.ts (1)
src/common/types.ts (1)
  • IndivisibleUnits (92-92)
src/features/pot-application/components/PotApplicationModal.tsx (2)
src/features/pot-application/hooks/forms.ts (2)
  • PotApplicationFormParams (18-24)
  • usePotApplicationForm (26-57)
src/common/ui/form/components/textarea.tsx (1)
  • TextAreaField (20-66)
🔇 Additional comments (21)
src/features/donation/components/user-entrypoints.tsx (1)

75-75: LGTM! Consistent label across donation entry points.

The label now matches the one used in DonateToPotProjects (Line 67), improving UI consistency.

src/common/contracts/sputnikdao2/types.ts (2)

90-90: LGTM! Correct type for gas values.

Changing gas from number to IndivisibleUnits (string) aligns with NEAR protocol standards, where gas values can exceed JavaScript's safe integer range.


152-155: LGTM! Clean input type for proposals.

The new ProposalInput type provides a focused interface for proposal creation, separating input concerns from the full Proposal type that includes runtime state.

src/features/matching-pool-contribution/hooks/forms.ts (1)

41-41: No UTF-8 encoding issues in objectToBase64Json
utf8StringToBase64 leverages Buffer.from(value, "utf8").toString("base64"), which correctly encodes UTF-8 strings. No changes required.

src/common/contracts/core/pot/utils.ts (1)

14-30: Verify calculateDepositByDataSize units and multiplier

  • Does calculateDepositByDataSize (from @wpdas/naxios) return a value in NEAR or yoctoNEAR?
  • Is the 0.01× factor intended for the “apply” deposit formula?
  • Are there edge cases (very small/large data) where this produces under- or oversized deposits?

Location: src/common/contracts/core/pot/utils.ts:21

src/common/contracts/sputnikdao2/client.ts (1)

29-44: Verify gas allocation for add_proposal

All calls to add_proposal currently attach FULL_TGAS (300 Tgas per transaction, NEAR’s maximum) (docs.near.org). If your proposals consistently consume ≤200 Tgas, reduce the attachment to save fees (e.g. "200000000000000" for 200 Tgas) and confirm no “Exceeded prepaid gas” errors. Only retain FULL_TGAS if specific proposal kinds genuinely require >200 Tgas.

src/common/contracts/core/pot/index.ts (1)

3-3: LGTM!

Clean addition of the utility export to the pot contract barrel, enabling access to deposit calculation helpers.

Also applies to: 8-8

src/features/profile-configuration/models/effects.ts (1)

23-23: LGTM! Standardized base64 encoding for DAO proposals.

The replacement of direct Buffer usage with objectToBase64Json improves consistency. The ?? {} fallback ensures undefined args are handled gracefully. Error handling is covered by the outer .catch blocks.

Also applies to: 123-123

src/features/pot-application/model/effects.ts (2)

47-53: Error handling delegated to caller.

The promise chain returns unhandled rejections to the caller. Ensure the calling code (likely in the form hook or modal) has appropriate error handling and user feedback for both policy retrieval and proposal submission failures.


15-57: Well-structured dual-path application flow.

The implementation cleanly separates DAO proposal submission from direct application. The DAO path correctly constructs a Sputnik DAO v2 proposal with base64-encoded args and calculated deposit.

src/common/contracts/core/pot/client.ts (3)

112-114: LGTM!

The type definition correctly defines the application arguments with an optional message field.


2-2: Fix the import syntax for big.js.

The library exports Big as the default export, not a named export. This will cause a runtime error.

Apply this diff:

-import { Big } from "big.js";
+import Big from "big.js";
⛔ Skipped due to learnings
Learnt from: akaia-shadowfox
PR: PotLock/potlock-nextjs-app#451
File: src/features/profile-configuration/models/effects.ts:2-2
Timestamp: 2025-09-29T11:23:41.179Z
Learning: In big.js, both import patterns work: `import { Big } from "big.js"` (named import) and `import Big from "big.js"` (default import) are functionally equivalent due to TypeScript's CommonJS interoperability with the library's `export = Big` pattern.
Learnt from: akaia-shadowfox
PR: PotLock/potlock-nextjs-app#451
File: src/features/profile-configuration/models/effects.ts:2-2
Timestamp: 2025-09-29T11:23:41.179Z
Learning: In big.js, both import patterns work: `import { Big } from "big.js"` (named import) and `import Big from "big.js"` (default import) are functionally equivalent due to TypeScript's module interoperability features.

116-126: Confirm apply gas allocation
The apply call uses ONE_TGAS.mul(100) (100 TGas) while all other pot operations use FULL_TGAS (300 TGas). Ensure 100 TGas is sufficient for the apply operation, or switch to FULL_TGAS if not.

src/layout/pot/components/layout.tsx (3)

29-29: LGTM!

The toast hook and data fetching are correctly implemented.

Also applies to: 47-48


50-60: LGTM!

The success callback correctly closes the modal and displays an appropriate success toast message that distinguishes between DAO and direct application flows.


105-113: LGTM!

The modal props are correctly updated to use the new potConfig prop and wire the success/failure callbacks.

src/features/pot-application/hooks/forms.ts (2)

18-24: LGTM!

The type definition correctly captures all parameters needed for the unified application flow.


26-56: LGTM!

The refactored hook correctly delegates to applyToPot and handles success/failure flows appropriately. The form reset on success ensures a clean state for subsequent submissions.

src/features/pot-application/components/PotApplicationModal.tsx (3)

16-23: LGTM!

The props type correctly leverages Pick to maintain type consistency with PotApplicationFormParams.


36-42: LGTM!

The form hook is correctly invoked with all required parameters.


51-83: Disable logic is correct: message is required by schema.
The potApplicationSchema mandates a message with at least 3 characters, so blocking submission until form.formState.isValid is true is intentional and should remain.

Likely an incorrect or invalid review comment.

args: applyArgs,
}),

gas: ONE_TGAS.toString(),
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify gas allocation is sufficient for pot application.

The gas is set to ONE_TGAS (1 TGas). Ensure this is adequate for the apply method on the pot contract, as insufficient gas will cause the proposal execution to fail.

Run the following script to check the pot contract's apply method implementation and gas usage patterns:


🏁 Script executed:

#!/bin/bash
# Search for pot contract apply method and gas patterns
echo "Searching for apply method gas requirements:"
rg -n "apply.*gas|gas.*apply" --type ts -C3

echo "---"
echo "Other pot contract gas allocations:"
rg -n "potContract.*gas|ONE_TGAS|FIFTY_TGAS|FULL_TGAS" --type ts -C2

Length of output: 20489


🏁 Script executed:

#!/bin/bash
# Show pot-application effects code context
rg -n "potContract" -C4 src/features/pot-application/model/effects.ts

# Show apply usage in pot-application effects
rg -n "apply" -C4 src/features/pot-application/model/effects.ts

Length of output: 2011


Increase DAO apply action gas to 100 TGas. In applyToPot (src/features/pot-application/model/effects.ts:32), replace

gas: ONE_TGAS.toString(),

with

gas: ONE_TGAS.mul(100).toString(),

so that DAO-submitted proposals use the same 100 TGas default as potContractClient.apply and avoid execution failures.

🤖 Prompt for AI Agents
In src/features/pot-application/model/effects.ts around line 32, the gas for the
DAO apply action is set to ONE_TGAS.toString() which is too low; update the gas
value to use 100 TGas by replacing that assignment with
ONE_TGAS.mul(100).toString() so DAO-submitted proposals use the same 100 TGas
default as potContractClient.apply and avoid execution failures.

Copy link
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

♻️ Duplicate comments (1)
src/layout/pot/components/layout.tsx (1)

62-74: Fixed: Destructive variant now present.

The error toast now correctly includes variant: "destructive" as per project conventions. The issue raised in the previous review has been resolved.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 641303d and 5b00a81.

📒 Files selected for processing (2)
  • src/common/lib/string.ts (1 hunks)
  • src/layout/pot/components/layout.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/common/lib/string.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-02-14T13:55:21.902Z
Learnt from: carina-akaia
PR: PotLock/potlock-nextjs-app#341
File: src/entities/campaign/hooks/forms.ts:94-116
Timestamp: 2025-02-14T13:55:21.902Z
Learning: The toast component from shadcn/ui is imported from "@/hooks/use-toast". For success messages, use a simple toast with description. For error states, use the "destructive" variant.

Applied to files:

  • src/layout/pot/components/layout.tsx
🧬 Code graph analysis (1)
src/layout/pot/components/layout.tsx (2)
src/layout/pot/hooks/tab-navigation.ts (1)
  • usePotLayoutTabNavigation (157-225)
src/common/ui/layout/components/templates/page-with-banner.tsx (1)
  • PageWithBanner (8-12)
🔇 Additional comments (6)
src/layout/pot/components/layout.tsx (6)

8-8: LGTM: useToast import added.

The import is correctly placed and supports the new toast-based feedback mechanism.


29-29: LGTM: useToast hook properly initialized.

The hook is correctly destructured to access the toast function.


50-60: LGTM: Success callback properly implemented.

The success handler correctly closes the modal and shows contextual feedback based on whether the user is a DAO representative. The toast follows project conventions for success messages.


79-82: LGTM: Clear technical debt marker.

The FIXME comment appropriately flags that this modal should be replaced with toast calls, aligning with the PR's direction toward toast-based feedback.


90-93: LGTM: Consistent technical debt documentation.

The FIXME comment matches the pattern established above and clearly marks the need to replace this modal with toast calls.


108-116: No action needed; types align. The usePot hook returns data of type Pot, which exactly matches the PotApplicationModal’s potConfig: Pot prop.

Comment on lines +47 to +48
const { activeTab, orderedTabList } = usePotLayoutTabNavigation({ potId });
const { data: pot } = indexer.usePot({ potId });
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify error handling for pot data query.

The component now fetches pot data using indexer.usePot, but there's no visible error or loading state handling. If the query fails or is loading, the modals at line 100 won't render (due to the pot && condition), but users won't see any feedback about what's happening.

Consider adding loading and error states:

const { data: pot, isLoading, error } = indexer.usePot({ potId });

// Show loading state
if (isLoading) {
  return <PageWithBanner><LoadingSpinner /></PageWithBanner>;
}

// Show error state
if (error) {
  return <PageWithBanner><ErrorMessage error={error} /></PageWithBanner>;
}

Run this script to check if error handling exists elsewhere in the component or related hooks:


🏁 Script executed:

#!/bin/bash
# Check if indexer.usePot returns error/loading states and if they're handled
ast-grep --pattern 'indexer.usePot'

# Check for error handling patterns in the file
rg -n "isLoading|isError|error" src/layout/pot/components/layout.tsx

Length of output: 1847


🏁 Script executed:

#!/bin/bash
sed -n '1,120p' src/layout/pot/components/layout.tsx

Length of output: 4256


Handle loading and error for usePot in PotLayout (src/layout/pot/components/layout.tsx:47-48)
Destructure isLoading and error from indexer.usePot and early-return a spinner or error banner to ensure users see fetch status before rendering the layout.

🤖 Prompt for AI Agents
In src/layout/pot/components/layout.tsx around lines 47-48, the call to
indexer.usePot only destructures data and doesn't handle loading or error
states; update the hook call to also destructure isLoading and error, then add
early returns: if isLoading return a Spinner (or loading component) centered
where the layout would render, and if error return an ErrorBanner (or simple
error message) with context (e.g., "Failed to load pot"). Keep the existing
activeTab and orderedTabList logic intact and only render the full PotLayout
when isLoading is false and error is null.

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

Labels

None yet

Projects

None yet

1 participant