aegis-swap-widget 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +200 -0
  2. package/DESIGN.md +77 -0
  3. package/LICENSE +21 -0
  4. package/README.md +346 -24
  5. package/dist/abis.d.ts +18 -1
  6. package/dist/address.d.ts +5 -0
  7. package/dist/aegis-swap-widget.js +2716 -1470
  8. package/dist/color.d.ts +2 -0
  9. package/dist/components/icons.d.ts +30 -0
  10. package/dist/config.d.ts +3 -2
  11. package/dist/core.d.ts +15 -0
  12. package/dist/core.js +66 -0
  13. package/dist/encoding.d.ts +5 -1
  14. package/dist/errors.d.ts +3 -0
  15. package/dist/format.d.ts +3 -3
  16. package/dist/hooks/useChangeEmitter.d.ts +25 -0
  17. package/dist/hooks/useLifecycleToast.d.ts +16 -0
  18. package/dist/hooks/usePoolChangeEmitter.d.ts +1 -0
  19. package/dist/hooks/useResolvedTheme.d.ts +4 -0
  20. package/dist/hooks/useStableValue.d.ts +2 -0
  21. package/dist/hooks/useStatusChangeEmitter.d.ts +2 -0
  22. package/dist/hooks/useSubmitSwap.d.ts +75 -0
  23. package/dist/hooks/useSwapQuote.d.ts +30 -0
  24. package/dist/hooks/useTokenAllowance.d.ts +27 -0
  25. package/dist/hooks/useTokenBalances.d.ts +15 -0
  26. package/dist/hooks/useTokenIconMetadata.d.ts +7 -0
  27. package/dist/hooks/useValueRefresh.d.ts +1 -0
  28. package/dist/hooks/useWalletConnection.d.ts +18 -0
  29. package/dist/index.d.ts +12 -46
  30. package/dist/metadata.d.ts +8 -1
  31. package/dist/permit.d.ts +31 -1
  32. package/dist/polling-BI4pZg8Z.js +1252 -0
  33. package/dist/polling.d.ts +3 -0
  34. package/dist/quote.d.ts +30 -2
  35. package/dist/styles.css +1 -1
  36. package/dist/swap/authorizePermit.d.ts +30 -0
  37. package/dist/swap/executeSwapTransaction.d.ts +37 -0
  38. package/dist/swap/types.d.ts +7 -0
  39. package/dist/toast/ToastItem.d.ts +33 -0
  40. package/dist/toast/Toaster.d.ts +31 -0
  41. package/dist/toast/layout.d.ts +21 -0
  42. package/dist/toast/state.d.ts +69 -0
  43. package/dist/toast/swipe.d.ts +23 -0
  44. package/dist/toast/timer.d.ts +16 -0
  45. package/dist/toast/toast.d.ts +28 -0
  46. package/dist/toast/useDocumentVisible.d.ts +6 -0
  47. package/dist/toast/useToastTimer.d.ts +13 -0
  48. package/dist/transactionProgress.d.ts +42 -0
  49. package/dist/wallet.d.ts +19 -8
  50. package/dist/widget/AegisDirectSwapWidget.d.ts +20 -0
  51. package/dist/widget/AmountField.d.ts +20 -0
  52. package/dist/widget/ExecutionDetails.d.ts +27 -0
  53. package/dist/widget/NetworkSelector.d.ts +6 -0
  54. package/dist/widget/PoolSelector.d.ts +13 -0
  55. package/dist/widget/ProgressRail.d.ts +23 -0
  56. package/dist/widget/types.d.ts +129 -0
  57. package/dist/widget/useSwapController.d.ts +89 -0
  58. package/dist/widget/viewState.d.ts +55 -0
  59. package/package.json +45 -8
  60. package/dist/abis.d.ts.map +0 -1
  61. package/dist/config.d.ts.map +0 -1
  62. package/dist/encoding.d.ts.map +0 -1
  63. package/dist/format.d.ts.map +0 -1
  64. package/dist/index.d.ts.map +0 -1
  65. package/dist/metadata.d.ts.map +0 -1
  66. package/dist/permit.d.ts.map +0 -1
  67. package/dist/quote.d.ts.map +0 -1
  68. package/dist/wallet.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,16 +1,17 @@
