Skip to content

feat: withdraw and liquidity adapter#276

Merged
antoncoding merged 3 commits intomasterfrom
fix/withdraw-and-allocator
Jan 5, 2026
Merged

feat: withdraw and liquidity adapter#276
antoncoding merged 3 commits intomasterfrom
fix/withdraw-and-allocator

Conversation

@antoncoding
Copy link
Owner

@antoncoding antoncoding commented Jan 5, 2026

Summary by CodeRabbit

  • New Features

    • Modal-driven deposit and withdrawal flows for vaults with new centralized vault deposit/withdraw modals and registry entries.
    • Dedicated deposit and withdraw buttons in the vault detail view to open those modals.
    • Added market-specific withdrawal API to support withdrawals targeting a specific market (with allocator setup).
  • UI Improvements

    • Owner highlighting in agent lists and minor table/notification formatting refinements.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Contributor

vercel bot commented Jan 5, 2026

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

Project Deployment Review Updated (UTC)
monarch Ready Ready Preview, Comment Jan 5, 2026 5:25am

@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

📝 Walkthrough

Walkthrough

Refactored vault deposit/withdraw to use the centralized modal system; added a VaultWithdrawModal and renamed VaultDepositModal; extended modal registry and modal store types; added market-specific withdrawal support (withdrawFromMarket) and an encoder for market params; updated UI to open modals and refresh on success.

Changes

Cohort / File(s) Summary
Vault UI
src/features/autovault/components/vault-detail/total-supply-card.tsx
Replaced local modal state with useModal openings for vaultDeposit/vaultWithdraw; added separate deposit/withdraw icon buttons and a handleSuccess refetch callback.
Vault Modals
src/modals/vault/vault-deposit-modal.tsx, src/modals/vault/vault-withdraw-modal.tsx
Renamed deposit modal/type to VaultDepositModal/VaultDepositModalProps (added onOpenChange, SupportedNetworks chainId). Added new VaultWithdrawModal with market selection, validation, allocator handling, and withdrawFromMarket invocation.
Modal Registry & Store
src/modals/registry.tsx, src/stores/useModalStore.ts
Registered lazy-loaded vaultDeposit and vaultWithdraw modals; extended ModalProps with vaultDeposit and vaultWithdraw shapes (vault metadata, asset info, chainId, optional onSuccess).
Vault Hook & Utils
src/hooks/useVaultV2.ts, src/utils/morpho.ts
Added withdrawFromMarket to useVaultV2 (optional allocator setup, setLiquidityAdapterAndData, multicall withdraw); simplified withdraw signature to derive account from hook; added encodeMarketParams(market: Market) to encode market params for calls.
Misc UI & Small Changes
src/features/autovault/components/vault-detail/settings/AgentListItem.tsx, src/features/autovault/components/vault-detail/settings/AgentsTab.tsx, src/components/layout/notification-banner.tsx, src/features/markets/components/table/markets-table-actions.tsx
AgentListItem: optional ownerAddress prop, conditional Avatar/Owner label; AgentsTab: pass ownerAddress to AgentListItem usages. NotificationBanner & MarketsTableActions: minor JSX/formatting tweaks.

Sequence Diagram

sequenceDiagram
    participant User
    participant Card as TotalSupplyCard (UI)
    participant ModalSystem as Modal Registry / useModal
    participant Modal as Vault Deposit/Withdraw Modal
    participant Hook as useVaultV2
    participant Contract as Vault / Multicall

    rect rgb(240,248,255)
    User->>Card: Click Deposit / Withdraw
    Card->>ModalSystem: openModal(vaultDeposit / vaultWithdraw, payload)
    ModalSystem->>Modal: mount with payload
    end

    User->>Modal: enter amount & confirm
    Modal->>Hook: deposit(...) / withdrawFromMarket(market, amount)
    Hook->>Hook: build steps (optional allocator setup, encodeMarketParams)
    Hook->>Contract: submit multicall tx
    Contract-->>Hook: tx confirmed
    Hook-->>Modal: onSuccess()
    Modal-->>ModalSystem: close modal
    ModalSystem-->>Card: invoke onSuccess callback
    Card->>Card: refetch vault data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.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 accurately reflects the main changes: withdraw functionality and liquidity adapter integration across multiple files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/withdraw-and-allocator

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 added feature request Specific feature ready to be implemented ui User interface labels Jan 5, 2026
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 (5)
src/hooks/useVaultV2.ts (1)

220-234: Variable shadowing: setAllocatorTx and submitSetAllocatorTx are redeclared in Step 6.2.

