Choose a chain
Senddy runs on Base and Robinhood Chain. What each one supports, and the configuration for each.
Senddy runs a privacy pool on each chain. Your users' balances live on the chain your app uses, and they can deposit from many more. See Move money in and out.
Networks your users can use#
Users can deposit and withdraw on any of these, whichever chain your app holds balances on. Everything but your own chain is bridged through Relay deposit addresses:
| Network | Deposit | Withdraw | Moves |
|---|---|---|---|
| Base | Yes | Yes | USDC |
| Ethereum | Yes | Yes | USDC |
| Solana | Yes | Yes | USDC |
| Arbitrum | Yes | Yes | USDC |
| Optimism | Yes | Yes | USDC |
| Polygon | Yes | Yes | USDC |
| BNB Chain | Yes | Yes | USDC |
| Avalanche | Yes | Yes | USDC |
| Hyperliquid | Yes | Yes | USDC (perps) |
| Robinhood Chain | Yes | Yes | USDG |
| Lighter | Via the user's wallet | Yes | USDC (perps) |
Lighter can't pay out to a deposit address, so users withdraw from Lighter to their own wallet and deposit from there.
How it works for each is in Move money in and out.
Where private balances are held#
| Base | Robinhood Chain | |
|---|---|---|
| Chain ID | 8453 | 4663 |
| Main asset | USDC | USDG (Global Dollar) |
| Other assets | HOODS | |
| Private payments | Live | Live |
| Private swaps | Coming | Live |
| Yield on balances | USDC lending | Set per asset |
Pick Base for dollar payments, savings and checkout. Pick Robinhood Chain if you need private swaps today, or you're building around assets that live there.
One identity per chain#
A user's private keys are derived from their wallet signature and the pool's address. The same person on Base and on Robinhood Chain gets two separate, unlinkable private identities. That's intentional.
It also means the pool address must be right. A wrong pool doesn't throw an error: it derives a different identity and shows the user an empty balance.
Base#
import { createSenddyClient, SHARED_CONTRACTS, V3_CONTRACTS } from '@senddy/client';
const senddy = createSenddyClient({
publicClient,
walletClient,
chainId: 8453,
pool: V3_CONTRACTS.Pool,
usdc: SHARED_CONTRACTS.USDC,
subgraphUrl: V3_CONTRACTS.Subgraph[0],
apiUrl: V3_CONTRACTS.ApiEndpoint,
apiKey: process.env.SENDDY_API_KEY,
});| Value | |
|---|---|
| Pool | 0x0b4e0C18e4005363A10a93cb30e0a11A88bee648 |
| USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| API | https://api.senddy.com |
| Explorer | https://basescan.org |
Robinhood Chain#
Robinhood Chain has more than one asset, so pass the assets table. Each token signs deposits under its own EIP-712 domain, and a wrong domain makes every deposit signature invalid.
import { createSenddyClient } from '@senddy/client';
import { ROBINHOOD_CONTRACTS } from '@senddy/core';
const USDG = ROBINHOOD_CONTRACTS.USDG;
const HOODS = '0x…'; // the HOODS token address
const senddy = createSenddyClient({
publicClient,
walletClient,
chainId: ROBINHOOD_CONTRACTS.chainId,
pool: ROBINHOOD_CONTRACTS.Pool,
subgraphUrl: ROBINHOOD_CONTRACTS.Subgraph[0],
apiUrl: ROBINHOOD_CONTRACTS.ApiUrl,
apiKey: process.env.SENDDY_API_KEY,
assets: {
[USDG]: { symbol: 'USDG', decimals: 6, domain: { name: 'Global Dollar', version: '1' }, kind: 'stable' },
[HOODS]: { symbol: 'HOODS', decimals: 6, domain: { name: 'HOODS', version: '1' }, kind: 'token' },
},
defaultAsset: USDG,
});| Value | |
|---|---|
| Pool | 0x797814350dBa33B99E773706a55EF9583F57bA99 |
| USDG | 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 |
| API | https://api-rh.senddy.com |
| Explorer | https://robin.etherscan.io |
Robinhood Chain isn't in viem/chains. Define it once:
import { defineChain } from 'viem';
export const robinhood = defineChain({
id: 4663,
name: 'Robinhood Chain',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: { default: { http: [process.env.NEXT_PUBLIC_RPC_URL!] } },
blockExplorers: { default: { name: 'Etherscan', url: 'https://robin.etherscan.io' } },
});Bringing your own chain#
If you want a Senddy pool for an asset or chain that isn't listed, get in touch. HOODS on Robinhood Chain started this way.