npm package: @ravenhouse/omni-sdk
Description: Raven House Omni SDK
Author: Raven House
Latest version: 0.1.53

# @ravenhouse/omni-sdk

TypeScript SDK for bridging tokens between Ethereum (L1) and Aztec (L2) with privacy-preserving features.

**Full documentation:** https://docs.ravenhouse.xyz/omni-sdk

## Installation

```bash
bun add @ravenhouse/omni-sdk
```

## Quick Start

```tsx
import { RavenBridge, AzguardBrowserWalletClient } from '@ravenhouse/omni-sdk'
import { useBridgeL1ToL2 } from '@ravenhouse/omni-sdk/react'
import { DEVNET_CONFIG } from '@ravenhouse/omni-sdk/config'

// Create once outside component
const bridge = new RavenBridge({ network: DEVNET_CONFIG })

function BridgeComponent() {
  const { bridgeL1ToL2, isLoading, steps, result, error } = useBridgeL1ToL2(bridge)

  const handleBridge = async () => {
    // l1Wallet: wagmi wallet client (extended with publicActions)
    // wallet: Wallet from @aztec/aztec.js/wallet (provided by @aztec/wallet-sdk)
    const l2Wallet = new AzguardBrowserWalletClient(aztecWallet, aztecAddress)

    await bridgeL1ToL2({
      l1Wallet,
      l2Wallet,
      token: bridge.getToken('RHT')!,
      amount: '100',
      isPrivate: true,
    })
  }

  return (
    <div>
      <button onClick={handleBridge} disabled={isLoading}>Bridge</button>

      {steps.map((step) => (
        <div key={step.id}>
          {step.label}: {step.status}
          {step.txHash && <a href={step.explorerUrl}>View TX</a>}
        </div>
      ))}

      {error && <p>Error: {error.message}</p>}
      {result && <p>Done! {result.finalTxHash}</p>}
    </div>
  )
}
```

## Bridge Flows

**L1 → L2** (3 steps): Deposit to L1 portal → Sync (3 L2 blocks) → Claim on L2

**L2 → L1** (3 steps): Burn on L2 + exit → Wait for block proof → Withdraw on L1

Both flows support `isPrivate: true` (shielded balance) and `isPrivate: false` (public balance).

## Wallet Interfaces

### L1 Wallet

Any Wagmi/Viem wallet client extended with `publicActions`:

```typescript
const l1Wallet = walletClient.extend(publicActions) // from wagmi/actions + viem
```

The SDK expects `{ account: { address: string }, extend(actions): ... }`.

### L2 Wallet (`AzguardBrowserWalletClient`)

Wraps any `Wallet` from `@aztec/aztec.js/wallet` (provided by the Azguard extension via `@aztec/wallet-sdk`) and implements the SDK's `L2WalletClient` interface:

```typescript
import { AzguardBrowserWalletClient } from '@ravenhouse/omni-sdk'

const l2Wallet = new AzguardBrowserWalletClient(
  wallet,       // Wallet from @aztec/aztec.js/wallet
  aztecAddress, // AztecAddress of the connected account
)
```

The adapter translates the SDK's internal operation protocol into direct Aztec.js contract calls:

| Operation | What the adapter does |
|---|---|
| `claim_private` | `TokenBridge.claim_private(...).send()` |
| `claim_public` | `TokenBridge.claim_public(...).send()` |
| `exit_to_l1_public` | `BatchCall([SetPublicAuthwit, exit_to_l1_public]).send()` |
| `exit_to_l1_private` | `wallet.createAuthWit(burn_private)` → `exit_to_l1_private.send({ authWitnesses })` |

### Custom L2 Wallet

If you have a different wallet, implement the `L2WalletClient` interface directly:

```typescript
import type { L2WalletClient } from '@ravenhouse/omni-sdk'

const myL2Wallet: L2WalletClient = {
  account: '0x...',   // Aztec address string
  sessionId: 'my-session',
  request: async (method, params) => { /* handle execute operations */ },
}
```

## API

### `RavenBridge`

```typescript
const bridge = new RavenBridge({ network: DEVNET_CONFIG })

bridge.bridgeL1ToL2({ l1Wallet, l2Wallet, token, amount, isPrivate, onStep? })
bridge.bridgeL2ToL1({ l1Wallet, l2Wallet, token, amount, isPrivate, onStep? })
bridge.getSupportedTokens()  // → TokenConfig[]
bridge.getToken('RHT')       // → TokenConfig | undefined
```