1
1
  # Aegis Swap Widget
2
2
 
3
- Embeddable React swap widget for Aegis direct pools. It does not import the private Aegis Engine SDK and does not use Uniswap widget packages.
3
+ Embeddable React swap widget for Aegis direct pools. Import the component, import the CSS, and configure pools, wallet handoff, and host styling through props.
4
4
 
5
- The widget quotes the configured Uniswap v4 pool, handles ERC20 approval to Permit2, signs a Permit2 batch for the Aegis router, and submits the direct pool swap.
5
+ The host app normally only renders `<AegisSwapWidget />`. Quoting, token approval, Permit2 signing, and swap submission are handled inside the widget.
6
6
 
7
7
  Typing in the Sell field creates an exact-input swap. Typing in the Buy field
8
8
  creates an exact-output swap. Active quotes refresh every five seconds while the
9
9
  amount is valid.
10
10
 
11
- On Ethereum mainnet, widget swaps apply a fixed 50 bps output-token fee to
12
- `0x8ea2C7de60ef1Cd4dcE64ea2DDDe2f99F0127eFC`. The widget does not apply this
13
- fee on other chains yet.
11
+ Widget swaps apply an internal output-token fee per configured chain. The bundled
12
+ Ethereum mainnet config carries a fixed 50 bps fee to
13
+ `0x8ea2C7de60ef1Cd4dcE64ea2DDDe2f99F0127eFC`. A chain carries a fee only when its
14
+ bundled config sets one; today only Ethereum mainnet is bundled.
14
15
 
15
16
  ## Install
16
17
 
@@ -18,6 +19,9 @@ fee on other chains yet.
18
19
  npm install aegis-swap-widget
19
20
  ```
20
21
 
22
+ Peer dependencies (provided by the host app): `react >=18 <20`, `react-dom >=18 <20`,
23
+ and `viem ^2`. The package is ESM-only and ships no runtime dependencies of its own.
24
+
21
25
  ## Basic Usage
22
26
 
23
27
  ```tsx
@@ -25,12 +29,7 @@ import { AegisSwapWidget } from 'aegis-swap-widget'
25
29
  import 'aegis-swap-widget/styles.css'
26
30
 
27
31
  export function Swap() {
28
- return (
29
- <AegisSwapWidget
30
- theme="dark"
31
- accentColor="#31d48a"
32
- />
33
- )
32
+ return <AegisSwapWidget />
34
33
  }
35
34
  ```
36
35
 
@@ -43,6 +42,32 @@ Token icons are hydrated from the same metadata table used by the Aegis website:
43
42
  `GlobalToken.iconURL` from the Aegis metadata GraphQL API. The default endpoint is
44
43
  `https://aegis.markets/graphql/meta`.
45
44
 
45
+ ## Headless Core
46
+
47
+ Use the root import for the React widget and CSS. Use the `/core` entry when a
48
+ host, backend-backed client, or future query layer needs the swap primitives
49
+ without importing the widget UI:
50
+
51
+ ```ts
52
+ import {
53
+ AEGIS_SWAP_MARKETS,
54
+ createSwapPublicClient,
55
+ fetchDirectSwapQuote,
56
+ } from 'aegis-swap-widget/core'
57
+ ```
58
+
59
+ The core entry exports chain and pool config helpers, quote fetching, calldata
60
+ encoding, Permit2 helpers, wallet/provider utilities, metadata fetchers, and the
61
+ pure transaction-progress helpers. It does not import the React widget or the
62
+ package stylesheet.
63
+
64
+ To render token icons with the widget's exact resolution order (config
65
+ `iconUrl` → metadata endpoint → ICHI guess), use `resolveTokenIconUrl`:
66
+
67
+ ```ts
68
+ import { resolveTokenIconUrl, fetchGlobalTokenMetadata } from 'aegis-swap-widget/core'
69
+ ```
70
+
46
71
  ## Appearance
