Skip to main content
The modal can pull balances a user already holds in a supported third-party app and fund the deposit from there — no manual transfer first. Each provider is off by default; opt in per provider via assetMigrations.
When at least one provider is enabled, a Migrate assets row appears on the deposit entry screen and opens a picker over that provider’s positions. The user picks one, and the modal pulls the funds into their smart account and bridges them to targetChain / targetToken like any other source. Only the providers you enable are listed. Enabling one that isn’t live yet shows it as a disabled “Coming soon” row rather than hiding it.

Polymarket

Set assetMigrations={{ polymarket: true }}. The modal looks up the connected EOA’s Polymarket proxy wallet on Polygon and surfaces any pUSD or USDC.e balance. pUSD is unwrapped to USDC.e on the way out; USDC.e is what lands in the smart account and what the orchestrator bridges from. Polymarket exposes two wallet types and the modal handles both: some transfer on-chain directly from the user’s signed transaction, while others are relayed through your backend proxy, which must forward POST /polymarket/withdraw.

Open directly into Polymarket

To skip the deposit-method home screen and land the user straight on the Polymarket transfer, pass initialAssetMigration="polymarket" alongside assetMigrations={{ polymarket: true }}. While balances load the modal shows a skeleton; if there’s no account, no balance, or no connected wallet it falls back to the home screen.

Headless account lookup

If you build your own UI and only need the data, the @rhinestone/deposit-modal/polymarket subpath exports a headless getPolymarketAccount — no React, modal, or wallet-connect code pulled into your bundle. Give it the connected EOA and it returns the user’s Polymarket proxy wallet address and on-chain pUSD / USDC.e balances on Polygon.
Returns null only when the EOA has no Polymarket account. A transient failure (network, 5xx, abort) rejects instead, so an outage is never mistaken for “no account”. Pass rpcUrl to override the default public Polygon RPC, or a signal (AbortSignal) to cancel the lookup.

Aave

Set assetMigrations={{ aave: true }}. The modal lists the connected EOA’s Aave v3 supply positions across every chain you can take deposits from, and picking one produces a single Pool.withdraw that sends the proceeds straight to the user’s deposit address — the funds never pass through their wallet, and the normal deposit pipeline ingests them like any other incoming transfer. Requires your backend proxy to forward GET /positions/:address and POST /positions/:address/unwind. A read-scoped API key is enough for both. Three behaviours worth knowing, because they are deliberate:
  • The amount offered is what can actually be withdrawn right now, not the full supplied balance. Aave caps a withdrawal against the user’s health factor, E-Mode thresholds, paused reserves and pool liquidity, so a position worth 100mayonlypermit100 may only permit 40 out. The modal offers the $40 — the user is never shown a figure the pool would reject.
  • The user’s wallet signs; Rhinestone never holds a key on this path. The aTokens sit in the user’s own EOA, so only they can authorise the withdraw. The transaction we build is unsigned and inert until they sign it.
  • A position whose proceeds your config would refuse is not offered at all — under your minDepositUsd, or outside your source-token allowlist. Offering it and failing afterwards would leave the user having irreversibly exited a position for funds we then reject.
Positions are keyed by market, not chain: Ethereum alone hosts four Aave v3 markets with distinct pools and risk parameters, so the same token supplied to two of them appears as two rows, each naming its market. If a market can’t be read, the positions that did load are still listed and the screen says the list may be incomplete — an outage never silently reports a smaller balance than the user holds.

Open directly into Aave

As with Polymarket, initialAssetMigration="aave" skips the home screen: