From f54d991603bf589c6eb5b10a5b15dc1aa394ba19 Mon Sep 17 00:00:00 2001 From: Soos3D <99700157+soos3d@users.noreply.github.com> Date: Mon, 25 May 2026 08:25:56 -0400 Subject: [PATCH 1/5] enable simple deposit --- docs.json | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs.json b/docs.json index 749432a..5814d28 100644 --- a/docs.json +++ b/docs.json @@ -150,7 +150,26 @@ } ] }, - + { + "tab": "Simple Deposit", + "groups": [ + { + "group": "OVERVIEW", + "pages": [ + "simple-deposit/overview", + "simple-deposit/quickstart" + ] + }, + { + "group": "SDK Reference", + "pages": [ + "simple-deposit/react-sdk", + "simple-deposit/core-sdk", + "simple-deposit/reference" + ] + } + ] + }, { "tab": "Social Logins", "groups": [ From c7d43a28f0ab23699c626f94a80281ae2ec71896 Mon Sep 17 00:00:00 2001 From: Soos3D <99700157+soos3d@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:37:14 -0400 Subject: [PATCH 2/5] deposit edits --- simple-deposit/core-sdk.mdx | 104 ++++++++++++++++++++++++ simple-deposit/react-sdk.mdx | 7 ++ simple-deposit/reference.mdx | 152 +++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) diff --git a/simple-deposit/core-sdk.mdx b/simple-deposit/core-sdk.mdx index dac025c..0ebef07 100644 --- a/simple-deposit/core-sdk.mdx +++ b/simple-deposit/core-sdk.mdx @@ -37,6 +37,8 @@ client.startWatching(); | `pollingIntervalMs` | `number` | No | `3000` | Polling interval (ms). | | `recovery` | `RecoveryConfig` | No | — | Recovery behavior. | | `refund` | `RefundConfig` | No | `{ enabled: false }` | Auto-refund (experimental). | +| `funding` | `FundingConfig` | No | `{ enabled: false }` | Fund the deposit address from a connected browser wallet (zero-config). | +| `onDepositEvent` | `(event: DepositLifecycleEvent) => void` | No | — | Deposit-lifecycle callback. Best-effort and client-side (not a server webhook). | | `uaProjectId` | `string` | No | SDK default | Particle project ID for UA operations only. | --- @@ -63,12 +65,109 @@ client.startWatching(); | `refundAll(reason?)` | `RefundReason?` | `Promise` | Refund all pending. | | `canRefund(id)` | `string` | `Promise<{ eligible, reason? }>` | Check refund eligibility. | | `getRefundConfig()` | — | `RefundConfig` | Current refund config. | +| `getWalletBalances(wallet)` | `FundingWallet` | `Promise` | Discover a connected wallet's cross-chain balances. | +| `fundFromWallet(wallet, balance, opts?)` | `FundingWallet, FundingBalance, FundOptions?` | `Promise` | Transfer a discovered balance to the deposit address. | | `getTransactions(page, pageSize)` | `number, number` | `Promise` | Page-based transaction history. | | `getTokenTransactions(filter, cursor?)` | `TokenTransactionFilter, string?` | `Promise` | Cursor-based filtered transactions. | | `getTransaction(id)` | `string` | `Promise` | Single transaction lookup. | --- +## Notifications (Headless) + +Track each deposit through its lifecycle without any UI. Pass the `onDepositEvent` +callback, or subscribe to the `deposit:lifecycle` event — both deliver the same +normalized `DepositLifecycleEvent`, with a stable `id` across phase transitions +(`detected → processing → credited`, or `failed` / `below_threshold`). + +```typescript +const client = new DepositClient({ + ownerAddress: '0x...', + intermediaryAddress: '0x...', + destination: { chainId: CHAIN.BASE }, + onDepositEvent: (e) => { + console.log(e.phase, e.token, `$${e.amountUSD}`); + }, +}); + +// Or subscribe to the same stream as an event: +client.on('deposit:lifecycle', (e) => console.log(e.phase, e.id)); +``` + + + Lifecycle events fire client-side and best-effort — they are **not** a reliable + server webhook. Don't use them as the source of truth for crediting funds. + + +--- + +## Fund from Wallet (Headless) + +Let a user top up their deposit address from a connected **browser wallet** +(MetaMask / Rabby / Phantom / any injected EIP-1193 or Solana wallet). The SDK +discovers the wallet's cross-chain balances, builds a plain transfer to the deposit +address, and submits it through the wallet; the watcher then detects and sweeps it +as usual. The browser wallet is a funding source only — it never becomes the +Universal Account owner/signer. + +Balance discovery is **zero-config** (Particle's hosted service — no Moralis key, +proxy, or backend). Enable it on the client: + +```typescript +const client = new DepositClient({ + ownerAddress: '0x...', + intermediaryAddress: '0x...', + authCoreProvider: provider, + destination: { chainId: CHAIN.BASE }, + funding: { enabled: true }, // ← that's it +}); +``` + +### Flow + +```typescript +import { + detectInjectedEvmWallets, + connectInjectedEvm, +} from '@particle-network/simple-deposit'; + +// 1. Detect + connect an injected wallet (or bring your own EIP-1193 provider). +const [wallet] = await detectInjectedEvmWallets(); +const address = await connectInjectedEvm(wallet.provider); + +// 2. Discover spendable balances across supported chains. +const balances = await client.getWalletBalances({ evm: { provider: wallet.provider, address } }); + +// 3. Transfer a chosen balance to the deposit address (omit rawAmount for the +// gas-adjusted max). Emits funding:started → funding:complete (or funding:error). +const result = await client.fundFromWallet( + { evm: { provider: wallet.provider, address } }, + balances[0], +); +``` + +### Detection Utilities + +Framework-agnostic helpers for discovering and connecting injected wallets: + +```typescript +import { + detectInjectedEvmWallets, // EIP-6963 + legacy window.ethereum fallback → DetectedEvmWallet[] + connectInjectedEvm, // eth_requestAccounts → address + getEvmAccounts, // current eth_accounts + promptEvmAccountSelection, // wallet_requestPermissions → eth_accounts + detectInjectedSolanaWallet, // window.phantom?.solana / window.solana → DetectedSolanaWallet | null + connectInjectedSolana, // connect() → publicKey + formatUnits, parseUnits, // smallest-unit ↔ human-readable amount helpers +} from '@particle-network/simple-deposit'; +``` + + + Balance discovery covers **Ethereum, BNB Chain, Base, Arbitrum, and Solana**. X Layer is not supported by the Moralis-backed service, so it is skipped during discovery — other supported chains are unaffected. + + +--- + ## Transaction History Query the Universal Account's transaction history. Results are cached (30s TTL, LRU) to avoid redundant API calls when paginating. @@ -139,7 +238,9 @@ client.off('deposit:detected', handler); | `deposit:detected` | `DetectedDeposit` | New deposit found. | | `deposit:processing` | `DetectedDeposit` | Sweep started. | | `deposit:complete` | `SweepResult` | Sweep succeeded. | +| `deposit:below_threshold` | `DetectedDeposit` | Deposit detected below `minValueUSD`. | | `deposit:error` | `Error, DetectedDeposit?` | Sweep failed. | +| `deposit:lifecycle` | `DepositLifecycleEvent` | Normalized phase transition (mirrors the events above). | | `recovery:started` | — | Recovery started. | | `recovery:complete` | `RecoveryResult[]` | Recovery finished. | | `recovery:failed` | `DetectedDeposit, Error` | Single recovery failed. | @@ -147,6 +248,9 @@ client.off('deposit:detected', handler); | `refund:processing` | `DetectedDeposit, attempt` | Refund attempt. | | `refund:complete` | `RefundResult` | Refund succeeded. | | `refund:failed` | `DetectedDeposit, Error, exhausted` | Refund failed. | +| `funding:started` | `FundingBalance` | Wallet-funding transfer started. | +| `funding:complete` | `FundingTransferResult` | Wallet-funding transfer submitted. | +| `funding:error` | `Error, FundingBalance?` | Wallet-funding transfer failed. | | `status:change` | `ClientStatus` | Status changed. | --- diff --git a/simple-deposit/react-sdk.mdx b/simple-deposit/react-sdk.mdx index 12c8278..b9e5c90 100644 --- a/simple-deposit/react-sdk.mdx +++ b/simple-deposit/react-sdk.mdx @@ -32,6 +32,9 @@ import { DepositProvider, CHAIN } from '@particle-network/simple-deposit/react'; | `minValueUSD` | `number` | `0.50` | Minimum USD value to trigger sweep. | | `pollingIntervalMs` | `number` | `3000` | Balance check interval (ms). | | `refund` | `RefundConfig` | `{ enabled: false }` | Auto-refund config (experimental). | +| `notifications` | `boolean \| DepositNotificationsProps` | `false` | Show in-app deposit notifications. Set `true` to auto-mount the toaster, or pass props to configure it. | +| `funding` | `FundingConfig` | `{ enabled: false }` | Let users fund the deposit address from a connected browser wallet. When enabled, `DepositWidget` shows a "Fund from wallet" toggle. | +| `fundingWallet` | `{ evm?: { provider, address } }` | — | Reuse your app's already-connected wallet as the funding source (skips the connect step). | | `uaProjectId` | `string` | SDK default | Particle project ID for UA operations only. | --- @@ -129,6 +132,10 @@ import { DepositWidget } from '@particle-network/simple-deposit/react'; | `className` | `string` | — | Custom CSS class. | | `onClose` | `() => void` | — | Close handler. | + + When `funding.enabled` is set on the provider, `DepositWidget` automatically gains a **"Receive | Fund from wallet"** toggle — no extra props needed. + + ### DepositModal Modal wrapper around `DepositWidget`. Accepts all `DepositWidget` props plus: diff --git a/simple-deposit/reference.mdx b/simple-deposit/reference.mdx index 9261ed1..7ff05f8 100644 --- a/simple-deposit/reference.mdx +++ b/simple-deposit/reference.mdx @@ -73,6 +73,68 @@ interface RefundResult { } ``` +### DepositLifecycleEvent + +Normalized, serializable view of a deposit at a single lifecycle phase — delivered by the `onDepositEvent` callback and the `deposit:lifecycle` event, and the basis for the notification UI. `id` is stable across phase transitions. + +```typescript +type DepositPhase = 'detected' | 'processing' | 'credited' | 'failed' | 'below_threshold'; + +interface DepositLifecycleEvent { + id: string; + phase: DepositPhase; + token: TokenType; + chainId: number; + amount: string; + amountUSD: number; + destination?: { address: string; chainId: number }; + transactionId?: string; // present on 'credited' + explorerUrl?: string; // present on 'credited' + error?: string; // present on 'failed' + timestamp: number; +} + +// DepositNotification (from useDepositNotifications) adds `createdAt` / `updatedAt`. +``` + +### FundingConfig + +```typescript +interface FundingConfig { + enabled: boolean; // Master switch (default: false) + apiKey?: string; // Moralis key for DIRECT browser calls — dev/PoC only + proxyUrl?: string; // Your own reverse proxy (injects the key server-side) + balanceProvider?: BalanceProvider; // Replace balance discovery entirely + evmRpcUrls?: Record; // Per-chain EVM RPC overrides + solanaRpcUrl?: string; // Solana RPC for building SPL/native transfers + minValueUSD?: number; // Min USD value for a balance to be offered +} +``` + +### Funding Types + +```typescript +// A connected browser wallet used as a funding source. +interface FundingWallet { + evm?: { provider: unknown; address: string }; // EIP-1193 provider + address + solana?: { provider: unknown; address: string }; // Solana wallet provider + address +} + +// Options when transferring a discovered balance. +interface FundOptions { + rawAmount?: bigint; // Omit for the gas-adjusted max +} + +// Result of a fund-from-wallet transfer. +interface FundingTransferResult { + txHash: string; + // …plus chain/token/amount metadata for the submitted transfer +} + +// FundingBalance is the descriptor returned by getWalletBalances() — pass one +// straight back into fundFromWallet(). Treat it as opaque; don't construct it by hand. +``` + ### UATransaction ```typescript @@ -214,6 +276,22 @@ import { } from '@particle-network/simple-deposit'; ``` +### Wallet Detection Utilities + +Framework-agnostic helpers for the [Fund from Wallet](#fund-from-wallet) feature — discover and connect injected browser wallets without React. + +```typescript +import { + detectInjectedEvmWallets, // EIP-6963 + legacy window.ethereum fallback + connectInjectedEvm, // eth_requestAccounts → address + getEvmAccounts, // current eth_accounts + promptEvmAccountSelection, // wallet_requestPermissions → eth_accounts + detectInjectedSolanaWallet, // window.phantom?.solana / window.solana + connectInjectedSolana, // connect() → publicKey + formatUnits, parseUnits, // smallest-unit ↔ human-readable amount helpers +} from '@particle-network/simple-deposit'; +``` + --- ## Advanced Topics @@ -279,6 +357,79 @@ const dest = client.getDestination(); // { address, chainId } Throws `ConfigurationError` if the chain ID or address is invalid. +### Notifications + +Surface deposit progress to your users. Set `notifications: true` on `DepositProvider` to auto-mount the toaster, build a custom UI with the `useDepositNotifications` hook, or go headless with the `onDepositEvent` callback / `deposit:lifecycle` event — all driven by the same `DepositLifecycleEvent`. + + + +```tsx React + + + +``` + +```typescript Headless +const client = new DepositClient({ + ownerAddress: '0x...', + intermediaryAddress: '0x...', + destination: { chainId: CHAIN.BASE }, + onDepositEvent: (e) => console.log(e.phase, e.token, e.amountUSD), +}); +``` + + + +#### DepositNotificationsProps + +| Property | Type | Default | Description | +|---|---|---|---| +| `position` | `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left' \| 'top-center' \| 'bottom-center'` | `'top-right'` | Anchor on screen. | +| `theme` | `'dark' \| 'light'` | `'dark'` | Color theme. | +| `durationMs` | `number` | `6000` | Auto-dismiss delay for terminal toasts. `0` disables. | +| `max` | `number` | `5` | Max simultaneous toasts. | +| `className` | `string` | — | Extra classes for the fixed container. | +| `renderItem` | `(n, dismiss) => ReactNode` | — | Render-prop escape hatch (replaces default markup). | + + + Lifecycle events fire client-side and best-effort — they are **not** a reliable server webhook. Don't use them as the source of truth for crediting funds. + + +### Fund from Wallet + +Let users top up their deposit address from a connected browser wallet (MetaMask / Rabby / Phantom / any injected EIP-1193 or Solana wallet). The SDK discovers cross-chain balances, builds a transfer to the deposit address, and the watcher sweeps it as usual. Balance discovery is **zero-config** (Particle's hosted service — no Moralis key, proxy, or backend). The browser wallet is a funding source only — it never becomes the Universal Account owner/signer. + +```typescript +const client = new DepositClient({ + ownerAddress: '0x...', + intermediaryAddress: '0x...', + authCoreProvider: provider, + destination: { chainId: CHAIN.BASE }, + funding: { enabled: true }, // ← that's it +}); +``` + +When `funding.enabled` is set, `DepositWidget` automatically shows a "Receive | Fund from wallet" toggle. For a standalone surface use the `FundFromWallet` component, or call `client.getWalletBalances()` / `client.fundFromWallet()` directly — see the [React SDK](/simple-deposit/react-sdk) and [Core SDK](/simple-deposit/core-sdk). + +#### FundingConfig + +| Property | Type | Default | Description | +|---|---|---|---| +| `enabled` | `boolean` | `false` | Master switch for the feature. | +| `apiKey` | `string` | — | Moralis API key for **direct** browser calls. Dev/PoC only — exposes the key. Prefer the hosted default. | +| `proxyUrl` | `string` | Hosted service | Base URL of your own reverse proxy. Omit to use Particle's hosted service. | +| `balanceProvider` | `BalanceProvider` | — | Replace balance discovery entirely; `apiKey` / `proxyUrl` / hosted default are ignored. | +| `evmRpcUrls` | `Record` | Public RPCs | Per-chain EVM RPC overrides (native gas checks / send fallback). | +| `solanaRpcUrl` | `string` | Public RPC | Solana RPC used to build SPL/native transfers. | +| `minValueUSD` | `number` | Client `minValueUSD` | Minimum USD value for a balance to be offered. | + + + Balance-source precedence: `balanceProvider` > `apiKey` (direct) > `proxyUrl` > hosted default. Balance discovery covers **Ethereum, BNB Chain, Base, Arbitrum, and Solana**; X Layer is not supported and is skipped during discovery. + + ### Recovery Recover funds that are stuck in the intermediary wallet (e.g., due to failed sweeps). @@ -344,6 +495,7 @@ import { UniversalAccountError, // UA operations failed SweepError, // Sweep failed RefundError, // Refund failed + FundingError, // Fund-from-wallet failed NetworkError, // Network issues } from '@particle-network/simple-deposit'; ``` From e4d16d1e0049f3fd9043fc79ccbee1e15c507f3e Mon Sep 17 00:00:00 2001 From: Soos3D <99700157+soos3d@users.noreply.github.com> Date: Mon, 29 Jun 2026 12:43:42 -0400 Subject: [PATCH 3/5] add on ramp to deposit sdk --- simple-deposit/core-sdk.mdx | 32 ++++++++++++++++ simple-deposit/react-sdk.mdx | 73 +++++++++++++++++++++++++++++++++++- simple-deposit/reference.mdx | 64 +++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 2 deletions(-) diff --git a/simple-deposit/core-sdk.mdx b/simple-deposit/core-sdk.mdx index 0ebef07..5cc13d8 100644 --- a/simple-deposit/core-sdk.mdx +++ b/simple-deposit/core-sdk.mdx @@ -38,6 +38,7 @@ client.startWatching(); | `recovery` | `RecoveryConfig` | No | — | Recovery behavior. | | `refund` | `RefundConfig` | No | `{ enabled: false }` | Auto-refund (experimental). | | `funding` | `FundingConfig` | No | `{ enabled: false }` | Fund the deposit address from a connected browser wallet (zero-config). | +| `onramp` | `OnrampConfig` | No | `{ enabled: false }` | Buy crypto with fiat via RampNow, delivered straight to the destination (zero-config). | | `onDepositEvent` | `(event: DepositLifecycleEvent) => void` | No | — | Deposit-lifecycle callback. Best-effort and client-side (not a server webhook). | | `uaProjectId` | `string` | No | SDK default | Particle project ID for UA operations only. | @@ -67,6 +68,8 @@ client.startWatching(); | `getRefundConfig()` | — | `RefundConfig` | Current refund config. | | `getWalletBalances(wallet)` | `FundingWallet` | `Promise` | Discover a connected wallet's cross-chain balances. | | `fundFromWallet(wallet, balance, opts?)` | `FundingWallet, FundingBalance, FundOptions?` | `Promise` | Transfer a discovered balance to the deposit address. | +| `openOnramp(opts?)` | `{ amount?: number }` | `Promise` | Open the RampNow onramp to buy crypto with fiat. | +| `isOnrampSupported()` | — | `boolean` | Whether the current destination chain supports onramp. | | `getTransactions(page, pageSize)` | `number, number` | `Promise` | Page-based transaction history. | | `getTokenTransactions(filter, cursor?)` | `TokenTransactionFilter, string?` | `Promise` | Cursor-based filtered transactions. | | `getTransaction(id)` | `string` | `Promise` | Single transaction lookup. | @@ -168,6 +171,34 @@ import { --- +## Onramp — Buy with Fiat (Headless) + +Let a user **buy crypto with fiat** (card / Apple Pay / bank) via [RampNow](https://rampnow.io), delivered **straight to the configured destination**. Unlike Fund from Wallet, the purchase bypasses the deposit-address → watcher → sweeper pipeline — RampNow's order events are the source of truth, forwarded by the SDK as `onramp:*` events. The SDK delivers **USDC**, matching the sweep. + +Onramp is a single flag — zero-config. The SDK ships with a default RampNow key, so there's nothing to source; pass your own `apiKey` only to route purchases through your own RampNow partner account. The underlying `@rampnow/sdk` is loaded lazily (dynamic `import()`) only when the onramp is opened, so it never bundles into apps that don't use it. + +```typescript +const client = new DepositClient({ + ownerAddress: '0x...', + intermediaryAddress: '0x...', + authCoreProvider: provider, + destination: { chainId: CHAIN.BASE }, + onramp: { enabled: true }, // ← that's it +}); + +// Events report progress. +client.on('onramp:complete', (order) => console.log('delivered', order.amount, order.token)); + +// Open the overlay (100 USD by default fiat currency). +await client.openOnramp({ amount: 100 }); +``` + + + Onramp delivery is supported for **Ethereum, BNB Chain, Base, Arbitrum, and Solana**. X Layer is **not** supported — `isOnrampSupported()` returns `false` for it. Order events only flow in `mode: 'overlay'`; a `redirect` tab has no channel back to report progress. + + +--- + ## Transaction History Query the Universal Account's transaction history. Results are cached (30s TTL, LRU) to avoid redundant API calls when paginating. @@ -251,6 +282,7 @@ client.off('deposit:detected', handler); | `funding:started` | `FundingBalance` | Wallet-funding transfer started. | | `funding:complete` | `FundingTransferResult` | Wallet-funding transfer submitted. | | `funding:error` | `Error, FundingBalance?` | Wallet-funding transfer failed. | +| `onramp:complete` | `OnrampOrder` | Fiat purchase delivered to the destination. | | `status:change` | `ClientStatus` | Status changed. | --- diff --git a/simple-deposit/react-sdk.mdx b/simple-deposit/react-sdk.mdx index b9e5c90..430d8a8 100644 --- a/simple-deposit/react-sdk.mdx +++ b/simple-deposit/react-sdk.mdx @@ -35,7 +35,8 @@ import { DepositProvider, CHAIN } from '@particle-network/simple-deposit/react'; | `notifications` | `boolean \| DepositNotificationsProps` | `false` | Show in-app deposit notifications. Set `true` to auto-mount the toaster, or pass props to configure it. | | `funding` | `FundingConfig` | `{ enabled: false }` | Let users fund the deposit address from a connected browser wallet. When enabled, `DepositWidget` shows a "Fund from wallet" toggle. | | `fundingWallet` | `{ evm?: { provider, address } }` | — | Reuse your app's already-connected wallet as the funding source (skips the connect step). | -| `uaProjectId` | `string` | SDK default | Particle project ID for UA operations only. | +| `onramp` | `OnrampConfig` | `{ enabled: false }` | Let users buy crypto with fiat (card / Apple Pay / bank) via RampNow, delivered straight to the destination. When enabled, `DepositWidget` shows a "Buy" toggle. | +| --- @@ -108,6 +109,36 @@ const { client, stuckFunds, recoverFunds, refundDeposit } = useDepositContext(); --- +## useOnramp + +Drive a fiat purchase (buy crypto with fiat via RampNow) from a fully custom UI. Returns nothing useful until `onramp.enabled` is set on the provider. + +```tsx +import { useOnramp } from '@particle-network/simple-deposit/react'; + +const { open, status, error, enabled, supported } = useOnramp(); + +// Render your own button: + +``` + +### Return Value + +| Property | Type | Description | +|---|---|---| +| `open` | `(opts?: { amount?: number }) => Promise` | Open the RampNow overlay/redirect. | +| `close` | `() => void` | Close the overlay. | +| `isOpen` | `boolean` | Overlay is open. | +| `status` | `'idle' \| 'opening' \| 'open' \| 'processing' \| 'success' \| 'error'` | Current purchase status. | +| `lastOrder` | `OnrampOrder \| null` | Most recent completed order. | +| `error` | `Error \| null` | Last error. | +| `enabled` | `boolean` | Onramp is enabled in config. | +| `supported` | `boolean` | Destination chain supports onramp (see [supported chains](/simple-deposit/reference#onramp)). | + +--- + ## Components ### DepositWidget @@ -133,7 +164,7 @@ import { DepositWidget } from '@particle-network/simple-deposit/react'; | `onClose` | `() => void` | — | Close handler. | - When `funding.enabled` is set on the provider, `DepositWidget` automatically gains a **"Receive | Fund from wallet"** toggle — no extra props needed. + When `funding.enabled` and/or `onramp.enabled` are set on the provider, `DepositWidget` automatically gains a **"Receive | Fund | Buy"** toggle — segments appear only for the features you enable, no extra props needed. Pass `showBuy={false}` to suppress the Buy tab. ### DepositModal @@ -152,6 +183,44 @@ import { DepositModal } from '@particle-network/simple-deposit/react'; | `onClose` | `() => void` | Close handler (required). | | `overlayClassName` | `string` | Custom overlay CSS class. | +### OnrampButton + +Standalone "Buy" button for custom placement. Renders nothing when `onramp` is disabled. Must live inside a `DepositProvider` with `onramp: { enabled: true }`. + +```tsx +import { OnrampButton } from '@particle-network/simple-deposit/react'; + + console.log('bought', order)} /> +``` + +| Prop | Type | Default | Description | +|---|---|---|---| +| `client` | `DepositClient` | Context | Optional client (uses context if omitted). | +| `theme` | `'dark' \| 'light'` | `'dark'` | Color theme. | +| `amount` | `number` | — | Pre-fill the fiat amount. | +| `onPurchased` | `(order: OnrampOrder) => void` | — | Called when a purchase completes. | + +### OnrampStatusToast + +A small floating "purchase in progress / complete / failed" popup that persists even after the user switches tabs or closes the RampNow overlay. Mount it once near your app root — it portals to `document.body` and is driven by `useOnramp` status. + +```tsx +import { OnrampStatusToast } from '@particle-network/simple-deposit/react'; + + +``` + +| Prop | Type | Default | Description | +|---|---|---|---| +| `client` | `DepositClient` | Context | Optional client (uses context if omitted). | +| `theme` | `'dark' \| 'light'` | `'dark'` | Color theme. | +| `position` | `'top-right' \| 'top-left' \| 'bottom-right' \| 'bottom-left' \| 'top-center' \| 'bottom-center'` | `'bottom-right'` | Where the toast anchors. | +| `successDurationMs` | `number` | `6000` | Auto-dismiss delay for the success toast. `0` keeps it until dismissed. | + + + Order events only flow in `mode: 'overlay'`. A `redirect` tab has no postMessage channel back, so there's nothing to track and the toast stays hidden. + + ### RecoveryWidget UI for scanning and recovering stuck funds. diff --git a/simple-deposit/reference.mdx b/simple-deposit/reference.mdx index 7ff05f8..13d7115 100644 --- a/simple-deposit/reference.mdx +++ b/simple-deposit/reference.mdx @@ -135,6 +135,42 @@ interface FundingTransferResult { // straight back into fundFromWallet(). Treat it as opaque; don't construct it by hand. ``` +### OnrampConfig + +```typescript +interface OnrampConfig { + enabled: boolean; // Master switch (default: false) + apiKey?: string; // Your RampNow partner key (pk_live_…); omit to use the SDK default + mode?: 'overlay' | 'redirect'; // 'overlay' = in-page iframe (emits order events); 'redirect' = new tab (no events). Default 'overlay' + fiatCurrency?: string; // Fiat the user spends, e.g. 'EUR' (default 'USD') + defaultFiatAmount?: number; // Pre-fill the fiat amount + paymentMode?: string; // 'card' | 'apple_pay' | 'google_pay' | 'sepa' | … + widgetUrl?: string; // Override the RampNow widget base URL (e.g. a sandbox host) +} +``` + +| Property | Type | Default | Description | +|---|---|---|---| +| `enabled` | `boolean` | `false` | Master switch for the feature. | +| `apiKey` | `string` | _(SDK default)_ | **Optional.** Your RampNow partner key (`pk_live_…`). Omit to use the SDK's built-in key; set it to route through your own partner account (your fees / KYC / limits). | +| `mode` | `'overlay' \| 'redirect'` | `'overlay'` | `'overlay'` = in-page iframe modal (emits order events → activity feed updates). `'redirect'` = open in a new tab (no close button to manage, but **no order events**). | +| `fiatCurrency` | `string` | `'USD'` | Fiat currency the user spends (e.g. `'EUR'`). | +| `defaultFiatAmount` | `number` | — | Pre-fill the fiat amount. | +| `paymentMode` | `string` | — | Preferred method: `'card'`, `'apple_pay'`, `'google_pay'`, `'sepa'`, … | +| `widgetUrl` | `string` | `https://app.rampnow.io` | Override the RampNow widget base URL (e.g. a sandbox host). For testing you normally just use a sandbox `apiKey`. | + +### OnrampOrder + +The completed-order payload from RampNow, delivered by the `onramp:complete` event and exposed as `lastOrder` from `useOnramp`. + +```typescript +interface OnrampOrder { + amount: string; // Delivered amount + token: TokenType; // Delivered token (USDC today) + // …plus RampNow order metadata (status, id, destination) +} +``` + ### UATransaction ```typescript @@ -430,6 +466,34 @@ When `funding.enabled` is set, `DepositWidget` automatically shows a "Receive | Balance-source precedence: `balanceProvider` > `apiKey` (direct) > `proxyUrl` > hosted default. Balance discovery covers **Ethereum, BNB Chain, Base, Arbitrum, and Solana**; X Layer is not supported and is skipped during discovery. +### Onramp + +Let users buy crypto with fiat (card / Apple Pay / bank) via [RampNow](https://rampnow.io), delivered straight to the destination. Onramp bypasses the watcher/sweeper pipeline — RampNow's order events are the source of truth and are forwarded as `onramp:*` events. Zero-config: the SDK ships a default RampNow key, and `@rampnow/sdk` loads lazily only when the onramp opens. + +```typescript +const client = new DepositClient({ + ownerAddress: '0x...', + intermediaryAddress: '0x...', + authCoreProvider: provider, + destination: { chainId: CHAIN.BASE }, + onramp: { enabled: true }, // ← that's it +}); +``` + +When `onramp.enabled` is set, `DepositWidget` automatically adds a "Buy" tab. For custom placement use `OnrampButton`, the `useOnramp()` hook, or the `OnrampStatusToast` popup — see the [React SDK](/simple-deposit/react-sdk). Headless apps call `client.openOnramp({ amount })` directly — see the [Core SDK](/simple-deposit/core-sdk#onramp-buy-with-fiat-headless). + + + **Supported chains:** Ethereum, BNB Chain, Base, Arbitrum, and Solana. X Layer is **not** supported — `isOnrampSupported()` returns `false` and the "Buy" UI is disabled with an explanation. + + + + **Delivered token:** Onramp always buys **USDC**, matching the token the sweep delivers to the destination. + + + + Order events only flow in `mode: 'overlay'`. A `redirect` tab has no postMessage channel back, so there's nothing to track (no activity-feed updates, no status toast). + + ### Recovery Recover funds that are stuck in the intermediary wallet (e.g., due to failed sweeps). From f443dc147b805968a19c8292de22040c4014cad1 Mon Sep 17 00:00:00 2001 From: Soos3D <99700157+soos3d@users.noreply.github.com> Date: Mon, 29 Jun 2026 13:48:32 -0400 Subject: [PATCH 4/5] Update react-sdk.mdx --- simple-deposit/react-sdk.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simple-deposit/react-sdk.mdx b/simple-deposit/react-sdk.mdx index 6eb0270..40a6148 100644 --- a/simple-deposit/react-sdk.mdx +++ b/simple-deposit/react-sdk.mdx @@ -12,7 +12,7 @@ Wraps your app and manages the deposit lifecycle. Handles Auth Core context inte import { DepositProvider, CHAIN } from '@particle-network/simple-deposit/react'; From 32d47aacd7a56512a518ac85bb2613976f91e7c6 Mon Sep 17 00:00:00 2001 From: Soos3D <99700157+soos3d@users.noreply.github.com> Date: Mon, 29 Jun 2026 21:06:11 -0400 Subject: [PATCH 5/5] update deposit --- simple-deposit/core-sdk.mdx | 4 +-- simple-deposit/react-sdk.mdx | 1 + simple-deposit/reference.mdx | 58 ++++++++++++++++++++++++++++++------ 3 files changed, 52 insertions(+), 11 deletions(-) diff --git a/simple-deposit/core-sdk.mdx b/simple-deposit/core-sdk.mdx index 5cc13d8..bf09c1f 100644 --- a/simple-deposit/core-sdk.mdx +++ b/simple-deposit/core-sdk.mdx @@ -29,7 +29,7 @@ client.startWatching(); | `ownerAddress` | `string` | Yes | — | User's wallet address. | | `intermediaryAddress` | `string` | Yes | — | JWT wallet from Auth Core. | | `authCoreProvider` | `AuthCoreProvider` | No* | — | Provider for signing. *Required for sweeps. | -| `destination` | `DestinationConfig` | Yes | — | Where swept funds go (`chainId` required). | +| `destination` | `DestinationConfig` | Yes | — | Where swept funds go (`chainId` required; optional `token` defaults to USDC — see [Destination tokens](/simple-deposit/reference#destination-tokens)). | | `supportedTokens` | `TokenType[]` | No | All | Tokens to watch. | | `supportedChains` | `number[]` | No | All 17 | Chains to watch. | | `autoSweep` | `boolean` | No | `true` | Auto-sweep on detection. | @@ -173,7 +173,7 @@ import { ## Onramp — Buy with Fiat (Headless) -Let a user **buy crypto with fiat** (card / Apple Pay / bank) via [RampNow](https://rampnow.io), delivered **straight to the configured destination**. Unlike Fund from Wallet, the purchase bypasses the deposit-address → watcher → sweeper pipeline — RampNow's order events are the source of truth, forwarded by the SDK as `onramp:*` events. The SDK delivers **USDC**, matching the sweep. +Let a user **buy crypto with fiat** (card / Apple Pay / bank) via [RampNow](https://rampnow.io), delivered **straight to the configured destination**. Unlike Fund from Wallet, the purchase bypasses the deposit-address → watcher → sweeper pipeline — RampNow's order events are the source of truth, forwarded by the SDK as `onramp:*` events. The onramp buys the **destination token** (USDC by default), matching the sweep — note RampNow supports a narrower token set than the sweep, so gate the "Buy" UI on `isOnrampSupported()` / `isOnrampTokenSupported(token, chainId)` (see [Destination tokens](/simple-deposit/reference#destination-tokens)). Onramp is a single flag — zero-config. The SDK ships with a default RampNow key, so there's nothing to source; pass your own `apiKey` only to route purchases through your own RampNow partner account. The underlying `@rampnow/sdk` is loaded lazily (dynamic `import()`) only when the onramp is opened, so it never bundles into apps that don't use it. diff --git a/simple-deposit/react-sdk.mdx b/simple-deposit/react-sdk.mdx index 40a6148..bf31feb 100644 --- a/simple-deposit/react-sdk.mdx +++ b/simple-deposit/react-sdk.mdx @@ -26,6 +26,7 @@ import { DepositProvider, CHAIN } from '@particle-network/simple-deposit/react'; |---|---|---|---| | `destination.chainId` | `number` | — | **Required.** Destination chain (use `CHAIN` constant). | | `destination.address` | `string` | Owner's EOA | Custom sweep destination address. | +| `destination.token` | `TokenType` | `'USDC'` | Token delivered to the destination (sweep converts to it; onramp buys it). See [Destination tokens](/simple-deposit/reference#destination-tokens). | | `supportedTokens` | `TokenType[]` | All | Tokens to watch. | | `supportedChains` | `number[]` | All 17 chains | Chains to watch. | | `autoSweep` | `boolean` | `true` | Auto-sweep detected deposits. | diff --git a/simple-deposit/reference.mdx b/simple-deposit/reference.mdx index 13d7115..ff76de3 100644 --- a/simple-deposit/reference.mdx +++ b/simple-deposit/reference.mdx @@ -10,11 +10,45 @@ description: "Type definitions, constants, chain utilities, and advanced configu ```typescript interface DestinationConfig { - address?: string; // Defaults to ownerAddress - chainId: number; // Required — use CHAIN constant + address?: string; // Defaults to ownerAddress + chainId: number; // Required — use CHAIN constant + token?: TokenType; // Defaults to 'USDC' — see "Destination tokens" } ``` +#### Destination tokens + +By default every deposit is delivered as **USDC** on the destination chain. Set +`destination.token` to deliver a different asset; the sweep converts each deposit +into that token and the fiat onramp buys it directly. The token must be +deliverable on the destination chain or the client throws a `ConfigurationError`. + +```typescript +// Deliver native ETH on Base instead of USDC +destination: { chainId: CHAIN.BASE, token: 'ETH' } +``` + +Deliverable token × chain matrix: + +| Token | Ethereum | Base | Arbitrum | BNB | Solana | +|-------|:--------:|:----:|:--------:|:---:|:------:| +| USDC | ✓ | ✓ | ✓ | ✓ | ✓ | +| USDT | ✓ | — | ✓ | ✓ | ✓ | +| ETH | ✓ (native) | ✓ (native) | ✓ (native) | ✓ (bridged) | — | +| SOL | — | — | — | — | ✓ (native) | + +Deliverability mirrors the Universal Account's routable primary assets (Base has +no USDT route, for example). Use `isSupportedDestinationToken(token, chainId)` to +check a combo at runtime. + +> **X Layer** is a deposit *source* only — the Universal Account has no X Layer +> primary tokens, so it cannot be used as a sweep destination. + +> **Onramp note:** RampNow's fiat "Buy" supports a narrower set than the sweep — +> stablecoins on all chains, but `ETH` only on Ethereum/Base, `BNB` on BNB, and +> `SOL` on Solana. `isOnrampSupported()` / `isOnrampTokenSupported(token, chainId)` +> reflect this; gate "Buy" UI on them. + ### DetectedDeposit ```typescript @@ -303,12 +337,14 @@ import { ```typescript import { - getChainName, // getChainName(42161) → "Arbitrum" - isValidDestinationChain, // isValidDestinationChain(42161) → true - getAddressType, // getAddressType(101) → 'solana' - isValidEvmAddress, // Validates 0x + 40 hex chars - isValidSolanaAddress, // Validates base58 format - validateAddressForChain, // Validates address format for chain type + getChainName, // getChainName(42161) → "Arbitrum" + isValidDestinationChain, // isValidDestinationChain(42161) → true + isSupportedDestinationToken, // isSupportedDestinationToken('ETH', CHAIN.BASE) → true + isOnrampTokenSupported, // isOnrampTokenSupported('ETH', CHAIN.BASE) → true + getAddressType, // getAddressType(101) → 'solana' + isValidEvmAddress, // Validates 0x + 40 hex chars + isValidSolanaAddress, // Validates base58 format + validateAddressForChain, // Validates address format for chain type } from '@particle-network/simple-deposit'; ``` @@ -385,12 +421,16 @@ setDestination({ chainId: CHAIN.ETHEREUM }); ```typescript Headless client.setDestination({ chainId: CHAIN.BASE, address: '0xTreasury...' }); const dest = client.getDestination(); // { address, chainId } + +// Deliver a non-default token (see "Destination tokens") +client.setDestination({ chainId: CHAIN.BASE, token: 'ETH' }); ``` - Throws `ConfigurationError` if the chain ID or address is invalid. + Throws `ConfigurationError` if the chain ID, address, or token is invalid — see the + [deliverable token × chain matrix](#destination-tokens). ### Notifications