47
72
 
48
73
  Hosts can choose the widget surface and brand accent at instantiation time:
@@ -55,7 +80,13 @@ Hosts can choose the widget surface and brand accent at instantiation time:
55
80
  />
56
81
  ```
57
82
 
58
- `theme` supports `dark` and `light`. `accentColor` accepts any CSS color value and is used for the CTA, selected pools, focus states, links, and swap controls. `accentTextColor` is optional and only needed when the accent color needs a different CTA text color for contrast.
83
+ `theme` supports `dark`, `light`, or `auto` (which follows the host's `prefers-color-scheme`). `accentColor` accepts any CSS color value and is used for the CTA, selected pools, focus states, links, and swap controls. The widget auto-derives a readable CTA text color from `accentColor` — for hex, `rgb()`, and `hsl()` values everywhere, and for any CSS color (named, `oklch()`, …) in the browser. `accentTextColor` overrides that and is the deterministic choice when you want an exact CTA text color (or are rendering a named/`oklch()` accent during SSR).
84
+
85
+ The default accent is neutral green. Pass `accentColor`, `accentTextColor`, `title`, `subtitle`, `className`, or `style` when the widget should blend into another product UI.
86
+
87
+ The default palette is authored in OKLCH (tinted neutrals, a calmer accent). The
88
+ full set of host-overridable CSS variables — color, motion, rail, and toast
89
+ tokens — is documented in [DESIGN.md](./DESIGN.md).
59
90
 
60
91
  ## Configure Pools
61
92
 
@@ -86,24 +117,27 @@ export function Swap() {
86
117
  }
87
118
  ```
88
119
 
89
- Use `defaultPoolId` to choose which pool opens first. Use `targets` when a host wants a different pool order or subset:
120
+ Use `defaultPoolId` to choose which pool opens first. Use `poolIds` when a host wants a different pool order or subset:
90
121
 
91
122
  ```tsx
92
123
  <AegisSwapWidget
93
124
  pools={pools}
94
125
  defaultPoolId="GRASS_USDC"
95
- targets={['GRASS_USDC', 'WATER_USDC']}
126
+ poolIds={['GRASS_USDC', 'WATER_USDC']}
96
127
  />
97
128
  ```
98
129
 
99
130
  ## Fees
100
131
 
101
- The widget applies a fixed output-token fee only on Ethereum mainnet:
132
+ The widget applies an internal output-token fee per chain. Each bundled chain config
133
+ carries its own fee. Today only Ethereum mainnet is bundled:
102
134
 
103
135
  - Fee: `50` bps
104
136
  - Recipient: `0x8ea2C7de60ef1Cd4dcE64ea2DDDe2f99F0127eFC`
105
137
 
106
- This fee is internal to the widget and is not configured by host sites.
138
+ This fee is internal to the widget and is not configured by host sites — host `chains`
139
+ overrides cannot set or change it. Chains added later carry a fee only when their
140
+ bundled config provides one.
107
141
 
108
142
  For custom assets, provide full pool and token metadata:
109
143
 
@@ -255,24 +289,291 @@ host connect modal creates the provider during the button click:
255
289
  If none of these props are supplied, the widget still checks `window.ethereum`
256
290
  automatically when it renders and again when the user clicks connect/swap.
257
291
 
