Skip to content

257: Extend refinance to migrate collateral#575

Merged
Seranged merged 14 commits into
developmentfrom
euler-lite-migration
Jul 1, 2026
Merged

257: Extend refinance to migrate collateral#575
Seranged merged 14 commits into
developmentfrom
euler-lite-migration

Conversation

@dglowinski

@dglowinski dglowinski commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Extend refinance so debt, collateral, or both can migrate from one form.
  • Surface incompatible vault choices as grouped modal sections and guard incompatible or
    unhealthy execution.
  • Route standalone swap-collateral entry points into refinance and remove the old standalone
    flow.

Changes

  • Compose collateral and debt migration plans through a shared refinance planner, including
    same-asset and swap-backed paths.
  • Allow CoW only for collateral-only refinance swaps and block mixed debt changes with
    explicit copy.
  • Update transaction review step decoding and execution scenarios for the combined refinance
    flow.
  • Bump @eulerxyz/euler-v2-sdk to 1.0.5 for the migration planners used here.

Summary by CodeRabbit

  • New Features

    • Unified refinance flow now supports changing collateral, debt, or both in a single transaction.
    • Added refinance operation support across the review and confirmation experience.
  • Changes

    • Reworked collateral/vault selection to show grouped compatible vs migration-required options with compatibility warnings.
    • Replaced the previous collateral-swap page with refinance routing and updated per-leg risk/impact details.
  • Documentation

    • Updated geo-blocking guidance for enabled-vault fallback routes.
  • Bug Fixes / Quality

    • Improved transaction review step labeling and refinance execution error messaging; updated and expanded related tests.

@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 16, 2026 14:44 Destroyed
@railway-app

railway-app Bot commented Jun 16, 2026

Copy link
Copy Markdown

🚅 Deployed to the euler-lite-pr-575 environment in euler-lite

Service Status Web Updated (UTC)
dev-build ✅ Success (View Logs) Web Jul 1, 2026 at 9:29 am

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces the standalone collateral-swap page with a unified refinance flow at pages/position/[number]/borrow/swap.vue that can migrate collateral vault, debt vault, or both in one transaction. Introduces planRefinancePosition in useEulerTx, extends collateral/debt option composables with all-vaults discovery, adds grouped UI for collateral compatibility warnings, enhances step decoding for multi-leg swap display, and updates batch overlay handling to suppress removed borrow positions when they are refinance replacements.

Changes

Unified Refinance Flow

Layer / File(s) Summary
CollateralOption type and barrel reorders
types/collateral-option.ts, entities/cowswap/index.ts, composables/cowswap/index.ts
CollateralOption gains optional compatibilityWarning?: { title, message } field; named re-exports in cowswap entity and composable barrels are reordered.
PlanRefinancePositionInput and planRefinancePosition planner
composables/useEulerTx.ts
Exports PlanRefinancePositionInput with optional collateral/debt sub-inputs and preloaded account; implements planRefinancePosition that validates inputs, builds up to two sub-plans (swap vs same-asset migration per side), merges them via sdk.executionService.mergePlans, and wires the function into useEulerTx return.
useSwapCollateralOptions: filtering and allCollateralOptions
composables/useSwapCollateralOptions.ts
Adds filterTargetCollateralCandidates (address dedup, OP_SKIM check, supply-cap boundary); computes baseCollateralCandidates and allCollateralVaults; introduces buildCollateralOptionForVault shared builder; wires both collateralOptions and allCollateralOptions reactive maps.
useSwapDebtOptions: allBorrowVaults and allBorrowOptions
composables/useSwapDebtOptions.ts
Widens collateralVault param to EVault | SecuritizeCollateralVault; adds allBorrowVaults (broad eligibility) and refactors borrowVaults (collateral-compatible subset via borrowLTV > 0); introduces buildBorrowOption helper; builds allBorrowOptions reactive map and exposes both in hook return.
ChooseCollateralModal and AssetInput: grouped compatible/incompatible rendering
components/entities/vault/ChooseCollateralModal.vue, components/entities/asset/AssetInput.vue
Introduces FilteredCollateralOption and GroupedRow types in modal, splits options into compatible vs incompatible buckets using compatibilityWarning, builds groupedRows with sticky section headers ("Compatible" / "Requires migration"), and updates template to iterate grouped rows preserving original idx; updates AssetInput to emit selected option alongside index in change-collateral event.
VaultForm and OperationReviewModal form controls
components/entities/vault/form/VaultForm.vue, components/entities/operation/OperationReviewModal.vue
Adds optional backAlwaysFallback prop to VaultForm wired to BackButton; adds 'refinance' to OperationReviewModal type prop union with a dedicated "Refinance" button label.
stepDecoding: vault asset inference and Swap-to-repay sequencing
utils/stepDecoding.ts
Derives Withdraw asset from getVault(targetContract) instead of ctx.asset; adds inferredSwapInput/pendingSwapStep state to propagate and patch asset info across Withdraw/BorrowSwapVerify sequences; renames pending Swap label to "Swap to repay" when Verify max debt follows.
borrow/swap.vue rewritten as unified refinance page
pages/position/[number]/borrow/swap.vue
Replaces single-leg debt-swap logic with full refinance: dual vault selection, compatibility warning computation, parallel quote fetching, CoW collateral-only execution branch, portfolio-level risk metrics (ROE before/after, LTV, health score, liquidation buffer/price, borrow LTV), price-impact gating, OperationReviewModal(type='refinance') submission, and template with dual AssetInput rows, conditional route selectors, refinance alerts, and expanded info card with slippage tolerance editor.
position/index.vue routing, UI updates, and geo-blocking docs
pages/position/[number]/index.vue, docs/geo-blocking.md
Switches position to a mutable ref populated during load(); replaces computed disabled flags with inlined checks; reworks loadCollaterals to use on-chain accountLens with stale-request protection; retargets per-collateral row link from collateral/swap to borrow/swap?collateral=... with "Refinance" label; renames main refinance button to "Refinance"; updates liquidation/restriction banner visibility; removes collateral-swap route from geo-blocking docs.
useTxBatch: refinance-aware batch overlay handling
composables/useTxBatch.ts, tests/composables/useTxBatchModifiedKeys.test.ts
Adds buildRefinanceReplacementBorrowPositionKeys helper to extract refinance source debt/collateral vaults from batch entry review metadata; syncOverlay passes refinance replacement keys to getRemovedBorrowPositions to suppress removed borrow positions that are part of a refinance replacement; getRemovedBorrowPositions is exported with extended signature to filter removals using replacement keys; describeExecError becomes async to decode execution errors via getTxErrorMessage; test suite covers refinance replacement key filtering behavior.
Tests: merged plan ordering, step decoding, execution scenario cleanup
tests/golden/tx-plan-builders.test.ts, tests/utils/stepDecoding.test.ts, tests/execution/groups.json, tests/execution/scenarios.json
Adds golden test asserting enableCollateral precedes enableController in merged same-asset migration batch using new EVC ABI selectors; expands step-decoding test with weETH/WETH/cbBTC vault fixtures, extends batchItem helper with targetContract parameter, and adds combined refinance batch test verifying Withdraw/Swap/Borrow/Swap-to-repay labels and asset flow; removes swap-collateral group, scenario, and required transaction type.
CoW step index fixes, TypeScript strictness, and script improvements
composables/borrow/useMultiplyCowSwap.ts, composables/repay/useCollateralSwapRepay.ts, composables/repay/useRepaySwapCore.ts, composables/repay/useWalletRepay.ts, composables/repay/useWalletSwapRepay.ts, composables/useWallets.ts, scripts/execution-record.mjs
Removes post-increment side effects on idx/wIdx when pushing CoW sign/verify steps; changes amountNano/key declarations from initialized values to explicit typed declarations; attaches original error as cause in preflightSwapApi; changes body initial value to undefined in attachNetworkRecorder.

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant Page as borrow/swap.vue
  participant CollOpts as useSwapCollateralOptions
  participant DebtOpts as useSwapDebtOptions
  participant EulerTx as useEulerTx
  participant SDK as sdk.executionService
  participant Modal as OperationReviewModal

  User->>Page: navigate with ?collateral=0x...
  Page->>CollOpts: allCollateralOptions (includes compatibilityWarning)
  Page->>DebtOpts: allBorrowOptions (allBorrowVaults)
  User->>Page: select target collateral and/or debt vault
  Page->>Page: fetch collateral + debt swap quotes in parallel
  User->>Page: click "Review Refinance"
  Page->>EulerTx: planRefinancePosition({ collateral, debt })
  EulerTx->>SDK: planSwapCollateral or planMigrateSameAssetCollateral
  EulerTx->>SDK: planSwapDebt or planMigrateSameAssetDebt
  SDK-->>EulerTx: sub-plans
  EulerTx->>SDK: mergePlans([collateralPlan, debtPlan])
  SDK-->>EulerTx: TransactionPlan
  EulerTx-->>Page: TransactionPlan
  Page->>Modal: open (type='refinance')
  User->>Modal: confirm
  Modal->>EulerTx: execute TransactionPlan
  EulerTx-->>Page: success → refresh + redirect
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • euler-xyz/euler-lite#157: Both PRs touch components/entities/operation/OperationReviewModal.vue—the main PR adds 'refinance' to the modal's button-label selection logic, while the retrieved PR refactors the modal's confirm flow to support async onConfirm/loading—so the refinance review modal behavior is directly impacted by the same component changes.
  • euler-xyz/euler-lite#379: Both PRs modify components/entities/vault/ChooseCollateralModal.vue, with the main PR changing the selector to grouped rows and updating onSave to pass (index, selectedOption), while the retrieved PR refactors the same modal's row rendering/metrics (balance/APY) and extends collateral option typing (e.g., showBalance), making them code-level related.
  • euler-xyz/euler-lite#158: Both PRs modify components/entities/operation/OperationReviewModal.vue's confirmation behavior—main PR extends its type handling to support 'refinance', while the retrieved PR adjusts the modal's async onConfirm flow and close/submit UI gating.

