Skip to content

Commit 0138ff4

Browse files
committed
Add Superchain addresses, fix L1-L2 routes
1 parent 26ebd1b commit 0138ff4

File tree

2 files changed

+200
-26
lines changed

2 files changed

+200
-26
lines changed

docs/specs/bridge-provider-detailed.md

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,27 @@ This implementation mirrors the established provider pattern:
6262

6363
Following the wallet provider pattern, bridge clients are **instantiated by developers** and passed to Actions SDK. This keeps API keys out of Actions SDK.
6464

65-
### Example: Native Bridge (No Client Needed)
65+
The native bridge is the **default** and requires no configuration. For L2 ↔ L2 transfers or advanced routing, developers can configure custom bridge providers.
66+
67+
### Example: Native Bridge (Default - No Client Needed)
68+
69+
The native bridge handles L1 ↔ L2 transfers using Optimism's StandardBridge contracts. It is the default and requires no configuration.
6670

6771
```typescript
6872
const actions = createActions({
6973
wallet: { /* ... */ },
70-
// No bridge config = native bridge (default)
74+
chains: [
75+
{ chainId: 1, rpcUrl: '...' }, // Ethereum L1
76+
{ chainId: 10, rpcUrl: '...' }, // OP Mainnet
77+
],
78+
// bridge defaults to native - no config needed
7179
})
7280
```
7381

7482
### Example: Custom Bridge Provider
7583

84+
Custom bridges support L2 ↔ L2 transfers and advanced routing through third-party aggregators.
85+
7686
```typescript
7787
import { CustomBridgeClient } from 'third-party-bridge-sdk'
7888

@@ -84,6 +94,10 @@ const bridgeClient = new CustomBridgeClient({
8494
// Pass client to Actions
8595
const actions = createActions({
8696
wallet: { /* ... */ },
97+
chains: [
98+
{ chainId: 10, rpcUrl: '...' }, // OP Mainnet
99+
{ chainId: 8453, rpcUrl: '...' }, // Base
100+
],
87101
bridge: {
88102
type: 'custom',
89103
client: bridgeClient, // ← Actions never sees API key
@@ -850,6 +864,103 @@ const ERC20_ABI = [
850864
] as const
851865
```
852866