292
+ ## UI Integration
293
+
294
+ Use `className` for host CSS selectors and `style` for one-off layout overrides or CSS variables:
295
+
296
+ ```tsx
297
+ <AegisSwapWidget
298
+ className="partner-swap"
299
+ style={{ maxWidth: 520 }}
300
+ title="Swap"
301
+ subtitle="Aegis direct pools"
302
+ ariaLabel="Partner swap"
303
+ />
304
+ ```
305
+
306
+ `accentColor`, `accentTextColor`, `title`, and `subtitle` are the intended props for making the widget fit a host surface without forking the component.
307
+
308
+ ## Transaction Status
309
+
310
+ The widget paints swap progress on a rail along its own top edge — an accent
311
+ sweep while pending/mining, solid green on success, solid red on errors
312
+ (including wallet rejections), then a short hold and fade. The rail is the
313
+ default of two built-in status surfaces; `statusSurface` switches to toast
314
+ notifications, shows `'both'`, or `'none'` (see Toasts below). Hosts can tune
315
+ the rail through the widget's `style` prop:
316
+
317
+ ```tsx
318
+ <AegisSwapWidget
319
+ style={{
320
+ '--aegis-rail-height': '4px',
321
+ '--aegis-rail-hold': '1800ms',
322
+ '--aegis-rail-fade': '260ms'
323
+ }}
324
+ />
325
+ ```
326
+
327
+ Use CSS time strings (`ms` or `s`) for `--aegis-rail-hold` and
328
+ `--aegis-rail-fade`; the widget reads the computed tokens at runtime so the
329
+ success auto-reset stays in sync with the host-tuned animation. Hosts can
330
+ mirror that state outside the widget three ways:
331
+
332
+ `onStatusChange` fires a snapshot on every lifecycle transition. `walletPrompts`
333
+ is the SDK-owned provider-prompt plan: ERC20 approval, Permit2 authorization,
334
+ and swap submission are counted from the current allowance/authorization reads.
335
+ Use that for step copy or external rails instead of guessing from status names:
336
+
337
+ ```tsx
338
+ <AegisSwapWidget
339
+ onStatusChange={({ status, progress, walletPrompts, txHash, error }) => {
340
+ // status: 'idle' | 'approving' | 'signing' | 'submitting' | 'success'
341
+ // progress: 'pending' | 'mining' | 'success' | 'error' | null (rail hidden)
342
+ // walletPrompts: { total, completed, active } // active is 1-based or null
343
+ // error: user-facing message, e.g. "Request rejected."
344
+ }}
345
+ />
346
+ ```
347
+
348
+ The rail separates step completion from activity: `completePct` fills the calm
349
+ completed portion (for example 33.3% after one of three wallet prompts), while
350
+ the pending/mining shimmer is always a full-width top-border sweep.
351
+
352
+ Errors never appear in `status` (the submit machine resets to `'idle'`); read
353
+ them from `progress === 'error'` plus `error`. For classifying the raw `Error`
354
+ from `onError`, `isUserRejectedError` is exported.
355
+
356
+ Success clears itself after confirming (by default a 3s hold plus the 420ms
357
+ rail fade-out), the widget resets and emits a final
358
+ `{ status: 'idle', progress: null }` snapshot. Errors persist until the
359
+ user interacts with the widget again — the in-widget rail fades out visually,
360
+ but the last snapshot and `data-progress-state` keep reporting `'error'`,
361
+ matching the widget's own inline error message.
362
+
363
+ `AegisSwapProgressRail` renders the animated rail standalone — mount it inside
364
+ any `position: relative` container and feed it the snapshot's `progress`:
365
+
366
+ ```tsx
367
+ import { useState } from 'react'
368
+ import {
369
+ AegisSwapProgressRail,
370
+ AegisSwapWidget,
371
+ type AegisSwapStatusSnapshot
372
+ } from 'aegis-swap-widget'
373
+ import 'aegis-swap-widget/styles.css'
374
+
375
+ export function CheckoutHeader() {
376
+ const [status, setStatus] = useState<AegisSwapStatusSnapshot | null>(null)
377
+
378
+ return (
379
+ <>
380
+ <AegisSwapWidget onStatusChange={setStatus} />
381
+
382
+ <header style={{ position: 'relative' }}>
383
+ <AegisSwapProgressRail
384
+ state={status?.progress ?? null}
385
+ label={status?.label ?? undefined}
386
+ completePct={status?.completePct ?? undefined}
387
+ activeWidthPct={status?.activeWidthPct ?? undefined}
388
+ />
389
+ My checkout
390
+ </header>
391
+ </>
392
+ )
393
+ }
394
+ ```
395
+
396
+ Driven from `progress` alone, the standalone rail uses simplified geometry — a
397
+ full-width sweep while in flight, solid fill when terminal. Feed the snapshot's
398
+ `completePct` and `activeWidthPct` to mirror the in-widget prompt-aware fill.
399
+ Theme it by setting `--aegis-swap-accent` (plus
400
+ `--aegis-swap-danger` and `--aegis-swap-success` for the terminal fills) on an
401
+ ancestor. `--aegis-rail-height`, `--aegis-rail-hold`, and
402
+ `--aegis-rail-fade` apply to the standalone rail as CSS too; only the in-widget
403
+ auto-reset timer reads those motion tokens. The built-in color fallbacks are
404
+ the dark-theme values, so light hosts should set them.
405
+
406
+ For CSS-only hooks, the widget root mirrors the rail as
407
+ `data-progress-state="pending | mining | success | error"`, so a host
408
+ stylesheet can react without JavaScript. The attribute stays on for every
409
+ `statusSurface`, including `'toast'` and `'none'`.
410
+
411
+ ## Toasts
412
+
413
+ `statusSurface="toast"` replaces the rail with toast notifications; `'both'`
414
+ shows rail and toasts, `'none'` neither. With `'toast'` or `'both'` the widget
415
+ provides its own toast viewport — no extra setup:
416
+
417
+ ```tsx
418
+ <AegisSwapWidget statusSurface="toast" toastPosition="bottom-right" />
419
+ ```
420
+
421
+ One toast per swap attempt morphs through the lifecycle: a loading card
422
+ follows the same wallet-prompt copy as the rail, then settles into success
423
+ (received amount plus a View transaction link) or error in place. Wallet
424
+ rejections auto-dismiss after 4s and other errors after 8s — the widget's
425
+ inline error message and `data-progress-state` persist regardless. A retry
426
+ withdraws the previous attempt's toast and starts a fresh one.
427
+
428
+ Mounting `<AegisSwapToaster />` anywhere in the app takes over rendering from
429
+ every widget-provided toaster — exactly one toast viewport exists per page —
430
+ which is how hosts control placement and toast options:
431
+
432
+ ```tsx
433
+ import { AegisSwapToaster, AegisSwapWidget } from 'aegis-swap-widget'
434
+ import 'aegis-swap-widget/styles.css'
435
+
436
+ <AegisSwapWidget statusSurface="toast" />
437
+ <AegisSwapToaster position="top-right" theme="light" accentColor="#006adc" closeButton />
438
+ ```
439
+
440
+ `AegisSwapToaster` props: `position` (`top`/`bottom` × `left`/`center`/`right`,
441
+ default `'bottom-right'`), `theme`/`accentColor`/`accentTextColor` (the toaster
442
+ portals to `document.body`, so widget-root accent variables do not reach it —
443
+ pass these props or set `--aegis-swap-accent` and friends on `:root`),
444
+ `duration` (auto-dismiss for info/success, default 5000ms), `visibleToasts`
445
+ (default 3), `closeButton` (default `false` — swipe and auto-dismiss always
446
+ work), `expand` (always-expanded list instead of the hover-expanded stack),
447
+ `offset` (viewport inset, default 24px / 16px on small screens), `className`,
448
+ and `style` (e.g. `'--aegis-swap-toast-z'`, `'--aegis-swap-toast-width'`).
449
+
450
+ The toast system is the widget's own implementation, not a dependency: toasts
451
+ stack with depth, hovering (or focusing) the stack expands it into a list,
452
+ swiping toward the nearest viewport edge dismisses (momentum-based — a quick
453
+ flick is enough), auto-dismiss timers pause while hovered, while swiping, and
454
+ in hidden tabs, and `prefers-reduced-motion` reduces all motion to fades.
455
+
456
+ `aegisToast` is exported so host apps can fire their own toasts through the
457
+ same viewport — a plain function, no hooks or context:
458
+
459
+ ```tsx
460
+ import { aegisToast } from 'aegis-swap-widget'
461
+
462
+ aegisToast('Copied') // info
463
+ aegisToast.success('Saved', { description: 'Order #1204' })
464
+ aegisToast.error('Could not save', { duration: 8000 })
465
+
466
+ const id = aegisToast.loading('Uploading…') // sticky until updated
467
+ aegisToast.success('Uploaded', { id }) // reusing an id morphs in place
468
+
469
+ aegisToast.promise(save(), {
470
+ loading: 'Saving…',
471
+ success: (result) => `Saved ${result.name}`,
472
+ error: (reason) => (reason instanceof Error ? reason.message : 'Save failed')
473
+ })
474
+ ```
475
+
476
+ Every creator returns the toast id. Actions render as a new-tab link
477
+ (`{ label, href }`) or a button (`{ label, onClick }`); acting on a toast
478
+ dismisses it. `duration: Infinity` keeps a toast until dismissed;
479
+ `dismissible: false` hides the close affordance and blocks swiping. One caveat
480
+ shared with every module-store toast library: two bundled copies of the
481
+ package keep two independent toast stores (and two viewports).
482
+
483
+ ## Motion
484
+
485
+ Switching pools or the approval mode glides a single active pill between the
486
+ options (iOS-segmented-control style), and the Sell/Buy token pills cross-fade
487
+ when a switch retargets them. It is all CSS — no animation library — and every
488
+ animation respects `prefers-reduced-motion` automatically.
489
+
490
+ Hosts tune or disable the switch motion through CSS tokens on the `style` prop:
491
+
492
+ ```tsx
493
+ <AegisSwapWidget
494
+ style={{
495
+ '--aegis-switch-dur': '180ms', // pill glide + token swap; '0ms' disables
496
+ '--aegis-spring': 'cubic-bezier(0.2, 0, 0, 1)' // switch easing
497
+ }}
498
+ />
499
+ ```
500
+
501
+ ## Migrating to 0.3.0
502
+
503
+ 0.3.0 removes the props that were deprecated in 0.2.x. The replacements are
504
+ drop-in:
505
+
506
+ | Removed | Use instead |
507
+ | --- | --- |
508
+ | `targets={[...]}` | `poolIds={[...]}` (same `string[]`) |
509
+ | `markets={map}` | `pools={Object.values(map)}` |
510
+ | `defaultDirection="intoTarget"` | `defaultDirection="buy"` |
511
+ | `defaultDirection="outOfTarget"` | `defaultDirection="sell"` |
512
+ | `showApprovalModeControl` | `showApprovalMode` |
513
+
514
+ No other props changed. See the [CHANGELOG](./CHANGELOG.md) for the full entry.
515
+
258
516
  ## Props
