3send is a Next.js App Router dApp that lets any Base wallet pay a burn fee, encrypt a file locally, and deliver it to another wallet’s inbox over Ratio1 edge services. Every transfer is protected end-to-end: encryption happens in the browser, the 3send Manager contract validates payment, and the encrypted payload lives on Ratio1 R1FS while metadata is tracked in CStore.
- RainbowKit/wagmi wallet connection locked to Base Sepolia by default (
components/Providers.tsx) with automatic chain guards in the send flow. - Multi-asset payments (R1, USDC, ETH) handled in
components/SendFileCard.tsx, including allowance management, automated swaps, and burn verification against the Manager contract ABI inlib/SmartContracts.ts. - Client-side X25519 + AES-GCM encryption for files and optional notes (
lib/encryption.ts) with deterministic handshake messages assembled inlib/handshake.ts. - Key management that supports vault-managed keys, WebAuthn passkeys, and recovery phrases; APIs live under
app/api/keys/*andapp/api/vault/*, while UI controls ship fromapp/profile/page.tsx. - Inbox and outbox pages backed by Ratio1 CStore and R1FS (
app/api/inbox/*,app/api/sent/route.ts,app/api/send/upload/route.ts) with in-browser decryption viaapp/inbox/page.tsx. - Base identity lookups for avatars and display names via Coinbase OnchainKit identity utilities.
- Documentation and pricing routes (
app/docs,app/pricing) so the production app and repo stay aligned on feature explanations and tiering.
- Connect & resolve keys. Users connect with RainbowKit. The sender resolves the recipient’s X25519 public key through
/api/send/getReceiverPublicKey, which pulls a registered passkey/seed from CStore or provisions a vault key (lib/vault.ts). - Pay & encrypt. The sender picks a file and preferred asset.
SendFileCardquotes the Manager contract, confirms balances/allowances, and submits the burn (transferPayment*functions). The file is encrypted locally with an ephemeral X25519 exchange and AES-GCM, and the sender signs a handshake (lib/handshake.ts) that binds ciphertext lengths, metadata digests, and the payment transaction hash. - Upload & record.
/api/send/uploadverifies the signed handshake, re-fetches the on-chain receipt, decodes thePaymentProcessedevent usingManager3sendAbi, stores the ciphertext in Ratio1 R1FS, and stores the metadata record in Ratio1 CStore under both the sender and recipient hashes. - Receive & decrypt. The recipient wallet pulls its inbox from
/api/inbox, downloads the encrypted payload via/api/inbox/download, and decrypts it in the browser using the active key source—vault (lib/vaultClient.ts), passkey PRF (lib/passkeyClient.ts), or recovery phrase (lib/keys.ts). Notes can be decrypted on demand; key-rotation guards prevent mismatched credentials from unlocking older transfers.
app/– Next.js App Router routes (outbox,inbox,profile,docs,pricing) plus API routes underapp/api.app/api/send/upload/route.ts– validates payment receipts and pushes encrypted files to Ratio1.app/api/inbox/*&app/api/sent/route.ts– list/download stored transfers from CStore/R1FS.app/api/keys/*&app/api/vault/*– manage registered keys and vault secrets.
components/– shared UI (wallet providers, send card, pricing CTA, profile controls).lib/– protocol helpers: encryption, handshake construction, formatting, key derivation, vault crypto, shared types, and ABI exports.public/– static assets.stubs/– development shims (e.g., custompino-prettyloader).types/– shared TypeScript definitions consumed across the app and API layer.
Set these variables in .env.local (never commit them):
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID– WalletConnect Cloud project id for RainbowKit.NEXT_PUBLIC_GOOGLE_ANALYTICS_ID– GA4 measurement id (e.g.,G-XXXXXXXXXX) used for site analytics.MINIAPP_ACCOUNT_ASSOCIATION_HEADER/MINIAPP_ACCOUNT_ASSOCIATION_PAYLOAD/MINIAPP_ACCOUNT_ASSOCIATION_SIGNATURE– values generated in Base Build once the manifest is live.EE_CHAINSTORE_API_URL/CSTORE_API_URL– Ratio1 CStore endpoint used by@ratio1/edge-sdk-ts.EE_R1FS_API_URL/R1FS_API_URL– Ratio1 R1FS endpoint for encrypted payload storage.VAULT_PRIVATE_KEY_SECRET– server-side secret (≥ 32 chars) used to encrypt vault private keys at rest (lib/vault.ts).EE_CHAINSTORE_PEERS(optional) – JSON array of peer URLs if you need to hydrate the SDK with additional Ratio1 nodes.RPC_URL_BASEandRPC_URL_BASE_SEPOLIA(optional) – override RPC endpoints for the on-chain receipt checks performed in/api/send/upload.EE_HOST_ID(optional) – shows which edge node served the request incomponents/ServedByComponent.tsx.
The Ratio1 SDK also accepts the above variables via window.__RATIO1_ENV__ when deployed.
- The Farcaster manifest is served from
app/.well-known/farcaster.json/route.ts. Populate the Base BuildaccountAssociationvalues in your environment once you verify the domain. components/Providers.tsxcallssdk.actions.ready()from@farcaster/miniapp-sdkwhen the app boots inside the Base app shell to dismiss the loading splash.app/layout.tsxinjects the requiredfc:miniappmetadata so Base renders embeds and the launch button correctly. Update the splash, image, and screenshot URLs via the environment variables above.
- Install prerequisites: Node 18+ and npm.
- Create
.env.localwith the variables above (base values for local development are provided in the internal.env, but don’t commit secrets). - Install dependencies:
npm install - Run the dev server:
npm run dev(opens http://localhost:3000) - Lint before pushing:
npm run lint
npm run build and npm run start are available for production smoke tests, but the preferred local validation loop is npm run lint.
- Connect a wallet on Base Sepolia, ensure chain guards prompt if you’re on the wrong network.
- Visit Profile to register a passkey or recovery phrase and confirm the status indicator updates.
- Send a file from Outbox using each supported asset (R1, USDC, ETH); wait for the burn transaction, sign the handshake, and observe the upload succeed.
- Confirm the entry appears in Outbox (sender view) and Inbox (recipient view).
- Download the payload from Inbox and verify the decrypted file matches the source; decrypt any attached note.
- Rotate your key (passkey → seed or vice versa) and confirm old transfers warn about key mismatch while new transfers still decrypt.
- Protocol deep-dive lives at
/docs(rendered fromapp/docs/page.tsx). - Pricing tiers are defined in
lib/constants.tsand rendered in/pricing. - Smart contract source of truth: https://github.com/aledefra/3sendxyz-sc (ABI mirror in
lib/SmartContracts.ts).
@software{butusina2025_3sendxyz,
author = {Butusina, Petrica and Defranceschi, Alessandro},
title = {{3sendxyz}: End-to-end encrypted, decentralized file transfer Web App},
year = {2025},
url = {https://github.com/3lemmalabs/3sendxyz},
organization = {3lemma Labs},
note = {Software library}
}