diff --git a/docs/base-account/framework-integrations/wagmi/other-use-cases.mdx b/docs/base-account/framework-integrations/wagmi/other-use-cases.mdx index bf57aab2..24e25c47 100644 --- a/docs/base-account/framework-integrations/wagmi/other-use-cases.mdx +++ b/docs/base-account/framework-integrations/wagmi/other-use-cases.mdx @@ -106,7 +106,7 @@ Execute multiple transactions in a single user confirmation. ### Gasless Transactions Sponsor gas fees for your users. -**Learn more:** [Gasless Transactions Guide](/base-account/improve-ux/sponsor-gas/paymaster) | [Coinbase Developer Platform Paymaster](https://docs.cdp.coinbase.com/paymaster/introduction/welcome) +**Learn more:** [Gasless Transactions Guide](/base-account/improve-ux/sponsor-gas/paymasters) | [Coinbase Developer Platform Paymaster](https://docs.cdp.coinbase.com/paymaster/introduction/welcome) ### Full list of provider methods and capabilities Access the full list of Base Account provider methods and capabilities. diff --git a/docs/base-chain/quickstart/base-solana-bridge.mdx b/docs/base-chain/quickstart/base-solana-bridge.mdx new file mode 100644 index 00000000..e54e75b7 --- /dev/null +++ b/docs/base-chain/quickstart/base-solana-bridge.mdx @@ -0,0 +1,546 @@ +--- +title: "Base-Solana Bridge" +description: "Bridge tokens and messages between Base and Solana" +icon: "bridge" +--- + +import { GithubRepoCard } from "/snippets/GithubRepoCard.mdx" + + + The Base-Solana bridge is currently in **testnet only** (Base Sepolia ↔ Solana Devnet). + + +The Base-Solana bridge enables bidirectional token transfers and message passing between Base and Solana networks. This bridge allows you to: + +* **Transfer tokens** between Base and Solana +* **Send arbitrary cross-chain messages** +* **Combine both flows (transfer with arbitrary calls)** +* **Deploy wrapped tokens** on either chain + +This guide covers the bridge architecture and provides practical examples for implementation. + +## How it works + +### On Base +The Base bridge contract locks or burns tokens when sending messages to Solana, +and mints or unlocks tokens when receiving messages from Solana. +The Bridge contract itself builds Merkle trees from outgoing messages. +Validators verify the Merkle root every 300 finalized blocks and relay it to Solana. +You then prove your message exists in the tree to complete the transfer on Solana. + + +Tokens that are native to Base are locked and tokens that are native to Solana are burned when bridging to Solana. +Tokens that are native to Solana are minted and tokens that are native to Base are unlocked when bridging to Base. + + +**Key Smart contracts:** +- [**Bridge Contract**](https://github.com/base/bridge/blob/main/base/src/Bridge.sol): Handles outgoing transfers +- [**CrossChainERC20**](https://github.com/base/bridge/blob/main/base/src/CrossChainERC20.sol): Mintable/burnable tokens for cross-chain transfers +- [**BridgeValidator**](https://github.com/base/bridge/blob/main/base/src/BridgeValidator.sol): Validates messages with oracle signatures +- [**Twin Contract**](https://github.com/base/bridge/blob/main/base/src/Twin.sol): Your personal smart contract on Base for executing calls from Solana + + +**What is the Twin Contract?** +The Twin Contract is a smart contract that acts as your execution context on Base. +It represents the `msg.sender` on Base when you send an arbitrary contract call from Solana. + + +### On Solana +The Solana bridge program handles token transfers by locking or burning tokens and emitting events. For messaging, validators relay these messages to Base where they're executed through your personal Twin contract - a smart contract that acts as your execution context on Base. + +**Key Programs:** +- [**Bridge Program**](https://github.com/base/bridge/blob/main/solana/programs/bridge): Handles outgoing transfers +- [**Base Relayer Program**](https://github.com/base/bridge/blob/main/solana/programs/base_relayer): Coordinates message relay (not part of the core bridge) + +You can access the full repository from the link below: + + + +The relayer program is not part of the core bridge. +This is a convenience feature built on top of the bridge to reduce friction in the Solana -> Base direction. + + +## Bridging Flows + + + + Automatic relay system for seamless transfers + + + + Manual proof-based transfers with full control + + + + Complete application with frontend integration + + + +## Solana to Base + +**Flow:** Lock SOL → Wait for Validator Pre-approval → Execute on Base + +The Solana to Base bridge uses a pull-based model that requires 3 steps: + +1. **Initiate the bridge on Solana** - Lock your SOL or native SPL token in a Solana vault +2. **Wait for validators to pre-approve the message** - Validators verify and approve your bridge message +3. **Execute the message on Base** - The approved message is executed on Base to mint SOL and execute any additional arbitrary calls + + +Tokens that are native to Solana are locked and tokens that are native to Base are burned when bridging to Solana. +Tokens that are native to Base are minted and tokens that are native to Solana are unlocked when bridging to Base. + + +For convenience, a relayer service is available that automatically handles step 3 for users who only want to worry about the initial transaction on Solana. This provides a seamless bridging experience while maintaining the security of the pull-based model. + + + +### Auto-Relay Example +This is a sample script that shows how to bridge SOL with auto-relay + +```typescript solToBaseWithAutoRelay/index.ts expandable +// Configure +const TO = "0x8c1a617bdb47342f9c17ac8750e0b070c372c721"; // Base address +const AMOUNT = 0.001; // SOL amount + +// Bridge SOL with auto-relay +const 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); +``` + +For more details, see the [Solana to Base Relay Script](https://github.com/base/bridge/blob/main/scripts/src/commands/sol/onchain/bridge/solana-to-base/bridge-sol.handler.ts). + +### Wrap Custom SPL Tokens + +The example above shows how to bridge native SOL to Base. +To bridge custom SPL tokens, +you need to create wrapped ERC20 representations on Base using the CrossChainERC20Factory. + + + +```typescript wrapSolTokenOnBase/index.ts expandable +// Deploy wrapped token on Base +const 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], +}); +``` + +## Base to Solana + +**Flow:** Burn SOL (on Base) → Wait 15min → Generate Proof → Execute on Solana + +The Base to Solana flow requires manual proof generation. You burn wrapped SOL on Base, wait for finalization (~15 minutes), then generate a cryptographic proof to execute on Solana and receive native SOL. + + + +```typescript bridgeSolFromBaseToSolana/index.ts expandable +// Step 1: Burn SOL on Base +const 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 finalization +const isProvable = await isBridgeMessageProvable(txHash); + +// Step 3: Generate proof +const { event, rawProof } = await generateProof(txHash, baseBlockNumber); + +// Step 4: Execute on Solana +const 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); +``` + +## Utilities + +The repository includes utilities for converting between Solana and Base address formats, +getting your Solana CLI keypair for signing transactions, +and building and sending Solana transactions. + + + +### Address Conversion + +Convert Solana pubkey to bytes32 for Base contracts: +```typescript example.ts +// Convert Solana pubkey to bytes32 for Base contracts +import { pubkeyToBytes32 } from "./utils/pubkeyToBytes32"; + +const bytes32Address = pubkeyToBytes32(solanaAddress); +``` + +### Keypair Management + +Get your Solana CLI keypair for signing transactions: + +```typescript example.ts +import { getSolanaCliConfigKeypairSigner } from "./utils/keypair"; + +const payer = await getSolanaCliConfigKeypairSigner(); +``` + +### Transaction Building + +Build and send Solana transactions: + +```typescript example.ts +import { buildAndSendTransaction } from "./utils/buildAndSendTransaction"; + +const signature = await buildAndSendTransaction(SOLANA_RPC_URL, ixs, payer); +``` + +## Sol2Base: Full Stack Example + + + +Sol2Base is a production-ready Next.js application that demonstrates how to build a complete frontend for the Base-Solana bridge. It features a "hacker" aesthetic with Matrix-style animations and includes wallet integration, CDP faucet, ENS/Basename resolution, and real-time transaction monitoring. + +### Bridge Service Implementation + +The core bridge service handles SOL transfers with automatic relay and address resolution: + +```typescript src/lib/bridge.ts expandable +export class SolanaBridge { + private connection: Connection; + + constructor() { + this.connection = new Connection(SOLANA_DEVNET_CONFIG.rpcUrl, 'confirmed'); + } + + async createBridgeTransaction( + walletAddress: PublicKey, + amount: number, + destinationAddress: string, + signTransaction: (transaction: Transaction) => Promise + ): Promise { + // Import address resolver and real bridge + const { addressResolver } = await import('./addressResolver'); + const { realBridgeImplementation } = await import('./realBridgeImplementation'); + + // Resolve destination address (handles ENS/basename) + const resolvedAddress = await addressResolver.resolveAddress(destinationAddress); + + // Validate amount + if (amount < BRIDGE_CONFIG.minBridgeAmount / Math.pow(10, 9)) { + throw new Error(`Minimum bridge amount is ${BRIDGE_CONFIG.minBridgeAmount / Math.pow(10, 9)} SOL`); + } + + // Create the real bridge transaction + const transaction = await realBridgeImplementation.createBridgeTransaction( + walletAddress, + amount, + resolvedAddress + ); + + // Submit the transaction + const signature = await realBridgeImplementation.submitBridgeTransaction( + transaction, + walletAddress, + signTransaction + ); + + return signature; + } +} +``` +### Address Resolution Service + +Supports ENS names and Basenames for user-friendly addressing: + +```typescript src/lib/addressResolver.ts expandable +export class AddressResolver { + async resolveAddress(input: string): Promise { + const trimmedInput = input.trim(); + + // If it's already a valid Ethereum address, return as-is + if (this.isValidEthereumAddress(trimmedInput)) { + return trimmedInput; + } + + // Handle ENS names (.eth) + if (trimmedInput.endsWith('.eth') && !trimmedInput.endsWith('.base.eth')) { + return await this.resolveEns(trimmedInput); + } + + // Handle basenames (.base.eth or .base) + if (trimmedInput.endsWith('.base.eth') || trimmedInput.endsWith('.base')) { + return await this.resolveBasename(trimmedInput); + } + + throw new Error('Invalid address format'); + } + + private async resolveEns(ensName: string): Promise { + const response = await fetch(`https://api.ensdata.net/${ensName}`); + const data = await response.json(); + + if (!data.address) { + throw new Error(`ENS name ${ensName} does not resolve to an address`); + } + + return data.address; + } +} +``` + +### React Bridge Interface + +Complete UI component with wallet integration and form validation: + +```typescript src/components/BridgeInterface.tsx expandable +export const BridgeInterface: React.FC = () => { + const { publicKey, connected, signTransaction } = useWallet(); + const [solBalance, setSolBalance] = useState(0); + const [transactions, setTransactions] = useState([]); + + // Handle bridge transaction + const handleBridge = async (amount: number, destinationAddress: string) => { + if (!publicKey || !signTransaction) { + setError('Wallet not connected'); + return; + } + + try { + const txHash = await solanaBridge.createBridgeTransaction( + publicKey, + amount, + destinationAddress, + signTransaction + ); + + // Add to transaction history + const newTransaction: BridgeTransaction = { + txHash, + amount, + destinationAddress, + status: 'confirmed', + timestamp: Date.now(), + type: 'bridge' + }; + + setTransactions(prev => [newTransaction, ...prev]); + await loadBalances(); + + } catch (err) { + setError(err instanceof Error ? err.message : 'Bridge transaction failed'); + } + }; + + return ( +
+ + + + +
+ ); +}; +``` + +### Bridge Form with Address Resolution + +Smart form component with ENS/Basename support and validation: + +```typescript src/components/BridgeForm.tsx expandable +export const BridgeForm: React.FC = ({ onBridge, maxAmount }) => { + const [amount, setAmount] = useState(''); + const [destinationAddress, setDestinationAddress] = useState(''); + const [resolvedAddress, setResolvedAddress] = useState(''); + const [isResolvingAddress, setIsResolvingAddress] = useState(false); + + // Debounced address resolution + const resolveAddress = useCallback(async (address: string) => { + if (!address.trim()) return; + + setIsResolvingAddress(true); + try { + const type = addressResolver.getInputType(address); + + if (type === 'Ethereum Address') { + setResolvedAddress(address); + } else { + const resolved = await addressResolver.resolveAddress(address); + setResolvedAddress(resolved); + } + } catch (error) { + setErrors(prev => ({ + ...prev, + address: error instanceof Error ? error.message : 'Failed to resolve address' + })); + } finally { + setIsResolvingAddress(false); + } + }, []); + + return ( +
+ setAmount(e.target.value)} + placeholder="Enter SOL amount" + /> + + setDestinationAddress(e.target.value)} + placeholder="0x..., Basename, or ENS" + /> + + {resolvedAddress && resolvedAddress !== destinationAddress && ( +
+ ✓ Resolved to: {resolvedAddress} +
+ )} + + +
+ ); +}; +``` + +### Setup and Development + +```bash Terminal expandable +# Clone and setup +git clone https://github.com/base/sol2base.git +cd sol2base +npm install --legacy-peer-deps + +# Environment setup +cp env.template .env.local +# Add Coinbase Developer Platform (CDP) API credentials for faucet (optional) + +# Start development server +npm run dev +# Open http://localhost:3000 +``` + + +**Get your Coinbase Developer Platform (CDP) API credentials from the [the portal](https://portal.cdp.coinbase.com/projects/api-keys/client-key).** + +The example above uses the Coinbase Developer Platform faucet for SOL. +To get access to the faucet API, you can follow the instructions [here](https://docs.cdp.coinbase.com/faucets/introduction/welcome). + + +## Contract Addresses + +### Base Sepolia + +```json +{ + "Bridge": "0xB2068ECCDb908902C76E3f965c1712a9cF64171E", + "CrossChainERC20Factory": "0x58207331CBF8Af87BB6453b610E6579D9878e4EA", + "WrappedSOL": "0xC5b9112382f3c87AFE8e1A28fa52452aF81085AD" +} +``` + +### Solana Devnet + +```json +{ + "BridgeProgram": "HSvNvzehozUpYhRBuCKq3Fq8udpRocTmGMUYXmCSiCCc", + "BaseRelayerProgram": "ExS1gcALmaA983oiVpvFSVohi1zCtAUTgsLj5xiFPPgL" +} +``` + +## Troubleshooting + + + + * Ensure sufficient ETH for gas fees + * For ERC20 tokens, approve the bridge contract first using `approve()` + * Verify token addresses are correct and match the expected format + * Check that your private key is correctly set in the `.env` file + + + + * Wait at least 15 minutes for message relay + * Check that your Base transaction was successful and included a `MessageRegistered` event + * Verify you're using the correct network (testnet/devnet) + * Ensure the Solana bridge has processed the Base block number + + + + * Ensure you're using the latest Base block number from the Solana bridge + * Verify the message hash matches the original transaction + * Check that the proof was generated at the correct block height + * Make sure all account addresses are correctly derived + + + + * Verify you have sufficient SOL to pay for relay fees + * Check that the Base Relayer program is properly configured + * Ensure the outgoing message was created successfully + * Monitor both Solana and Base explorers for transaction status + + + +## Security + + + **Important Security Notes:** + + * Only use testnet funds (Solana devnet SOL and Base Sepolia ETH) + * Validate all addresses before bridging + * Monitor transactions on both chains + * Keep your private keys secure and never share them + + +## Resources + + + + View source code and examples + + + + Monitor Solana devnet transactions + + + + Monitor Base Sepolia transactions + + + + Get help from the community + + diff --git a/docs/base-chain/quickstart/bridge-token.mdx b/docs/base-chain/quickstart/bridge-token.mdx index 4810fcba..6395a39c 100644 --- a/docs/base-chain/quickstart/bridge-token.mdx +++ b/docs/base-chain/quickstart/bridge-token.mdx @@ -1,6 +1,6 @@ --- title: "Bridging an L1 token to Base" -sidebarTitle: 'Bridge Tokens to Base' +sidebarTitle: 'Base-Mainnet Bridge' description: How to submit ERC-20 tokens for bridging between Ethereum and Base as a token issuer. --- diff --git a/docs/docs.json b/docs/docs.json index 99e6c07c..d9b4057c 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -84,7 +84,8 @@ "base-chain/quickstart/why-base", "base-chain/quickstart/deploy-on-base", "base-chain/quickstart/connecting-to-base", - "base-chain/quickstart/bridge-token" + "base-chain/quickstart/bridge-token", + "base-chain/quickstart/base-solana-bridge" ] }, {