259
517
 
260
518
  - `provider`: optional EIP-1193 provider. Defaults to auto-detected `window.ethereum`.
261
519
  - `account`: optional connected account from the host wallet UI.
262
520
  - `getProvider`: optional provider resolver for wallet libraries with async providers.
263
521
  - `onConnectWallet`: optional callback for opening the host wallet modal. It may return `{ provider, account }`.
522
+ - `onSubmitted`: `(hash, swap?)` fired when the swap transaction is sent. `swap` carries mode, token symbols, amounts, and fee for analytics.
523
+ - `onSuccess`: `({ hash, explorerUrl, swap? })` fired when the receipt confirms.
524
+ - `onError`: `(error)` fired on approval/swap failures (after the widget shows its own message).
525
+ - `onQuote`: `(quote)` fired on every quote refresh.
526
+ - `onStatusChange`: `(snapshot)` fired on every lifecycle transition with what the top progress rail shows (see Transaction Status).
527
+ - `onPoolChange`: `(poolId)` fired when the active pool changes — user selection or a `poolIds` update that forces a fallback. Silent on mount.
264
528
  - `chainId`: active chain id. Defaults to Ethereum mainnet.
265
529
  - `chains`: optional chain config overrides or additional chain configs, including per-chain `v4Contracts`.
