Quickstart

Quickstart

Run the SODA v0 demo end-to-end: a Solana program signs a real Sepolia transaction, or, with DEMO_CHAIN=sui-testnet, a Sui testnet transfer from the same wallet’s Sui address. The whole flow takes about 10 seconds once funds are in place.

Just want to use the SDK in your own project? You don’t need to clone this repo. Run pnpm add @soda-sdk/core and follow the Sign an Ethereum tx guide. This page is for the full local demo (validator + Anchor build + signer daemon).

v0 ships with a single dev signing key (no real t-of-n committee yet). That key persists across runs, so the derived ETH address is stable: fund it once on Sepolia, demo many times.

⚠️

Sepolia / devnet only. Don’t send real ETH (mainnet) or other valuable assets to a SODA-derived address while v0 is the deployed committee. A single-key signer means anyone with the keystore can move every derived balance — fine for a hackathon demo, not for production. See Concepts → Signing committee for the v1 design.

Prerequisites

  • WSL (Ubuntu) or Linux.
  • Rust 1.95+, Solana CLI 3.1+, Anchor 0.32.1, Node 24+, pnpm 10+.
  • A Solana keypair at ~/.config/solana/id.json.
  • A .env file at the repo root with SEPOLIA_RPC_URL=... (Alchemy / Infura). The Sui path needs no RPC key: it uses Mysten’s public GraphQL endpoint.

Setup

Clone and install

git clone https://github.com/JingYuan0926/frontier
cd frontier
pnpm install

Set up .env

cat > .env <<EOF
SEPOLIA_RPC_URL=https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY
SOLANA_DEVNET_RPC_URL=https://devnet.helius-rpc.com/?api-key=YOUR_KEY
# Optional sponsors that top the derived address up with gas just in time.
# 32-byte secp256k1 hex secrets holding testnet money only.
SEPOLIA_FUNDER_KEY=
SUI_FUNDER_KEY=
EOF

Fund the derived address

The first time you run the demo, it prints the derived Sepolia address (stable across runs as long as keyshare.dev.json exists). Send around 0.001 ETH of Sepolia testnet ETH to that address from a faucet.

For Sui, the derived address is different (the chain tag is a derivation input) and needs at least 0.02 SUI. The demo asks the public faucet itself, or sends from SUI_FUNDER_KEY if set; if both fail it prints the address and polls until you fund it from faucet.sui.io.

Run the demo

The CLI is the canonical demo path.

./demo.sh

Defaults to Solana devnet and an Aave V3 depositETH of 0.0001 ETH: the derived address ends up holding aWETH, a lending position owned by a Solana account. After a successful broadcast, demo.sh automatically runs pnpm verify <hash>, which prints the cryptographic audit trail (the checks tying the Solana SigRequest PDA to the broadcast EVM transaction).

DEMO_ACTION=borrow ./demo.sh             # Pool.borrow 0.1 USDC against the aWETH
DEMO_CHAIN=base-sepolia ./demo.sh        # destination chain: sepolia | base-sepolia
DEMO_CHAIN=sui-testnet ./demo.sh         # Sui: buy DEEP on DeepBook from the derived Sui address
DEMO_ACTION=sell DEMO_CHAIN=sui-testnet ./demo.sh    # sell that DEEP back for SUI
SODA_DRY_RUN=1 ./demo.sh                 # skip the foreign-chain broadcast, verify on-chain only
SOLANA_CLUSTER=local ./demo.sh           # against a local validator

The borrow is the proof of control: a deposit could be read as “sent ETH to a contract”, but only the position’s owner can borrow against it, and the derived address ends up holding USDC it never had. Both actions go through the same pipeline; only the calldata differs.

DEMO_CHAIN=sui-testnet (or sui-devnet) runs the same pipeline against a non-EVM chain: sui_demo BCS-encodes the Sui transaction on-chain, derives the sender itself, and commits sha256(blake2b(intent || tx)) instead of keccak(rlp). Sui accepts the committee’s secp256k1 signature natively, so soda and finalize_signature are untouched.

The Sui actions are DeepBook V3 trades, and they are the Sui counterpart of the Aave pair: swap buys DEEP with SUI off Sui’s on-chain central limit order book, sell spends that DEEP back. The sell is the proof of control — it moves coin objects the address acquired itself, not the SUI it was funded with. DEMO_ACTION=transfer is a plain transfer, kept as a fallback. demo.sh chains into pnpm verify:sui <digest> afterwards. See Sign a Sui tx.

What you should see

  1. The CLI prints the derived ETH address and waits for funding (skipped if already funded).
  2. A SigRequested event fires on Solana devnet. Visible on Solscan.
  3. The signer signs the payload off-chain with k256.
  4. A finalize_signature instruction runs secp256k1_recover on-chain and stores the signature.
  5. The signed RLP is broadcast to Sepolia. Etherscan link is printed.
  6. pnpm verify <hash> runs automatically and prints the six audit checks.

Verifying any past run

pnpm verify 0x<sepolia_tx_hash>
DEMO_CHAIN=sui-testnet pnpm verify:sui <sui_tx_digest>

Reads only public state. Re-derives the ETH (or Sui) address from the on-chain SigRequest PDA and confirms the broadcast from / sender address matches. Both tools look the SigRequest up under your CLI wallet; set VERIFY_REQUESTER=<pubkey> to audit a run signed by another account.

Troubleshooting

SymptomFix
insufficient fundsFund the derived address from a Sepolia faucet.
AlreadyCompleted (custom error 0x1770)Idempotent: another process (relayer / daemon) already signed. Safe to ignore.
Anchor build fails on BPF stackYou enabled on-chain ProjectivePoint ops. v0 keeps derivation off-chain.
Web UI shows blank balanceSet SEPOLIA_RPC_URL in apps/web/.env.local (mirrors repo-root .env).
Sui faucet 429 Wait for NsThe public faucet rate-limits per IP. Use faucet.sui.io, the Sui Discord, or set SUI_FUNDER_KEY; the demo keeps polling the balance.
no SUI coin objects at 0x…Unfunded, or the GraphQL indexer has not seen the faucet transfer yet. Check Suiscan and rerun in a few seconds.
Sui dry-run FAILURE after a top-upA gas coin’s version changed between reading it and building the tx. Rerun; the demo refetches coins and the payload is new.
DEMO_CHAIN=sui-testnet still runs the EVM demoAn older demo.sh let .env override the environment. Current demo.sh keeps what you set on the command line.