Suggested reviewers

  • Seranged

Poem

🐇 Hoppity-hop, one page to rule them all,
Collateral and debt can migrate in one call!
planRefinancePosition merges the plans,
Compatible vaults grouped neatly in bands.
"Swap to repay" now decoded with care —
One unified refinance, a bunny's finest flair! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: extending refinance functionality to support collateral migration, which is the central objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch euler-lite-migration

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.

@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.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@composables/useSwapDebtOptions.ts`:
- Around line 75-79: The allBorrowOptions reactive map is missing
collateralVault from its dependency array, even though the buildBorrowOption
function uses collateralVault?.value?.address to build borrow options with the
correct collateral context. When the selected collateral changes but the other
dependencies (viewer, enableIntrinsicApy, enableRewardsApy) remain the same,
allBorrowOptions will not recompute and will continue to use stale APY data from
the previous collateral context. Add collateralVault to the dependencies array
passed to useReactiveMap at lines 75-79 to ensure the options recompute whenever
the collateral changes.

In `@tests/golden/tx-plan-builders.test.ts`:
- Around line 548-560: The test's findIndex calls for enableCollateralIndex and
enableControllerIndex currently only match on selector and targetContract,
without verifying the decoded function arguments. This means if another
enableCollateral or enableController call with different arguments is added
earlier in the batch, the test could still pass even if the refinance-specific
order regresses. Enhance both findIndex predicates to also decode the call
arguments and verify they match the expected values: the enableCollateralIndex
should match calls with arguments (ADDR.subAccount1, ADDR.vaultWeth), and the
enableControllerIndex should match calls with arguments (ADDR.subAccount1,
ADDR.vaultUsdt).

In `@tests/utils/stepDecoding.test.ts`:
- Around line 101-103: The batchItem function in
tests/utils/stepDecoding.test.ts at lines 101-103 currently defaults
targetContract to verifier, which is not representative of real swap batches and
can mask target-aware decoding regressions. Change the default parameter or
remove it entirely to use an explicit swapper address instead of verifier. Apply
the same fix at the sibling locations at lines 180-184 and 196-199 where
batchItem is used in multicall fixture items, ensuring each call explicitly
passes a swapper targetContract rather than relying on the verifier default.

In `@utils/stepDecoding.ts`:
- Around line 299-308: The Withdraw label block incorrectly resolves the vault
asset by calling getVaultAssetInfo with the decoded calldata, which extracts the
receiver address from the second parameter of the withdraw/redeem function.
Since the receiver could be another known vault, this causes the wrong vault's
asset to display. Instead of using getVaultAssetInfo which parses the calldata
to get vault info, directly obtain the vault asset information from
targetContract (which represents the source vault) to ensure the asset displayed
always belongs to the source vault where the withdrawal originates, regardless
of the receiver's identity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: euler-xyz/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 20749a81-4afc-47d6-acfa-aeea6efd4c90

📥 Commits

Reviewing files that changed from the base of the PR and between 945023a and 7983177.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (19)
  • components/entities/operation/OperationReviewModal.vue
  • components/entities/vault/ChooseCollateralModal.vue
  • composables/cowswap/index.ts
  • composables/useEulerTx.ts
  • composables/useSwapCollateralOptions.ts
  • composables/useSwapDebtOptions.ts
  • docs/geo-blocking.md
  • entities/cowswap/index.ts
  • package.json
  • pages/position/[number]/borrow/swap.vue
  • pages/position/[number]/collateral/swap.vue
  • pages/position/[number]/index.vue
  • tests/execution/groups.json
  • tests/execution/scenarios.json
  • tests/golden/tx-plan-builders.test.ts
  • tests/utils/stepDecoding.test.ts
  • types/collateral-option.ts
  • utils/sdk-snapshot-populate-stubs.ts
  • utils/stepDecoding.ts
💤 Files with no reviewable changes (3)
  • pages/position/[number]/collateral/swap.vue
  • tests/execution/scenarios.json
  • tests/execution/groups.json

Comment thread composables/useSwapDebtOptions.ts
Comment thread tests/golden/tx-plan-builders.test.ts Outdated
Comment thread tests/utils/stepDecoding.test.ts
Comment thread utils/stepDecoding.ts

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head 7983177.

Verdict: request changes. The combined refinance direction is sensible, but I found two blocking issues before the route is safe to merge.

Scope reviewed:

  • position refinance route consolidation
  • collateral/debt option compatibility handling
  • CoW collateral-only path gating
  • transaction review / step decoding updates
  • dependency bump and lockfile health

Validation performed:

  • inspected PR metadata, changed files, full diff, and surrounding route/composable context
  • checked existing Leonard comments/reviews: none to update or clean up
  • attempted clean install with the repo package manager path: npm ci --ignore-scripts fails because package.json and package-lock.json are not in sync
  • attempted non-lock install for local validation: blocked by the repo min-release-age=7 policy for @eulerxyz/euler-v2-sdk@1.0.5
  • checked PR status: Railway dev-build is failing

Smoke coverage: not run. Local install/bootstrap and the deployed preview build are both failing, so a browser/mobile smoke would not be meaningful yet. Screenshots were not posted for the same reason.

Findings are inline.

@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 16, 2026 15:05 Destroyed
@dglowinski dglowinski force-pushed the euler-lite-migration branch from 6bd7fc2 to 71a82ed Compare June 16, 2026 15:36
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 16, 2026 15:36 Destroyed

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head 71a82ed.

Verdict: comment — the two prior Leonard blockers I found are resolved on the current head, and I did not find a new blocking issue in this pass. I am not submitting approval because this review run was not explicitly scoped to approve.

What changed in the re-review:

  • The collateral-row Refinance link now preserves the selected collateral as ?collateral=..., and the consolidated refinance page reads route.query.collateral, selects the matching collateral position, and uses that position's assets/shares for the source side.
  • The lockfile/dependency bootstrap issue is resolved: npm ci --ignore-scripts now passes.
  • I deleted the two stale Leonard inline comments that referred to those resolved issues.

Validation performed:

  • npm ci --ignore-scripts — passed
  • npx nuxt prepare — passed
  • npm run test:run -- tests/utils/stepDecoding.test.ts tests/utils/portfolio-borrow-position.test.ts tests/utils/position-roe.test.ts tests/composables/useMultiplyForm.test.ts tests/utils/token-categories.test.ts — passed, 20 tests
  • npm run typecheck — passed
  • npm run build — passed
  • Headed Chromium under Xvfb, local production build:
    • desktop /position/1/borrow/swap?network=1 rendered the Refinance route
    • mobile /position/1/borrow/swap?network=1 rendered the Refinance route
    • desktop /position/1/borrow/swap?network=1&collateral=0x797DD80692c3b2dAdabCe8e30C07fDE5307D48a9 rendered the Refinance route with the selected-collateral query accepted
    • legacy /position/1/collateral/swap?network=1 renders Nuxt Page not found after the route deletion

Non-blocking caveat:

  • The old /position/:number/collateral/swap route is now gone. If preserving old deep links/bookmarks is a product requirement, add a redirect/shim to the consolidated refinance route; otherwise this is just an intentional route migration.

Smoke coverage: browser visual smoke and mobile smoke over the consolidated refinance route; no wallet/signing smoke. I did not post screenshots because the smoke did not reveal a visual delta/finding that screenshots would clarify.

@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 16, 2026 15:50 Destroyed
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 16, 2026 15:53 Destroyed

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head 6f38bf8.

Verdict: request changes. The core planner/test/build path is healthy, but I found one blocking URL-state bug in the consolidated refinance route, plus one smaller navigation regression to fix while touching this surface.

Scope reviewed:

  • consolidated debt/collateral refinance route and planner inputs
  • collateral/debt modal selection behavior and compatibility grouping
  • removed legacy collateral swap route behavior
  • latest incremental changes since my prior review (ChooseCollateralModal, VaultForm, route back behavior)

Validation performed:

  • inspected PR metadata, full diff, incremental diff since my prior review, prior Leonard reviews/comments, and surrounding route/composable context
  • npm run test:run -- tests/utils/stepDecoding.test.ts tests/utils/portfolio-borrow-position.test.ts tests/utils/position-roe.test.ts tests/composables/useMultiplyForm.test.ts tests/utils/token-categories.test.ts — passed, 20 tests
  • npm run typecheck — passed
  • npm run build — passed
  • headed Chromium under Xvfb against the PR preview:
    • desktop /position/1/borrow/swap?network=1&collateral=... rendered the Refinance route
    • desktop invalid collateral=... still rendered the Refinance route, matching the code-path concern below rather than rejecting/canonicalizing the stale source
    • mobile /position/1/borrow/swap?network=1 back navigation did not preserve the intended position route context
    • legacy /position/1/collateral/swap?network=1 renders Nuxt Page not found after route deletion

Smoke coverage: browser route smoke and mobile back-navigation smoke; no wallet/signing smoke. Screenshots were captured locally but not posted because they only show generic route shells / 404 and do not add useful public evidence beyond the route results above.

Comment lifecycle: no Leonard top-level issue comment existed to edit; no current Leonard inline review comments were present to delete. This review supersedes my prior comment on head 71a82ed.

Filter refinance debt and collateral selections so each choice leaves a compatible counterpart, and keep the modal grouping explicit when no compatible vaults remain.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 16, 2026 17:11 Destroyed

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head b326003.

Verdict: request changes. The focused test/type/build path is healthy and the preview route renders, but the current head still has one transaction-state blocker from the prior review, plus one new route-state/selection-risk issue worth fixing before merge.

Findings:

  • Blocking: the existing Leonard inline comment on sourceCollateralVault still applies. A stale/crafted collateral= query can resolve any known vault via the registry even when that vault is not one of the position's collaterals; the amount then falls back to the position's primary supplied amount. That can make the refinance planner/UI pair the wrong source vault with the wrong source balance.
  • Blocking/risk: modal selection is still index-based while options are produced asynchronously separately from the vault arrays. If the displayed options lag the vault arrays during a debt/collateral context change, a row click can select a different vault than the user clicked.
  • Navigation gap: the newly generated Refinance links/back fallbacks should preserve the active query state, especially network, and the deleted /position/:number/collateral/swap route has no compatibility redirect to the consolidated route.

Validation performed:

  • inspected PR metadata, body, changed files, full diff, incremental diff since the prior Leonard review, and surrounding route/composable/modal context
  • inspected existing Leonard issue comments, formal reviews, and inline comments for PR #575
  • npm run test:run -- tests/utils/stepDecoding.test.ts passed
  • npm run typecheck passed
  • npm run build passed
  • headed Chromium under Xvfb mobile route smoke: PR preview and development comparator both loaded /position/1/borrow/swap?network=1 with HTTP 200 and no page errors; PR rendered the consolidated Refinance route, development rendered the old Refinance debt route

Smoke coverage: mobile browser route smoke only; no wallet/signing coverage. Screenshots were not posted because the findings are URL/state and planner-input issues, not visual-only deltas.

Comment lifecycle: no Leonard top-level issue comment existed to edit; kept the still-current stale-collateral inline finding; updated the earlier Leonard back-navigation inline wording to remove stale smoke-specific phrasing; added fresh inline comments for the current head.

…ration

# Conflicts:
#	components/entities/operation/OperationReviewModal.vue
#	package-lock.json
#	package.json
#	pages/position/[number]/borrow/swap.vue
#	pages/position/[number]/collateral/swap.vue
#	pages/position/[number]/index.vue
#	utils/sdk-snapshot-populate-stubs.ts
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 22, 2026 12:47 Destroyed

@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.

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)
pages/position/[number]/index.vue (2)

725-729: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard collateralVault before seeding collateralItems.

When position exists but collateralVault.value is unavailable, this inserts an undefined vault into collateralItems; downstream row code dereferences item.vault.address before loadCollaterals() necessarily replaces it.

Proposed fix
     position.value = getPositionBySubAccountIndex(+positionIndex)
     if (position.value) {
-      collateralItems.value = [{
-        vault: collateralVault.value as EVault,
-        assets: position.value.supplied,
-      }]
+      const initialCollateralVault = collateralVault.value
+      collateralItems.value = initialCollateralVault
+        ? [{
+            vault: initialCollateralVault,
+            assets: position.value.supplied,
+          }]
+        : []
       // Load collaterals: always for multi-collateral, or when oracle failed (to get actual assets)
       if (positionCollateralAddresses.value.length > 1 || hasQueryFailure.value) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pages/position/`[number]/index.vue around lines 725 - 729, The code seeds