266
- - `pools`: direct pool configs to render and swap against.
530
+ - `networkOptions`: optional display-only entries for the network menu (`{ id, name, icon?, selected?, disabled?, status? }`). The active entry is resolved by explicit `selected: true`, else by `id` matching the active chain id, else by `name`; a list without the active chain gets it prepended so the trigger never misreports the chain in use. Non-selected options render disabled by default with optional `status` text (e.g. "Coming soon"); `icon` is `'ethereum' | 'base'`. Clicking the enabled option only closes the menu (disabled options ignore clicks) — switching is not wired yet.
531
+ - `pools`: direct pool configs to render and swap against. This is the canonical config path.
267
532
  - `defaultPoolId`: initial pool id when `pools` is provided.
268
- - `targets`: optional pool id order/subset.
269
- - `defaultDirection`: `intoTarget` for USDC to target, or `outOfTarget` for target to USDC.
270
- - `markets`: advanced override for normalized pool/router/quoter config.
271
- - `rpcUrls`: optional RPC fallback list for the active chain.
533
+ - `poolIds`: optional pool id order/subset. Use this when the host wants to hide pools or control ordering.
534
+ - `defaultDirection`: `buy` (counter token into the pool token) or `sell` (pool token into the counter token). Defaults to `buy`.
535
+ - `defaultSlippagePercent`: number (canonical) or numeric string, in percent. Defaults to `1`.
536
+ - `rpcUrls`: RPC fallback list for the active chain, or a `{ [chainId]: string[] }` map for multichain hosts.
272
537
  - `metadataEndpoint`: optional metadata GraphQL endpoint for `GlobalToken.iconURL` token icons. Set to `null` to disable.
