Skip to content

Instantly share code, notes, and snippets.

@patcito
Created May 5, 2026 15:04
Show Gist options
  • Select an option

  • Save patcito/851c6e63f0501bc0012914e4682a43d2 to your computer and use it in GitHub Desktop.

Select an option

Save patcito/851c6e63f0501bc0012914e4682a43d2 to your computer and use it in GitHub Desktop.
ft-api v0.3.52: totalRepayableMax field — FE migration note

totalRepayableMax — new sibling field on /mm/lend (v0.3.52)

What changed

assets[].userMetrics now exposes three new fields alongside the existing totalRepayable*:

type LendAssetUserMetrics = {
  // existing — unchanged
  totalRepayable: number;       // session-aware: min(wallet, debt) − relayer-fee headroom
  totalRepayableRaw: string;
  totalRepayableUsd: number;

  // new — wallet-only, no fee carve-out
  totalRepayableMax: number;    // = min(wallet, debt), exact
  totalRepayableMaxRaw: string;
  totalRepayableMaxUsd: number;

  // ...other fields
};

totalRepayableMax is always ≥ totalRepayable. The two converge to the same value when the relayer fee is zero (e.g. on Sonic) or when min(wallet, debt) == debt and the user has plenty of headroom.

When to use which

const repayMax = sessionsEnabled
  ? asset.userMetrics.totalRepayable      // legacy field — already deducts session fee
  : asset.userMetrics.totalRepayableMax;  // new field — full wallet-side cap

Or, since you already do your own conditional session-fee deduction in useLendDrawers.tsx (lines 519–545), you can simplify to always read totalRepayableMax and let your existing FE-side logic do the deduction in session mode:

// Always pull the wallet-only cap from the API.
maxInputValueRaw = asset.userMetrics.totalRepayableMaxRaw;
maxInputValue = asset.userMetrics.totalRepayableMax;

// Then your existing session-fee deduction kicks in unchanged:
if (sessionsEnabled && sessionFeeTokenRaw && maxInputValueRaw) {
  const feeWei = BigInt(sessionFeeTokenRaw);
  const walletWei = BigInt(asset.userMetrics.walletBalanceRaw ?? '0');
  const sessionMaxWei = walletWei > feeWei ? walletWei - feeWei : 0n;
  const repayWei = BigInt(maxInputValueRaw);
  const effectiveWei = sessionMaxWei < repayWei ? sessionMaxWei : repayWei;
  if (effectiveWei !== repayWei) {
    maxInputValueRaw = effectiveWei.toString();
    maxInputValue = Number(formatUnits(effectiveWei, decimals));
  }
}

This second form is the cleaner end-state — the API stops being session-aware and the FE owns that policy.

Concrete example (Ethereum, manager's wallet)

walletBalance:    0.001
totalBorrowed:    0.001000275778364331
totalRepayable:   0.000615…     ← old field, deducts session relayer fee
totalRepayableMax: 0.001        ← new field, raw min(wallet, debt)

Direct repay (no sessions): user can submit 0.001 and clear the entire debt. Pre-fix the FE was capping at 0.000615 and leaving them stuck.

Session repay: tx submits userInput + relayerFee on-chain. The FE-side deduction caps userInput so total stays under wallet — same outcome as before.

Migration timeline

  • Now (v0.3.52): both fields available. totalRepayable* semantics unchanged. Switch the repay-drawer's MAX source to totalRepayableMax whenever you're ready.
  • Future: once everyone's on totalRepayableMax, the legacy totalRepayable* fields can be deprecated and eventually removed.

Endpoint

Same URL, same auth: GET https://api.flyingtulip.com/mm/lend?chainId=<id>&user=<addr>

origin-api-aws.flyingtulip.com/mm/lend?... works the same — both go through CloudFront to the same origin.

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