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:

NetworkDepositWithdrawMoves
BaseYesYesUSDC
EthereumYesYesUSDC
SolanaYesYesUSDC
ArbitrumYesYesUSDC
OptimismYesYesUSDC
PolygonYesYesUSDC
BNB ChainYesYesUSDC
AvalancheYesYesUSDC
HyperliquidYesYesUSDC (perps)
Robinhood ChainYesYesUSDG
LighterVia the user's walletYesUSDC (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#

BaseRobinhood Chain
Chain ID84534663
Main assetUSDCUSDG (Global Dollar)
Other assetsHOODS
Private paymentsLiveLive
Private swapsComingLive
Yield on balancesUSDC lendingSet 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#

TypeScript
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
Pool0x0b4e0C18e4005363A10a93cb30e0a11A88bee648
USDC0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
APIhttps://api.senddy.com
Explorerhttps://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.

TypeScript
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
Pool0x797814350dBa33B99E773706a55EF9583F57bA99
USDG0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
APIhttps://api-rh.senddy.com
Explorerhttps://robin.etherscan.io

Robinhood Chain isn't in viem/chains. Define it once:

TypeScript
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.