273
- - `theme`: `dark` or `light`. Defaults to `dark`.
274
- - `accentColor`: CSS accent color for the CTA, selected pools, focus states, links, and swap controls.
538
+ - `className`: optional class for host layout and styling hooks.
539
+ - `style`: optional root style object, useful for host layout constraints or CSS variables.
540
+ - `title`: optional widget heading. Empty by default (compact topbar, no header row).
541
+ - `subtitle`: optional header eyebrow. Defaults to empty. Pass a string to show it.
542
+ - `ariaLabel`: optional accessible label for the widget region.
543
+ - `theme`: `dark`, `light`, or `auto` (follows `prefers-color-scheme`). Defaults to `dark`.
544
+ - `accentColor`: CSS accent color for the CTA, selected pools, focus states, links, and swap controls. When set without `accentTextColor`, the widget derives a readable CTA text color automatically (see Appearance for supported color formats).
275
545
  - `accentTextColor`: optional CTA text color for contrast with the chosen accent.
546
+ - `showPoolLink`: whether to render the direct-pool link when the active chain provides `poolExplorerUrl`.
547
+ - `defaultApprovalMode`: `exact` approves only the current quote spend by default; `max` opts into the old unlimited ERC-20 to Permit2 approval behavior.
548
+ - `showApprovalMode`: whether the details drawer exposes the Exact/Max approval toggle. Defaults to `true`.
549
+ - `showExecutionDetails`: whether to render the collapsible settings drawer for slippage, approval mode, and route. Defaults to `true`.
550
+ - `defaultExecutionDetailsOpen`: whether that settings drawer starts expanded. Defaults to `false`.
551
+ - `statusSurface`: where transaction status is surfaced — `'rail'` (default), `'toast'`, `'both'`, or `'none'`. The root `data-progress-state` attribute stays on regardless (see Toasts).
552
+ - `toastPosition`: viewport corner for the widget-provided toaster. Defaults to `'bottom-right'`; ignored once a host mounts `<AegisSwapToaster />`.
553
+
554
+ The widget intentionally does not inherit the host font; set the
555
+ `--aegis-swap-font-family` CSS variable (via `style` or a stylesheet) to match
556
+ the host typography.
557
+
558
+ ### With wagmi
559
+
560
+ The widget consumes the host's wallet instead of bundling its own connect UI.
561
+ From a wagmi app, derive the EIP-1193 provider from the active connector:
562
+
563
+ ```tsx
564
+ import { useAccount } from 'wagmi'
565
+
566
+ function Swap() {
567
+ const { address, connector } = useAccount()
568
+ return (
569
+ <AegisSwapWidget
570
+ account={address}
571
+ getProvider={async () => (await connector?.getProvider()) ?? null}
572
+ onConnectWallet={openConnectModal}
573
+ />
574
+ )
575
+ }
576
+ ```
276
577
 
