feat(swap-widget): external wallet demo + chain-aware send/receive addresses#12346
Conversation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a second demo route (#external) that exercises the widget's external wallet path — the demo page itself owns Reown AppKit and feeds walletClient into <SwapWidget>. Both demos share a single theme/partner customizer panel extracted to DemoCustomizer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ingleton Drops the dual-tree dispatcher in SwapWidget: there's no longer a "host brings its own walletClient" path. The widget always reads from AppKit's global state for EVM, BTC and Solana, with the only mode difference being whether the widget itself calls createAppKit (when walletConnectProjectId is passed) or the host did so first. AppKitWalletProvider replaces the prior render-prop InternalWalletProvider and gates render on AppKit init. Public API cleanups: - Removed walletClient prop (now derived via useWalletClient internally). - Removed appUrl prop; the ShapeShift redirect URL is hardcoded. - Added allowShapeshiftRedirect (default true). When false, unsupported routes render a disabled "Route not supported" button instead of opening ShapeShift in a new tab. - Renamed enableWalletConnection -> showConnectButton; post-collapse it's purely a UI toggle for the widget's built-in Connect button. Drops the standaloneWagmiConfig file. Adds .env.example documenting the VITE_SWAP_WIDGET_API_URL override for local dev. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ters/buyFilters Public API previously had 11 flat filter props (bilateral defaults plus sell/buy variants) with implicit precedence rules. Replaced with a single SwapWidgetFilters type taken per-side via sellFilters and buyFilters. What you pass is what's used — no fallbacks to remember, no inconsistency between which lists had bilateral shortcuts. Also wires up allowedSwapperNames, which was declared on SwapWidgetProps but never plumbed: now threads SwapWidget → useSwapDisplayValues → useSwapRates so partners can actually restrict the swapper set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
onConnectWallet was vestigial: the widget can now call useAppKit().open() directly when the user clicks the action button without a connected EVM wallet, so no partner wiring is required. Previously the button silently did nothing in that state unless a partner happened to pass the callback. Also fixes the disabled-state logic so the action button stays enabled in the "Connect X Wallet" states (EVM / BTC / Solana) instead of being gated by the rate-fetch and receive-address checks. onAssetSelect was a fine-grained funnel-analytics hook with no consumer. Drop until a partner asks for an event stream. onSwapSuccess and onSwapError remain — those have real value (analytics, toasts, balance refresh on the partner side). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…it gate defaultReceiveAddress let a partner pre-fill the receive-address slot AND silently make it read-only — the user could not change where their swap proceeds were sent without noticing a 4-char-truncated address. Niche AA / vault flows can build that on top of the widget; the default behavior should always route swap proceeds to the user's wallet with a manual override available. The receive-address slot is now a button in all cases: defaults to the wallet-derived address for the buy chain, and the address modal handles overrides. Also trims AppKitWalletProvider: removes the polling fallback and the useState-init optimization. Single source of truth: useEffect inits AppKit when projectId is provided and flips isReady once the singleton is set up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace EVM-only walletAddress with sendAddress derived from the sell chain, and drop the "effective" prefix from receiveAddress. Both fields are now string | undefined throughout context, machine, and props. Introduce useEvmSigning to colocate EVM signer state alongside bitcoin and solana, exposed as evm on the wallet context so approval/execution hooks read the EVM signer the same way they read the others. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ll chain Drop the unused wagmi.ts config file (SupportedChainId was a glorified number alias; inlined). In appkit.ts, internalize the BTC/SOL adapter factories into initializeAppKit since they had no external readers, and drop unused EVM_NETWORKS/ALL_NETWORKS/SupportedNetwork/EvmNetwork exports. ConnectWalletButton now derives its state purely from sendAddress so the label tracks the sell chain's wallet rather than mixing EVM-scoped state with the unscoped AppKit account (which previously surfaced a BTC/SOL address in the EVM connect button when only those were connected). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR refactors the swap widget's address management from a wallet-address/effective-receive-address model to a per-chain send/receive-address model, centralizes AppKit initialization with guard-based readiness, and restructures the demo with hash-based routing and theme customization. ChangesSwap Widget Address & Wallet Model Refactor
Sequence DiagramsequenceDiagram
participant SwapWidgetCore
participant useEvmSigning
participant SwapMachine
participant SwapWalletContext as SwapWallet<br/>Context
participant InputStep
SwapWidgetCore->>useEvmSigning: get evm.address<br/>per sell/buy chain
useEvmSigning-->>SwapWidgetCore: walletClient, address, isConnected
SwapWidgetCore->>SwapMachine: dispatch SET_SEND_ADDRESS
SwapWidgetCore->>SwapMachine: dispatch SET_RECEIVE_ADDRESS
SwapWidgetCore->>SwapWalletContext: update context value<br/>sendAddress, receiveAddress
InputStep->>SwapWalletContext: read sendAddress,<br/>receiveAddress
InputStep->>InputStep: render button states based<br/>on address availability
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (7)
packages/swap-widget/src/components/InputStep.tsx (1)
72-72: 💤 Low valueUnused prop
showConnectButton.The
showConnectButtonprop is destructured as_showConnectButtonbut never used in the component. Either use it to conditionally control UI behavior or remove it from the props interface if it's no longer needed.🤖 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 `@packages/swap-widget/src/components/InputStep.tsx` at line 72, The prop showConnectButton is destructured as _showConnectButton in the InputStep component but never used; either remove it from the props interface/parameter list to avoid dead code (delete _showConnectButton from the destructuring and the prop type) or use it to control the UI by replacing the hardcoded connect-button render with a conditional render tied to showConnectButton (e.g., in InputStep's JSX, wrap the ConnectButton/its container with a check using the showConnectButton prop). Ensure you update both the destructuring (remove or rename) and the props type/usage (Prop interface or callers) accordingly.packages/swap-widget/src/hooks/useSwapQuoting.ts (1)
63-68: 💤 Low valueUnreachable dead code.
The check
if (!resolvedReceiveAddress)at line 65 is unreachable. SincesendAddressis guaranteed to be truthy at this point (line 58 returns early if it's falsy),resolvedReceiveAddresswill always bereceiveAddress || sendAddress, which is at minimumsendAddress(truthy).This block can be safely removed.
♻️ Suggested simplification
if (!sendAddress) { actorRef.send({ type: 'QUOTE_ERROR', error: 'No wallet address available' }) return } const resolvedReceiveAddress = receiveAddress || sendAddress - if (!resolvedReceiveAddress) { - actorRef.send({ type: 'QUOTE_ERROR', error: 'No receive address available' }) - return - } - const response = await apiClient.getQuote({🤖 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 `@packages/swap-widget/src/hooks/useSwapQuoting.ts` around lines 63 - 68, The conditional checking for `resolvedReceiveAddress` is dead code because `sendAddress` is guaranteed truthy earlier, so remove the unreachable block that sends `actorRef.send({ type: 'QUOTE_ERROR', error: 'No receive address available' })`; instead, keep the assignment const resolvedReceiveAddress = receiveAddress || sendAddress and proceed with the existing quoting logic in useSwapQuoting (referencing resolvedReceiveAddress, receiveAddress, sendAddress, and actorRef) without the redundant null-check to simplify the flow.packages/swap-widget/src/demo/InternalWalletApp.tsx (1)
25-27: ⚡ Quick winSurface swap failures through toast feedback.
Line 25 only logs errors; users won’t get immediate failure feedback. Use
useErrorToastinhandleSwapError.As per coding guidelines "ALWAYS use
useErrorToasthook for displaying errors with translated error messages and handle different error types appropriately".🤖 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 `@packages/swap-widget/src/demo/InternalWalletApp.tsx` around lines 25 - 27, The handleSwapError callback currently only logs errors to console; update it to use the useErrorToast hook to display a translated toast to users and handle different error shapes (Error, string, or custom error objects). Import and call useErrorToast inside the component, then inside handleSwapError invoke the toast with a user-friendly, translated message (include error.message or mapped messages for known error types) and still keep console.error for debugging; ensure the handler signature remains handleSwapError(error: Error | unknown) so it safely normalizes the error before passing text to useErrorToast.packages/swap-widget/src/demo/ExternalWalletApp.tsx (1)
74-76: ⚡ Quick winShow swap errors to users, not only the console.
Line 74 currently logs failures only. Wire
onSwapErrortouseErrorToastso failed swaps are visible in the demo UI.As per coding guidelines "ALWAYS use
useErrorToasthook for displaying errors with translated error messages and handle different error types appropriately".🤖 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 `@packages/swap-widget/src/demo/ExternalWalletApp.tsx` around lines 74 - 76, The handler currently only logs errors to console; import and call the useErrorToast hook in ExternalWalletApp.tsx, create a showError function (e.g., const showError = useErrorToast()) and update handleSwapError to call showError(error, 'Swap failed') (and optionally keep console.error) so users see a translated UI toast. Then wire that handler into the swap component by passing handleSwapError to the onSwapError prop (ensure the function name handleSwapError and the onSwapError prop are used so the demo UI surfaces failed swaps).packages/swap-widget/src/components/WalletProvider.tsx (1)
19-39: 💤 Low valuePotential rendering issue when
projectIdis omitted but AppKit was initialized externally.If
projectIdisundefined,initializeAppKitis not called, butisAppKitInitialized()is still checked. If AppKit was initialized elsewhere (e.g., external wallet demo), this works. However, if neither provides initialization, the component returnsnullforever. This seems intentional for the external-wallet case but may warrant a comment for clarity.📝 Suggested clarifying comment
export const AppKitWalletProvider = ({ projectId, children }: AppKitWalletProviderProps) => { const [isReady, setIsReady] = useState(false) useEffect(() => { + // Initialize AppKit only if projectId is provided (internal wallet mode). + // External wallet mode expects AppKit to be initialized by the host app. if (projectId) initializeAppKit(projectId) if (isAppKitInitialized()) setIsReady(true) }, [projectId])🤖 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 `@packages/swap-widget/src/components/WalletProvider.tsx` around lines 19 - 39, The AppKitWalletProvider currently only calls initializeAppKit(projectId) when projectId is provided but still checks isAppKitInitialized(), which means the component intentionally allows external initialization but will return null if neither initializes AppKit; add a clear comment inside AppKitWalletProvider near the useEffect and the early return (referencing initializeAppKit, isAppKitInitialized, projectId, isReady, and the wagmiConfig early return) explaining that omission of projectId expects external initialization and that the component will render null until AppKit is initialized to avoid silently mounting with an invalid wagmiConfig.packages/swap-widget/src/components/SwapWidget.tsx (1)
424-430: 💤 Low valueConsider consolidating address dispatch effects.
Two separate
useEffecthooks dispatch address changes to the actor. While functionally correct, combining them could reduce effect overhead when both addresses change simultaneously (e.g., on initial wallet connection).♻️ Optional consolidation
- useEffect(() => { - actorRef.send({ type: 'SET_SEND_ADDRESS', address: sendAddress }) - }, [sendAddress, actorRef]) - - useEffect(() => { - actorRef.send({ type: 'SET_RECEIVE_ADDRESS', address: receiveAddress }) - }, [receiveAddress, actorRef]) + useEffect(() => { + actorRef.send({ type: 'SET_SEND_ADDRESS', address: sendAddress }) + actorRef.send({ type: 'SET_RECEIVE_ADDRESS', address: receiveAddress }) + }, [sendAddress, receiveAddress, actorRef])🤖 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 `@packages/swap-widget/src/components/SwapWidget.tsx` around lines 424 - 430, Two separate useEffect hooks dispatch SET_SEND_ADDRESS and SET_RECEIVE_ADDRESS to actorRef; consolidate them into a single useEffect that watches [sendAddress, receiveAddress, actorRef] and sends both updates in one effect (call actorRef.send for SET_SEND_ADDRESS and SET_RECEIVE_ADDRESS sequentially or send a combined action if the actor supports it) so address updates emitted at the same time (e.g., on wallet connect) only trigger one effect invocation.packages/swap-widget/src/config/appkit.ts (1)
65-68: 💤 Low valueRemove unnecessary
as anycast on Solana wallet adapters array.The
as anycast is not required by the SolanaAdapter API and violates TypeScript strict mode type checking. All official examples show the wallets array works correctly without the cast. Removing it will maintain proper type safety:const solanaAdapter = new SolanaAdapter({ wallets: [new PhantomWalletAdapter(), new SolflareWalletAdapter()], })🤖 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 `@packages/swap-widget/src/config/appkit.ts` around lines 65 - 68, Remove the unnecessary type cast on the Solana wallets array: in the SolanaAdapter instantiation (SolanaAdapter) where wallets is set with new PhantomWalletAdapter() and new SolflareWalletAdapter(), drop the trailing "as any" so the wallets property is passed with its natural type; ensure the wallets array uses [new PhantomWalletAdapter(), new SolflareWalletAdapter()] directly to satisfy TypeScript strict mode and the SolanaAdapter API.
🤖 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 `@packages/swap-widget/src/demo/DemoCustomizer.tsx`:
- Around line 167-170: The clipboard write call in DemoCustomizer using
navigator.clipboard.writeText(code) can reject and is currently unhandled; add
explicit rejection handling by attaching a .catch to the promise (or use
async/try-catch if converting the handler to async) to set a user-visible error
state/feedback (e.g., setCopyError or show a toast/alert) and ensure setCopied
is not set true on failure, and implement a fallback copy method (create a
hidden textarea, select, document.execCommand('copy')) invoked when
navigator.clipboard.writeText fails; update the copy handler that calls
navigator.clipboard.writeText, and keep using setCopied to indicate success only
after the operation or fallback succeeds.
In `@packages/swap-widget/src/demo/main.tsx`:
- Around line 25-28: The demo root currently renders <Router /> directly via
ReactDOM.createRoot(rootElement).render(...) so a runtime error in Router will
blank the app; wrap the root render with an Error Boundary component (e.g.,
create a simple ErrorBoundary class or use an existing ErrorBoundary utility)
and provide a user-friendly fallback UI and error logging inside its
componentDidCatch (or onError handler) so that errors from Router are caught,
logged (include the error and info), and a fallback UI is shown instead of a
blank screen; update the ReactDOM.createRoot(...).render call to render
<ErrorBoundary><Router /></ErrorBoundary> (using rootElement and Router names to
locate the code).
---
Nitpick comments:
In `@packages/swap-widget/src/components/InputStep.tsx`:
- Line 72: The prop showConnectButton is destructured as _showConnectButton in
the InputStep component but never used; either remove it from the props
interface/parameter list to avoid dead code (delete _showConnectButton from the
destructuring and the prop type) or use it to control the UI by replacing the
hardcoded connect-button render with a conditional render tied to
showConnectButton (e.g., in InputStep's JSX, wrap the ConnectButton/its
container with a check using the showConnectButton prop). Ensure you update both
the destructuring (remove or rename) and the props type/usage (Prop interface or
callers) accordingly.
In `@packages/swap-widget/src/components/SwapWidget.tsx`:
- Around line 424-430: Two separate useEffect hooks dispatch SET_SEND_ADDRESS
and SET_RECEIVE_ADDRESS to actorRef; consolidate them into a single useEffect
that watches [sendAddress, receiveAddress, actorRef] and sends both updates in
one effect (call actorRef.send for SET_SEND_ADDRESS and SET_RECEIVE_ADDRESS
sequentially or send a combined action if the actor supports it) so address
updates emitted at the same time (e.g., on wallet connect) only trigger one
effect invocation.
In `@packages/swap-widget/src/components/WalletProvider.tsx`:
- Around line 19-39: The AppKitWalletProvider currently only calls
initializeAppKit(projectId) when projectId is provided but still checks
isAppKitInitialized(), which means the component intentionally allows external
initialization but will return null if neither initializes AppKit; add a clear
comment inside AppKitWalletProvider near the useEffect and the early return
(referencing initializeAppKit, isAppKitInitialized, projectId, isReady, and the
wagmiConfig early return) explaining that omission of projectId expects external
initialization and that the component will render null until AppKit is
initialized to avoid silently mounting with an invalid wagmiConfig.
In `@packages/swap-widget/src/config/appkit.ts`:
- Around line 65-68: Remove the unnecessary type cast on the Solana wallets
array: in the SolanaAdapter instantiation (SolanaAdapter) where wallets is set
with new PhantomWalletAdapter() and new SolflareWalletAdapter(), drop the
trailing "as any" so the wallets property is passed with its natural type;
ensure the wallets array uses [new PhantomWalletAdapter(), new
SolflareWalletAdapter()] directly to satisfy TypeScript strict mode and the
SolanaAdapter API.
In `@packages/swap-widget/src/demo/ExternalWalletApp.tsx`:
- Around line 74-76: The handler currently only logs errors to console; import
and call the useErrorToast hook in ExternalWalletApp.tsx, create a showError
function (e.g., const showError = useErrorToast()) and update handleSwapError to
call showError(error, 'Swap failed') (and optionally keep console.error) so
users see a translated UI toast. Then wire that handler into the swap component
by passing handleSwapError to the onSwapError prop (ensure the function name
handleSwapError and the onSwapError prop are used so the demo UI surfaces failed
swaps).
In `@packages/swap-widget/src/demo/InternalWalletApp.tsx`:
- Around line 25-27: The handleSwapError callback currently only logs errors to
console; update it to use the useErrorToast hook to display a translated toast
to users and handle different error shapes (Error, string, or custom error
objects). Import and call useErrorToast inside the component, then inside
handleSwapError invoke the toast with a user-friendly, translated message
(include error.message or mapped messages for known error types) and still keep
console.error for debugging; ensure the handler signature remains
handleSwapError(error: Error | unknown) so it safely normalizes the error before
passing text to useErrorToast.
In `@packages/swap-widget/src/hooks/useSwapQuoting.ts`:
- Around line 63-68: The conditional checking for `resolvedReceiveAddress` is
dead code because `sendAddress` is guaranteed truthy earlier, so remove the
unreachable block that sends `actorRef.send({ type: 'QUOTE_ERROR', error: 'No
receive address available' })`; instead, keep the assignment const
resolvedReceiveAddress = receiveAddress || sendAddress and proceed with the
existing quoting logic in useSwapQuoting (referencing resolvedReceiveAddress,
receiveAddress, sendAddress, and actorRef) without the redundant null-check to
simplify the flow.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b6c33dc7-9c80-41d2-9971-4dbc49cdf5eb
📒 Files selected for processing (33)
packages/swap-widget/.env.examplepackages/swap-widget/src/components/AddressInputModal.tsxpackages/swap-widget/src/components/InputStep.tsxpackages/swap-widget/src/components/SwapWidget.tsxpackages/swap-widget/src/components/TokenSelectModal.tsxpackages/swap-widget/src/components/WalletProvider.tsxpackages/swap-widget/src/config/appkit.tspackages/swap-widget/src/config/standaloneWagmi.tspackages/swap-widget/src/config/wagmi.tspackages/swap-widget/src/constants/swappers.tspackages/swap-widget/src/contexts/SwapWalletContext.tsxpackages/swap-widget/src/demo/App.csspackages/swap-widget/src/demo/App.tsxpackages/swap-widget/src/demo/DemoCustomizer.tsxpackages/swap-widget/src/demo/ExternalWalletApp.tsxpackages/swap-widget/src/demo/InternalWalletApp.tsxpackages/swap-widget/src/demo/main.tsxpackages/swap-widget/src/hooks/useBalances.tspackages/swap-widget/src/hooks/useEvmSigning.tspackages/swap-widget/src/hooks/useSwapApproval.tspackages/swap-widget/src/hooks/useSwapDisplayValues.tspackages/swap-widget/src/hooks/useSwapExecution.tspackages/swap-widget/src/hooks/useSwapHandlers.tspackages/swap-widget/src/hooks/useSwapQuoting.tspackages/swap-widget/src/index.tspackages/swap-widget/src/machines/__tests__/guards.test.tspackages/swap-widget/src/machines/__tests__/swapMachine.test.tspackages/swap-widget/src/machines/__tests__/types.test.tspackages/swap-widget/src/machines/guards.tspackages/swap-widget/src/machines/swapMachine.tspackages/swap-widget/src/machines/types.tspackages/swap-widget/src/types/index.tspackages/swap-widget/src/utils/redirect.ts
💤 Files with no reviewable changes (3)
- packages/swap-widget/src/demo/App.tsx
- packages/swap-widget/src/config/wagmi.ts
- packages/swap-widget/src/config/standaloneWagmi.ts
Description
Adds an external-wallet demo for the swap-widget and lands the supporting refactors that came out of building it. The widget now models the wallet boundary symmetrically across chains: a chain-aware
sendAddress(derived from the sell asset's chain) mirrors the existing receive-side behaviour, dropping the "effective" prefix and the EVM-onlywalletAddressleak.Commit-level overview (in order):
ExternalWalletApproute where the host page owns AppKit and the widget reads it via the singleton. Demo customizer is now shared between internal and external apps.sellFilters/buyFilters— single nested-object props instead of four flat ones.onConnectWalletandonAssetSelectcallbacks — partners interact via the widget's own UI; the callback surface wasn't load-bearing.defaultReceiveAddressprop, simplify AppKit gate —defaultReceiveAddresswas unused after the chain-aware receive plumbing; the AppKit gate no longer needs the explicit guard.walletAddress→sendAddress(chain-aware via sell chain),effectiveReceiveAddress→receiveAddress, bothstring | undefined. NewuseEvmSigninghook colocates EVM signer state alongsideuseBitcoinSigning/useSolanaSigning, exposed asevmonSwapWalletContext. Approval and execution hooks readevm.{walletClient,address}instead of context-level fields. Tests and machine event names updated.src/config/wagmi.ts(only export was anumberalias), internalize Bitcoin/Solana adapter factories ininitializeAppKit, drop unused exports.ConnectWalletButtonnow derives label state fromsendAddressso it correctly tracks the sell chain rather than mixing EVM-scopedevm.*with the unscoped AppKit account (which previously surfaced BTC/SOL addresses in the EVM-flavoured button).Issue (if applicable)
closes #
Risk
Low-to-medium. No on-chain transaction shape changes. The signing call sites (
useSwapApproval,useSwapExecution) now read the EVM signer throughevm.walletClient/evm.addressrather than top-level context fields — same underlying AppKit hook output, just routed through a named field. QuotesendAddressis now chain-aware end-to-end; previously the quoting hook branched per chain to pick the right address, so the on-wire payload is identical.Affected surface:
walletAddress/effectiveReceiveAddresscontext fields renamed; not exported to consumersTesting
Engineering
pnpm -w run type-check— clean.pnpm -w run lint— clean for swap-widget (only pre-existing warnings inhdwallet-*packages remain).pnpm --filter @shapeshiftoss/swap-widget test— machine + guard tests cover the renamed events/fields./): connect via the widget's own button; verify the badge in the sell section shows the chain-correct address as you flip the sell asset between EVM / BTC / SOL. Run a small EVM → EVM and EVM → BTC swap end-to-end.#external): connect via the host header button; verify the widget reflects the same connection state and that quote → approve → execute still works without the widget owning init.Operations
The swap-widget package is a standalone embed; it isn't surfaced in the main web app's runtime paths. Internal demo at
/and external demo at#externalof the widget's dev server.Screenshots (if applicable)
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Refactor
Documentation