collateralItems with a collateralVault that may be undefined, causing downstream
code that dereferences item.vault.address to fail. Add a guard condition to
check that both position.value and collateralVault.value are available before
populating the collateralItems array; only set collateralItems when both
conditions are met to prevent undefined vault objects from being inserted into
the array.

600-618: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not turn account-lens failures into zero-balance collateral rows.

If getAccountInfo fails for a non-primary collateral, assets remains 0n and the row is still rendered as if the user has no balance. Fall back to the SDK collateral entry when available, or skip the row and surface the query failure instead of showing a synthetic zero.

Proposed fix
-          let assets = 0n
+          let assets: bigint | undefined

           try {
             const res = await client.readContract({
               address: lensAddress as Address,
               abi: eulerAccountLensABI as Abi,
@@
               authorizationList: undefined,
             }) as Record<string, Record<string, unknown>>
             assets = res.vaultAccountInfo.assets as bigint
           }
           catch {
             if (address === primaryAddress) {
               assets = position.value!.supplied
             }
+            else {
+              const matchedCollateral = position.value!.collaterals?.find(collateral => {
+                try {
+                  return getAddress(collateral.vaultAddress || collateral.vault?.address || '') === address
+                }
+                catch {
+                  return false
+                }
+              })
+              assets = matchedCollateral?.assets
+            }
           }

-          return vault ? { vault, assets } : null
+          return vault && assets !== undefined ? { vault, assets } : null
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pages/position/`[number]/index.vue around lines 600 - 618, The catch block in
the getAccountInfo call only handles the primary address case, leaving assets as
0n for non-primary addresses when the query fails, which renders a misleading
zero-balance row. Modify the catch block to also handle the non-primary address
case by either returning null to skip rendering the row entirely, or falling
back to an alternative data source (such as the SDK collateral entry) instead of
allowing assets to remain 0n. This ensures failed queries are not shown as zero
balances to the user.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@composables/repay/useCollateralSwapRepay.ts`:
- Line 326: The condition on Line 326 in the useCollateralSwapRepay composable
has inverted logic using AND when it should use OR. The current condition `if
(!isConnected.value && !isSpyMode.value) return false` only disables submit when
BOTH the user is disconnected AND not in spy mode, but it should disable submit
for any disconnected user regardless of spy mode. Change the AND operator to OR
so that the function returns false to disable submit whenever the user is not
connected, unless they are in spy mode which may have special permissions.

