Solana to Base
Push-based flow with an optional relayer for instant execution on Base. See Solana to Base.
The Base-Solana bridge moves tokens and arbitrary messages in both directions between Base and Solana, with optional contract-call execution at the destination.
The bridge supports:
This page covers the bridge architecture, production contract addresses, and reference implementation patterns.
The Base bridge contract locks or burns outgoing tokens and mints or unlocks incoming tokens. It also assembles outgoing messages into Merkle trees. Validators sign the Merkle root every ~300 finalized blocks and relay it to Solana, where users prove their message exists in the tree to complete the transfer.
Key smart contracts:
The Solana bridge program manages token transfers by locking or burning the asset and emitting events. For messages, validators forward those events to Base, where execution runs through your Twin contract.
Key programs (Solana Mainnet-Beta):
The full repository lives at github.com/base/bridge.
Solana to Base
Push-based flow with an optional relayer for instant execution on Base. See Solana to Base.
Base to Solana
Proof-based burn-and-unlock flow with full custody. See Base to Solana.
Terminally Onchain
Production terminal UI for bridging plus contract calls. See Terminally Onchain example.
Flow: Lock SOL/SPL → (optional) pay for relay → validators approve → mint and execute on Base.
The Solana to Base direction uses a pull-based model with three steps:
Reference scripts for auto-relay, token wrapping, and CLI utilities live in the scripts/ directory of the official repository: Solana to Base CLI scripts.
This sample bridges SOL with auto-relay enabled.
// Configureconst TO = "0x8c1a617bdb47342f9c17ac8750e0b070c372c721"; // Base addressconst AMOUNT = 0.001; // SOL amount
// Bridge SOL with auto-relayconst ixs = [ getBridgeSolInstruction({ payer, from: payer, solVault: solVaultAddress, bridge: bridgeAccountAddress, outgoingMessage, to: toBytes(TO), remoteToken: toBytes("0xC5b9112382f3c87AFE8e1A28fa52452aF81085AD"), // SOL on Base amount: BigInt(AMOUNT * 10**9), }), await buildPayForRelayIx(RELAYER_PROGRAM_ID, outgoingMessage, payer)];
await buildAndSendTransaction(SOLANA_RPC_URL, ixs, payer);Full handler: Solana to Base relay script.
The example above bridges native SOL. To bridge custom SPL tokens, deploy wrapped ERC20 representations on Base via CrossChainERC20Factory.
Reference: token wrapping example.
// Deploy wrapped token on Baseconst mintBytes32 = getBase58Codec().encode(SOLANA_SPL_MINT_ADDRESS).toHex();
await client.writeContract({ address: "0x58207331CBF8Af87BB6453b610E6579D9878e4EA", // Factory abi: TokenFactory, functionName: "deploy", args: [`0x${mintBytes32}`, "Token Name", "SYMBOL", 9],});Flow: Burn ERC20 SOL on Base → wait for finalization → generate Merkle proof → execute on Solana.
The return path burns the wrapped token on Base, waits for the message to become provable, then submits the proof on Solana to release the native asset. This route preserves full custody and requires a prover.
Reference: Base to Solana example.
// Step 1: Burn SOL on Baseconst transfer = { localToken: "0xC5b9112382f3c87AFE8e1A28fa52452aF81085AD", // SOL (on Base) remoteToken: pubkeyToBytes32(SOL_ADDRESS), to: pubkeyToBytes32(solanaAddress), remoteAmount: BigInt(AMOUNT * 10**9),};
const txHash = await client.writeContract({ address: "0xB2068ECCDb908902C76E3f965c1712a9cF64171E", // Bridge abi: Bridge, functionName: "bridgeToken", args: [transfer, []],});
// Step 2: Wait for finalizationconst isProvable = await isBridgeMessageProvable(txHash);
// Step 3: Generate proofconst { event, rawProof } = await generateProof(txHash, baseBlockNumber);
// Step 4: Execute on Solanaconst proveIx = getProveMessageInstruction({ nonce: event.message.nonce, sender: toBytes(event.message.sender), data: toBytes(event.message.data), proof: rawProof.map(e => toBytes(e)), messageHash: toBytes(event.messageHash),});
const relayIx = getRelayMessageInstruction({ message: messagePda });await buildAndSendTransaction(SOLANA_RPC_URL, [proveIx, relayIx], payer);The repository ships utilities for converting between Solana and Base address formats, loading the Solana CLI keypair for signing, and building/sending Solana transactions: scripts/src/commands.
Convert a Solana pubkey to bytes32 for Base contracts:
// Convert Solana pubkey to bytes32 for Base contractsimport { pubkeyToBytes32 } from "./utils/pubkeyToBytes32";
const bytes32Address = pubkeyToBytes32(solanaAddress);Load the Solana CLI keypair for signing:
import { getSolanaCliConfigKeypairSigner } from "./utils/keypair";
const payer = await getSolanaCliConfigKeypairSigner();Build and submit a Solana transaction:
import { buildAndSendTransaction } from "./utils/buildAndSendTransaction";
const signature = await buildAndSendTransaction(SOLANA_RPC_URL, ixs, payer);Repository: github.com/base/sol2base.
Terminally Onchain is a production Next.js app that exposes the bridge through a command terminal UI. Users connect a Solana wallet and run commands that bridge value and call a contract on Base:
bridge 0.0001 sol 0xYourTwin --call-contract 0x311935Cd80B76769bF2ecC9D8Ab7635b2139cf82 \ --call-selector "transfer(address,uint256)" \ --call-args 0x0000000000000000000000000000000000000000 100000000000000The workflow:
queueBridge validates SPL overrides, ABI-encodes the Base call via encodeFunctionData, and stages relay overrides.solanaBridge.bridge() resolves the destination (ENS or Basename), confirms balances, and calls realBridgeImplementation to sign and submit the Solana transaction.Key implementation references:
src/lib/bridge.ts — asset resolution (including mint addresses), environment-aware RPC connections, and call attachmentsrc/lib/realBridgeImplementation.ts — builds Solana transactions with PayForRelay plus bridge_sol/bridge_spl instructions, using per-environment PDAs and gas-fee receiverssrc/components/MainContent.tsx — terminal UI with command staging, log viewer, and ABI encoding for arbitrary Base callssrc/components/WalletConnection.tsx — fetches the deterministic Twin address on Base Mainnet/Sepolia for the connected Solana walletgit clone https://github.com/base/sol2base.gitcd sol2basenpm install --legacy-peer-deps
# Configure env (RPC URLs, relayer addresses, CDP API keys, etc.)cp env.template .env.local
npm run dev # defaults to http://localhost:3000{ "Bridge": "0x3eff766C76a1be2Ce1aCF2B69c78bCae257D5188", "BridgeValidator": "0xAF24c1c24Ff3BF1e6D882518120fC25442d6794B", "CrossChainERC20Factory": "0xDD56781d0509650f8C2981231B6C917f2d5d7dF2", "SOL": "0x311935Cd80B76769bF2ecC9D8Ab7635b2139cf82"}{ "BridgeProgram": "HNCne2FkVaNghhjKXapxJzPaBvAKDG1Ge3gqhZyfVWLM", "BaseRelayerProgram": "g1et5VenhfJHJwsdJsDbxWZuotD5H4iELNG61kS4fb9"}{ "Bridge": "0x01824a90d32A69022DdAEcC6C5C14Ed08dB4EB9B", "BridgeValidator": "0xa80C07DF38fB1A5b3E6a4f4FAAB71E7a056a4EC7", "CrossChainERC20Factory": "0x488EB7F7cb2568e31595D48cb26F63963Cc7565D", "SOL": "0xCace0c896714DaF7098FFD8CC54aFCFe0338b4BC", "FLYWHEEL_ADDRESS": "0x00000F14AD09382841DB481403D1775ADeE1179F", "BRIDGE_CAMPAIGN_ADDRESS": "0xE2AD1C34382410C30d826B019A0B3700F5c4e6c9"}{ "BridgeProgram": "7c6mteAcTXaQ1MFBCrnuzoZVTTAEfZwa6wgy4bqX3KXC", "BaseRelayerProgram": "56MBBEYAtQAdjT4e1NzHD8XaoyRSTvfgbSVVcEcHj51H", "GasFeeReceiver": "AFs1LCbodhvwpgX3u3URLsud6R1XMSaMiQ5LtXw4GKYT"}Base Bridge repository
Source code, contracts, programs, and scripts: github.com/base/bridge.
Solana Explorer
Inspect Solana mainnet-beta transactions at explorer.solana.com.
Base Explorer
Inspect Base Mainnet transactions at basescan.org.
Discord support
Get help from the Base community at base.org/discord.