867+
---
868+
869+
### Superchain Bridge Addresses
870+
871+
The SDK includes StandardBridge contract addresses for all Superchain networks, sourced from the [Superchain Registry](https://github.com/ethereum-optimism/superchain-registry).
872+
873+
#### L2StandardBridge (Predeploy)
874+
875+
All OP Stack chains use a standardized predeploy address for the L2StandardBridge:
876+
877+
```typescript
878+
// packages/sdk/src/bridge/providers/native/addresses.ts
879+
880+
/**
881+
* L2StandardBridge predeploy address - same across all OP Stack chains
882+
* See: https://docs.optimism.io/stack/protocol/predeploys
883+
*/
884+
export const L2_STANDARD_BRIDGE = '0x4200000000000000000000000000000000000010' as const
885+
```
886+
887+
#### L1StandardBridge Addresses
888+
889+
Each L2 chain has a unique L1StandardBridge contract on Ethereum:
890+
891+
```typescript
892+
// packages/sdk/src/bridge/providers/native/addresses.ts
893+
894+
/**
895+
* L1StandardBridge addresses for each Superchain network
896+
* Source: https://github.com/ethereum-optimism/superchain-registry
897+
*/
898+
export const L1_STANDARD_BRIDGES: Record<number, Address> = {
899+
// Mainnet chains
900+
10: '0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1', // OP Mainnet
901+
8453: '0x3154Cf16ccdb4C6d922629664174b904d80F2C35', // Base
902+
34443: '0x735aDBbE72226BD52e818E7181953f42E3b0FF21', // Mode
903+
252: '0x34C0bD5877A5Ee7099D0f5688D65F4bB9158BDE2', // Fraxtal
904+
42220: '0x9C4955b92F34148dbcfDCD82e9c9eCe5CF2badfe', // Celo (OP Stack)
905+
906+
// Testnet chains
907+
11155420: '0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1', // OP Sepolia
908+
84532: '0xfd0Bf71F60660E2f608ed56e1659C450eB113120', // Base Sepolia
909+
} as const
910+
911+
/**
912+
* Get bridge addresses for a given chain
913+
*/
914+
export function getNativeBridgeAddresses(chainId: number): {
915+
l1Bridge: Address
916+
l2Bridge: Address
917+
} {
918+
const l1Bridge = L1_STANDARD_BRIDGES[chainId]
919+
if (!l1Bridge) {
920+
throw new Error(`No native bridge found for chain ${chainId}`)
921+
}
922+
923+
return {
924+
l1Bridge,
925+
l2Bridge: L2_STANDARD_BRIDGE,
926+
}
927+
}
928+
```
929+
930+
#### Supported Routes
931+
932+
The native bridge only supports **L1 ↔ L2** transfers, NOT direct L2 ↔ L2 transfers:
933+
934+
```typescript
935+
// packages/sdk/src/bridge/providers/native/addresses.ts
936+
937+
/**
938+
* Supported native bridge routes
939+
* Note: Native bridge only supports L1 ↔ L2, not L2 ↔ L2
940+
*/
941+
export const SUPPORTED_ROUTES: BridgeRoute[] = [
942+
// Mainnet routes (all L1 ↔ L2)
943+
{ fromChainId: 1, toChainId: 10, provider: 'native' }, // ETH → OP Mainnet
944+
{ fromChainId: 10, toChainId: 1, provider: 'native' }, // OP Mainnet → ETH
945+
{ fromChainId: 1, toChainId: 8453, provider: 'native' }, // ETH → Base
946+
{ fromChainId: 8453, toChainId: 1, provider: 'native' }, // Base → ETH
947+
{ fromChainId: 1, toChainId: 34443, provider: 'native' }, // ETH → Mode
948+
{ fromChainId: 34443, toChainId: 1, provider: 'native' }, // Mode → ETH
949+
950+
// Testnet routes (all L1 ↔ L2)
951+
{ fromChainId: 11155111, toChainId: 11155420, provider: 'native' }, // Sepolia → OP Sepolia
952+
{ fromChainId: 11155420, toChainId: 11155111, provider: 'native' }, // OP Sepolia → Sepolia
953+
{ fromChainId: 11155111, toChainId: 84532, provider: 'native' }, // Sepolia → Base Sepolia
954+
{ fromChainId: 84532, toChainId: 11155111, provider: 'native' }, // Base Sepolia → Sepolia
955+
956+
// Note: For L2 ↔ L2 (e.g., Base → OP Mainnet), use a custom bridge provider
957+
]
958+
```
959+
960+
**Important:** To bridge from Base to OP Mainnet (or any L2 ↔ L2), developers must use a custom bridge provider that supports direct L2-to-L2 routing (e.g., Socket, Across, etc.).
961+
962+
---
963+
853964
### Custom Bridge Provider (Example Implementation)
854965

855966
```typescript

docs/specs/bridge-provider-summary.md

Lines changed: 87 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -193,28 +193,35 @@ interface BridgeRoute {
193193

194194
### ActionsConfig
195195

196+
Bridge configuration defaults to the native Optimism bridge. Developers can optionally configure a custom third-party provider for L2 ↔ L2 transfers.
197+
196198
```typescript
197199
const actions = createActions({
198200
wallet: { /* ... */ },
199-
chains: [ /* ... */ ],
201+
chains: [
202+
{ chainId: 1, rpcUrl: '...' }, // Ethereum L1
203+
{ chainId: 10, rpcUrl: '...' }, // OP Mainnet
204+
{ chainId: 8453, rpcUrl: '...' }, // Base
205+
],
200206

201-
// Bridge configuration
207+
// Bridge defaults to native (L1 ↔ L2 only) - config optional
202208
bridge: {
203-
type: 'native', // Default: Optimism Native Bridge
204-
// Or configure a third-party provider
205-
// type: 'custom',
206-
// client: customBridgeClient,
209+
type: 'native', // Optional - this is the default
207210
},
211+
212+
// Or configure a third-party provider for L2 ↔ L2
213+
// bridge: {
214+
// type: 'custom',
215+
// client: customBridgeClient,
216+
// },
208217
})
209218
```
210219

211220
### BridgeConfig Type
212221

213-
Developers can use the native bridge (default) or provide a custom bridge client following the provider pattern.
214-
215222
```typescript
216223
interface BridgeConfig {
217-
/** Bridge provider type ('native' or 'custom') */
224+
/** Bridge provider type (defaults to 'native') */
218225
type?: 'native' | 'custom'
219226

220227
/** Custom bridge client instance (required if type === 'custom') */
@@ -255,45 +262,67 @@ interface BridgeClient {
255262
## Design Decisions
256263

257264
- **Provider pattern** - Developers instantiate bridge clients and pass to Actions SDK (Actions never handles API keys)
258-
- **Adapter pattern** - `BridgeProvider` base class with `NativeBridgeProvider` default and extensible custom implementations
265+
- **Adapter pattern** - `BridgeProvider` base class with `NativeBridgeProvider` (default) and extensible custom implementations
266+
- **Default to native** - Native bridge is the default for L1 ↔ L2 transfers, custom providers available for L2 ↔ L2
259267
- **Transparent integration** - Bridge triggered automatically when `fromChainId !== toChainId` in `send()`
260268
- **Smart chain detection** - Auto-detect source chain from wallet balances when not specified
269+
- **Superchain registry** - Built-in bridge addresses for all Superchain networks, extensible for custom chains
261270
- **Fee protection** - Optional max fee percentage to prevent expensive bridges
262271
- **Route restrictions** - Optional allowlist/blocklist by asset and chain pairs
263272

264273
---
265274

266275
## Bridge Providers
267276

268-
### Native Bridge (Built-in Default)
277+
### Native Bridge (Optimism Standard Bridge)
269278

270-
The Actions SDK includes a built-in implementation of the Optimism Native Bridge:
279+
The Actions SDK includes a built-in implementation of the Optimism Native Bridge for L1 ↔ L2 transfers.
271280

272-
- **Routes:** OP Mainnet ↔ Base, OP Sepolia ↔ Base Sepolia, Ethereum ↔ OP/Base
273-
- **Fee:** 0% (gas only)
281+
**Important:** The native bridge only supports transfers between Ethereum L1 and individual L2 chains. It does NOT support direct L2 ↔ L2 transfers (e.g., Base → OP Mainnet). For L2 ↔ L2 bridging, use a custom bridge provider.
282+
283+
- **Routes:** Ethereum L1 ↔ Each L2 (OP Mainnet, Base, OP Sepolia, Base Sepolia, Mode, Fraxtal, etc.)
284+
- **Fee:** 0% bridge fee (gas only)
274285
- **Time:** ~10 minutes (L1 → L2), ~7 days (L2 → L1 with fault proofs)
275286
- **Assets:** ETH, USDC, USDT, and all Superchain-native tokens
276-
- **Configuration:** Zero config required - works out of the box
287+
- **L2 Contract:** `0x4200000000000000000000000000000000000010` (standard predeploy across all OP Stack chains)
288+
- **L1 Contracts:** Chain-specific StandardBridge addresses (sourced from [Superchain Registry](https://github.com/ethereum-optimism/superchain-registry))
289+
290+
#### Superchain Bridge Addresses
291+
292+
The SDK includes L1StandardBridge addresses for all Superchain networks:
293+
294+
| Network | Chain ID | L1StandardBridge |
295+
|---------|----------|------------------|
296+
| OP Mainnet | 10 | `0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1` |
297+
| Base | 8453 | `0x3154Cf16ccdb4C6d922629664174b904d80F2C35` |
298+
| OP Sepolia | 11155420 | `0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1` |
299+
| Base Sepolia | 84532 | `0xfd0Bf71F60660E2f608ed56e1659C450eB113120` |
300+
| Mode | 34443 | `0x735aDBbE72226BD52e818E7181953f42E3b0FF21` |
301+
| Fraxtal | 252 | `0x34C0bD5877A5Ee7099D0f5688D65F4bB9158BDE2` |
302+
303+
For additional Superchain networks, the SDK will fetch addresses from the [Superchain Registry](https://github.com/ethereum-optimism/superchain-registry).
277304

278305
### Custom Bridge Providers
279306

280-
Third-party bridge providers can be integrated by implementing the `BridgeClient` interface and passing the client instance to Actions configuration. See the provider pattern documentation for implementation details.
307+
For L2 ↔ L2 transfers and advanced routing, developers can integrate third-party bridge aggregators by implementing the `BridgeClient` interface and passing the client instance to Actions configuration.
281308

282309
---
283310

284311
## Usage Example
285312

313+
### Using Native Bridge (L1 ↔ L2) - Default
314+
286315
```typescript
287316
import { createActions, USDC } from '@eth-optimism/actions-sdk'
288317

289-
// Initialize SDK (bridge defaults to native)
318+
// Initialize SDK - native bridge is default
290319
const actions = createActions({
291320
wallet: { /* ... */ },
292321
chains: [
293-
{ chainId: 84532, rpcUrl: '...' }, // Base Sepolia
294-
{ chainId: 11155420, rpcUrl: '...' }, // OP Sepolia
322+
{ chainId: 11155111, rpcUrl: '...' }, // Ethereum Sepolia (L1)
323+
{ chainId: 11155420, rpcUrl: '...' }, // OP Sepolia (L2)
295324
],
296-
// bridge config optional - defaults to native bridge
325+
// bridge config optional - defaults to native
297326
})
298327

299328
// Get wallet instance
@@ -303,27 +332,61 @@ const wallet = await actions.wallet.getSmartWallet({ signer })
303332
const quote = await actions.bridge.quote({
304333
asset: USDC,
305334
amount: 100,
306-
fromChainId: 84532,
307-
toChainId: 11155420,
335+
fromChainId: 11155111, // L1
336+
toChainId: 11155420, // L2
308337
})
309338

310339
console.log(`Bridge via ${quote.provider}`)
311340
console.log(`Fee: ${quote.feePercent * 100}%`)
312341
console.log(`Time: ~${quote.estimatedTime}s`)
313342

314-
// Execute cross-chain transfer
343+
// Execute cross-chain transfer (L1 → L2)
315344
const receipt = await wallet.send({
316345
amount: 100,
317346
asset: USDC,
318347
to: '0x...',
319-
fromChainId: 84532,
348+
fromChainId: 11155111,
320349
toChainId: 11155420,
321350
})
322351

323352
console.log(`Bridged! Tx: ${receipt.receipt.transactionHash}`)
324353
console.log(`Track: ${receipt.trackingUrl}`)
325354
```
326355

356+
### Using Custom Bridge (L2 ↔ L2)
357+
358+
```typescript
359+
import { createActions } from '@eth-optimism/actions-sdk'
360+
import { BridgeClient } from 'third-party-bridge-sdk'
361+
362+
// Initialize custom bridge client
363+
const bridgeClient = new BridgeClient({
364+
apiKey: process.env.BRIDGE_API_KEY!,
365+
})
366+
367+
// Initialize SDK with custom bridge
368+
const actions = createActions({
369+
wallet: { /* ... */ },
370+
chains: [
371+
{ chainId: 84532, rpcUrl: '...' }, // Base Sepolia
372+
{ chainId: 11155420, rpcUrl: '...' }, // OP Sepolia
373+
],
374+
bridge: {
375+
type: 'custom',
376+
client: bridgeClient, // Custom bridge for L2 ↔ L2
377+
},
378+
})
379+
380+
// Now can bridge between L2s
381+
const receipt = await wallet.send({
382+
amount: 100,
383+
asset: USDC,
384+
to: '0x...',
385+
fromChainId: 84532, // Base Sepolia
386+
toChainId: 11155420, // OP Sepolia
387+
})
388+
```
389+
327390
---
328391

329392
## Demo Application

0 commit comments

Comments
 (0)