diff --git a/README.md b/README.md index d9dcbde..f9f3969 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ | ----- | ----------- | | [Adding Builder Codes](./skills/adding-builder-codes/SKILL.md) | Appends Base builder codes to transactions across Privy, Wagmi, Viem, and standard Ethereum RPC implementations. Automatically detects the user's framework before applying the correct integration pattern. | | [Building with Base Account](./skills/building-with-base-account/SKILL.md) | Integrates Base Account SDK for authentication and payments, including SIWB, Base Pay, Paymasters, Sub Accounts, and Spend Permissions. | +| [Building with Bridge SDK](./skills/building-with-bridge-sdk/SKILL.md) | Cross-chain bridging between Solana and EVM via the Base Bridge SDK (Solana ↔ Base/Ethereum). Does not cover L1 Ethereum ↔ Base Standard Bridge. | | [Connecting to Base Network](./skills/connecting-to-base-network/SKILL.md) | Provides Base Mainnet and Sepolia network configuration, RPC endpoints, chain IDs, and explorer URLs. | | [Converting Farcaster Miniapp to App](./skills/convert-farcaster-miniapp-to-app/SKILL.md) | Converts Farcaster Mini App SDK projects into regular Base web apps, with an option to preserve a small separate Farcaster-specific surface when needed. | | [Deploying Contracts on Base](./skills/deploying-contracts-on-base/SKILL.md) | Deploys and verifies contracts on Base with Foundry, plus common troubleshooting guidance. | diff --git a/skills/building-with-bridge-sdk/SKILL.md b/skills/building-with-bridge-sdk/SKILL.md new file mode 100644 index 0000000..bfa27da --- /dev/null +++ b/skills/building-with-bridge-sdk/SKILL.md @@ -0,0 +1,161 @@ +--- +name: building-with-bridge-sdk +description: > + Use when bridging assets between Solana and EVM chains via the Base Bridge SDK. + Invoke for: "bridge to Base from Solana", "bridge SOL to Base", "bridge assets from Solana + to Ethereum", "cross-chain transfer Solana Base", "use the Base bridge SDK", or "bridge + from EVM to Solana". Does not apply to L1 Ethereum <-> Base Standard Bridge bridging. +--- + +# Building with Bridge SDK + +Integrate the Base Bridge SDK (`bridge-sdk`) for cross-chain transfers and calls between Solana and Base/Ethereum. Covers `createBridgeClient`, `transfer`, `call`, `prove`, `execute`, `status`, and `monitor`. + +**SDK status: unaudited — not yet recommended for production use.** + +## When to Use This Skill + +Use this skill when a developer asks to: +- "Bridge SOL to Base" +- "Bridge assets from Solana to Ethereum" +- "Cross-chain transfer using the Base bridge SDK" +- "How do I use the bridge SDK?" +- "Bridge from Base to Solana" +- Set up Solana ↔ EVM bridging in an app + +## Prerequisites + +- Node.js 18+ or browser +- A keypair/wallet on the source chain +- `npm install bridge-sdk` (or `bun add bridge-sdk`) + +## Quick Start + +### Solana → Base (auto-relay) + +```typescript +import { createBridgeClient, makeEvmAdapter, makeSolanaAdapter, base, solanaMainnet } from "bridge-sdk"; +import { loadSolanaKeypair } from "bridge-sdk/node"; + +const payer = await loadSolanaKeypair("~/.config/solana/id.json"); + +const client = createBridgeClient({ + chains: { + solana: makeSolanaAdapter({ rpcUrl: "https://api.mainnet-beta.solana.com", payer, chain: solanaMainnet }), + base: makeEvmAdapter({ chain: base, rpcUrl: "https://mainnet.base.org", wallet: { type: "none" } }), + }, +}); + +const op = await client.transfer({ + route: { sourceChain: solanaMainnet.id, destinationChain: base.id }, + asset: { kind: "native" }, + amount: 1_000_000n, // lamports + recipient: "0x644e3DedB0e4F83Bfcf8F9992964d240224B74dc", + relay: { mode: "auto" }, +}); + +for await (const s of client.monitor(op.messageRef, { timeoutMs: 60_000 })) { + console.log(s.type); +} +``` + +### EVM → Solana (manual prove + execute) + +No auto-relay for this direction. Manual `prove` then `execute` required: + +```typescript +const op = await client.transfer({ + route: { sourceChain: base.id, destinationChain: solanaMainnet.id }, + asset: { kind: "native" }, + amount: 1n, + recipient: "11111111111111111111111111111111", +}); + +await client.prove(op.messageRef); // submit proof on Solana +await client.execute(op.messageRef); // execute the message +``` + +## Chain IDs + +| Chain | ID | +|-------|-----| +| Base Mainnet | `eip155:8453` | +| Base Sepolia | `eip155:84532` | +| Solana Mainnet | `solana:mainnet` | +| Solana Devnet | `solana:devnet` | + +## Supported Flows + +| Direction | Primitive | Relay | Notes | +|-----------|-----------|-------|-------| +| Solana → EVM | `transfer`, `call` | `auto` supported | automatic prove + execute | +| EVM → Solana | `transfer`, `call` | not supported | manual `prove` + `execute` required | +| EVM → EVM | not a target | — | — | +| L1 Ethereum ↔ Base | not covered | — | use Standard Bridge | + +**Important:** EVM → Solana always requires a separate `prove` call followed by `execute`. There is no auto-relay for this direction. + +## Workflows + +### Solana → EVM Transfer + +1. Create `BridgeClient` with Solana + EVM adapters +2. `client.transfer({ route: { sourceChain, destinationChain }, asset, amount, recipient, relay: { mode: "auto" } })` +3. `client.monitor(op.messageRef, { timeoutMs })` to wait for terminal state +4. `client.status(op.messageRef)` to read final state + +### EVM → Solana Transfer + +1. Create `BridgeClient` with EVM + Solana adapters (EVM adapter needs a wallet) +2. `client.transfer({ route, asset, amount, recipient })` — no relay option +3. Wait for `InitiatedTxConfirmed` via monitor +4. `client.prove(op.messageRef)` — submit proof on Solana +5. `client.execute(op.messageRef)` — execute on Solana +6. `client.status(op.messageRef)` for final state + +### Cross-Chain Call + +```typescript +const op = await client.call({ + route: { sourceChain: solanaMainnet.id, destinationChain: base.id }, + call: { + kind: "evm", + call: { to: "0x...", value: 0n, data: "0xd09de08a" }, + }, + relay: { mode: "auto" }, +}); +``` + +For Solana→Solana calls via Base: use `kind: "solana"` with `instructions` array. See `examples/baseToSolanaCall.ts` in the SDK repo. + +## Route Reference + +See [references/routes.md](references/routes.md) for field schemas, token mappings, and relay mode details. + +## Monitoring Reference + +See [references/monitor.md](references/monitor.md) for terminal states and `status`/`capabilities` API details. + +## Security Notes + +- **SDK is unaudited** — do not overclaim production readiness +- Never hardcode private keys; use environment variables +- `loadSolanaKeypair` is Node.js-only (`bridge-sdk/node` subpath) — do not import in browser code +- **EVM → Solana prove/execute is irreversible** — a message that executes cannot be undone; always confirm the target address before initiating +- **Relayer operator risk**: If you operate a manual relayer that signs with its own key, do not sign transactions where the relayer pubkey appears as a signer in the instruction set — malicious users can craft txs that steal relayer funds + +## Common Issues + +| Error | Cause | Fix | +|-------|-------|-----| +| `Transfer failed` / `Call failed` | Wrong `sourceChain`/`destinationChain` | Verify chain IDs match the intended direction | +| `prove` hangs forever | Source message not yet finalized | Wait for `InitiatedTxConfirmed` before calling `prove` | +| `execute` reverts | Message not yet proven | Always `prove` before `execute` | +| `wallet type "none"` errors | No EVM signing wallet configured | Set `wallet: { type: "privateKey", key: "0x..." }` on the EVM adapter | +| `tokenMappings` missing | Bridging ERC20 → Solana without mint mapping | Add `bridgeConfig.tokenMappings` to `createBridgeClient()` | + +## Key References + +- SDK repo: https://github.com/base/bridge-sdk +- Base Bridge docs: https://docs.base.org/base-chain/quickstart/base-solana-bridge +- Version: 0.1.0 diff --git a/skills/building-with-bridge-sdk/references/monitor.md b/skills/building-with-bridge-sdk/references/monitor.md new file mode 100644 index 0000000..5a182a7 --- /dev/null +++ b/skills/building-with-bridge-sdk/references/monitor.md @@ -0,0 +1,56 @@ +# Monitoring Reference + +## `monitor(messageRef, options)` + +Polls a `MessageRef` until a terminal state is reached or the timeout expires. + +```typescript +for await (const s of client.monitor(messageRef, { timeoutMs: 60_000 })) { + console.log(s.type, s.at); +} +``` + +### Terminal States + +| State | Meaning | +|-------|---------| +| `Executed` | Message executed successfully — done | +| `Failed` | Execution failed — done | +| `Expired` | Message expired before execution — done | + +Non-terminal states (`Initiated`, `InitiatedTxConfirmed`, `Proven`, `ProvenTxConfirmed`) keep the loop running. + +### Timeout + +```typescript +client.monitor(messageRef, { timeoutMs: 120_000 }); +``` + +- If timeout is reached without a terminal state, the loop ends cleanly (no error) +- Always follow the loop with `client.status(messageRef)` to determine final state + +## `status(messageRef)` + +Reads current state without polling: + +```typescript +const s = await client.status(messageRef); +console.log(s.type); // "Initiated" | "InitiatedTxConfirmed" | "Proven" | "ProvenTxConfirmed" | "Executed" | "Failed" | "Expired" +``` + +## `capabilities(route)` + +Checks whether a route supports `transfer`/`call` and auto-relay: + +```typescript +const caps = client.capabilities({ + route: { sourceChain: solanaMainnet.id, destinationChain: base.id }, +}); +// { supportsTransfer, supportsCall, supportsAutoRelay } +``` + +## Key Rules + +- **Always `prove` before `execute`** — calling `execute` on an unproven message reverts +- **Wait for `InitiatedTxConfirmed`** before calling `prove` — otherwise the proof will be submitted for a non-finalized message +- **`monitor` is polled locally** — it calls `status` in a loop; be mindful of RPC rate limits diff --git a/skills/building-with-bridge-sdk/references/routes.md b/skills/building-with-bridge-sdk/references/routes.md new file mode 100644 index 0000000..c57b5c3 --- /dev/null +++ b/skills/building-with-bridge-sdk/references/routes.md @@ -0,0 +1,112 @@ +# Route Reference + +## Supported Routes + +| Source | Destination | `transfer` | `call` | Auto-relay | +|--------|-------------|------------|--------|-----------| +| Solana Mainnet | Base Mainnet | yes | yes | yes | +| Solana Devnet | Base Sepolia | yes | yes | yes | +| Base Mainnet | Solana Mainnet | yes | yes | **no** — manual `prove` + `execute` | +| Base Sepolia | Solana Devnet | yes | yes | **no** — manual `prove` + `execute` | +| EVM → EVM | — | not a target | — | — | + +## Transfer + +### Native asset (`kind: "native"`) + +```typescript +client.transfer({ + route: { sourceChain, destinationChain }, + asset: { kind: "native" }, + amount: bigint, // in source chain's smallest unit (wei / lamports) + recipient: string, // address on destination chain + relay?: { mode: "auto" | "manual" }, +}); +``` + +### Token asset (`kind: "token"`) + +```typescript +client.transfer({ + route: { sourceChain, destinationChain }, + asset: { + kind: "token", + address: "0x...", // ERC20 address on source (EVM side) + }, + amount: bigint, + recipient: string, + relay?: { mode: "auto" | "manual" }, +}); +``` + +### ERC20 → Solana mint mapping + +Required when bridging ERC20s to Solana. Add to `bridgeConfig.tokenMappings` in `createBridgeClient()`: + +```typescript +bridgeConfig: { + tokenMappings: { + [`${base.id}->${solanaMainnet.id}`]: { + "0xERC20Address": "SolanaMintBase58", + }, + }, +} +``` + +## Call + +### EVM call (`kind: "evm"`) + +```typescript +client.call({ + route: { sourceChain: solanaMainnet.id, destinationChain: base.id }, + call: { + kind: "evm", + call: { to: "0x...", value: bigint, data: "0x..." }, + }, + relay?: { mode: "auto" }, +}); +``` + +### Solana call (`kind: "solana"`) — EVM → Solana only + +```typescript +client.call({ + route: { sourceChain: base.id, destinationChain: solanaMainnet.id }, + call: { + kind: "solana", + call: { + instructions: [{ + programId: "SolanaProgramId111111111111111111111111111111", + accounts: [{ + pubkey: "AccountPubkey11111111111111111111111111111111", + isWritable: boolean, + isSigner: boolean, + }], + data: Uint8Array, + }], + }, + }, +}); +``` + +## Relay Modes + +| Mode | Behavior | +|------|----------| +| `auto` | SDK submits the transaction on the destination automatically after initiation | +| `manual` | Caller must call `prove` then `execute` | + +`auto` only works for **Solana → EVM**. EVM → Solana always requires manual `prove` + `execute`. + +## Chain ID Constants + +```typescript +import { base, baseSepolia, solanaMainnet, solanaDevnet } from "bridge-sdk"; +// base.id → "eip155:8453" +// baseSepolia.id → "eip155:84532" +// solanaMainnet.id → "solana:mainnet" +// solanaDevnet.id → "solana:devnet" +``` + +Always use `.id` from these constants in `route` objects.