Skip to content

feat: funkit checkout integration for Core-market supplies#3010

Open
yogurtandjam wants to merge 2 commits into
aave:mainfrom
yogurtandjam:main
Open

feat: funkit checkout integration for Core-market supplies#3010
yogurtandjam wants to merge 2 commits into
aave:mainfrom
yogurtandjam:main

Conversation

@yogurtandjam
Copy link
Copy Markdown

What

Routes Supply for 4 allowlisted assets (USDC, USDT, WBTC, cbBTC) on the Core mainnet market through the funkit checkout modal, letting users supply from any EVM asset or fiat. All other assets/markets keep the native Aave supply flow.

How

Integration architecture

  • FunkitCheckoutssr:false modal host mounted once in _app next to the app's other modal hosts (SupplyModal etc.). Owns the single useFunkitCheckout instance; per-asset configs are passed at call time via beginCheckout(configOverride). Reuses the app's wagmi + react-query + ConnectKit (no funkit wallet stack). Honors the SDK's onLoginFinished resume contract by replaying it once ConnectKit connects.
  • funSupplyBridge — module-level imperative bridge across the client-only island boundary (@funkit/connect is browser-only ESM; the app pre-renders/static-exports). The island registers beginSupply on mount; Supply buttons invoke it at click time. Clicks before the chunk loads fall back to the native modal instead of dropping.
  • useSupplyButtonAction — single shared click handler for all 3 Supply list-item variants; gates on isFunSupplyAsset(currentMarket, underlyingAsset).

Data sourcing — no integrator-owned copies

  • The only hardcoded data is FUN_SUPPLY_UNDERLYINGS: a 4-address allowlist Set — purely the product decision of which assets route through fun checkout.
  • Receipt-token metadata (aToken address, real on-chain symbol aEthUSDC, decimals, hosted icon) comes from app state: useAppDataContext().supplyReserves (@aave/react SDK reserves whose aToken/underlyingToken are Currency objects) — the same source AddTokenDropdown renders on reserve-overview. Display fields (symbol, APY, collateral state) come from the clicked dashboard reserve; pool address from currentMarketData.
  • Gated on CustomMarket.proto_mainnet_v3 (not chainId): mainnet hosts three markets (Core/Prime/EtherFi) and e.g. USDC exists in all three with different aTokens and pools. Prime/EtherFi supplies of the same underlyings stay native. If SDK market data hasn't loaded at click time, the click falls back to the native modal.

Add-to-wallet icon parity

The native flow's wallet icon is generated, not hosted: Base64Token wraps the underlying's local SVG in Aave's gradient TokenRing and base64-encodes it. Fun-routed supply rows mount the same hidden generator via the shared useFunSupplyATokenIcon hook (the AddTokenDropdown pattern) and pass the data URI through the click payload — ready at row mount, never delays beginCheckout. The SDK's hosted aToken.imageUrl is the fallback for a click that beats generation.

Zero-balance behavior

fun-routed assets always render in "Assets to supply" and keep an enabled Supply button at 0 wallet balance (users can pay with any asset/fiat). Protocol-level blocks (inactive/frozen/paused/capped) still disable.

Styling shim

funkitPreflight.css — a zero-specificity (:where()) slice of Tailwind preflight scoped under funkit's [data-rk] portal root. @funkit/connect implicitly relies on host Tailwind preflight (its production hosts are Tailwind apps) for form-control font inheritance and img { max-width: 100% }; this app is MUI/emotion. Without it the amount-input digits render in system font at weight 400 and host-supplied icons paint at intrinsic size.

Compat

  • patches/@funkit+connect+9.18.0.patch — React 18 forwardRef shim for funkit's React 19 ref-as-prop components (patch-package, wired via postinstall).
  • .yarnrc ignore-engines@solana/addresses (transitive) requires node ≥20.18 while the repo targets node 18; browser-only code, engines gate is install-only.

Not in scope (flagged for follow-up)

  • Reserve-overview page Supply button (ReserveActions.tsx) and SuppliedPositions "supply more" buttons still go native — product decision pending.
  • Permit toggle for the Aave native-supply fast path: tracked internally at fun.xyz (ENG-4068).
  • funkit SDK: lazy addToWalletToken.iconSrc resolver (consume-time semantics) would remove the begin-time icon coupling for all integrators.

Test plan

  • Core market: Supply USDC/USDT/WBTC/cbBTC from dashboard → funkit modal opens immediately, themed correctly (light+dark), amount input renders in Inter at correct weight
  • Modal shows "You receive … aEthUSDC" (SDK-sourced ticker) with the ringed aToken icon
  • Checkout complete: destination row shows 15px Aave icon; "Add aEthUSDC to your balance" registers the gradient-ringed icon in MetaMask with no symbol-mismatch warning
  • Prime/EtherFi markets: Supply USDC → native Aave modal
  • Non-allowlisted assets: native modal
  • Empty wallet on Core: 4 fun assets visible with enabled Supply buttons
  • Disconnected: Supply click opens ConnectKit

🤖 Generated with Claude Code

* feat: route allowlisted Core-market supplies through funkit checkout

Adds a funkit checkout path for USDC/USDT/WBTC/cbBTC supplies on the Core
mainnet market, mirroring the production Polymarket integration:

- FunkitCheckout: ssr:false modal host in _app (next to the app's other
  modal hosts), single useFunkitCheckout instance, per-asset configs via
  beginCheckout(configOverride). Honors the SDK's onLoginFinished resume
  contract by replaying it once ConnectKit connects.