277
578
  ## Default Markets
278
579
 
@@ -287,9 +588,30 @@ These use the Aegis mainnet router `0x00581261a1200956ea05d1810fF28ffd66D8E254`
287
588
  and the Ethereum entry in `UNISWAP_V4_CONTRACTS_BY_CHAIN` for Permit2 and the
288
589
  public Uniswap v4 Quoter.
289
590
 
591
+ ## License
592
+
593
+ MIT.
594
+
290
595
  ## Local Demo
291
596
 
292
597
  ```bash
293
- npm install
598
+ npm ci
294
599
  npm run dev
295
600
  ```
601
+
602
+ ## Releasing
603
+
604
+ Maintainer release flow (publishes to npm). `dist/` is not committed — the `prepack`
605
+ hook rebuilds it on every `npm pack`/`npm publish`, and `prepublishOnly` runs the
606
+ typecheck + test gate.
607
+
608
+ ```bash
609
+ git switch main && git pull
610
+ npm ci
611
+ npm run typecheck && npm run test && npm run build
612
+ # Move CHANGELOG [Unreleased] entries under the new version heading.
613
+ npm version minor # e.g. 0.2.1 -> 0.3.0 for a breaking 0.x release; commits and tags vX.Y.Z
614
+ git push origin main --follow-tags
615
+ npm publish --access public # prepublishOnly gate + prepack build run automatically
616
+ npm view aegis-swap-widget version
617
+ ```
package/dist/abis.d.ts CHANGED
@@ -72,6 +72,24 @@ export declare const AEGIS_ROUTER_ABI: readonly [{
72
72
  readonly outputs: readonly [];
73
73
  }];
74
74
  export declare const PERMIT2_ABI: readonly [{
75
+ readonly type: "function";
76
+ readonly name: "approve";
77
+ readonly stateMutability: "nonpayable";
78
+ readonly inputs: readonly [{
79
+ readonly name: "token";
80
+ readonly type: "address";
81
+ }, {
82
+ readonly name: "spender";
83
+ readonly type: "address";
84
+ }, {
85
+ readonly name: "amount";
86
+ readonly type: "uint160";
87
+ }, {
88
+ readonly name: "expiration";
89
+ readonly type: "uint48";
90
+ }];
91
+ readonly outputs: readonly [];
92
+ }, {
75
93
  readonly type: "function";
76
94
  readonly name: "allowance";
77
95
  readonly stateMutability: "view";
@@ -185,4 +203,3 @@ export declare const UNISWAP_V4_QUOTER_ABI: readonly [{
185
203
  readonly type: "uint256";
186
204
  }];
187
205
  }];
188
- //# sourceMappingURL=abis.d.ts.map
@@ -0,0 +1,5 @@
1
+ import type { Address, Hex } from 'viem';
2
+ export declare function addressKey(address: Address | null | undefined): string;
3
+ export declare function sameAddress(a: Address, b: Address): boolean;
4
+ export declare function sameOptionalAddress(a: Address | null, b: Address | null): boolean;
5
+ export declare function samePoolId(a: Hex, b: Hex): boolean;