From 81904eba2431e79839a4fa5cf9fc06b5a385b50a Mon Sep 17 00:00:00 2001 From: Manank Patni Date: Sun, 28 Jun 2026 02:23:38 +0530 Subject: [PATCH] Expose routingAndLiquidityOptions on EpochIntentWidget. Forward routing presets through pay, swap, and earn flows so integrators can control filler vs external liquidity paths at quote and submit time. Co-authored-by: Cursor --- .agents/skills/epoch-widget/reference/props.md | 1 + package-lock.json | 18 +++++++++--------- package.json | 4 ++-- src/EpochIntentWidget.tsx | 3 +++ src/components/EarnIntentWidget.tsx | 4 ++++ src/components/PaySwapIntentWidget.tsx | 3 +++ src/earn/use-earn-intent-flow.ts | 14 +++++++++++++- src/index.ts | 1 + src/types.ts | 11 +++++++++-- src/use-intent-flow.ts | 5 +++++ 10 files changed, 50 insertions(+), 14 deletions(-) diff --git a/.agents/skills/epoch-widget/reference/props.md b/.agents/skills/epoch-widget/reference/props.md index 5c60a39..b4b2d9f 100644 --- a/.agents/skills/epoch-widget/reference/props.md +++ b/.agents/skills/epoch-widget/reference/props.md @@ -20,6 +20,7 @@ Required: `isOpen`, `onClose`, `api`. Everything else optional. | `lockDestinationToken` | `boolean` | `true` | Pay-only; `false` lets user re-pick destination. Forced off in swap. | | `usdPriceFor` | `(t:{chainId,address,symbol}) => number\|null\|Promise` | — | Resolver for "≈ $…" line. | | `ctaLabels` | `Partial<{submit, switchNetwork, quoting, preparing, signing, submitting, polling, complete, insufficientBalance, configureRequired}>` | — | Per-state CTA copy. | +| `routingAndLiquidityOptions` | `RoutingAndLiquidityOptions` | `{ preset: "any" }` | Restrict solver routing for quote + submit. Presets: `any`, `filler-single-transaction`, `external-multi-transactions`, `custom`. | | `earnDefaultTab` | `'deposit' \| 'withdraw'` | `'deposit'` | Earn starting tab. | | `earnHideTabs` | `boolean` | `false` | Hide deposit/withdraw tabs. | | `earnMarketsSource` | `OneDeltaConfig[]` | — | Static market configs (e.g. `HARDCODED_ONEDELTA_CONFIGS`). | diff --git a/package-lock.json b/package-lock.json index 16d16c4..581698c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,9 @@ "version": "0.1.1", "license": "MIT", "dependencies": { - "@epoch-protocol/epoch-commons-sdk": "^0.1.13", - "@epoch-protocol/epoch-flows-sdk": "^0.1.1", - "@epoch-protocol/epoch-intents-sdk": "^1.0.26", + "@epoch-protocol/epoch-commons-sdk": "^0.1.14", + "@epoch-protocol/epoch-flows-sdk": "^0.1.2", + "@epoch-protocol/epoch-intents-sdk": "^1.0.27", "clsx": "^2.1.1", "tailwind-merge": "^2.6.1" }, @@ -46,7 +46,7 @@ }, "../epoch-commons-sdk": { "name": "@epoch-protocol/epoch-commons-sdk", - "version": "0.1.13", + "version": "0.1.14", "license": "MIT", "dependencies": { "ethers": "^6.0.0", @@ -68,11 +68,11 @@ }, "../epoch-flows-sdk": { "name": "@epoch-protocol/epoch-flows-sdk", - "version": "0.1.1", + "version": "0.1.2", "license": "MIT", "dependencies": { - "@epoch-protocol/epoch-commons-sdk": "^0.1.13", - "@epoch-protocol/epoch-intents-sdk": "^1.0.26" + "@epoch-protocol/epoch-commons-sdk": "^0.1.14", + "@epoch-protocol/epoch-intents-sdk": "^1.0.27" }, "devDependencies": { "@types/node": "^20.9.1", @@ -88,10 +88,10 @@ }, "../smallocator/sdk": { "name": "@epoch-protocol/epoch-intents-sdk", - "version": "1.0.26", + "version": "1.0.27", "license": "MIT", "dependencies": { - "@epoch-protocol/epoch-commons-sdk": "^0.1.13", + "@epoch-protocol/epoch-commons-sdk": "^0.1.14", "viem": "^2.29.2" }, "devDependencies": { diff --git a/package.json b/package.json index ddfb342..112f1af 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,8 @@ }, "dependencies": { "@epoch-protocol/epoch-commons-sdk": "^0.1.14", - "@epoch-protocol/epoch-flows-sdk": "^0.1.1", - "@epoch-protocol/epoch-intents-sdk": "^1.0.26", + "@epoch-protocol/epoch-flows-sdk": "^0.1.2", + "@epoch-protocol/epoch-intents-sdk": "^1.0.27", "clsx": "^2.1.1", "tailwind-merge": "^2.6.1" }, diff --git a/src/EpochIntentWidget.tsx b/src/EpochIntentWidget.tsx index 6d586a9..fd7c0cf 100644 --- a/src/EpochIntentWidget.tsx +++ b/src/EpochIntentWidget.tsx @@ -58,6 +58,7 @@ export function EpochIntentWidget(props: EpochIntentWidgetProps) { usdPriceFor, onSourceTokenChange, onQuote, + routingAndLiquidityOptions, } = props; const rawMode = modeProp ?? flowProp ?? 'pay'; @@ -111,6 +112,7 @@ export function EpochIntentWidget(props: EpochIntentWidgetProps) { usdPriceFor, onSourceTokenChange, onQuote, + routingAndLiquidityOptions, }; if (!isConnected) { @@ -162,6 +164,7 @@ export function EpochIntentWidget(props: EpochIntentWidgetProps) { onSign={onSign} onSuccess={onSuccess} onStatus={onStatus} + routingAndLiquidityOptions={routingAndLiquidityOptions} /> ); } diff --git a/src/components/EarnIntentWidget.tsx b/src/components/EarnIntentWidget.tsx index 1d973f1..25a8734 100644 --- a/src/components/EarnIntentWidget.tsx +++ b/src/components/EarnIntentWidget.tsx @@ -22,6 +22,7 @@ import type { OnStartCtx, OnStatusCtx, OnSuccessCtx, + RoutingAndLiquidityOptions, } from '../types'; import { buildEarnDepositIntent } from '../earn/build-deposit-intent'; import { buildEarnWithdrawIntent } from '../earn/build-withdraw-intent'; @@ -124,6 +125,7 @@ interface EarnIntentWidgetProps { onSign?: (ctx: OnSignCtx) => void; onSuccess?: (ctx: OnSuccessCtx) => void; onStatus?: (ctx: OnStatusCtx) => void; + routingAndLiquidityOptions?: RoutingAndLiquidityOptions; } export function EarnIntentWidget({ @@ -158,6 +160,7 @@ export function EarnIntentWidget({ onSign, onSuccess, onStatus, + routingAndLiquidityOptions, }: EarnIntentWidgetProps) { const sessionId = useSessionId(isOpen); const { address, isConnected, connector } = useAccount(); @@ -603,6 +606,7 @@ export function EarnIntentWidget({ walletClient, address, sessionId, + routingAndLiquidityOptions, onIntentSent, onIntentComplete, onError, diff --git a/src/components/PaySwapIntentWidget.tsx b/src/components/PaySwapIntentWidget.tsx index 57a51cd..68cea77 100644 --- a/src/components/PaySwapIntentWidget.tsx +++ b/src/components/PaySwapIntentWidget.tsx @@ -75,6 +75,7 @@ export type PaySwapIntentWidgetProps = Pick< | "usdPriceFor" | "onSourceTokenChange" | "onQuote" + | "routingAndLiquidityOptions" > & { /** `pay` vs `swap` — same SDK path; affects copy and `onStart` / internal `mode`. */ variant: "pay" | "swap"; @@ -115,6 +116,7 @@ export function PaySwapIntentWidget({ usdPriceFor, onSourceTokenChange, onQuote, + routingAndLiquidityOptions, }: PaySwapIntentWidgetProps) { // `lockDestinationToken` is a Pay-only concept — Swap UX always lets the // user pick what they receive. Force-disable for Swap regardless of the @@ -379,6 +381,7 @@ export function PaySwapIntentWidget({ sessionId, mode: variant, receiver, + routingAndLiquidityOptions, onIntentSent, onIntentComplete, onRequestClose: onClose, diff --git a/src/earn/use-earn-intent-flow.ts b/src/earn/use-earn-intent-flow.ts index f2b0b28..0cfb57e 100644 --- a/src/earn/use-earn-intent-flow.ts +++ b/src/earn/use-earn-intent-flow.ts @@ -3,6 +3,7 @@ import { keccak256, parseUnits, toBytes } from 'viem'; import type { WalletClient } from 'viem'; import { TaskType } from '@epoch-protocol/epoch-commons-sdk'; import { CollateralType, EpochIntentSDK } from '@epoch-protocol/epoch-intents-sdk'; +import type { RoutingAndLiquidityOptions } from '../types'; import type { EarnMidenCreateP2IDNote, EpochEarnMarket, @@ -84,6 +85,7 @@ interface UseEarnIntentFlowParams { sessionId: string; /** @deprecated SIO now selects the solver via smallocator; this prop is ignored. */ earnSolverUrl?: string; + routingAndLiquidityOptions?: RoutingAndLiquidityOptions; onIntentSent?: (data: IntentSentPayload) => void; onIntentComplete?: (data: IntentCompletePayload) => void; onError?: (ctx: OnErrorCtx) => void; @@ -137,6 +139,7 @@ export function useEarnIntentFlow({ address, walletClient, sessionId, + routingAndLiquidityOptions, onIntentSent, onIntentComplete, onError, @@ -385,6 +388,9 @@ export function useEarnIntentFlow({ taskTypeString, intentData, isNative: false, + ...(routingAndLiquidityOptions + ? { routingAndLiquidityOptions } + : {}), }); if (callId !== quoteCallIdRef.current || !mountedRef.current) return; @@ -419,7 +425,7 @@ export function useEarnIntentFlow({ setStatus('idle'); } }, - [address, walletClient, apiBaseUrl, buildParams], + [address, walletClient, apiBaseUrl, buildParams, routingAndLiquidityOptions], ); // ---- Poll intent status --------------------------------------------------- @@ -515,6 +521,9 @@ export function useEarnIntentFlow({ taskTypeString, intentData, isNative: false, + ...(routingAndLiquidityOptions + ? { routingAndLiquidityOptions } + : {}), }); if (!qr?.success) { throw new Error((qr as { error?: string } | undefined)?.error ?? 'Quote failed'); @@ -532,6 +541,9 @@ export function useEarnIntentFlow({ taskTypeString, intentData, quoteResult, + ...(routingAndLiquidityOptions + ? { routingAndLiquidityOptions } + : {}), }; if (submitParams.isMidenDeposit && input.midenSource) { diff --git a/src/index.ts b/src/index.ts index a9c2c56..3461e34 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,6 +38,7 @@ export type { OnErrorCtx, OnStatusCtx, WidgetLifecycleStatus, + RoutingAndLiquidityOptions, } from './types'; export { DEFAULT_THEME, LIGHT_THEME, DARK_THEME, resolveTheme, themeToCssVars } from './theme'; export { cn } from './lib/cn'; diff --git a/src/types.ts b/src/types.ts index fd6170d..ff12c5c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -26,6 +26,7 @@ export type { OneDeltaMarketRow, OneDeltaTokenRisk, OneDeltaUnderlyingAsset, + RoutingAndLiquidityOptions, SessionCtx, WidgetFlow, WidgetMode, @@ -49,6 +50,7 @@ import type { OneDeltaConfig, WidgetFlow, WidgetMode, + RoutingAndLiquidityOptions, } from '@epoch-protocol/epoch-flows-sdk'; /** SDK config plus widget-only testnet endpoint overrides. */ @@ -295,12 +297,17 @@ export interface EpochIntentWidgetProps { earnPoolsSortDir?: 'ASC' | 'DESC'; /** @deprecated retained for backwards compatibility. */ earnUseMockData?: boolean; - /** - * Optional Miden wallet adapter for funding earn deposits from Miden (testnet). + /** Optional Miden wallet adapter for funding earn deposits from Miden (testnet). * Requires a connected EVM wallet as intent sponsor; Miden supplies collateral via P2IDE note. */ earnMiden?: EarnMidenAdapter; + /** + * Restrict which solver liquidity paths SIO may quote for pay, swap, and earn flows. + * Use the same value for quote and submit. Defaults to `{ preset: "any" }` when omitted. + */ + routingAndLiquidityOptions?: RoutingAndLiquidityOptions; + // ---- API ------------------------------------------------------------------ api: ApiConfig; diff --git a/src/use-intent-flow.ts b/src/use-intent-flow.ts index f3fecb2..93cbf9b 100644 --- a/src/use-intent-flow.ts +++ b/src/use-intent-flow.ts @@ -13,6 +13,7 @@ import type { OnSignCtx, OnSuccessCtx, OnErrorCtx, + RoutingAndLiquidityOptions, WidgetFlow, } from './types'; @@ -29,6 +30,7 @@ interface UseIntentFlowParams { sessionId: string; mode: WidgetFlow; receiver?: `0x${string}`; + routingAndLiquidityOptions?: RoutingAndLiquidityOptions; onIntentSent?: (data: IntentSentPayload) => void; onIntentComplete?: (data: IntentCompletePayload) => void; onError?: (error: Error) => void; @@ -71,6 +73,7 @@ export function useIntentFlow(params: UseIntentFlowParams): UseIntentFlowReturn sessionId, mode, receiver, + routingAndLiquidityOptions, onIntentSent, onIntentComplete, onError, @@ -136,6 +139,7 @@ export function useIntentFlow(params: UseIntentFlowParams): UseIntentFlowReturn intentConfig, isTestnet, receiver, + routingAndLiquidityOptions, }); sessionRef.current = session; @@ -183,6 +187,7 @@ export function useIntentFlow(params: UseIntentFlowParams): UseIntentFlowReturn mode, isTestnet, receiver, + routingAndLiquidityOptions, // Hot deps for the session config — recreating on intent shape changes is // fine because typed-data signing is per-submit anyway. requiredToken,