- funSupplyBridge: module-level imperative bridge across the client-only
  island boundary; clicks before the chunk loads fall back to the native
  supply modal.
- funSupplyAssets: allowlist holds only what live reserve data can't
  provide (real on-chain aToken symbols — wallet_watchAsset validates
  against the contract — and absolute icon URLs for EIP-747). Everything
  else (symbol, decimals, addresses, pool) comes from the clicked
  reserve + current market store. Gated on CustomMarket.proto_mainnet_v3
  so Prime/EtherFi reserves of the same underlyings stay native.
- funkitPreflight.css: scoped, zero-specificity slice of Tailwind
  preflight under [data-rk] — @funkit/connect relies on host preflight
  for form-control font inheritance and img max-width (its production
  hosts are Tailwind apps; this one isn't).
- patches/@Funkit+connect: React 18 forwardRef shim for funkit's
  React 19 ref-as-prop components, applied via patch-package.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: always show fun-routed assets in supply list regardless of balance

fun checkout lets users supply from any EVM asset or fiat, so an empty
wallet must not hide the row or disable the Supply button for allowlisted
Core-market assets:

- SupplyAssetsList dust filter: fun-routed reserves always pass
- SupplyAssetsListItem / SupplyAssetsListMobileItem: zero wallet balance
  no longer disables Supply for fun-routed reserves (protocol-level
  blocks - inactive/frozen/paused/capped - still apply)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: pin FUN_SUPPLY_ASSETS aToken symbols to the address-book tokenlist

The catalog hardcodes receipt-token symbols (dashboard reserve data only
carries aTokenAddress; importing the 352KB/895-entry tokenlist at runtime
for 4 strings is a bad bundle trade). This test imports the tokenlist
where bundle size doesn't matter and fails CI if an address-book bump or
catalog typo ever disagrees - wallets validate wallet_watchAsset symbols
against the contract, so drift breaks the add-to-wallet flow.

Loaded via a node subprocess: the package's CJS tokenlist artifact is
data-less (its only module.exports is esbuild's dead-code annotation)
and next/jest forbids transforming node_modules, so neither artifact is
importable in-process under jest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor: source funkit receipt-token data from app state

The @aave/react SDK migration means useAppDataContext().supplyReserves
already carries each reserve's aToken Currency (address, symbol, decimals,
imageUrl) - the same state AddTokenDropdown renders on reserve-overview.
Look it up at the Supply click instead of keeping integrator-owned copies:

- FUN_SUPPLY_ASSETS catalog (hardcoded aToken symbols + icon URLs) shrinks
  to FUN_SUPPLY_UNDERLYINGS, a 4-address allowlist Set - purely the
  product decision of which assets route through fun checkout
- useSupplyButtonAction resolves the SDK reserve and threads
  aToken/underlyingToken metadata through the bridge; falls back to the
  native modal if SDK market data isn't loaded yet
- receipt icon improves: actual aToken artwork from aToken.imageUrl
  instead of reusing the underlying's icon
- deletes the address-book drift test (nothing hardcoded left to drift)
  and its node-subprocess workaround for the tokenlist's broken CJS
  artifact; drops the decimals/aTokenAddress threading from list items
- removes stray console.log debug block in SupplyAssetsListItemMobile

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat: register the ringed aToken icon via fun checkout add-to-wallet

The native flow's wallet icon is generated, not hosted: Base64Token wraps
the underlying's local SVG in Aave's gradient TokenRing and base64-encodes
it (Success.tsx / AddTokenDropdown). The SDK's aToken.imageUrl points at a
plain third-party logo (token-logos.family.co), so the fun checkout's
add-to-wallet icon didn't match.

Per-row generation, mirroring AddTokenDropdown: fun-routed supply rows
mount a hidden Base64Token via the shared useFunSupplyATokenIcon hook and
pass the data URI through the click payload - explicit data flow, ready at
row mount, never delays beginCheckout. Hosted imageUrl stays as the
fallback for a click that beats generation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: drop unused tronweb dependency

tronweb isn't a @funkit/connect dependency or peer, isn't referenced in
its bundle, and nothing in this repo imports it - a leftover from early
integration setup. Its exact bignumber.js@9.1.2 pin hoisted over the
repo's ^9.0.2 -> 9.3.1 resolution, hiding the UMD global type that
upstream's PriceInput.tsx relies on post-rebase.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 5, 2026

@yogurtandjam is attempting to deploy a commit to the Aave Team on Vercel.

A member of the Team first needs to authorize it.

@yogurtandjam yogurtandjam marked this pull request as draft June 5, 2026 14:21
tronweb is a peerDependency of @relayprotocol/relay-tron-wallet-adapter,
statically imported via @funkit/connect -> @funkit/fun-relay. pnpm
auto-installs peers (why funkit's monorepo never trips on it); yarn v1
doesn't, so the host must declare it - removing it broke `next build`
with "Module not found: Can't resolve 'tronweb'".

Its exact bignumber.js@9.1.2 pin used to hoist over the repo's
^9.0.2 -> 9.3.x resolution, hiding the BigNumber UMD global type that
PriceInput.tsx relies on. Solved via the repo's existing resolutions
block: "bignumber.js": "^9.3.1" collapses all ranges to one 9.3.1 copy.

Verified with a full `next build` (clean compile, 17/17 pages).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@yogurtandjam yogurtandjam marked this pull request as ready for review June 5, 2026 14:30
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.

1 participant