In `@composables/repay/useWalletRepay.ts`:
- Line 131: The condition at line 131 in the useWalletRepay composable has
inverted logic: when the wallet is not connected and not in spy mode
(!isConnected.value && !isSpyMode.value), the function currently returns false
which enables the submit action when it should be blocked. Change the return
value from false to true in this condition to properly disable the submit action
for disconnected non-spy users.

In `@composables/useWallets.ts`:
- Around line 165-166: In the useWallets composable, the condition at line 166
that checks if balanceAddress.value matches targetAddress applies getAddress()
normalization only to balanceAddress.value but not to targetAddress, causing the
comparison to fail when addresses differ only in case. Fix this by normalizing
both sides of the comparison: apply getAddress() to targetAddress as well, so
the condition compares getAddress(balanceAddress.value) ===
getAddress(targetAddress) to ensure consistent address normalization on both
sides of the equality check.

---

Outside diff comments:
In `@pages/position/`[number]/index.vue:
- Around line 725-729: The code seeds collateralItems with a collateralVault
that may be undefined, causing downstream code that dereferences
item.vault.address to fail. Add a guard condition to check that both
position.value and collateralVault.value are available before populating the
collateralItems array; only set collateralItems when both conditions are met to
prevent undefined vault objects from being inserted into the array.
- Around line 600-618: The catch block in the getAccountInfo call only handles
the primary address case, leaving assets as 0n for non-primary addresses when
the query fails, which renders a misleading zero-balance row. Modify the catch
block to also handle the non-primary address case by either returning null to
skip rendering the row entirely, or falling back to an alternative data source
(such as the SDK collateral entry) instead of allowing assets to remain 0n. This
ensures failed queries are not shown as zero balances to the user.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: euler-xyz/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 58904d9b-fbbb-42fa-85e4-9c7fd0c51594

📥 Commits

Reviewing files that changed from the base of the PR and between b326003 and 647654a.

📒 Files selected for processing (12)
  • components/entities/operation/OperationReviewModal.vue
  • composables/borrow/useMultiplyCowSwap.ts
  • composables/repay/useCollateralSwapRepay.ts
  • composables/repay/useRepaySwapCore.ts
  • composables/repay/useWalletRepay.ts
  • composables/repay/useWalletSwapRepay.ts
  • composables/useEulerTx.ts
  • composables/useWallets.ts
  • pages/position/[number]/borrow/swap.vue
  • pages/position/[number]/index.vue
  • scripts/execution-record.mjs
  • utils/stepDecoding.ts
✅ Files skipped from review due to trivial changes (3)
  • composables/repay/useRepaySwapCore.ts
  • scripts/execution-record.mjs
  • composables/repay/useWalletSwapRepay.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • components/entities/operation/OperationReviewModal.vue
  • composables/useEulerTx.ts
  • utils/stepDecoding.ts
  • pages/position/[number]/borrow/swap.vue

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

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)
pages/position/[number]/index.vue (2)

725-729: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard collateralVault before seeding collateralItems.

When position exists but collateralVault.value is unavailable, this inserts an undefined vault into collateralItems; downstream row code dereferences item.vault.address before loadCollaterals() necessarily replaces it.

