jb-relayr
Relayr API reference for multi-chain transaction bundling. Pay gas on one chain, execute on many. Used for omnichain deployments, cross-chain operations, and meta-transactions.
Install
mkdir -p .claude/skills/jb-relayr && curl -L -o skill.zip "https://mcp.directory/api/skills/download/9183" && unzip -o skill.zip -d .claude/skills/jb-relayr && rm skill.zipInstalls to .claude/skills/jb-relayr
About this skill
Relayr: Multi-Chain Transaction Bundling
Relayr is a meta-transaction relay service by 0xBASED that bundles transactions across chains. Users sign transactions for multiple chains, pay gas on one chain, and Relayr relayers execute on all others.
Overview
1. User signs ERC2771 forward requests for each target chain
2. POST to Relayr API to get quote with payment options
3. User selects which chain to pay on
4. User sends single payment transaction
5. Relayr relayers execute on all other chains
6. Poll for bundle completion status
API Base URLs
Production API: https://api.relayr.ba5ed.com
Dashboard: https://relayr.ba5ed.com
Authentication
No API key required. Relayr is permissionless. Anyone can submit bundles.
API Endpoints
1. Create Bundle Quote
POST /v1/bundle/prepaid
Creates a bundle of transactions and returns payment options.
Request Body:
{
"transactions": [
{
"chain": 1,
"target": "0x...",
"data": "0x...",
"value": "0"
}
],
"virtual_nonce_mode": "Disabled"
}
Fields:
| Field | Type | Description |
|---|---|---|
chain | number | Target chain ID |
target | string | Contract to call (usually ERC2771Forwarder) |
data | string | Encoded calldata |
value | string | ETH value in wei (usually "0" for meta-txs) |
virtual_nonce_mode | string | "Disabled" or "Enabled" for sequential ordering |
Response:
{
"bundle_uuid": "550e8400-e29b-41d4-a716-446655440000",
"payment_info": [
{
"chain": 1,
"target": "0x...",
"amount": "1234567890",
"calldata": "0x..."
},
{
"chain": 10,
"target": "0x...",
"amount": "987654321",
"calldata": "0x..."
}
],
"per_txn": [
{
"txn_uuid": "...",
"chain": 1,
"gas_cost": "500000",
"status": "Quoted"
}
],
"txn_uuids": ["uuid1", "uuid2"]
}
Response Fields:
| Field | Description |
|---|---|
bundle_uuid | Unique identifier for tracking |
payment_info | Array of payment options (one per supported chain) |
payment_info[].chain | Chain ID to pay on |
payment_info[].target | Address to send payment to |
payment_info[].amount | Wei amount to pay |
payment_info[].calldata | Transaction data for payment |
per_txn | Per-transaction details |
txn_uuids | Array of transaction UUIDs |
2. Get Bundle Status
GET /v1/bundle/{bundle_uuid}
Poll this endpoint to check execution status.
Response:
{
"bundle_uuid": "550e8400-e29b-41d4-a716-446655440000",
"payment_received": true,
"payment_chain": 1,
"transactions": [
{
"txn_uuid": "...",
"chain": 1,
"status": "Success",
"tx_hash": "0x...",
"block_number": 12345678
},
{
"txn_uuid": "...",
"chain": 10,
"status": "Pending",
"tx_hash": null,
"block_number": null
}
]
}
3. Get Transaction Status
GET /v1/transaction/{txn_uuid}
Get status of individual transaction within a bundle.
Transaction Status Values
| Status | Meaning |
|---|---|
Quoted | Bundle created, awaiting payment |
PaymentReceived | Payment confirmed, queued for execution |
Pending | Transaction submitted, awaiting confirmation |
Success | Transaction confirmed on-chain |
Completed | Alias for Success |
Failed | Transaction reverted |
Expired | Payment not received within deadline (48h) |
ERC2771 Forward Request Format
Relayr uses ERC2771 meta-transactions. The forwarder contract validates signatures and executes calls with the original sender preserved via _msgSender().
TypedData Domain
const domain = {
name: 'Juicebox', // Or actual contract name
version: '1',
chainId: 1, // Target chain ID
verifyingContract: '0x...' // ERC2771Forwarder address
};
TypedData Types
const types = {
ForwardRequest: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'gas', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint48' },
{ name: 'data', type: 'bytes' }
]
};
Message Fields
| Field | Type | Description |
|---|---|---|
from | address | Original signer address |
to | address | Target contract to call |
value | uint256 | ETH to forward (usually 0) |
gas | uint256 | Gas limit for execution |
nonce | uint256 | User's forwarder nonce (query from contract) |
deadline | uint48 | Unix timestamp expiry (max 48 hours from now) |
data | bytes | Encoded function calldata |
Getting the Nonce
Query the forwarder contract for the user's current nonce:
const forwarder = new ethers.Contract(forwarderAddress, [
'function nonces(address) view returns (uint256)'
], provider);
const nonce = await forwarder.nonces(userAddress);
Complete JavaScript Example
import { ethers } from 'ethers';
const RELAYR_API = 'https://api.relayr.ba5ed.com';
// ERC2771Forwarder addresses (same on all chains - deterministic deployment)
const FORWARDER = {
1: '0x...', // Ethereum mainnet
10: '0x...', // Optimism
8453: '0x...', // Base
42161: '0x...' // Arbitrum
};
/**
* Deploy or execute across multiple chains with single payment
*/
async function executeOmnichain(signer, targetChains, targetContract, calldata) {
const address = await signer.getAddress();
const signedRequests = [];
// Step 1: Sign forward request for each chain
for (const chainId of targetChains) {
// Get nonce from forwarder contract
const nonce = await getNonce(chainId, address);
const domain = {
name: 'Juicebox',
version: '1',
chainId: chainId,
verifyingContract: FORWARDER[chainId]
};
const types = {
ForwardRequest: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'gas', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint48' },
{ name: 'data', type: 'bytes' }
]
};
// 48-hour deadline (maximum allowed)
const deadline = Math.floor(Date.now() / 1000) + 48 * 60 * 60;
const message = {
from: address,
to: targetContract,
value: '0',
gas: '1000000',
nonce: nonce,
deadline: deadline,
data: calldata
};
// Sign the typed data
const signature = await signer.signTypedData(domain, types, message);
// Encode for forwarder.execute()
const forwarderAbi = [
'function execute((address from, address to, uint256 value, uint256 gas, uint256 nonce, uint48 deadline, bytes data) request, bytes signature)'
];
const iface = new ethers.Interface(forwarderAbi);
const encodedData = iface.encodeFunctionData('execute', [
[message.from, message.to, message.value, message.gas, message.nonce, message.deadline, message.data],
signature
]);
signedRequests.push({
chain: chainId,
target: FORWARDER[chainId],
data: encodedData,
value: '0'
});
}
// Step 2: Get quote from Relayr
const quoteResponse = await fetch(`${RELAYR_API}/v1/bundle/prepaid`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
transactions: signedRequests,
virtual_nonce_mode: 'Disabled'
})
});
if (!quoteResponse.ok) {
throw new Error(`Quote failed: ${quoteResponse.statusText}`);
}
const quote = await quoteResponse.json();
console.log('Bundle UUID:', quote.bundle_uuid);
console.log('Payment options:', quote.payment_info.map(p =>
`Chain ${p.chain}: ${ethers.formatEther(p.amount)} ETH`
));
return quote;
}
/**
* Execute payment on chosen chain
*/
async function payForBundle(signer, paymentInfo) {
const tx = await signer.sendTransaction({
to: paymentInfo.target,
value: paymentInfo.amount,
data: paymentInfo.calldata
});
console.log('Payment tx:', tx.hash);
await tx.wait();
console.log('Payment confirmed');
return tx;
}
/**
* Poll until all transactions complete
*/
async function waitForCompletion(bundleUuid, onUpdate) {
while (true) {
const response = await fetch(`${RELAYR_API}/v1/bundle/${bundleUuid}`);
const status = await response.json();
if (onUpdate) onUpdate(status);
const allDone = status.transactions.every(
tx => ['Success', 'Completed', 'Failed'].includes(tx.status)
);
if (allDone) {
const anyFailed = status.transactions.some(tx => tx.status === 'Failed');
if (anyFailed) {
const failed = status.transactions.filter(tx => tx.status === 'Failed');
throw new Error(`Transactions failed on chains: ${failed.map(t => t.chain).join(', ')}`);
}
return status;
}
console.log('Status:', status.transactions.map(t => `Chain ${t.chain}: ${t.status}`).join(', '));
await new Promise(r => setTimeout(r, 3000));
}
}
/**
* Helper: Get nonce from forwarder on specific chain
*/
async function getNonce(chainId, address) {
const rpcUrls = {
1: 'https://eth.llamarpc.com',
10: 'https://mainnet.optimism.io',
8453: 'https://mainnet.base.org',
42161: 'https://arb1.arbitrum.io/rpc'
};
const provider = new ethers.JsonRpcProvider(rpcUrls[chainId]);
const forwarder = new ethers.Contract(
FORWARDER[chainId],
['function nonces(address) view returns (uint256)'],
provider
);
return await forwarder.nonces(address);
}
/**
* Complete flow example
*/
async function main() {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
// Example: Deploy to Ethereum, Optimism, and
---
*Content truncated.*
More by openclaw
View all skills by openclaw →You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
drawio-diagrams-enhanced
jgtolentino
Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.
ui-ux-pro-max
nextlevelbuilder
"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."
godot
bfollington
This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.
nano-banana-pro
garg-aayush
Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.
pdf-to-markdown
aliceisjustplaying
Convert entire PDF documents to clean, structured Markdown for full context loading. Use this skill when the user wants to extract ALL text from a PDF into context (not grep/search), when discussing or analyzing PDF content in full, when the user mentions "load the whole PDF", "bring the PDF into context", "read the entire PDF", or when partial extraction/grepping would miss important context. This is the preferred method for PDF text extraction over page-by-page or grep approaches.
Related MCP Servers
Browse all serversBoost your AI code assistant with Context7: inject real-time API documentation from OpenAPI specification sources into y
Empower your CLI agents with NotebookLM—connect AI tools for citation-backed answers from your docs, grounded in your ow
Easily retrieve swift language documentation from GitHub, NPM, PyPI, and web pages with accurate, up-to-date references
TypeScript Refactoring offers advanced TypeScript/JavaScript code analysis and intelligent refactoring for seamless and
Foundry Toolkit: Deploy, test, and analyze smart contracts on EVM networks and local Anvil with powerful blockchain dev
Access Svelte documentation, code analysis, and autofix tools for Svelte 5 & SvelteKit. Improve projects with smart migr
Stay ahead of the MCP ecosystem
Get weekly updates on new skills and servers.