Both variables are declared again at lines 237 and 243. This works due to block scoping but hurts readability. Consider renaming to setOwnerAllocatorTx/submitSetOwnerAllocatorTx for clarity.

Suggested rename
-      // Step 6.1 Set user as allocator (for withdrawal / setting Withdrawal Data) 
-      const setAllocatorTx = encodeFunctionData({
+      // Step 6.1 Set user as allocator (for withdrawal / setting Withdrawal Data) 
+      const setOwnerAllocatorTx = encodeFunctionData({
         abi: vaultv2Abi,
         functionName: 'setIsAllocator',
         args: [account, true],
       });

-      const submitSetAllocatorTx = encodeFunctionData({
+      const submitSetOwnerAllocatorTx = encodeFunctionData({
         abi: vaultv2Abi,
         functionName: 'submit',
-        args: [setAllocatorTx],
+        args: [setOwnerAllocatorTx],
       });

-      txs.push(submitSetAllocatorTx, setAllocatorTx);
+      txs.push(submitSetOwnerAllocatorTx, setOwnerAllocatorTx);
src/features/autovault/components/vault-detail/total-supply-card.tsx (1)

96-108: assetAddress is passed but unused by VaultWithdrawModal.

The modal component doesn't destructure or use assetAddress (see line 31-39 in vault-withdraw-modal.tsx). Either remove it from the props here or add usage in the modal.

src/modals/vault/vault-deposit-modal.tsx (1)

24-36: onOpenChange is declared in props but never used.

The prop exists in VaultDepositModalProps (line 24) but isn't destructured or utilized. The Modal's onOpenChange at line 76 uses an inline handler instead. Remove from props or wire it up.

Option 1: Remove unused prop
 type VaultDepositModalProps = {
   vaultAddress: Address;
   vaultName: string;
   assetAddress: Address;
   assetSymbol: string;
   assetDecimals: number;
   chainId: SupportedNetworks;
   onSuccess?: () => void;
   onClose: () => void;
-  onOpenChange: (open: boolean) => void;
 };
src/modals/vault/vault-withdraw-modal.tsx (2)

19-39: Unused props: assetAddress and onOpenChange.

Both are declared in the type but not destructured. Either remove them or add usage:

  • assetAddress could be used for the header icon (like deposit modal does with TokenIcon).
  • onOpenChange appears to be dead code.
Use assetAddress for token icon in header
 export function VaultWithdrawModal({
   vaultAddress,
   vaultName,
+  assetAddress,
   assetSymbol,
   assetDecimals,
   chainId,
   onSuccess,
   onClose,
 }: VaultWithdrawModalProps): JSX.Element {

Then in the header:

         <ModalHeader
           title={`Withdraw ${assetSymbol}`}
           description={`Withdraw from ${vaultName}`}
-          mainIcon={<FiArrowUpRight className="h-5 w-5" />}
+          mainIcon={
+            <TokenIcon
+              address={assetAddress}
+              chainId={chainId}
+              symbol={assetSymbol}
+              width={24}
+              height={24}
+            />
+          }
           onClose={onClose}
         />

92-105: Consider adding try-catch for explicit error handling.

withdrawFromMarket throws on non-rejection errors. While useVaultV2 shows toasts internally, wrapping in try-catch would allow local error state or logging if needed.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f46e48 and d7cf0dd.

📒 Files selected for processing (8)
  • src/features/autovault/components/vault-detail/total-supply-card.tsx
  • src/hooks/useVaultV2.ts
  • src/modals/registry.tsx
  • src/modals/vault/vault-deposit-modal.tsx
  • src/modals/vault/vault-deposit-process-modal.tsx
  • src/modals/vault/vault-withdraw-modal.tsx
  • src/stores/useModalStore.ts
  • src/utils/morpho.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-11-25T09:39:42.148Z
Learnt from: antoncoding
Repo: antoncoding/monarch PR: 87
File: app/home/HomePage.tsx:17-39
Timestamp: 2024-11-25T09:39:42.148Z
Learning: In `app/home/HomePage.tsx`, the `useEffect` hook depends on `[showCustomized]` because changing `showCustomized` triggers updates to the yield and risk terms.

Applied to files:

  • src/features/autovault/components/vault-detail/total-supply-card.tsx
📚 Learning: 2024-10-23T16:17:02.841Z
Learnt from: antoncoding
Repo: antoncoding/monarch PR: 77
File: src/graphql/queries.ts:168-193
Timestamp: 2024-10-23T16:17:02.841Z
Learning: In `src/graphql/queries.ts`, handling only `MarketTransferTransactionData` is intentional at this time.

Applied to files:

  • src/hooks/useVaultV2.ts
📚 Learning: 2025-12-09T10:06:39.848Z
Learnt from: antoncoding
Repo: antoncoding/monarch PR: 231
File: src/hooks/useDeployMorphoMarketV1Adapter.ts:3-3
Timestamp: 2025-12-09T10:06:39.848Z
Learning: In Wagmi v3, useConnection is the correct hook to obtain the connected wallet address, chainId, and connection status (isConnected). This replaces the useAccount hook from Wagmi v2. In your code under src/hooks, use: const { address, chainId, isConnected } = useConnection() from 'wagmi' to access the connected wallet information.

Applied to files:

  • src/hooks/useVaultV2.ts
🧬 Code graph analysis (4)
src/utils/morpho.ts (1)
src/utils/types.ts (1)
  • Market (293-345)
src/features/autovault/components/vault-detail/total-supply-card.tsx (2)
src/hooks/useModal.ts (1)
  • useModal (25-90)
src/components/shared/token-icon.tsx (1)
  • TokenIcon (23-95)
src/hooks/useVaultV2.ts (3)
src/abis/vaultv2.ts (1)
  • vaultv2Abi (3-3)
src/utils/types.ts (1)
  • Market (293-345)
src/utils/morpho.ts (1)
  • encodeMarketParams (247-266)
src/modals/registry.tsx (2)
src/modals/vault/vault-deposit-modal.tsx (1)
  • VaultDepositModal (27-148)
src/modals/vault/vault-withdraw-modal.tsx (1)
  • VaultWithdrawModal (31-217)
🔇 Additional comments (9)
src/utils/morpho.ts (1)

247-266: LGTM!

The encoding logic is correct and matches the tuple structure used elsewhere in the file (e.g., getMarketCapId). The field mappings align with the Market type definition.

src/modals/registry.tsx (1)

43-45: LGTM!

Registry entries follow the established pattern. Lazy loading is correctly configured for both vault modals.

Also applies to: 64-65

src/hooks/useVaultV2.ts (2)

539-571: LGTM!

Clean simplification. The withdraw function now consistently derives the sender from the hook's account, which is the expected behavior for basic withdrawals.


573-645: LGTM!

The withdrawFromMarket flow correctly sequences:

  1. Optional allocator setup (submit + execute)
  2. Set liquidity adapter with encoded market params
  3. Execute withdraw

All wrapped in a multicall for atomicity. Error handling is consistent with other methods in the hook.

src/features/autovault/components/vault-detail/total-supply-card.tsx (1)

78-94: LGTM!

Modal-driven approach is cleaner than local state. Callbacks are properly memoized with correct dependencies. The refetch-on-success pattern ensures data consistency.

Also applies to: 113-140

src/stores/useModalStore.ts (1)

55-75: LGTM!

Props are correctly typed. Both vault operations share the same shape, which is fine for now. If more vault modals are added, consider extracting a shared VaultModalBaseProps type.

src/modals/vault/vault-deposit-modal.tsx (1)

149-150: LGTM!

Default export enables the lazy import pattern used in the registry.

src/modals/vault/vault-withdraw-modal.tsx (2)

63-82: LGTM!

Allocator check and max withdrawable calculation are correct. The min(allocation, liquidity) logic prevents over-withdrawal.


109-216: LGTM!

UI handles loading states, empty states, and market selection flow well. The disabled logic on the button is comprehensive.

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 (1)
src/features/autovault/components/vault-detail/settings/AgentsTab.tsx (1)

131-134: Type assertion doesn't prevent undefined.

The pattern ownerAddress={owner as Address} appears in multiple places. Since owner can be undefined (from vaultData?.owner), the type assertion doesn't provide runtime safety. While AgentListItem correctly handles optional ownerAddress, the assertion misleads readers about type guarantees.

🔎 Consider removing the type assertion

The prop is already typed as optional in AgentListItem, so you can pass it directly:

-  ownerAddress={owner as Address}
+  ownerAddress={owner}

This makes the optionality explicit at the call site.

Also applies to: 165-168, 212-215

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7cf0dd and 74c2aea.

📒 Files selected for processing (7)
  • src/components/layout/notification-banner.tsx
  • src/features/autovault/components/vault-detail/settings/AgentListItem.tsx
  • src/features/autovault/components/vault-detail/settings/AgentsTab.tsx
  • src/features/markets/components/table/markets-table-actions.tsx
  • src/hooks/useVaultV2.ts
  • src/modals/vault/vault-withdraw-modal.tsx
  • src/utils/morpho.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/morpho.ts
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2024-10-12T09:23:16.495Z
Learnt from: antoncoding
Repo: antoncoding/monarch PR: 63
File: app/markets/components/MarketRowDetail.tsx:49-52
Timestamp: 2024-10-12T09:23:16.495Z
Learning: When rendering oracle feeds in `ExpandedMarketDetail` (`app/markets/components/MarketRowDetail.tsx`), prefer explicit rendering over iterating keys when dealing with a small number of feeds.

Applied to files:

  • src/features/markets/components/table/markets-table-actions.tsx
📚 Learning: 2024-10-23T16:17:02.841Z
Learnt from: antoncoding
Repo: antoncoding/monarch PR: 77
File: src/graphql/queries.ts:168-193
Timestamp: 2024-10-23T16:17:02.841Z
Learning: In `src/graphql/queries.ts`, handling only `MarketTransferTransactionData` is intentional at this time.

Applied to files:

  • src/hooks/useVaultV2.ts
📚 Learning: 2025-12-09T10:06:39.848Z
Learnt from: antoncoding
Repo: antoncoding/monarch PR: 231
File: src/hooks/useDeployMorphoMarketV1Adapter.ts:3-3
Timestamp: 2025-12-09T10:06:39.848Z
Learning: In Wagmi v3, useConnection is the correct hook to obtain the connected wallet address, chainId, and connection status (isConnected). This replaces the useAccount hook from Wagmi v2. In your code under src/hooks, use: const { address, chainId, isConnected } = useConnection() from 'wagmi' to access the connected wallet information.

Applied to files:

  • src/hooks/useVaultV2.ts
🧬 Code graph analysis (3)
src/features/autovault/components/vault-detail/settings/AgentListItem.tsx (3)
src/utils/monarch-agent.ts (1)
  • findAgent (19-21)
src/components/Avatar/Avatar.tsx (1)
  • Avatar (12-43)
src/components/shared/agent-icon.tsx (1)
  • AgentIcon (14-93)
src/features/autovault/components/vault-detail/settings/AgentsTab.tsx (1)
src/features/autovault/components/vault-detail/settings/AgentListItem.tsx (1)
  • AgentListItem (12-38)
src/hooks/useVaultV2.ts (2)
src/utils/types.ts (1)
  • Market (293-345)
src/utils/morpho.ts (1)
  • encodeMarketParams (247-271)
🔇 Additional comments (10)
src/components/layout/notification-banner.tsx (1)

35-38: LGTM!

Formatting-only changes. No functional impact.

src/features/markets/components/table/markets-table-actions.tsx (2)

4-4: LGTM: Import formatting.

Consistent quote style with the rest of the file.


69-73: LGTM: Multiline ternary.

Reformatting improves readability with no logic change.

src/features/autovault/components/vault-detail/settings/AgentListItem.tsx (1)

1-38: LGTM!

The conditional rendering logic correctly handles the optional ownerAddress prop and distinguishes between owner and agent displays.

src/hooks/useVaultV2.ts (3)

220-233: Verify: Owner automatically becomes allocator on initialization.

Step 6.1 now grants allocator permissions to the vault owner during initialization. This enables the withdrawal flow but changes the default permissions model for all new vaults.

Confirm this is the intended behavior for all vault deployments.


539-571: Approve withdraw signature simplification.

The function correctly simplifies the API by using the hook's internal account for the contract call, while maintaining the proper three-argument pattern for the vault's withdraw function.


578-639: The submit + direct call pattern is intentional and confirmed across the codebase.

The withdrawFromMarket function uses the submit+execute pattern within a single multicall. This identical pattern appears in completeInitialization and setAllocator, confirming it's a deliberate approach. The setIsAllocator function has no timelock—it executes immediately—so the direct call (line 597) takes effect within the same transaction, allowing the allocator permission to be available for subsequent calls like setLiquidityAdapterAndData.

src/modals/vault/vault-withdraw-modal.tsx (3)

75-82: LGTM - defensive null handling.

The optional chaining on market.state?.liquidityAssets provides safe fallback even though the Market type defines state as required. If liquidity data is missing, maxWithdrawable becomes 0n, preventing invalid withdrawals.


92-99: Allocator setup logic handles legacy vaults.

The check for needsAllocatorSetup (line 96) correctly handles vaults initialized before the owner-as-allocator change, while avoiding redundant setup for new vaults.


120-202: Well-structured progressive disclosure.

The UI flow properly gates data display behind loading states and progressively reveals the amount input only after market selection. The button disable logic prevents invalid submissions.

@antoncoding antoncoding merged commit 73f7dd4 into master Jan 5, 2026
4 checks passed
@antoncoding antoncoding deleted the fix/withdraw-and-allocator branch January 5, 2026 05:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request Specific feature ready to be implemented ui User interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant