Skip to content

feat: Implement escrow management and fee calculation with new hooks, types, services, and UI components. - #160

Merged
Benjtalkshow merged 1 commit into
boundlessfi:mainfrom
Franklivania:fear/use-escrow
Mar 30, 2026
Merged

feat: Implement escrow management and fee calculation with new hooks, types, services, and UI components.#160
Benjtalkshow merged 1 commit into
boundlessfi:mainfrom
Franklivania:fear/use-escrow

Conversation

@Franklivania

@Franklivania Franklivania commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Integrate Escrow UI for Bounty Fund Management (#143)

Description

This pull request introduces the Escrow UI, providing functionality for both creators and contributors to seamlessly view the state of bounty fund pools, release slots, and expected fee breakdowns. The data retrieval relies on a mocked service patterned identically to existing services (ComplianceService) to make integration with runtime TypeScript contract bindings seamless once available (139).

Closes #143

Commits

  • feat(escrow): add escrow tracking components and types
  • feat(escrow): implement fee breakdown calculator
  • feat(escrow): add use-escrow data hooks backed by mock service
  • test(escrow): add unit tests for use-escrow query hooks
  • fix(test): configure jest to handle .tsx test files
  • feat(bounty): integrate escrow status and panel into bounty detail and card views

Changes

  • New Types: Added strict interfaces for EscrowPool, EscrowSlot, and FeeBreakdown.
  • Mock Escrow Service: Introduced EscrowService to simulate on-chain escrow state.
  • TanStack Queries Layer: Created useEscrowPool, useEscrowSlots, and useFeeCalculation custom hooks.
  • UI Components:
    • EscrowStatus: Compact badge displaying escrow state (Escrowed, Released, Partial, Refunded).
    • EscrowDetailPanel: A comprehensive view displaying pool details, remaining lock, expiration date, and individual slot recipients.
    • FeeCalculator: A sidebar widget allowing creators to calculate platform and insurance fee splits dynamically.
  • Views Integrated: The escrow hooks and components were seamlessly wired into the BountyCard and BountyDetailClient views.
  • Testing Scope: Configured Jest's testMatch to detect .tsx test files alongside .ts and created a comprehensive test suite covering the query hooks' logic paths.

Acceptance Criteria Met

  • Escrow status visible on bounty cards and detail pages.
  • Pool details (locked amount, asset, expiry) displayed.
  • Release slots show recipient and amount.
  • Fee breakdown shown before funding dynamically via Fee Estimator.
  • Links to Stellar explorer introduced for on-chain verification.

Related Issues

Fixes #143
Depends on #139 mapping completion for API hooks.

@vercel

vercel Bot commented Mar 27, 2026

Copy link
Copy Markdown

@Franklivania is attempting to deploy a commit to the Threadflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Mar 27, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR introduces comprehensive escrow management functionality, adding new components (EscrowStatus, EscrowDetailPanel, FeeCalculator), React Query hooks (useEscrowPool, useEscrowSlots, useFeeCalculation), a mock-backed EscrowService, TypeScript types for escrow domain data, and integrates escrow UI into existing bounty views. Tests cover the new hooks, and Jest configuration is updated to support TSX test files.

Changes

Cohort / File(s) Summary
Escrow Type Definitions
types/escrow.ts
New types and interfaces for escrow domain: EscrowPoolStatus, EscrowSlotStatus, EscrowPool, EscrowSlot, and FeeBreakdown.
Escrow Service Layer
lib/services/escrow.ts
New EscrowService class with static methods for fetching pool data, slot data, and calculating fee breakdowns with tiered rates for different bounty subtypes (FIXED_PRICE, COMPETITION, MILESTONE_BASED).
Escrow Hooks
hooks/use-escrow.ts
Three React Query hooks: useEscrowPool() and useEscrowSlots() fetch escrow data with conditional enabling; useFeeCalculation() computes fees. Includes shared cache key factory (escrowKeys).
Escrow UI Components
components/bounty/escrow-status.tsx, components/bounty/escrow-detail-panel.tsx, components/bounty/fee-calculator.tsx
Three new components: EscrowStatus renders status badge with optional amount formatting; EscrowDetailPanel displays full escrow summary with slot table and Stellar Explorer link; FeeCalculator shows fee breakdown for user-entered bounty amounts.
Bounty View Integration
components/bounty-detail/bounty-detail-client.tsx, components/bounty/bounty-card.tsx
BountyDetailClient adds EscrowDetailPanel and FeeCalculator to main and sidebar content; BountyCard adds conditional EscrowStatus rendering with computed locked amounts.
Testing & Configuration
hooks/__tests__/use-escrow.test.tsx, jest.config.js
New test suite covering all three hooks with mocked queries; Jest config updated to include .test.tsx files alongside .test.ts.

Sequence Diagram

sequenceDiagram
    participant User
    participant BountyDetailClient
    participant useEscrowPool
    participant useEscrowSlots
    participant EscrowService
    participant EscrowDetailPanel
    participant UI

    User->>BountyDetailClient: View bounty detail (bountyId)
    BountyDetailClient->>useEscrowPool: useEscrowPool(bountyId)
    useEscrowPool->>EscrowService: getPool(bountyId)
    EscrowService-->>useEscrowPool: EscrowPool data
    useEscrowPool-->>BountyDetailClient: pool object
    
    BountyDetailClient->>useEscrowSlots: useEscrowSlots(bountyId)
    useEscrowSlots->>EscrowService: getSlots(bountyId)
    EscrowService-->>useEscrowSlots: EscrowSlot[] array
    useEscrowSlots-->>BountyDetailClient: slots array
    
    BountyDetailClient->>EscrowDetailPanel: Render with poolId=bountyId
    EscrowDetailPanel->>UI: Display pool summary & slot table
    UI-->>User: Show escrow details
    
    BountyDetailClient->>UI: Render FeeCalculator in sidebar
    User->>UI: Enter bounty amount & type
    UI->>EscrowService: calculateFee(amount, subType)
    EscrowService-->>UI: FeeBreakdown
    UI-->>User: Display platform fee, insurance fee, net payout
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • Benjtalkshow
  • 0xdevcollins

Poem

🐰 Escrow pools and fees align,
With bounties managed, crystal fine!
Detail panels, status true,
The rabbit's work is done—whoosh!poof!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 accurately describes the main changes: adding escrow management, fee calculation hooks, types, services, and UI components across multiple files.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (9)
hooks/__tests__/use-escrow.test.tsx (2)

10-14: Consider creating QueryClient per test for better isolation.

The shared module-level QueryClient may cause test pollution if other test files are added. Creating a fresh instance per test ensures complete isolation:

♻️ Suggested improvement
-const queryClient = new QueryClient();
-
-const wrapper = ({ children }: { children: React.ReactNode }) => (
-  <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
-);
+const createWrapper = () => {
+  const queryClient = new QueryClient({
+    defaultOptions: {
+      queries: { retry: false },
+    },
+  });
+  return ({ children }: { children: React.ReactNode }) => (
+    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
+  );
+};

// In each test:
-const { result } = renderHook(() => useEscrowPool("1"), { wrapper });
+const { result } = renderHook(() => useEscrowPool("1"), { wrapper: createWrapper() });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/__tests__/use-escrow.test.tsx` around lines 10 - 14, The current test
uses a shared module-level QueryClient which can cause cross-test pollution;
change to create a fresh QueryClient per test by moving the QueryClient
instantiation into the test setup (e.g., inside a beforeEach or by making the
wrapper a factory) so that QueryClient is re-created for each test, update the
wrapper to accept or create that per-test QueryClient, and ensure
QueryClientProvider receives the fresh instance in each test run (refer to
QueryClient, wrapper, and QueryClientProvider to locate the change).

78-114: Add test coverage for MILESTONE_BASED bounty type.

Tests cover FIXED_PRICE (5%+1%) and COMPETITION (8%+2%) but omit MILESTONE_BASED (6%+1%). Adding this test ensures all three fee calculation paths are verified:

💚 Missing test case
it("should return fee breakdown for MILESTONE_BASED", async () => {
  const { result } = renderHook(
    () => useFeeCalculation(1000, "MILESTONE_BASED"),
    { wrapper },
  );

  await waitFor(() => {
    expect(result.current.isSuccess).toBe(true);
  });

  expect(result.current.data).toEqual({
    grossAmount: 1000,
    platformFee: 60,
    insuranceFee: 10,
    netPayout: 930,
  });
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/__tests__/use-escrow.test.tsx` around lines 78 - 114, Add a new test
case in hooks/__tests__/use-escrow.test.tsx to cover the MILESTONE_BASED path of
useFeeCalculation: call renderHook(() => useFeeCalculation(1000,
"MILESTONE_BASED"), { wrapper }), wait for result.current.isSuccess to be true,
then assert result.current.data equals { grossAmount: 1000, platformFee: 60,
insuranceFee: 10, netPayout: 930 }; place this test alongside the existing
FIXED_PRICE and COMPETITION tests to ensure all bounty types are covered.
components/bounty/bounty-card.tsx (2)

131-145: Extract duplicated lockedAmount calculation.

The lockedAmount logic (pool.status === "Fully Released" ? 0 : pool.totalAmount - pool.releasedAmount) is duplicated here and in escrow-detail-panel.tsx (lines 62-65). Consider extracting this to a shared utility function or computing it within the EscrowStatus component itself.

♻️ Potential utility extraction
// lib/utils/escrow.ts
export function calculateLockedAmount(pool: EscrowPool): number {
  return pool.status === "Fully Released"
    ? 0
    : pool.totalAmount - pool.releasedAmount;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/bounty-card.tsx` around lines 131 - 145, The duplicated
lockedAmount computation (pool.status === "Fully Released" ? 0 :
pool.totalAmount - pool.releasedAmount) used when rendering <EscrowStatus>
should be extracted into a shared helper and used in both bounty-card.tsx and
escrow-detail-panel.tsx (or computed inside the EscrowStatus component); create
a utility like calculateLockedAmount(pool: EscrowPool) (or add a lockedAmount
prop computed inside EscrowStatus) and replace the inline expression in
bounty-card.tsx (and the duplicate in escrow-detail-panel.tsx) with a call to
that helper (or remove the prop and let EscrowStatus derive it from
pool/status/amounts).

88-90: Consider performance impact of fetching escrow data per card.

Calling useEscrowPool(bounty.id) in each BountyCard can trigger N parallel requests when rendering a bounty list. While React Query will cache and dedupe, the initial load may cause a waterfall of requests.

Consider:

  1. Prefetching pool data at the list level and passing it down
  2. Using a batch query hook that fetches multiple pools at once
  3. Accepting an optional pool prop to avoid fetching when parent already has the data
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/bounty-card.tsx` around lines 88 - 90, Calling
useEscrowPool(bounty.id) inside each BountyCard causes many parallel fetches on
list render; refactor so the parent list fetches or batches escrow data and
BountyCard optionally consumes it: add an optional prop pool to BountyCard (or
create a batch hook useEscrowPools(ids)) and update callers to prefetch/cache
pools at the list level (or call useEscrowPools once) then pass pool into
BountyCard to avoid per-card network requests and rely on React Query only for
misses.
components/bounty/fee-calculator.tsx (1)

49-57: Consider validating for non-negative amounts.

The min="0" attribute doesn't prevent negative values entered via keyboard. While the service likely handles this, consider clamping the value:

♻️ Suggested validation
- const amount = Number(debouncedAmountStr) || 0;
+ const amount = Math.max(0, Number(debouncedAmountStr) || 0);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/fee-calculator.tsx` around lines 49 - 57, The amount input
allows negative numbers via keyboard despite min="0"; update the onChange/onBlur
handling in the FeeCalculator component (where Input, amountInput, and
setAmountInput are used) to parse the incoming value to a number, clamp it to a
non-negative value (e.g., Math.max(0, parsedValue)), and then set the state
(convert back to string if you keep amountInput as a string); alternatively
enforce the clamp on blur/submit to ensure negative values are not stored or
sent to the fee calculation logic.
components/bounty/escrow-status.tsx (1)

50-58: Consider defensive fallback for unknown status.

While TypeScript ensures status matches EscrowPoolStatus, a defensive fallback prevents runtime crashes if the type and config ever drift:

♻️ Optional defensive pattern
- const { icon: Icon, colorClass, bgClass, variant } = config[status];
+ const statusConfig = config[status] ?? config["Escrowed"];
+ const { icon: Icon, colorClass, bgClass, variant } = statusConfig;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/escrow-status.tsx` around lines 50 - 58, The component
EscrowStatus currently indexes config with status directly (const { icon: Icon,
colorClass, bgClass, variant } = config[status]) which can throw if config lacks
that key at runtime; change this to a defensive lookup: resolve a fallback entry
when config[status] is undefined and destructure from that fallback (e.g.,
fallbackIcon/fallback classes or a defaultConfig object), ensuring you still
reference EscrowStatus, status, config and EscrowStatusProps so unknown or
drifting statuses render a safe default UI instead of crashing.
components/bounty-detail/bounty-detail-client.tsx (1)

77-85: Verify whether FeeCalculator should use bounty context.

The FeeCalculator is rendered as a standalone tool without any props from the current bounty (amount, type). This appears intentional as an estimator, but verify if it would be more useful to pre-populate values from the bounty being viewed.

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

In `@components/bounty-detail/bounty-detail-client.tsx` around lines 77 - 85, The
FeeCalculator is currently standalone and likely should be pre-populated from
the viewed bounty; update the rendering to pass the current bounty into
FeeCalculator (e.g., pass bounty or specific fields like amount and type from
the bounty variable used in this file) and then modify the FeeCalculator
component to accept an optional prop (bounty or { amount, type }) and use it to
initialize its inputs/state when provided; also update the FeeCalculator prop
types/interface and any related tests to handle both standalone and
pre-populated usage so existing estimator behavior remains backward compatible.
components/bounty/escrow-detail-panel.tsx (1)

162-172: Hardcoded testnet URL should be configurable.

The Stellar Explorer link points to a testnet URL with a mock account pattern. This should be environment-configurable for production deployment:

♻️ Suggested approach
// Use environment variable or config
const STELLAR_EXPLORER_BASE = process.env.NEXT_PUBLIC_STELLAR_EXPLORER_URL 
  ?? "https://stellar.expert/explorer/testnet";

// In component:
href={`${STELLAR_EXPLORER_BASE}/account/${pool.poolId}`}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/bounty/escrow-detail-panel.tsx` around lines 162 - 172, Replace
the hardcoded testnet Explorer link with a configurable base URL: add a constant
(e.g. STELLAR_EXPLORER_BASE) that reads
process.env.NEXT_PUBLIC_STELLAR_EXPLORER_URL with a fallback to the current
testnet URL, then update the anchor href in escrow-detail-panel to use
`${STELLAR_EXPLORER_BASE}/account/${poolId}` (remove the hardcoded
"escrow_mock_" prefix) while keeping the target, rel, and ExternalLink usage
intact so production can point to a different Stellar Explorer.
lib/services/escrow.ts (1)

110-112: Minor: Potential floating-point rounding inconsistency in fee calculation.

Rounding platformFee and insuranceFee separately before computing netPayout can cause grossAmount ≠ platformFee + insuranceFee + netPayout due to accumulated rounding differences. For a mock service this is acceptable, but when migrating to production, consider rounding only at the final step or calculating netPayout as a residual.

♻️ Suggested approach for production
-    const platformFee = Number((amount * platformRate).toFixed(2));
-    const insuranceFee = Number((amount * insuranceRate).toFixed(2));
-    const netPayout = Number((amount - platformFee - insuranceFee).toFixed(2));
+    const platformFee = Math.round(amount * platformRate * 100) / 100;
+    const insuranceFee = Math.round(amount * insuranceRate * 100) / 100;
+    // Derive netPayout as residual to ensure amounts sum correctly
+    const netPayout = Math.round((amount - platformFee - insuranceFee) * 100) / 100;

Using Math.round with multiplication/division is more explicit about the rounding behavior than toFixed + Number conversion.

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

In `@lib/services/escrow.ts` around lines 110 - 112, The current code rounds
platformFee and insuranceFee individually using toFixed before computing
netPayout, which can create rounding drift; instead compute raw fees from amount
* platformRate and amount * insuranceRate, round the final values using
Math.round(amountInCents / 100) style (or round fees then compute netPayout as
the residual: netPayout = amount - roundedPlatformFee - roundedInsuranceFee) so
grossAmount always equals platformFee + insuranceFee + netPayout; update the
calculations around platformFee, insuranceFee, and netPayout (referencing
amount, platformRate, insuranceRate, platformFee, insuranceFee, netPayout) to
use consistent rounding (prefer integer cents math) as described.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@components/bounty-detail/bounty-detail-client.tsx`:
- Around line 77-85: The FeeCalculator is currently standalone and likely should
be pre-populated from the viewed bounty; update the rendering to pass the
current bounty into FeeCalculator (e.g., pass bounty or specific fields like
amount and type from the bounty variable used in this file) and then modify the
FeeCalculator component to accept an optional prop (bounty or { amount, type })
and use it to initialize its inputs/state when provided; also update the
FeeCalculator prop types/interface and any related tests to handle both
standalone and pre-populated usage so existing estimator behavior remains
backward compatible.

In `@components/bounty/bounty-card.tsx`:
- Around line 131-145: The duplicated lockedAmount computation (pool.status ===
"Fully Released" ? 0 : pool.totalAmount - pool.releasedAmount) used when
rendering <EscrowStatus> should be extracted into a shared helper and used in
both bounty-card.tsx and escrow-detail-panel.tsx (or computed inside the
EscrowStatus component); create a utility like calculateLockedAmount(pool:
EscrowPool) (or add a lockedAmount prop computed inside EscrowStatus) and
replace the inline expression in bounty-card.tsx (and the duplicate in
escrow-detail-panel.tsx) with a call to that helper (or remove the prop and let
EscrowStatus derive it from pool/status/amounts).
- Around line 88-90: Calling useEscrowPool(bounty.id) inside each BountyCard
causes many parallel fetches on list render; refactor so the parent list fetches
or batches escrow data and BountyCard optionally consumes it: add an optional
prop pool to BountyCard (or create a batch hook useEscrowPools(ids)) and update
callers to prefetch/cache pools at the list level (or call useEscrowPools once)
then pass pool into BountyCard to avoid per-card network requests and rely on
React Query only for misses.

In `@components/bounty/escrow-detail-panel.tsx`:
- Around line 162-172: Replace the hardcoded testnet Explorer link with a
configurable base URL: add a constant (e.g. STELLAR_EXPLORER_BASE) that reads
process.env.NEXT_PUBLIC_STELLAR_EXPLORER_URL with a fallback to the current
testnet URL, then update the anchor href in escrow-detail-panel to use
`${STELLAR_EXPLORER_BASE}/account/${poolId}` (remove the hardcoded
"escrow_mock_" prefix) while keeping the target, rel, and ExternalLink usage
intact so production can point to a different Stellar Explorer.

In `@components/bounty/escrow-status.tsx`:
- Around line 50-58: The component EscrowStatus currently indexes config with
status directly (const { icon: Icon, colorClass, bgClass, variant } =
config[status]) which can throw if config lacks that key at runtime; change this
to a defensive lookup: resolve a fallback entry when config[status] is undefined
and destructure from that fallback (e.g., fallbackIcon/fallback classes or a
defaultConfig object), ensuring you still reference EscrowStatus, status, config
and EscrowStatusProps so unknown or drifting statuses render a safe default UI
instead of crashing.

In `@components/bounty/fee-calculator.tsx`:
- Around line 49-57: The amount input allows negative numbers via keyboard
despite min="0"; update the onChange/onBlur handling in the FeeCalculator
component (where Input, amountInput, and setAmountInput are used) to parse the
incoming value to a number, clamp it to a non-negative value (e.g., Math.max(0,
parsedValue)), and then set the state (convert back to string if you keep
amountInput as a string); alternatively enforce the clamp on blur/submit to
ensure negative values are not stored or sent to the fee calculation logic.

In `@hooks/__tests__/use-escrow.test.tsx`:
- Around line 10-14: The current test uses a shared module-level QueryClient
which can cause cross-test pollution; change to create a fresh QueryClient per
test by moving the QueryClient instantiation into the test setup (e.g., inside a
beforeEach or by making the wrapper a factory) so that QueryClient is re-created
for each test, update the wrapper to accept or create that per-test QueryClient,
and ensure QueryClientProvider receives the fresh instance in each test run
(refer to QueryClient, wrapper, and QueryClientProvider to locate the change).
- Around line 78-114: Add a new test case in hooks/__tests__/use-escrow.test.tsx
to cover the MILESTONE_BASED path of useFeeCalculation: call renderHook(() =>
useFeeCalculation(1000, "MILESTONE_BASED"), { wrapper }), wait for
result.current.isSuccess to be true, then assert result.current.data equals {
grossAmount: 1000, platformFee: 60, insuranceFee: 10, netPayout: 930 }; place
this test alongside the existing FIXED_PRICE and COMPETITION tests to ensure all
bounty types are covered.

In `@lib/services/escrow.ts`:
- Around line 110-112: The current code rounds platformFee and insuranceFee
individually using toFixed before computing netPayout, which can create rounding
drift; instead compute raw fees from amount * platformRate and amount *
insuranceRate, round the final values using Math.round(amountInCents / 100)
style (or round fees then compute netPayout as the residual: netPayout = amount
- roundedPlatformFee - roundedInsuranceFee) so grossAmount always equals
platformFee + insuranceFee + netPayout; update the calculations around
platformFee, insuranceFee, and netPayout (referencing amount, platformRate,
insuranceRate, platformFee, insuranceFee, netPayout) to use consistent rounding
(prefer integer cents math) as described.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9a33da9d-7db9-41ce-9b4e-e9bdf0e86a07

📥 Commits

Reviewing files that changed from the base of the PR and between 3aad250 and 5a424ba.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • components/bounty-detail/bounty-detail-client.tsx
  • components/bounty/bounty-card.tsx
  • components/bounty/escrow-detail-panel.tsx
  • components/bounty/escrow-status.tsx
  • components/bounty/fee-calculator.tsx
  • hooks/__tests__/use-escrow.test.tsx
  • hooks/use-escrow.ts
  • jest.config.js
  • lib/services/escrow.ts
  • types/escrow.ts

@drips-wave

drips-wave Bot commented Mar 27, 2026

Copy link
Copy Markdown

@Franklivania Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Benjtalkshow Benjtalkshow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

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.

Integrate Escrow UI for Bounty Fund Management

2 participants