@ravenhouse/omni-sdk
v0.1.61
Published
Raven House Omni SDK
Maintainers
Readme
@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
bun add @ravenhouse/omni-sdkQuick Start
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:
const l1Wallet = walletClient.extend(publicActions) // from wagmi/actions + viemThe 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:
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:
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
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 | undefinedReact Hooks
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.
// 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
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
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:
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 = nextConfigSubmodule 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
Internal Team understanding
- What is ISignatureTransfer ?
- We need to figure out the aztec Fee Juice portal address for sepolia and for mainnet. Testnet: 0xd3361019e40026ce8a9745c19e67fd3acc10d596 Mainnet: 0x2891f8b941067f8b5a3f34545a30cf71e3e23617
Fee Juice(Aztec token): Testnet: 0x762c132040fda6183066fa3b14d985ee55aa3c18 Mainnet: 0xa27ec0006e59f245217ff08cd52a7e8b169e62d2
Permit2 Sepolia address: 0x000000000022D473030F116dDEE9F6B43aC78BA3
