@wonderland/interop-cross-chain
v0.13.2
Published
Cross-chain interoperability library with standardized provider interface
Downloads
734
Readme
@wonderland/interop-cross-chain
[!CAUTION] Experimental release — not for production use.
This SDK is an early, preview release and has not been audited. It handles transaction and signature data for cross-chain value transfers, so a defect in the SDK could result in permanent, irrecoverable loss of user funds.
Do not use this SDK in production or with real user value.
The cross-chain package provides a standardized interface for interacting with cross-chain bridges and protocols. It enables seamless token transfers and swaps between different blockchain networks through a unified API.
Key features:
- Cross-chain token transfers between supported networks
- Cross-chain token swaps with customizable slippage
- Quote fetching for cross-chain operations
- Standardized provider interface for integrating different bridge protocols
- Type-safe interactions with comprehensive TypeScript support
- Step-based order model (signature and transaction steps)
Setup
- Install dependencies running
pnpm install
Installation
viem is a peer dependency — install it alongside the package:
npm install @wonderland/interop-cross-chain viem
# or
yarn add @wonderland/interop-cross-chain viem
# or
pnpm add @wonderland/interop-cross-chain viemSupported viem range: ^2.35.0.
Available Scripts
Available scripts that can be run using pnpm:
| Script | Description |
| ------------- | ------------------------------------------------------- |
| build | Build library using tsc |
| check-types | Check types issues using tsc |
| clean | Remove dist folder |
| lint | Run ESLint to check for coding standards |
| lint:fix | Run linter and automatically fix code formatting issues |
| format | Check code formatting and style using Prettier |
| format:fix | Run formatter and automatically fix issues |
| test | Run tests using vitest |
| test:cov | Run tests with coverage report |
Usage
import type { QuoteRequest } from "@wonderland/interop-cross-chain";
import {
createAggregator,
createCrossChainProvider,
getSignatureSteps,
getTransactionSteps,
isSignatureOnlyOrder,
} from "@wonderland/interop-cross-chain";
import { createWalletClient, http } from "viem";
import { sepolia } from "viem/chains";
const walletClient = createWalletClient({
chain: sepolia,
transport: http("https://..."),
account: "0x...",
});
// Create providers for different protocols
const acrossProvider = createCrossChainProvider("across");
const relayProvider = createCrossChainProvider("relay");
const oifProvider = createCrossChainProvider("oif", {
solverId: "my-solver",
url: "https://...",
});
const bungeeProvider = createCrossChainProvider("bungee", {
tier: "dedicated",
apiKey: "your-api-key",
});
// Create aggregator with providers (can mix Across, Relay, OIF, Bungee, etc.)
const aggregator = createAggregator({
providers: [acrossProvider, relayProvider, oifProvider, bungeeProvider],
});
// Get quotes using SDK QuoteRequest format
const quoteRequest: QuoteRequest = {
user: "0xYourAddress",
input: {
chainId: 11155111,
assetAddress: "0xTokenAddress",
amount: "1000000000000000000",
},
output: {
chainId: 84532,
assetAddress: "0xOutputTokenAddress",
recipient: "0xRecipientAddress",
},
swapType: "exact-input",
};
const response = await aggregator.getQuotes(quoteRequest);
const selectedQuote = response.quotes[0];
if (selectedQuote) {
if (isSignatureOnlyOrder(selectedQuote.order)) {
// Protocol mode: sign EIP-712 typed data (gasless for user)
const step = getSignatureSteps(selectedQuote.order)[0];
const { signatureType, ...typedData } = step.signaturePayload;
const signature = await walletClient.signTypedData(typedData);
await aggregator.submitOrder(selectedQuote, signature);
} else {
// User mode: execute transaction directly (user pays gas)
const step = getTransactionSteps(selectedQuote.order)[0];
await walletClient.sendTransaction({
to: step.transaction.to,
data: step.transaction.data,
value: step.transaction.value ? BigInt(step.transaction.value) : undefined,
});
}
}API
Providers
createCrossChainProvider(protocolName, config?)-- Create a provider for a supported protocol. Config is optional for Across, Relay, and Bungee (defaults to sandbox tier), required for OIF.CrossChainProvider(abstract class).protocolName-- Returns the protocol name..providerId-- Returns the provider identifier..getQuotes(params: QuoteRequest)-- Fetch quotes for a cross-chain request..buildQuote(params: BuildQuoteRequest)-- Build a quote locally without calling a solver API..submitOrder(quote, signature)-- Submit a signed order to the provider..getTrackingConfig()-- Get configuration for order tracking.
Tracking Notes (Across)
- Mainnet: fill tracking defaults to API-based polling via the Across API.
- Testnet: fill tracking defaults to event-based watching (Across testnet API is not reliable).
- The SDK still parses the origin-chain open event, so provide an origin-chain RPC URL for robust tracking.
Tracking Notes (Relay)
- Relay tracking is fully API-based for both mainnet and testnet.
- Both opened intent parsing and fill watching use the
/intents/status/v3endpoint. - No RPC URLs are required for Relay tracking.
- Relay automatically notifies the solver of new deposits via the pre-tracker for faster indexing.
Aggregator
createAggregator(config)-- Create an aggregator for batch quoting and execution.- Config:
{ providers: CrossChainProvider[], sortingStrategy?, timeoutMs?, trackerFactory? }
- Config:
Aggregator.getQuotes(params: QuoteRequest)-- Get quotes from all providers. Returns{ quotes: ExecutableQuote[], errors: GetQuotesError[] }..buildQuote(providerId, params: BuildQuoteRequest)-- Build a quote locally for a specific provider without calling a solver API..submitOrder(quote, signature)-- Submit a signed order..prepareTracking(providerId)-- Prepare order tracking for a provider..track(params)-- Track an existing transaction..getOrderStatus(params)-- Get current status without watching.
Asset Discovery
The SDK provides utilities to discover supported assets from providers. All discovery methods return a pre-processed DiscoveredAssets structure ready for consumption.
Via Aggregator (recommended):
import { createAggregator } from "@wonderland/interop-cross-chain";
const aggregator = createAggregator({ providers: [acrossProvider] });
// Discover assets from all configured providers
const discovered = await aggregator.discoverAssets({ chainIds: [1, 42161] });
// Get tokens for Ethereum using numeric chain ID
const ethTokens = discovered.tokensByChain[1];
// Get metadata for a specific token (nested by chainId then lowercase address)
const usdc = discovered.tokenMetadata[1]?.["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"];
console.log(usdc?.symbol); // "USDC"
console.log(usdc?.decimals); // 6Via individual service:
import { createAssetDiscoveryService } from "@wonderland/interop-cross-chain";
const service = createAssetDiscoveryService(provider);
if (service) {
const discovered = await service.getSupportedAssets();
const ethTokens = discovered.tokensByChain[1];
}Key concepts:
- Numeric chain IDs: Chain keys are plain numbers (e.g.,
1for Ethereum,42161for Arbitrum) — the same format used by viem and the rest of the SDK. - Plain addresses: All addresses in
tokensByChainandtokenMetadatause standard0x-prefixed format, ready for display or wallet interaction. - Nested metadata:
tokenMetadatais nested by chain ID then lowercase address to prevent cross-chain address collisions.
Types:
DiscoveredAssets-- Aggregated discovery result withtokensByChainandtokenMetadata.AssetInfo-- Token metadata:{ address, symbol, decimals }.
Types
QuoteRequest-- SDK-friendly quote request withuser,input,output, andswapType.BuildQuoteRequest-- Request for local quote building with required amounts,escrowContractAddress, andfillDeadline.Quote-- Quote with step-basedorder,preview,provider,latencyMs, andmetadata.ExecutableQuote-- Quote with provider context for submission.Order-- Step-based order model withsteps: (SignatureStep | TransactionStep)[].InteropAccountId-- Chain-aware account identifier:{ chainId: number, address: string }.
Step Helpers
getSignatureSteps(order)-- Extract signature steps from an order.getTransactionSteps(order)-- Extract transaction steps from an order.isSignatureOnlyOrder(order)-- Check if an order only requires signatures.isTransactionOnlyOrder(order)-- Check if an order only requires transactions.
Bungee Provider
The Bungee Provider enables integration with the Bungee protocol for cross-chain swaps and bridging.
Configuration
import { createCrossChainProvider } from "@wonderland/interop-cross-chain";
const provider = createCrossChainProvider("bungee", {
// API tier: "sandbox" (default, no auth), "dedicated" (API key), or "frontend" (domain whitelist)
tier: "dedicated",
apiKey: "your-api-key",
// Fee configuration (shared across protocols)
feeBps: "50", // 0.5% fee
feeTakerAddress: "0xYourFeeAddress", // required when feeBps is set
// Submission modes: "user-transaction" (onchain) or "gasless" (permit2)
submissionModes: ["user-transaction"],
// Optional
slippage: "0.5", // 0.5% slippage tolerance
refuel: true, // native gas on destination chain
affiliateId: "your-affiliate-id",
enableOtherProviders: true, // also return manual routes
enableMultipleRoutes: true, // return several route alternatives per quote (default: false)
});Manual Routes (Bungee)
With enableOtherProviders: true, getQuotes returns Bungee's auto routes plus one Quote per manual route — i.e. routes served by other bridges accessible through Bungee. Each manual route is built eagerly via GET /api/v1/bungee/build-tx so the quote ships with an executable TransactionStep. If one bridge's build-tx fails, the auto route and the remaining manual routes are still returned. The build response and the manual route entry are exposed on quote.metadata.bungeeBuildTx and quote.metadata.bungeeManualRoute.
Tracking Notes (Bungee)
- Bungee tracking is fully API-based using the
/api/v1/bungee/statusendpoint. - Auto routes are tracked by
requestHash(set asquote.tracking.orderId); manual routes have norequestHashand are tracked by the on-chaintxHashinstead. - Both opened intent parsing and fill watching use API polling with a 5-second interval.
- No RPC URLs are required for Bungee tracking.
OIF Provider
The OIF Provider enables integration with any Open Intents Framework compliant solver.
Usage
import type { QuoteRequest } from "@wonderland/interop-cross-chain";
import {
createCrossChainProvider,
getSignatureSteps,
getTransactionSteps,
isSignatureOnlyOrder,
} from "@wonderland/interop-cross-chain";
import { createWalletClient, http } from "viem";
import { mainnet } from "viem/chains";
const walletClient = createWalletClient({
chain: mainnet,
transport: http("https://..."),
account: "0x...",
});
// Create OIF provider with your solver endpoint
const provider = createCrossChainProvider("oif", {
solverId: "my-solver",
url: "https://...",
});
// Get quotes using SDK QuoteRequest
const quotes = await provider.getQuotes({
user: "0xYourAddress",
input: {
chainId: 1,
assetAddress: "0xTokenAddress",
amount: "1000000",
},
output: {
chainId: 42161,
assetAddress: "0xOutputTokenAddress",
},
swapType: "exact-input",
});
const quote = quotes[0];
if (!quote) throw new Error("No quotes returned");
if (isSignatureOnlyOrder(quote.order)) {
// Protocol Mode: Sign and submit order (gasless for user)
const step = getSignatureSteps(quote.order)[0];
const { signatureType, ...typedData } = step.signaturePayload;
const signature = await walletClient.signTypedData(typedData);
await provider.submitOrder(quote, signature);
} else {
// User Mode: Execute transaction directly (user pays gas)
const step = getTransactionSteps(quote.order)[0];
await walletClient.sendTransaction({
to: step.transaction.to,
data: step.transaction.data,
value: step.transaction.value ? BigInt(step.transaction.value) : undefined,
});
}Approval Requirements
Access approval info from the order checks:
// Get allowance requirements from order checks
const allowances = quote.order.checks?.allowances ?? [];
for (const { spender, tokenAddress, required } of allowances) {
// Approve token spend if needed
}Payload Validation
The SDK validates that calldata from solver APIs matches the user's intent. For Across, simple same-token bridges are fully validated (depositor, recipient, tokens, amount, chain). Cross-chain swap validation is coming soon.
References
- Open Intents Framework - OIF API specification and documentation
- Viem Documentation - Low-level Ethereum interface used for transaction handling
- Zod Documentation - TypeScript-first schema validation used for input validation
- Cross-Chain Interoperability Standards - Overview of cross-chain bridge concepts
The current SDK uses Across on testnet for demo purposes only. Performance may not reflect mainnet behavior and is not representative of the final production experience