Proposed fix
     position.value = getPositionBySubAccountIndex(+positionIndex)
     if (position.value) {
-      collateralItems.value = [{
-        vault: collateralVault.value as EVault,
-        assets: position.value.supplied,
-      }]
+      const initialCollateralVault = collateralVault.value
+      collateralItems.value = initialCollateralVault
+        ? [{
+            vault: initialCollateralVault,
+            assets: position.value.supplied,
+          }]
+        : []
       // Load collaterals: always for multi-collateral, or when oracle failed (to get actual assets)
       if (positionCollateralAddresses.value.length > 1 || hasQueryFailure.value) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pages/position/`[number]/index.vue around lines 725 - 729, The code seeds
collateralItems with a collateralVault that may be undefined, causing downstream
code that dereferences item.vault.address to fail. Add a guard condition to
check that both position.value and collateralVault.value are available before
populating the collateralItems array; only set collateralItems when both
conditions are met to prevent undefined vault objects from being inserted into
the array.

600-618: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not turn account-lens failures into zero-balance collateral rows.

If getAccountInfo fails for a non-primary collateral, assets remains 0n and the row is still rendered as if the user has no balance. Fall back to the SDK collateral entry when available, or skip the row and surface the query failure instead of showing a synthetic zero.

Proposed fix
-          let assets = 0n
+          let assets: bigint | undefined

           try {
             const res = await client.readContract({
               address: lensAddress as Address,
               abi: eulerAccountLensABI as Abi,
@@
               authorizationList: undefined,
             }) as Record<string, Record<string, unknown>>
             assets = res.vaultAccountInfo.assets as bigint
           }
           catch {
             if (address === primaryAddress) {
               assets = position.value!.supplied
             }
+            else {
+              const matchedCollateral = position.value!.collaterals?.find(collateral => {
+                try {
+                  return getAddress(collateral.vaultAddress || collateral.vault?.address || '') === address
+                }
+                catch {
+                  return false
+                }
+              })
+              assets = matchedCollateral?.assets
+            }
           }

-          return vault ? { vault, assets } : null
+          return vault && assets !== undefined ? { vault, assets } : null
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pages/position/`[number]/index.vue around lines 600 - 618, The catch block in
the getAccountInfo call only handles the primary address case, leaving assets as
0n for non-primary addresses when the query fails, which renders a misleading
zero-balance row. Modify the catch block to also handle the non-primary address
case by either returning null to skip rendering the row entirely, or falling
back to an alternative data source (such as the SDK collateral entry) instead of
allowing assets to remain 0n. This ensures failed queries are not shown as zero
balances to the user.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@composables/repay/useCollateralSwapRepay.ts`:
- Line 326: The condition on Line 326 in the useCollateralSwapRepay composable
has inverted logic using AND when it should use OR. The current condition `if
(!isConnected.value && !isSpyMode.value) return false` only disables submit when
BOTH the user is disconnected AND not in spy mode, but it should disable submit
for any disconnected user regardless of spy mode. Change the AND operator to OR
so that the function returns false to disable submit whenever the user is not
connected, unless they are in spy mode which may have special permissions.

In `@composables/repay/useWalletRepay.ts`:
- Line 131: The condition at line 131 in the useWalletRepay composable has
inverted logic: when the wallet is not connected and not in spy mode
(!isConnected.value && !isSpyMode.value), the function currently returns false
which enables the submit action when it should be blocked. Change the return
value from false to true in this condition to properly disable the submit action
for disconnected non-spy users.

In `@composables/useWallets.ts`:
- Around line 165-166: In the useWallets composable, the condition at line 166
that checks if balanceAddress.value matches targetAddress applies getAddress()
normalization only to balanceAddress.value but not to targetAddress, causing the
comparison to fail when addresses differ only in case. Fix this by normalizing
both sides of the comparison: apply getAddress() to targetAddress as well, so
the condition compares getAddress(balanceAddress.value) ===
getAddress(targetAddress) to ensure consistent address normalization on both
sides of the equality check.

---

Outside diff comments:
In `@pages/position/`[number]/index.vue:
- Around line 725-729: The code seeds collateralItems with a collateralVault
that may be undefined, causing downstream code that dereferences
item.vault.address to fail. Add a guard condition to check that both
position.value and collateralVault.value are available before populating the
collateralItems array; only set collateralItems when both conditions are met to
prevent undefined vault objects from being inserted into the array.
- Around line 600-618: The catch block in the getAccountInfo call only handles
the primary address case, leaving assets as 0n for non-primary addresses when
the query fails, which renders a misleading zero-balance row. Modify the catch
block to also handle the non-primary address case by either returning null to
skip rendering the row entirely, or falling back to an alternative data source
(such as the SDK collateral entry) instead of allowing assets to remain 0n. This
ensures failed queries are not shown as zero balances to the user.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: euler-xyz/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 58904d9b-fbbb-42fa-85e4-9c7fd0c51594

📥 Commits

Reviewing files that changed from the base of the PR and between b326003 and 647654a.

📒 Files selected for processing (12)
  • components/entities/operation/OperationReviewModal.vue
  • composables/borrow/useMultiplyCowSwap.ts
  • composables/repay/useCollateralSwapRepay.ts
  • composables/repay/useRepaySwapCore.ts
  • composables/repay/useWalletRepay.ts
  • composables/repay/useWalletSwapRepay.ts
  • composables/useEulerTx.ts
  • composables/useWallets.ts
  • pages/position/[number]/borrow/swap.vue
  • pages/position/[number]/index.vue
  • scripts/execution-record.mjs
  • utils/stepDecoding.ts
✅ Files skipped from review due to trivial changes (3)
  • composables/repay/useRepaySwapCore.ts
  • scripts/execution-record.mjs
  • composables/repay/useWalletSwapRepay.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • components/entities/operation/OperationReviewModal.vue
  • composables/useEulerTx.ts
  • utils/stepDecoding.ts
  • pages/position/[number]/borrow/swap.vue
🛑 Comments failed to post (3)
composables/repay/useCollateralSwapRepay.ts (1)

326-326: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Disconnected non-spy submit gate is inverted.

At Line 326, if (!isConnected.value && !isSpyMode.value) return false enables submit for a disconnected non-spy user. This should disable submit to prevent invalid execution attempts.

Suggested fix
-    if (!isConnected.value && !isSpyMode.value) return false
+    if (!isConnected.value && !isSpyMode.value) return true
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@composables/repay/useCollateralSwapRepay.ts` at line 326, The condition on
Line 326 in the useCollateralSwapRepay composable has inverted logic using AND
when it should use OR. The current condition `if (!isConnected.value &&
!isSpyMode.value) return false` only disables submit when BOTH the user is
disconnected AND not in spy mode, but it should disable submit for any
disconnected user regardless of spy mode. Change the AND operator to OR so that
the function returns false to disable submit whenever the user is not connected,
unless they are in spy mode which may have special permissions.
composables/repay/useWalletRepay.ts (1)

131-131: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Submit-disabled guard currently enables disconnected non-spy flow.

At Line 131, returning false for !isConnected && !isSpyMode makes the action appear submittable when it should be blocked.

Suggested fix
-    if (!isConnected.value && !isSpyMode.value) return false
+    if (!isConnected.value && !isSpyMode.value) return true
📝 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.

    if (!isConnected.value && !isSpyMode.value) return true
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@composables/repay/useWalletRepay.ts` at line 131, The condition at line 131
in the useWalletRepay composable has inverted logic: when the wallet is not
connected and not in spy mode (!isConnected.value && !isSpyMode.value), the
function currently returns false which enables the submit action when it should
be blocked. Change the return value from false to true in this condition to
properly disable the submit action for disconnected non-spy users.
composables/useWallets.ts (1)

165-166: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Address comparison can incorrectly discard valid wallet fetch results.

At Line 166, the condition compares getAddress(balanceAddress.value) (normalized) to targetAddress (possibly not normalized). Case-only differences can make this false and prevent balances from updating.

Suggested fix
-      const targetAddress = balanceAddress.value as Address
+      const targetAddress = getAddress(balanceAddress.value as Address)
...
-      if (chainId.value === currentChainId && balanceAddress.value && getAddress(balanceAddress.value) === targetAddress) {
+      if (chainId.value === currentChainId && balanceAddress.value && getAddress(balanceAddress.value) === targetAddress) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@composables/useWallets.ts` around lines 165 - 166, In the useWallets
composable, the condition at line 166 that checks if balanceAddress.value
matches targetAddress applies getAddress() normalization only to
balanceAddress.value but not to targetAddress, causing the comparison to fail
when addresses differ only in case. Fix this by normalizing both sides of the
comparison: apply getAddress() to targetAddress as well, so the condition
compares getAddress(balanceAddress.value) === getAddress(targetAddress) to
ensure consistent address normalization on both sides of the equality check.

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head 647654a.

Verdict: request changes. The refinance consolidation is directionally good and the current preview renders the combined debt/collateral flow on both desktop and mobile, but the current head still has several route-state issues that can make the action operate on the wrong context or lose the user’s intended network. I am keeping the existing Leonard inline comments as the concrete coordinates rather than reposting duplicates.

Blocking findings still relevant on this head:

  • pages/position/[number]/borrow/swap.vue: route.query.collateral can still fall back to getVault(queryCollateral) even when that collateral is not one of the loaded position collaterals. A crafted/stale URL can therefore set sourceCollateralVault to a known but unrelated vault while the position collateral lookup is null.
  • pages/position/[number]/borrow/swap.vue: debt/collateral modal selection still passes a numeric row index back into parallel vault arrays. With async option generation and context changes, this can select a vault different from the row the user clicked. Selection should resolve by option/vault address, not by a stale display index.
  • pages/position/[number]/borrow/swap.vue and pages/position/[number]/index.vue: fallback/back/refinance links still drop active query state such as network (and in some cases the selected collateral), which can move non-default-network users into the wrong route context.

Validation performed:

  • Focused tests: npm run test:run -- tests/golden/tx-plan-builders.test.ts tests/utils/stepDecoding.test.ts passed.
  • Typecheck: npm run typecheck passed.
  • Production build: npm run build passed.
  • Browser smoke: preview route /position/1/borrow/swap?network=1&spy=<public-mainnet-fixture> rendered the Refinance flow with both Collateral vault and Debt vault sections on desktop and mobile. I did not perform wallet signing.
  • Active bot feedback: I checked the current CodeRabbit comments against the diff; the material suggestions around allBorrowOptions, test fixture target contracts, and withdraw asset decoding are addressed/non-blocking on this head. The remaining blockers above are from direct code review.

Smoke coverage: browser visual smoke + mobile smoke; no wallet/signing smoke.

Screenshots from the preview smoke:

Desktop, 1365x900 — combined refinance route loaded with collateral and debt vault controls:
PR 575 desktop refinance smoke

Mobile, 390x844 — same route at narrow viewport:
PR 575 mobile refinance smoke

- add refinance entries to the batch builder

- keep simulated position highlights and removed rows scoped to changed assets

- decode batch execution errors and separate quoted amounts from verifier minimums
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 23, 2026 09:15 Destroyed

@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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@composables/useTxBatch.ts`:
- Around line 265-292: The buildRefinanceReplacementBorrowPositionKeys function
constructs position keys using sourceCollateralVaults from the review metadata,
which only includes collateral vaults with positive assets due to filtering in
currentCollateralLegs. However, the existing position key includes all enabled
collateral vaults regardless of asset balance, causing a mismatch. Ensure that
sourceCollateralVaults includes all enabled collateral vaults with zero assets
in addition to those with positive assets, so the keys generated for refinance
replacement positions match the structure of existing borrow position keys.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: euler-xyz/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f2d0504d-ef68-43ad-b2d5-619ae2f53bfb

📥 Commits

Reviewing files that changed from the base of the PR and between 647654a and c9a4579.

📒 Files selected for processing (6)
  • composables/useTxBatch.ts
  • pages/position/[number]/borrow/swap.vue
  • pages/position/[number]/index.vue
  • tests/composables/useTxBatchModifiedKeys.test.ts
  • tests/utils/stepDecoding.test.ts
  • utils/stepDecoding.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/utils/stepDecoding.test.ts
  • pages/position/[number]/borrow/swap.vue
  • utils/stepDecoding.ts
  • pages/position/[number]/index.vue

Comment thread composables/useTxBatch.ts

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head c9a4579.

Verdict: request changes.

The combined refinance route is much closer than the earlier heads, and the lightweight validation path is mostly healthy, but I still see merge-blocking issues around URL-controlled collateral source selection and a new lint failure. I also verified one current CodeRabbit concern as materially relevant, with a narrower explanation below.

Blocking findings:

  • The prior Leonard comment on the crafted/stale collateral= query still applies. sourceCollateralVault still falls back to the global vault registry when the query address is not one of the position’s collateral legs. That value then feeds input.collateral.fromVault with currentCollateralAssets, which falls back to the position’s primary supplied amount when no matching source collateral position exists. A bookmarked/crafted URL can therefore make the planner/UI reason about the wrong source collateral vault. This should be constrained to the position’s actual collateral set, or the query should be ignored/cleared when it does not match.
  • Lint now fails on composables/useSwapCollateralOptions.ts:56 (no-useless-assignment): let candidates: EVault[] = [] is overwritten by both branches before it is read.

Warnings / follow-ups:

  • The prior Leonard network-preservation comments still apply: the collateral-row Refinance link and the refinance back fallback still drop the active network query. This is not as dangerous as the source-collateral issue, but it can move users out of the network/context they were reviewing.
  • The prior Leonard numeric row-index selection comment still appears applicable: the modal event still passes a displayed row index and then indexes debtSelectionVaults / collateralSelectionVaults. Resolving by selected vault address would be safer when async option maps and synchronous vault arrays update around a context change.
  • CodeRabbit’s latest useTxBatch concern is substantively valid, but the sharper edge is this: sourceCollateralVaults is built from positive-balance currentCollateralLegs, while the canonical borrow-position key includes position.collateralVaults plus collateral/current-collateral addresses. If a position has a zero-balance enabled collateral, the replacement suppression key can fail to match and the batch overlay can still show the old borrow position as removed. See inline.

Validation performed:

  • Inspected PR metadata, current head, changed-file list, diff, surrounding refinance/portfolio code, prior Leonard reviews/comments, and active CodeRabbit review comments.
  • Focused tests passed: npm run test:run -- tests/composables/useTxBatchModifiedKeys.test.ts tests/utils/stepDecoding.test.ts tests/composables/useRepaySwapCore.test.ts tests/composables/useWalletSwapRepay.test.ts tests/utils/swap-route-visibility.test.ts → 25 tests passed.
  • npm run typecheck passed.
  • npm run build passed.
  • Targeted lint failed: npx eslint composables/useSwapCollateralOptions.ts pages/position/[number]/borrow/swap.vue pages/position/[number]/index.vue composables/useTxBatch.ts → one no-useless-assignment error.
  • Browser visual smoke: local production build, headed Chromium under Xvfb, desktop 1366×900 and mobile 390×844 on /position/0/borrow/swap?network=1; both rendered the refinance shell without page errors. This was disconnected route/mobile smoke only, not wallet/signing or transaction execution coverage.

Screenshots from the disconnected route smoke:

Desktop refinance route smoke, 1366x900

Mobile refinance route smoke, 390x844

Scalability / maintainability hygiene pass:

  • The PR consolidates collateral swap into the refinance route instead of leaving sibling route logic split; that direction is good.
  • The remaining risky pieces are exactly the shared state boundaries: route query parsing, modal selection identity, and batch overlay position-key construction. These should use canonical position/vault identity rather than local fallbacks or display-array indexes, because the same concepts are reused across debt-only, collateral-only, and combined refinance flows.

Smoke coverage: browser visual smoke + mobile smoke on disconnected route shell; no wallet/signing smoke.

Comment lifecycle: no Leonard top-level issue comment existed to edit; I kept the existing Leonard inline comments that remain relevant and did not delete other users’/bots’ comments.

- constrain collateral query selection to position collaterals
- preserve network query in refinance navigation
- resolve modal selections by selected vault address
- include all source collateral vaults in refinance replacement metadata
- fix disconnected submit guards and wallet address normalization
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 23, 2026 13:23 Destroyed

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head fa63ca1.

Verdict: request changes — the previous Leonard route-state blockers are fixed on this head, but one sibling entry point still drops the active network query before entering the consolidated refinance flow.

Scope reviewed:

  • consolidated refinance route state for debt/collateral/both migrations
  • position-page refinance entry points
  • modal selection identity and stale-query handling
  • batch replacement key / step-decoding changes
  • CoW collateral-only refinance gating
  • active CodeRabbit comments, verified against current code

Finding:

  • Blocking: the top-level position “Refinance” button still links to /position/${positionIndex}/borrow/swap without preserving route.query.network. The collateral-row refinance link now uses buildRefinanceRoute(...) and keeps network, so this is the remaining sibling inconsistency. On a non-default-network position page, this entry point can move the user into the default/wrong route context before they review the refinance.

Validation performed:

  • Focused tests passed: npm run test:run -- tests/utils/stepDecoding.test.ts tests/composables/useTxBatchModifiedKeys.test.ts tests/golden/tx-plan-builders.test.ts (Vitest matched and ran the two relevant suites, 19 tests passed).
  • npm run typecheck passed.
  • npm run build passed.
  • Focused lint over the touched refinance/batch/decoding files passed.
  • Full npm run lint still fails on existing unrelated files outside this PR’s changed surface; I did not treat that as a PR blocker.
  • Browser visual smoke on the Railway PR preview passed for the disconnected refinance shell on desktop and mobile: /position/0/borrow/swap?network=1 returned 200 and rendered the refinance page. I also checked /position/0?network=1; disconnected mode does not expose the actionable top-level position button, so the network-preservation issue above is code-path verified rather than browser-click verified.

Screenshots from the PR preview smoke:

Desktop refinance route smoke, 1366x900

Mobile refinance route smoke, 390x844

Desktop position route smoke, 1366x900

Bot/reviewer feedback pass:

  • CodeRabbit’s current useTxBatch source-collateral-vaults concern is stale/false positive on this head: the PR now builds sourceCollateralVaultAddresses from the full current position collateral set, and the focused batch modified-key test covers the multi-collateral replacement case.
  • CodeRabbit’s stepDecoding withdraw asset concern is also stale/false positive on this head: Withdraw now resolves the asset from batchItem.targetContract, and the test covers a receiver that is itself another known vault.
  • CodeRabbit’s multicall fixture concern is resolved in the current test fixture.

Scalability / maintainability hygiene pass:

  • Good: the PR consolidates the old standalone collateral-swap route into the shared refinance flow and moves more of the shared transaction review semantics into the common batch/step-decoding path.
  • Remaining issue: route construction is still duplicated between the top-level position action and the collateral-row action. The collateral-row helper has the correct network-preserving behavior; the top-level sibling should use the same helper or a shared query-preserving route builder so future refinance entry points cannot drift again.

Smoke coverage: browser visual smoke + mobile smoke on disconnected route shell; no wallet/signing smoke or transaction execution coverage.

Comment lifecycle: deleted four stale Leonard inline comments from the previous head that are fixed on this head; submitted this fresh review against fa63ca1.

@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 24, 2026 07:46 Destroyed

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head 8f80162.

Verdict: request changes. The latest commit fixes the prior Leonard network-preservation blocker, and the route/build/test smoke is healthy, but I found one remaining transaction-risk issue in the new combined refinance path.

Scope reviewed:

  • consolidated refinance route: pages/position/[number]/borrow/swap.vue
  • position entry links and query-state preservation in pages/position/[number]/index.vue
  • transaction planning/batch metadata in composables/useEulerTx.ts and composables/useTxBatch.ts
  • swap option helpers and step-decoding changes/tests
  • active CodeRabbit/reviewer comments, verified against current head rather than taken as authority

Finding:

  • Blocking: debt-swap price impact is signed in the wrong direction before passing into the shared high-price-impact gate. Bad debt quotes that require more target debt to repay the same old debt become positive impacts and can bypass the <= -10% danger confirmation. See inline comment.

Validation performed:

  • npm run typecheck — passed
  • npm run test:run -- tests/composables/useTxBatchModifiedKeys.test.ts tests/utils/stepDecoding.test.ts tests/composables/useSwapPageLogic.test.ts — passed, 22 tests
  • npm run build — passed, warnings only
  • headed Chromium/Xvfb browser smoke on Railway preview:
    • desktop /position/0/borrow/swap?network=1 returned 200 and rendered the Refinance shell with no page-error text
    • mobile /position/0/borrow/swap?network=1 returned 200 and rendered the Refinance shell with no page-error text
    • desktop /position/0?network=1 returned 200 and rendered portfolio shell; disconnected mode does not expose position action buttons
  • Wallet/signing smoke: not run.

Scalability / maintainability hygiene pass:

  • The prior route-link drift is fixed on current head: both top-level and collateral-row Refinance entries use the same buildRefinanceRoute(...) helper and preserve network.
  • No stale /position/:number/collateral/swap references found.
  • The unified route is necessarily larger, but it now concentrates the debt/collateral refinance action in one place. Follow-up worth considering, not blocking this review: the PR deletes the old collateral-swap execution scenario and the remaining execution scenario still appears named around the old swap-debt UI; add/update an execution scenario for selecting a collateral target on the unified refinance route so the main collateral-migration intent is covered end-to-end.

Bot/reviewer feedback pass:

  • Current CodeRabbit inline threads I checked are stale/resolved or already addressed on this head: allBorrowOptions dependency, explicit swapper test fixture target, Withdraw asset decoding, and refinance metadata coverage for zero-balance enabled collaterals.
  • I did not promote any bot claim to a Leonard finding without re-checking the code/tests.

Comment lifecycle:

  • Deleted one stale Leonard-owned inline comment from the previous head about the top-level Refinance link dropping network; current code fixes it.
  • No prior Leonard top-level walkthrough comment existed to edit, so this is a fresh formal review for the current head.

Screenshots:

  • Captured local desktop/mobile smoke screenshots, but did not attach them to GitHub: gh gist create rejected PNG binaries, and the blocking finding is code-path based rather than visual. The browser smoke evidence is summarized above.

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head ad542b5.

Verdict: comment — no remaining blockers found in this incremental re-review. The latest commit fixes the previous Leonard blocker: debt-swap price impact now treats worse debt quotes as negative impact before feeding the shared price-impact gate.

Scope reviewed:

  • Incremental diff from the last Leonard-reviewed head: pages/position/[number]/borrow/swap.vue
  • Prior route-state blocker in pages/position/[number]/index.vue
  • Prior batch/step-decoding/metadata concerns around the consolidated refinance flow
  • Active CodeRabbit/reviewer comments checked against current code rather than accepted as authority

Validation performed:

  • npm run typecheck — passed
  • npm run test:run -- tests/composables/useTxBatchModifiedKeys.test.ts tests/utils/stepDecoding.test.ts tests/composables/useSwapPageLogic.test.ts tests/golden/tx-plan-builders.test.ts — Vitest ran the non-golden matching suites; passed, 22 tests
  • npm run test:run -- tests/golden/tx-plan-builders.test.ts — not runnable through the normal Vitest script because tests/golden/** is excluded by config
  • npm run build — passed, warnings only
  • Headed Chromium/Xvfb browser smoke on the Railway PR preview:
    • desktop /position/0/borrow/swap?network=1 returned 200 and rendered the Refinance shell with no page errors
    • mobile /position/0/borrow/swap?network=1 returned 200 and rendered the Refinance shell with no page errors; one COOP fetch console error was observed and treated as browser/preview noise, not a route failure

Scalability / maintainability hygiene pass:

  • The prior duplicated route-construction drift is fixed: both top-level and collateral-row Refinance entries now go through buildRefinanceRoute(...) and preserve network.
  • The consolidated refinance route remains large, but the PR is at least concentrating debt/collateral migration behavior in one flow rather than adding another one-off sibling path.
  • Non-blocking follow-up remains useful: restore/add an execution scenario that exercises selecting a collateral target on the unified refinance route, since the old standalone collateral-swap scenario was removed and this PR’s main user-visible intent is collateral migration.

Bot/reviewer feedback pass:

  • Current CodeRabbit comments I checked are stale/resolved or non-blocking on this head: allBorrowOptions now includes collateralVault as a reactive dependency; withdraw step decoding uses targetContract; the multicall fixture target concern is covered in the current tests; the refinance metadata concern is covered by the current full-collateral-set replacement metadata/tests.

Smoke coverage: browser route smoke + mobile smoke on the disconnected refinance shell; no wallet/signing smoke or live quote/transaction execution coverage.

Screenshots: not posted for this incremental review. I did not capture new screenshots because the current head only changes non-visual price-impact arithmetic, and the route smoke was textual/health-oriented rather than visual evidence.

Comment lifecycle: deleted the stale Leonard-owned inline comment about debt price-impact sign after verifying the current head fixes it; no Leonard top-level walkthrough comment existed to edit.

@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 24, 2026 12:54 Destroyed

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head a088f40.

Verdict: comment — no remaining blockers found in this incremental re-review. The latest commit only changes the refinance summary display gate, and the new showNextRefinanceMetrics condition is consistent with the route validation/CTA gates: no “after” metrics are shown until the user has selected a debt/collateral change and all required quotes are present.

Scope reviewed:

  • Incremental diff from the last Leonard-reviewed head: pages/position/[number]/borrow/swap.vue
  • Consolidated refinance route metric readiness, validation errors, and CTA gating
  • Position-page refinance entry links and stale /collateral/swap references
  • Active CodeRabbit/reviewer comments checked against current code rather than treated as authority

Validation performed:

  • npm run typecheck — passed
  • npm run test:run -- tests/composables/useTxBatchModifiedKeys.test.ts tests/utils/stepDecoding.test.ts tests/composables/useSwapPageLogic.test.ts — passed, 22 tests
  • npx eslint pages/position/[number]/borrow/swap.vue pages/position/[number]/index.vue composables/useSwapCollateralOptions.ts composables/useSwapDebtOptions.ts composables/useTxBatch.ts utils/stepDecoding.ts — passed
  • npm run build — passed, warnings only
  • Headed Chromium/Xvfb route smoke on the PR preview:
    • desktop /position/0/borrow/swap?network=1 returned 200 and rendered the Refinance shell with no page errors
    • mobile /position/0/borrow/swap?network=1 returned 200 and rendered the Refinance shell with no page errors

Scalability / maintainability hygiene pass:

  • No stale /position/:number/collateral/swap route references found after the route consolidation.
  • The prior route-construction drift remains fixed: the top-level debt Refinance action and collateral-row Refinance action both use the same buildRefinanceRoute(...) helper and preserve network.
  • The new metric display gate is localized, but it matches analogous readiness gating patterns in sibling action routes. I did not find a sibling surface still showing the old “after metrics while no refinance change exists” behavior.
  • Non-blocking coverage gap remains worth addressing: the old collateral-swap execution scenario was removed, and I do not see a replacement execution scenario that opens the collateral-row Refinance action and selects a collateral target through the unified route. The current focused tests cover batch/decoding logic, but not the end-to-end collateral-migration UI path.

Bot/reviewer feedback pass:

  • The current CodeRabbit comments I checked are stale/resolved or non-blocking on this head: borrow-option dependency coverage, withdraw asset decoding, multicall fixture target, and refinance batch metadata coverage are covered by the current code/tests.
  • I did not promote any bot claim to a Leonard finding without re-checking the code.

Smoke coverage: browser route smoke + mobile smoke on the disconnected refinance shell; no wallet/signing smoke, live quote selection, or transaction execution coverage.

Screenshots: not posted for this incremental review. I did not capture new screenshots because the current head changes metric display gating and the smoke was a textual route-health check, not a visual regression artifact.

Comment lifecycle: no Leonard top-level walkthrough comment exists to edit; no Leonard-owned inline review comments remain on the current head to delete or update.

@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 26, 2026 12:20 Destroyed

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head 1de90ff8b3bb62e2b566854aaa79048dbdcf0f71.

Verdict: request changes. The consolidated refinance direction is much cleaner, and the current test/build path is healthy, but I found two route-state regressions that should be fixed before merge.

Scope reviewed:

  • consolidated refinance route and position-page entry links
  • collateral/debt target selection and query preselection
  • removed standalone collateral swap route behavior
  • batch/review metadata and step decoding changes
  • current CodeRabbit/bot comments as evidence, not authority
  • scalability / maintainability hygiene for sibling refinance flows and option-selection helpers

Validation performed:

  • npm run test:run -- tests/composables/useTxBatchModifiedKeys.test.ts tests/utils/stepDecoding.test.ts tests/composables/useSwapPageLogic.test.ts — passed, 24 tests
  • npx eslint pages/position/[number]/borrow/swap.vue pages/position/[number]/index.vue composables/useSwapCollateralOptions.ts composables/useSwapDebtOptions.ts composables/useTxBatch.ts utils/stepDecoding.ts — passed
  • npm run typecheck — passed
  • npm run build — passed, with existing bundle/chunk warnings only
  • headed Chromium / Xvfb preview smoke on https://dev-build-euler-lite-pr-575.up.railway.app:
    • desktop + mobile /position/1/borrow/swap?network=1&spy=... rendered the Refinance route shell without a Nuxt error, but the selected public fixture did not hydrate far enough to show the vault selectors before timeout, so this is incomplete route smoke rather than full interaction coverage.
    • desktop /position/1/collateral/swap?network=1&spy=... rendered a page-not-found error.

Blocking findings:

  1. The old collateral swap route now 404s instead of redirecting into the consolidated refinance flow. This breaks existing links/bookmarks and contradicts the PR body’s “remove/redirect” claim.
  2. A stale ?to= debt target can be re-applied after the user explicitly chooses to keep the current debt vault, if collateral changes later recompute the debt target list.

Non-blocking gaps worth addressing soon:

  • Execution scenario coverage is stale/incomplete for the new unified refinance UI: the old collateral-swap scenario was removed, and the remaining swap-debt scenario still appears to target old labels like To / Review Swap rather than the new Debt vault / Review Refinance flow.
  • Scalability / maintainability hygiene: the new grouped option-selection behavior is subtle and reusable, but mostly lives in the modal/page path. Focused tests around grouped option index preservation, address-based selection, and the new useSwapCollateralOptions / useSwapDebtOptions candidate filters would make this less fragile.

Bot/reviewer comment pass:

  • I verified the older CodeRabbit comments against the current head. The allBorrowOptions dependency, withdraw asset decoding, explicit multicall target fixture, selector-argument matching, and full-collateral batch metadata concerns appear addressed or no longer materially actionable on this head. I did not promote any unverified bot claim to a Leonard finding.

Screenshot evidence:

  • Old collateral route 404: Old collateral route 404
  • Desktop refinance route shell: Desktop refinance route shell
  • Mobile refinance route shell: Mobile refinance route shell

Smoke coverage: incomplete browser visual smoke; no wallet/signing coverage.

…ration

# Conflicts:
#	components/BatchReviewModal.vue
#	components/entities/operation/OperationReviewModal.vue
#	pages/position/[number]/borrow/swap.vue
#	pages/position/[number]/collateral/swap.vue
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 June 26, 2026 14:38 Destroyed

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head a304568f18eded87b8d317ccfd6793964d642c03.

Verdict: request changes. The current head still has two route-state blockers in the consolidated refinance flow. I kept the existing Leonard inline comments on this same head as the coordinates rather than reposting duplicate inline comments.

Blocking findings:

  • pages/position/[number]/collateral/swap.vue deletion still leaves the old collateral-swap URL as a hard 404. The PR states standalone swap-collateral entry points are routed into refinance, but the deployed PR preview renders Page not found for /position/1/collateral/swap?network=1 on both desktop and mobile. Existing bookmarks, external links, or stale client navigation should redirect/stub into the consolidated refinance route while preserving relevant query state.
  • pages/position/[number]/borrow/swap.vue still treats route.query.to as a persistent preselection source. If a user lands with ?to=<debtVault>, manually chooses the current debt vault to clear the debt change, and then changes collateral, the watcher can run again after debtTargetVaults recomputes and silently reselect the stale debt target. That can turn an intended collateral-only refinance into mixed debt+collateral refinance.

Validation performed:

  • Inspected PR metadata, current head, changed files, full PR diff, surrounding route/planner/review code, prior Leonard reviews/comments, and current CodeRabbit inline comments.
  • npm run test:run — passed, 98 files / 994 tests.
  • Focused route/planner tests: tests/composables/useTxBatchModifiedKeys.test.ts and tests/utils/stepDecoding.test.ts passed; tests/golden/tx-plan-builders.test.ts is excluded by the repo Vitest config, so it did not run through test:run.
  • npm run typecheck — passed.
  • npm run build — passed.
  • npm run lint — failed, but the remaining errors are also present on origin/development; not counted as a PR-specific blocker.
  • Headed/Xvfb Playwright smoke against the Railway PR preview:
    • desktop and mobile /position/1/collateral/swap?network=1 both rendered Error: Page not found.
    • desktop and mobile /position/1/borrow/swap?network=1 rendered the consolidated Refinance page shell.

Scalability / maintainability hygiene pass:

  • Checked sibling action entry points and the consolidated refinance source/target query model, not only the touched watcher. The main remaining consistency issue is the old-route compatibility gap: deleting the legacy route solves the visible component path but leaves sibling/stale navigation and bookmarks without a reusable redirect/mapping layer.
  • The to preselection behavior is still local watcher state rather than a one-shot/consumed URL-state abstraction. Because this route now composes collateral and debt migration in one form, stale query state is transaction-relevant and should be normalized at the route boundary.
  • No new one-off formatter/business-logic duplication stood out in this pass beyond the route-state issues above.

Bot/reviewer feedback pass:

  • Verified CodeRabbit's stale allBorrowOptions dependency concern is no longer applicable on current code: collateralVault is included in the useReactiveMap dependency list.
  • Verified CodeRabbit's withdraw asset-resolution concern is no longer applicable on current code: Withdraw now resolves the source asset from targetContract.
  • I did not promote the remaining test-fixture tightening suggestions to blockers.

Smoke coverage: browser visual/text route smoke on desktop and mobile; no wallet/signing smoke. Screenshots were not posted because the remaining evidence is route-state/textual (Page not found) rather than a subtle visual regression.

@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-575 July 1, 2026 09:28 Destroyed

@LeonardEulerXYZ LeonardEulerXYZ 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.

Reviewed head e814d9deb73c4820c8446bbe305a80eb53d809f4.

Verdict: approve — the prior Leonard blockers are resolved on this head, and I did not find a new blocker in the current re-review.

Scope reviewed:

  • consolidated refinance route in pages/position/[number]/borrow/swap.vue
  • legacy collateral-swap compatibility route in pages/position/[number]/collateral/swap.vue
  • route query consumption for to / targetCollateral
  • batch/review metadata wiring around collateral/debt refinance quotes
  • transaction batch modified-key behavior and step decoding tests
  • scalability / maintainability hygiene across sibling refinance-style route state and shared transaction paths
  • active bot/reviewer feedback checked as evidence, not authority

Validation performed:

  • npm run test:run -- tests/utils/stepDecoding.test.ts tests/composables/useTxBatchModifiedKeys.test.ts — passed, 21 tests.
  • npm run typecheck — passed.
  • npm run build — passed; only existing build-time warnings observed.
  • Built Nuxt server route/API checks: / returned HTTP 200; /api/rpc/1 eth_chainId returned 0x1.
  • Headed Playwright/Xvfb browser smoke:
    • desktop /position/1/borrow/swap?network=1&spy=<fixture> renders the refinance form with Collateral vault, Debt vault, warning copy, metrics panel, and Choose Vaults CTA.
    • mobile same route renders the same refinance controls without a blank/error state.
    • legacy /position/1/collateral/swap?...&to=<target> redirects to /position/1/borrow/swap?...&targetCollateral=<target> and renders the refinance page.

Screenshot evidence:

  • Desktop refinance route: desktop refinance route
  • Mobile refinance route: mobile refinance route
  • Legacy collateral route redirect: legacy collateral redirect

Notes:

  • The old collateral route now has a compatibility redirect/stub, so the previous hard-404 finding is fixed.
  • to / targetCollateral preselection is now consumed and the route query is cleared after applying, so the previous stale-query re-selection finding is fixed.
  • The blank smoke attempt with an invalid spy/position pairing was discarded; the posted screenshots use a subgraph-backed borrow/collateral fixture and were checked for the intended visible state before posting.
  • Browser smoke was disconnected/spy-mode coverage only; no wallet signing was exercised.
  • Non-blocking local noise observed: blank AppKit project id causes Reown 403 warnings in local disconnected smoke, and Merkl allowlist 404s appear for unsupported proxy paths. These did not block the refinance page render.

Scalability / maintainability hygiene:

  • This head centralizes the refinance action on the consolidated borrow/swap page rather than reintroducing one-off sibling action logic. Query consumption and target-vault matching are scoped to target-list candidates, which is the right reusable boundary for this route.
  • I did not find a sibling route that still carries the old stale to behavior or the old collateral-swap hard-404 behavior. The remaining debt/collateral summary helpers are local to this combined planner and covered indirectly by the focused transaction/batch tests plus browser smoke.

Bot/reviewer feedback checked:

  • Existing CodeRabbit comments were inspected. I did not find a current material blocker to amplify from them on this head; the active Leonard findings that mattered here were the two prior route-state blockers, both now resolved.

@Seranged Seranged self-requested a review July 1, 2026 11:17
@Seranged Seranged merged commit 5563fc1 into development Jul 1, 2026
2 checks passed
@Seranged Seranged deleted the euler-lite-migration branch July 1, 2026 11:18
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.

3 participants