### React Hooks

```typescript
import { useBridgeL1ToL2, useBridgeL2ToL1 } from '@ravenhouse/omni-sdk/react'

const { bridgeL1ToL2, isLoading, steps, currentStep, result, error, reset } = useBridgeL1ToL2(bridge)
const { bridgeL2ToL1, isLoading, steps, currentStep, result, error, reset } = useBridgeL2ToL1(bridge)
```

All state is managed internally. `steps` updates in real-time as each bridge step progresses.

### Checkpoint Callbacks

Both bridge methods accept optional callbacks that fire after critical mid-flow steps. Use these to persist recovery data in case the user closes the browser before the operation completes.

```typescript
// L1 → L2: called after the L1 deposit succeeds (before the L2 claim)
await bridgeL1ToL2({
  ...params,
  onDepositComplete: async ({ claimSecret, messageLeafIndex }) => {
    await db.savePendingClaim({ claimSecret, messageLeafIndex })
  },
})

// L2 → L1: called after the L2 burn succeeds (before the L1 withdrawal)
await bridgeL2ToL1({
  ...params,
  onBurnComplete: async ({ txHash, blockNumber }) => {
    await db.savePendingWithdrawal({ l2TxHash: txHash, blockNumber })
  },
})
```

Errors thrown inside these callbacks are silently swallowed. The bridge continues regardless.

### Types

```typescript
interface BridgeStep {
  id: string
  label: string
  status: 'pending' | 'loading' | 'completed' | 'error'
  description?: string
  txHash?: string
  explorerUrl?: string
  error?: Error
}

interface BridgeResult {
  success: boolean
  direction: 'l1-to-l2' | 'l2-to-l1'
  amount: string
  symbol: string
  finalTxHash?: string
  message: string
  steps: BridgeStep[]
}
```

## Configuration

```typescript
import { DEVNET_CONFIG, DEVNET_TOKENS } from '@ravenhouse/omni-sdk/config'
```

`DEVNET_CONFIG` includes Aztec devnet endpoints, token addresses (portal, bridge), and block explorer URLs. Token configs follow the `TokenConfig` shape with `l1` and `l2` address sections.

## Browser Setup (Next.js)

Add to `next.config.js`:

```javascript
const webpack = require('webpack')

const nextConfig = {
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback = {
        ...config.resolve.fallback,
        fs: false, 'fs/promises': false, net: false, tls: false, tty: false,
        os: require.resolve('os-browserify/browser'),
        crypto: require.resolve('crypto-browserify'),
        stream: require.resolve('stream-browserify'),
        buffer: require.resolve('buffer/'),
        process: require.resolve('process/browser'),
        util: require.resolve('util/'),
        path: require.resolve('path-browserify'),
      }
      config.plugins.push(
        new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'], process: 'process/browser' })
      )
      config.resolve.alias = { ...config.resolve.alias, pino: 'pino/browser.js' }
    }
    return config
  },
}
module.exports = nextConfig
```

## Submodule Exports

| Import path | Contents |
|---|---|
| `@ravenhouse/omni-sdk` | `RavenBridge`, `AzguardBrowserWalletClient`, all types |
| `@ravenhouse/omni-sdk/react` | `useBridgeL1ToL2`, `useBridgeL2ToL1` |
| `@ravenhouse/omni-sdk/config` | `DEVNET_CONFIG`, `DEVNET_TOKENS` |
| `@ravenhouse/omni-sdk/contracts/l2` | `TokenBridgeContract`, `TokenContract` |
| `@ravenhouse/omni-sdk/ethereum` | `L1TokenPortalManager`, `PortalManager` |
| `@ravenhouse/omni-sdk/vite` | Vite plugin for browser polyfills |

## Troubleshooting

**Sync step is slow.** Normal. The SDK waits for 3 L2 blocks (~30-90s on devnet).

**Proof step is slow (L2->L1).** Normal. Block proving takes 5-15 minutes on devnet.

**TypeScript: "separate declarations of private property 'config'".** Happens when `RavenBridge` is created in the same file that imports from `@ravenhouse/omni-sdk/react`. The tsup build inlines class declarations in each entry point. Fix: cast `bridge as any` when passing to `useBridgeL1ToL2(bridge as any)`.

---

MIT © Cyphronix Software