Stark Crossing is a self-custodial Ethereum mainnet → Starknet mainnet migration dApp. It indexes an EVM wallet, keeps every active StarkGate ERC-20 route selected by default, proves control of the destination Starknet account with a nonce-bound SNIP-12 signature, and submits the exact approvals and deposits from the user's Ethereum wallet.
The v1 scope is deliberately narrow: native ETH and ERC-20 assets only. NFTs, swaps, unsupported tokens, smart-deposit strategies, and other source chains are out of scope.
flowchart LR
E["Connect Ethereum wallet"] --> I["Index ETH + ERC-20 holdings"]
I --> S["Select active StarkGate routes"]
S --> D["Connect deployed Starknet account"]
D --> P["Sign nonce-bound SNIP-12 proof"]
P --> R["Review immutable migration plan"]
R --> C{"EIP-5792 available?"}
C -->|Yes| B["Submit wallet call batch"]
C -->|No| Q["Submit resumable sequence"]
B --> G["StarkGate L1 deposits"]
Q --> G
Requires Node.js 22+.
npm install
cp .env.example .env.local
npm run devOpen http://localhost:3000. The fixture-backed demo is usable without credentials in development.
For a real wallet, set at least:
ALCHEMY_API_KEY: server-side Alchemy Portfolio API and Ethereum RPC access.STARKNET_RPC_URL: Starknet mainnet RPC for deployment checks and signature verification.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID: optional for WalletConnect/mobile connections. Injected Rabby and MetaMask connections work without it.DATABASE_URL: PostgreSQL connection string. Development can use memory; production must use persistent storage.
NEXT_PUBLIC_ENABLE_MAINNET_EXECUTION is false by default. Leave it locked until the application has been exercised against forked mainnet state and independently security reviewed.
- Next.js 16, React 19, TypeScript, Wagmi/Viem, and RainbowKit for EVM wallet access.
@starknet-io/get-starknet-uiand Starknet.js for Starknet Wallet Standard connections, typed-data signing, deployment checks, and account-contract signature verification.- Alchemy's Tokens By Wallet endpoint supplies balances, metadata, and USD prices.
- The live Starknet bridged-token registry is the route allowlist. Entries marked
Deposit blockedare excluded. Production fails closed when that registry is unavailable; a small bundled subset is available only in development or by explicit override. - PostgreSQL stores full migration plans, action progress, expiring challenges, and replay-consumption state. Tables are created on first use. Production fails closed without
DATABASE_URLunless an ephemeral preview explicitly opts intoALLOW_IN_MEMORY_PERSISTENCE=true.
The API never trusts submitted balances or transaction calldata. When a plan is built, the server re-reads balances and allowances from Ethereum, resolves contracts from the current StarkGate registry, obtains estimateDepositFeeWei onchain, and generates the calls itself.
- Only active StarkGate ETH/ERC-20 routes can enter a plan. StarkGate's protocol documentation describes the legacy and multi-bridge deposit ABIs used here.
- Unsupported and suspicious holdings remain visible but unselected. Eligible holdings are selected by default, with a one-click control for positions worth less than $1.
- Every insufficient ERC-20 allowance gets an exact approval. Tokens with an existing non-zero allowance get a zero-reset first for compatibility.
- Native ETH is deposited last. The plan retains the configured Ethereum gas reserve plus all quoted L1→L2 messaging fees.
- The destination must already be deployed on Starknet mainnet. Its account contract verifies a readable SNIP-12 signature bound to the source address, destination address, exact plan hash, random nonce, and 10-minute expiry.
- The source wallet still authorizes every Ethereum action. No backend key or custody account exists.
- Wallets advertising the EIP-5792
atomiccapability receivewallet_sendCallswithatomicRequired: false; the returned batch is tracked withwallet_getCallsStatus. The EIP-5792 specification explicitly permits non-atomic handling, so wallets without that capability use the persisted sequential fallback. - Sequential progress is saved after submission and confirmation. On resume, an already-submitted transaction is waited on by hash and is not sent twice.
npm run typecheck
npm run lint
npm test
npm run buildBefore enabling mainnet writes:
- Use dedicated authenticated Ethereum and Starknet RPC endpoints, persistent PostgreSQL, and a real WalletConnect project ID.
- Set
ALLOW_DEMO_MODE=falseandALLOW_STARKGATE_REGISTRY_FALLBACK=false, rate-limit the API routes, and place the app behind a restrictive CSP and HTTPS-only edge configuration. - Validate the complete flow on an Ethereum mainnet fork with representative token behavior, including zero-reset approvals and batch partial failures.
- Add monitoring for failed API calls, challenge replay attempts, EIP-5792 bundle states, Ethereum receipts, and the StarkGate L1→L2 deposit lifecycle.
- Commission an independent application and smart-contract-integration security review. This repository invokes audited protocol contracts but is not itself audited.
- No ERC-721 or ERC-1155 support.
- No swap or alternative bridge path for assets absent from the StarkGate registry.
- USD values are estimates and can be missing or stale; they never determine an onchain amount.
- The selected ERC-20 amount is the wallet's full current balance. ETH is the full current balance minus messaging fees and the configured reserve.
- Stark Crossing tracks Ethereum transaction/batch confirmation and links to StarkGate activity; it does not yet persist
ACCEPTED_ON_L2delivery status. - A fixed reserve cannot guarantee future Ethereum gas costs. The final wallet confirmation remains authoritative.
- The in-memory development store is intentionally non-durable and unsuitable for multi-instance deployment.