diff --git a/content/relayer/1.1.x/plugins/channels.mdx b/content/relayer/1.1.x/plugins/channels.mdx new file mode 100644 index 00000000..72e35f8b --- /dev/null +++ b/content/relayer/1.1.x/plugins/channels.mdx @@ -0,0 +1,621 @@ +--- +title: Channels +--- + +## Overview + +Channels is a plugin for OpenZeppelin Relayer that enables parallel transaction submission on Stellar using channel accounts with automatic fee bumping. Channel accounts provide unique sequence numbers for concurrent transaction processing, eliminating sequence number conflicts. + +The plugin features: + +* ***Parallel Transaction Processing***: Uses a pool of channel accounts for concurrent submissions +* ***Automatic Fee Bumping***: Dedicated fund account pays transaction fees +* ***Dynamic Pool Management***: Channel accounts acquired and released automatically +* ***Transaction Simulation***: Automatically simulates and builds transactions with proper resources +* ***Management API***: Dynamic configuration of channel accounts + +## Using the SDK Client + +The fastest way to interact with Channels is through the OpenZeppelin Relayer SDK, which provides a type-safe, unified interface for transaction submission and management operations. + +### Installation + +```bash +npm install @openzeppelin/relayer-sdk +# or +pnpm add @openzeppelin/relayer-sdk +``` + +### Client Setup + +The SDK supports two connection modes with identical APIs: + +```typescript +import { ChannelsClient, ChannelsRelayerClient } from '@openzeppelin/relayer-sdk'; + +// Direct HTTP connection to Channels service +const directClient = new ChannelsClient({ + baseUrl: 'https://channels.openzeppelin.com', // Channels service URL + apiKey: process.env.CHANNELS_API_KEY, // Service API key + adminSecret: process.env.CHANNELS_ADMIN, // Optional: for management + timeout: 30000, // Optional: request timeout +}); + +// Connection via OpenZeppelin Relayer plugin +const relayerClient = new ChannelsRelayerClient({ + pluginId: 'channels-plugin', // Plugin identifier + baseUrl: 'http://localhost:8080', // Optional: Relayer URL + apiKey: process.env.RELAYER_API_KEY, // Relayer API key + adminSecret: process.env.CHANNELS_ADMIN, // Optional: for management +}); +``` + +### Submitting Transactions + +Channels provides separate methods for different transaction types: + +```typescript +// Submit signed transaction (XDR) +const result = await client.submitTransaction({ + xdr: 'AAAAAgAAAABQEp+s8xGPrF...', // Complete signed envelope +}); + +// Submit Soroban transaction (func + auth) +const result = await client.submitSorobanTransaction({ + func: 'AAAABAAAAAEAAAAGc3ltYm9s...', // Host function XDR + auth: ['AAAACAAAAAEAAAA...', 'AAAACAAAAAEAAAB...'], // Detached auth entries +}); + +console.log('Transaction:', result.transactionId, result.hash, result.status); +``` + +### Managing Channel Accounts + +For managing channel accounts, the SDK provides the following methods: + +```typescript +// List configured channel accounts +const accounts = await client.listChannelAccounts(); +console.log('Channel accounts:', accounts.relayerIds); + +// Update channel accounts (requires adminSecret) +const result = await client.setChannelAccounts(['channel-001', 'channel-002', 'channel-003']); +console.log('Update successful:', result.ok, result.appliedRelayerIds); +``` + +### Example Scripts + +Complete working examples are available in the SDK repository: + +* [channels-direct-example.ts](https://github.com/OpenZeppelin/openzeppelin-relayer-sdk/blob/main/examples/clients/channels-direct-example.ts) - Direct HTTP connection +* [channels-relayer-example.ts](https://github.com/OpenZeppelin/openzeppelin-relayer-sdk/blob/main/examples/clients/channels-relayer-example.ts) - OpenZeppelin Relayer connection + +## Prerequisites + +* Node.js >= 18 +* pnpm >= 10 +* OpenZeppelin Relayer (installed and configured) + +## Example Setup + +For a complete working example with Docker Compose, refer to the Channels plugin example in the OpenZeppelin Relayer repository: + +* ***Location***: [examples/channels-plugin-example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/channels-plugin-example) +* ***Documentation***: [README.md](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/channels-plugin-example/README.md) + +## Installation + +Channels can be added to any OpenZeppelin Relayer installation. + +***Resources:*** + +* ***npm Package***: [@openzeppelin/relayer-plugin-channels](https://www.npmjs.com/package/@openzeppelin/relayer-plugin-channels) +* ***GitHub Repository***: https://github.com/OpenZeppelin/relayer-plugin-channels +* ***Example Setup***: [channels-plugin-example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/channels-plugin-example) + +### Install from npm + +```bash +# From the root of your Relayer repository +cd plugins +pnpm add @openzeppelin/relayer-plugin-channels +``` + +### Create the plugin wrapper + +Inside your Relayer, create a directory for the plugin and expose its handler: + +```bash +mkdir -p plugins/channels +``` + +Create `plugins/channels/index.ts`: + +```typescript +export { handler } from '@openzeppelin/relayer-plugin-channels'; +``` + +## Configuration + +### Plugin Registration + +Register the plugin in your `config/config.json` file: + +```json +{ + "plugins": [ + { + "id": "channels-plugin", + "path": "channels/index.ts", + "timeout": 60 + } + ] +} +``` + +### Environment Variables + +Configure the required environment variables: + +```bash +# Required environment variables +export STELLAR_NETWORK="testnet" # or "mainnet" +export SOROBAN_RPC_URL="https://soroban-testnet.stellar.org" +export FUND_RELAYER_ID="channels-fund" # ID of the fund relayer +export PLUGIN_ADMIN_SECRET="your-secret-here" # Required for management API + +# Optional environment variables +export LOCK_TTL_SECONDS=30 # Lock timeout (default: 30, range: 3-30) +export MAX_FEE=1000000 # Maximum fee in stroops (default: 1,000,000) +``` + +***Required Variables:*** + +* `STELLAR_NETWORK`: Either "testnet" or "mainnet" +* `SOROBAN_RPC_URL`: Stellar Soroban RPC endpoint URL +* `FUND_RELAYER_ID`: Relayer ID for the account that pays transaction fees + +***Optional Variables:*** + +* `PLUGIN_ADMIN_SECRET`: Secret for accessing the management API (required to manage channel accounts) +* `LOCK_TTL_SECONDS`: TTL for channel account locks in seconds (default: 30, range: 3-30) +* `MAX_FEE`: Maximum transaction fee in stroops (default: 1,000,000) + +### Relayer Configuration + +Channels requires two types of relayers: + +1. ***Fund Account***: The account that pays transaction fees (should have `concurrent_transactions: true` enabled) +2. ***Channel Accounts***: At least one channel account (recommended: 2 or more for better throughput) + +Configure relayers in your `config/config.json`: + +```json +{ + "relayers": [ + { + "id": "channels-fund", + "name": "Channels Fund Account", + "network": "testnet", + "paused": false, + "network_type": "stellar", + "signer_id": "channels-fund-signer", + "policies": { + "concurrent_transactions": true + } + }, + { + "id": "channel-001", + "name": "Channel Account 001", + "network": "testnet", + "paused": false, + "network_type": "stellar", + "signer_id": "channel-001-signer" + }, + { + "id": "channel-002", + "name": "Channel Account 002", + "network": "testnet", + "paused": false, + "network_type": "stellar", + "signer_id": "channel-002-signer" + } + ], + "notifications": [], + "signers": [ + { + "id": "channels-fund-signer", + "type": "local", + "config": { + "path": "config/keys/channels-fund.json", + "passphrase": { + "type": "env", + "value": "KEYSTORE_PASSPHRASE_FUND" + } + } + }, + { + "id": "channel-001-signer", + "type": "local", + "config": { + "path": "config/keys/channel-001.json", + "passphrase": { + "type": "env", + "value": "KEYSTORE_PASSPHRASE_CHANNEL_001" + } + } + }, + { + "id": "channel-002-signer", + "type": "local", + "config": { + "path": "config/keys/channel-002.json", + "passphrase": { + "type": "env", + "value": "KEYSTORE_PASSPHRASE_CHANNEL_002" + } + } + } + ], + "networks": "./config/networks", + "plugins": [ + { + "id": "channels", + "path": "channel/index.ts", + "timeout": 30, + "emit_logs": true, + "emit_traces": true + } + ] +} +``` + +***Important Configuration Notes:*** + +* ***Fund Account*** (`channels-fund`): Must have `"concurrent_transactions": true` in policies to enable parallel transaction processing +* ***Channel Accounts***: Create at least 2 for better throughput (you can add more as `channel-003`, etc.) +* ***Network***: Use `testnet` for testing or `mainnet` for production +* ***Signers***: Each relayer references a signer by `signer_id`, and signers are defined separately with keystore paths +* ***Keystore Files***: You’ll need to create keystore files for each account - see [OpenZeppelin Relayer documentation](https://docs.openzeppelin.com/relayer) for details on creating and managing keys +* ***Plugin Registration***: The plugin `id` should match what you use in environment variables and API calls + +After configuration, fund these accounts on-chain and register them with Channels (see "Initializing Channel Accounts" below). + +## Initializing Channel Accounts + +After configuring your relayers in `config.json` and funding the Stellar accounts, register them with Channels via the Management API: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/channels-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "setChannelAccounts", + "adminSecret": "your-secret-here", + "relayerIds": ["channel-001", "channel-002"] + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "ok": true, + "appliedRelayerIds": ["channel-001", "channel-002"] + }, + "error": null +} +``` + +This tells Channels which relayers to use as channel accounts. All relayer IDs must match your configured relayer IDs in `config.json`. + +Channels is now ready to serve Soroban transactions. + +## Automated Setup + +To skip the manual configuration steps, use the provided automation script. It automates the entire setup process: creating signers and relayers via the API, funding accounts on-chain, and registering them with Channels. + +### Prerequisites + +When using the automated setup, you only need to configure and fund the ***fund account***: + +```json +{ + "relayers": [ + { + "id": "channels-fund", + "chain": "stellar", + "signer": "channels-fund-signer", + "policies": { + "concurrent_transactions": true + } + } + ] +} +``` + +The script creates all channel account signers and relayers dynamically - no config.json entries needed for channel accounts. + +### Running the Script + +```bash +pnpm exec tsx ./scripts/create-channel-accounts.ts \ + --total 3 \ + --base-url http://localhost:8080 \ + --api-key \ + --funding-relayer channels-fund \ + --plugin-id channels-plugin \ + --plugin-admin-secret \ + --network testnet +``` + +### What the Script Does + +1. ***Creates channel account signers and relayers via API***: Following the naming pattern `channel-0001`, `channel-0002`, etc. +2. ***Funds channel accounts on-chain***: Submits funding transactions through the fund relayer and waits for confirmation +3. ***Registers with Channels***: Automatically calls the Management API to register all channel accounts + +### Script Options + +* `--total`: Number of channel accounts to create (recommended: 2-3 for testing, more for production) +* `--fix`: Audit and heal partially created state (use if the script was interrupted) +* `--dry-run`: Preview actions without making changes +* `--prefix`: Customize the naming prefix (default: `channel-`) +* `--starting-balance`: XLM amount for each account (default: 5) + +### Script Location + +* Example directory: [`scripts/create-channel-accounts.ts`](https://github.com/OpenZeppelin/relayer-plugin-channels/blob/main/scripts/create-channel-accounts.ts) + +## API Usage + +Channels is invoked by making POST requests to the plugin endpoint: + +```bash +POST /api/v1/plugins/{plugin-id}/call +``` + +### Submitting Transactions + +There are two ways to submit transactions to Channels: + +#### Option 1: Complete Transaction XDR + +Submit a complete, signed transaction envelope: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/channels-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "xdr": "AAAAAgAAAAA..." + } + }' +``` + +#### Option 2: Soroban Function + Auth + +Submit just the Soroban function and authorization entries: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/channels-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "func": "AAAABAAAAAEAAAAGc3ltYm9s...", + "auth": ["AAAACAAAAAEAAAA..."] + } + }' +``` + +### Parameters + +* `xdr` (string): Complete transaction envelope XDR (base64) - must be signed, not a fee-bump envelope +* `func` (string): Soroban host function XDR (base64) +* `auth` (array of strings): Array of Soroban authorization entry XDRs (base64) + +***Important Notes:*** + +* Provide either `xdr` OR `func`+`auth`, not both +* When using `xdr`, the transaction must be a regular signed transaction (not a fee-bump envelope) +* When using `func`+`auth`, Channels will build and simulate the transaction automatically +* Transactions are always submitted with fee bumping from the fund account + +### Generating XDR with Stellar SDK + +Use the `@stellar/stellar-sdk` to generate the required XDR values: + +#### Full Transaction Envelope XDR + +```typescript +import { Networks, TransactionBuilder, rpc } from '@stellar/stellar-sdk'; + +// Build your transaction +const tx = new TransactionBuilder(account, { + fee: '100', + networkPassphrase: Networks.TESTNET, +}) + .addOperation(/* Operation.invokeHostFunction from Contract.call(...) */) + .setTimeout(30) + .build(); + +// Sign the transaction +tx.sign(keypair); + +// Export base64 envelope XDR +const envelopeXdr = tx.toXDR(); +``` + +#### Soroban Function + Auth XDRs + +```typescript +// Build and simulate first to obtain auth +const baseTx = /* TransactionBuilder(...).addOperation(...).build() */; +const sim = await rpcServer.simulateTransaction(baseTx); + +// Apply simulation, then extract from the InvokeHostFunction operation +const assembled = rpc.assembleTransaction(baseTx, sim).build(); +const op = assembled.operations[0]; // Operation.InvokeHostFunction + +const funcXdr = op.func.toXDR("base64"); +const authXdrs = (op.auth ?? []).map(a => a.toXDR("base64")); +``` + +## Management API + +Channels provides a management API to dynamically configure channel accounts. This API requires authentication via the `PLUGIN_ADMIN_SECRET` environment variable. + +### List Channel Accounts + +Get the current list of configured channel accounts: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/channels-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "listChannelAccounts", + "adminSecret": "your-secret-here" + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "relayerIds": ["channel-001", "channel-002"] + }, + "error": null +} +``` + +### Set Channel Accounts + +Configure the channel accounts that Channels will use. This replaces the entire list: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/channels-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "setChannelAccounts", + "adminSecret": "your-secret-here", + "relayerIds": ["channel-001", "channel-002", "channel-003"] + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "ok": true, + "appliedRelayerIds": ["channel-001", "channel-002", "channel-003"] + }, + "error": null +} +``` + +***Important Notes:*** + +* You must configure at least one channel account before Channels can process transactions +* The management API will prevent removing accounts that are currently locked (in use). On failure it returns HTTP 409 with code `LOCKED_CONFLICT` and `details.locked` listing the blocked IDs +* All relayer IDs must exist in your OpenZeppelin Relayer configuration +* The `adminSecret` must match the `PLUGIN_ADMIN_SECRET` environment variable + +## Responses + +All API responses use the standard Relayer envelope format: ` success, data, error, metadata `. + +### Success Response (HTTP 200) + +```json +{ + "success": true, + "data": { + "transactionId": "tx_123456", + "status": "confirmed", + "hash": "1234567890abcdef..." + }, + "error": null +} +``` + +***Response Fields:*** + +* `success`: `true` when the plugin executed successfully +* `data`: Contains the transaction result +* `transactionId`: The OpenZeppelin Relayer transaction ID +* `status`: Transaction status (e.g., "confirmed") +* `hash`: The Stellar transaction hash +* `error`: `null` on success + +### Error Response (HTTP 4xx) + +```json +{ + "success": false, + "data": { + "code": "POOL_CAPACITY", + "details": {} + }, + "error": "Too many transactions queued. Please try again later", + "metadata": { + "logs": [ + { "level": "error", "message": "All channel accounts in use" } + ] + } +} +``` + +***Error Response Fields:*** + +* `success`: `false` when the plugin encountered an error +* `data`: Contains error details +* `code`: Error code (e.g., "POOL_CAPACITY", "LOCKED_CONFLICT") +* `details`: Additional context about the error +* `error`: Human-readable error message +* `metadata.logs`: Plugin execution logs (if `emit_logs` is enabled) + +### Common Error Codes + +* `CONFIG_MISSING`: Missing required environment variable +* `UNSUPPORTED_NETWORK`: Invalid network type +* `INVALID_PARAMS`: Invalid request parameters +* `INVALID_XDR`: Failed to parse XDR +* `INVALID_ENVELOPE_TYPE`: Not a regular transaction envelope (e.g., fee bump) +* `INVALID_TIME_BOUNDS`: TimeBounds too far in the future +* `NO_CHANNELS_CONFIGURED`: No channel accounts have been configured via management API +* `POOL_CAPACITY`: All channel accounts in use +* `RELAYER_UNAVAILABLE`: Relayer not found +* `SIMULATION_FAILED`: Transaction simulation failed +* `ONCHAIN_FAILED`: Transaction failed on-chain +* `WAIT_TIMEOUT`: Transaction wait timeout +* `MANAGEMENT_DISABLED`: Management API not enabled +* `UNAUTHORIZED`: Invalid admin secret +* `LOCKED_CONFLICT`: Cannot remove locked channel accounts + +## Additional Resources + +* ***Stellar SDK Documentation***: https://stellar.github.io/js-stellar-sdk/ +* ***Soroban Documentation***: https://soroban.stellar.org/docs +* ***OpenZeppelin Relayer Documentation***: https://docs.openzeppelin.com/relayer diff --git a/content/relayer/1.1.x/plugins.mdx b/content/relayer/1.1.x/plugins/index.mdx similarity index 100% rename from content/relayer/1.1.x/plugins.mdx rename to content/relayer/1.1.x/plugins/index.mdx diff --git a/content/relayer/1.1.x/plugins/launchtube.mdx b/content/relayer/1.1.x/plugins/launchtube.mdx new file mode 100644 index 00000000..c80cf96b --- /dev/null +++ b/content/relayer/1.1.x/plugins/launchtube.mdx @@ -0,0 +1,499 @@ +--- +title: Launchtube +--- + +## Overview + +Launchtube is a plugin for OpenZeppelin Relayer that simplifies submitting Stellar Soroban transactions by automatically handling the complexity of getting transactions on-chain. + +The plugin features: + +* ***Automatic Fee Bumping***: Uses a dedicated fund account to pay transaction fees +* ***Sequence Number Management***: Maintains a pool of sequence accounts for concurrent transaction processing +* ***Transaction Simulation***: Automatically simulates and rebuilds transactions with proper resources +* ***Retry Logic***: Built-in error handling and retry mechanisms +* ***Management API***: Dynamic configuration of sequence accounts + +## Prerequisites + +* Node.js >= 18 +* pnpm >= 10 +* OpenZeppelin Relayer (installed and configured) +* Stellar testnet or mainnet account +* Funded Stellar accounts for the fund and sequence accounts + +## Installation + +Launchtube can be added to any OpenZeppelin Relayer installation. + +***Resources:*** + +* ***npm Package***: [@openzeppelin/relayer-plugin-launchtube](https://www.npmjs.com/package/@openzeppelin/relayer-plugin-launchtube) +* ***GitHub Repository***: https://github.com/OpenZeppelin/relayer-plugin-launchtube +* ***Example Setup***: [launchtube-plugin-example](https://github.com/OpenZeppelin/openzeppelin-relayer/blob/0550c49444be585c6d40c43514758f57604d818b/examples/launchtube-plugin-example) + +### Install from npm + +```bash +# From the root of your Relayer repository +cd plugins +pnpm add @openzeppelin/relayer-plugin-launchtube +``` + +### Create the plugin wrapper + +Inside your Relayer, create a directory for the plugin and expose its handler: + +```bash +mkdir -p plugins/launchtube +``` + +Create `plugins/launchtube/index.ts`: + +```typescript +export { handler } from '@openzeppelin/relayer-plugin-launchtube'; +``` + +## Configuration + +### Plugin Registration + +Register the plugin in your `config/config.json` file: + +```json +{ + "plugins": [ + { + "id": "launchtube-plugin", + "path": "launchtube/index.ts", + "timeout": 60 + } + ] +} +``` + +### Environment Variables + +Configure the required environment variables: + +```bash +# Required environment variables +export STELLAR_NETWORK="testnet" # or "mainnet" +export SOROBAN_RPC_URL="https://soroban-testnet.stellar.org" +export FUND_RELAYER_ID="launchtube-fund" # ID of the fund relayer +export LAUNCHTUBE_ADMIN_SECRET="your-secret-here" # Required for management API + +# Optional environment variables +export LOCK_TTL_SECONDS=30 # Lock timeout (default: 30, range: 10-30) +``` + +***Required Variables:*** + +* `STELLAR_NETWORK`: Either "testnet" or "mainnet" +* `SOROBAN_RPC_URL`: Stellar Soroban RPC endpoint URL +* `FUND_RELAYER_ID`: Relayer ID for the account that pays transaction fees + +***Optional Variables:*** + +* `LAUNCHTUBE_ADMIN_SECRET`: Secret for accessing the management API (required to manage sequence accounts) +* `LOCK_TTL_SECONDS`: TTL for sequence account locks in seconds (default: 30, range: 10-30) + +### Relayer Configuration + +Launchtube requires two types of relayers: + +1. ***Fund Account***: The account that pays transaction fees (should have `concurrent_transactions: true` enabled) +2. ***Sequence Accounts***: At least one sequence account (recommended: 2 or more for better throughput) + +Configure relayers in your `config/config.json`: + +```json +{ + "relayers": [ + { + "id": "launchtube-fund", + "chain": "stellar", + "signer": "launchtube-fund-signer", + "policies": { + "concurrent_transactions": true + } + }, + { + "id": "launchtube-seq-001", + "chain": "stellar", + "signer": "launchtube-seq-001-signer" + }, + { + "id": "launchtube-seq-002", + "chain": "stellar", + "signer": "launchtube-seq-002-signer" + } + ] +} +``` + + +The fund relayer should have `concurrent_transactions: true` enabled to allow parallel transaction processing. + + +After configuration, fund these accounts on-chain and register them with Launchtube (see "Initializing Sequence Accounts" below). + +## Initializing Sequence Accounts + +After configuring your relayers in `config.json` and funding the Stellar accounts, register them with Launchtube via the Management API: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "setSequenceAccounts", + "adminSecret": "your-secret-here", + "relayerIds": ["launchtube-seq-001", "launchtube-seq-002"] + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "appliedRelayerIds": ["launchtube-seq-001", "launchtube-seq-002"] + }, + "error": null +} +``` + +This tells Launchtube which relayers to use as sequence accounts. All relayer IDs must match your configured relayer IDs in `config.json`. + +Launchtube is now ready to serve Soroban transactions. + +## Automated Setup + +To skip the manual configuration steps, use the provided automation script. It automates the entire setup process: creating signers and relayers via the API, funding accounts on-chain, and registering them with Launchtube. + +### Prerequisites + +When using the automated setup, you only need to configure and fund the ***fund account***: + +```json +{ + "relayers": [ + { + "id": "launchtube-fund", + "chain": "stellar", + "signer": "launchtube-fund-signer", + "policies": { + "concurrent_transactions": true + } + } + ] +} +``` + +The script creates all sequence account signers and relayers dynamically - no config.json entries needed for sequence accounts. + +### Running the Script + +```bash +pnpm exec tsx ./scripts/create-sequence-accounts.ts \ + --total 3 \ + --base-url http://localhost:8080 \ + --api-key \ + --funding-relayer launchtube-fund \ + --plugin-id launchtube-plugin \ + --plugin-admin-secret \ + --network testnet +``` + +### What the Script Does + +1. ***Creates sequence account signers and relayers via API***: Following the naming pattern `lt-seq-0001`, `lt-seq-0002`, etc. +2. ***Funds sequence accounts on-chain***: Submits funding transactions through the fund relayer and waits for confirmation +3. ***Registers with Launchtube***: Automatically calls the Management API to register all sequence accounts + +### Script Options + +* `--total`: Number of sequence accounts to create (recommended: 2-3 for testing, more for production) +* `--fix`: Audit and heal partially created state (use if the script was interrupted) +* `--dry-run`: Preview actions without making changes +* `--prefix`: Customize the naming prefix (default: `lt-seq-`) +* `--starting-balance`: XLM amount for each account (default: 5) + +### Script Location + +* Example directory: [`scripts/create-sequence-accounts.ts`](https://github.com/OpenZeppelin/relayer-plugin-launchtube/blob/main/scripts/create-sequence-accounts.ts) + +## API Usage + +Launchtube is invoked by making POST requests to the plugin endpoint: + +```bash +POST /api/v1/plugins/{plugin-id}/call +``` + +### Submitting Transactions + +There are two ways to submit transactions to Launchtube: + +#### Option 1: Complete Transaction XDR + +Submit a complete, signed transaction envelope: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "xdr": "AAAAAgAAAAA...", + "sim": false + } + }' +``` + +#### Option 2: Soroban Function + Auth + +Submit just the Soroban function and authorization entries: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "func": "AAAABAAAAAEAAAAGc3ltYm9s...", + "auth": ["AAAACAAAAAEAAAA..."], + "sim": true + } + }' +``` + +### Parameters + +* `xdr` (string): Complete transaction envelope XDR (base64) +* `func` (string): Soroban host function XDR (base64) +* `auth` (array of strings): Array of Soroban authorization entry XDRs (base64) +* `sim` (boolean): Whether to simulate the transaction before submission + +***Important Notes:*** + +* Provide either `xdr` OR `func`+`auth`, not both +* When using `sim: true`, Launchtube will simulate the transaction and rebuild it with proper resource limits +* When using `sim: false` with `xdr`, you must provide a pre-assembled transaction with resource fees + +### Generating XDR with Stellar SDK + +Use the `@stellar/stellar-sdk` to generate the required XDR values: + +#### Full Transaction Envelope XDR + +```typescript +import { Networks, TransactionBuilder, rpc } from '@stellar/stellar-sdk'; + +// Build your transaction +const tx = new TransactionBuilder(account, { + fee: '100', + networkPassphrase: Networks.TESTNET, +}) + .addOperation(/* Operation.invokeHostFunction from Contract.call(...) */) + .setTimeout(30) + .build(); + +// Optional: pre-simulate to set resources/fees before signing +const sim = await rpcServer.simulateTransaction(tx); +const prepared = rpc.assembleTransaction(tx, sim).build(); +prepared.sign(keypair); + +// Export base64 envelope XDR +const envelopeXdr = prepared.toXDR(); +``` + +#### Soroban Function + Auth XDRs + +```typescript +// Build and simulate first to obtain auth +const baseTx = /* TransactionBuilder(...).addOperation(...).build() */; +const sim = await rpcServer.simulateTransaction(baseTx); + +// Apply simulation, then extract from the InvokeHostFunction operation +const assembled = rpc.assembleTransaction(baseTx, sim).build(); +const op = assembled.operations[0]; // Operation.InvokeHostFunction + +const funcXdr = op.func.toXDR("base64"); +const authXdrs = (op.auth ?? []).map(a => a.toXDR("base64")); +``` + +## Management API + +Launchtube provides a management API to dynamically configure sequence accounts. This API requires authentication via the `LAUNCHTUBE_ADMIN_SECRET` environment variable. + +### List Sequence Accounts + +Get the current list of configured sequence accounts: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "listSequenceAccounts", + "adminSecret": "your-secret-here" + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "relayerIds": ["launchtube-seq-001", "launchtube-seq-002"] + }, + "error": null +} +``` + +### Set Sequence Accounts + +Configure the sequence accounts that Launchtube will use. This replaces the entire list: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "setSequenceAccounts", + "adminSecret": "your-secret-here", + "relayerIds": ["launchtube-seq-001", "launchtube-seq-002", "launchtube-seq-003"] + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "appliedRelayerIds": ["launchtube-seq-001", "launchtube-seq-002", "launchtube-seq-003"] + }, + "error": null +} +``` + +***Important Notes:*** + +* You must configure at least one sequence account before Launchtube can process transactions +* The management API will prevent removing accounts that are currently locked (in use). On failure it returns HTTP 409 with code `LOCKED_CONFLICT` and `details.locked` listing the blocked IDs +* All relayer IDs must exist in your OpenZeppelin Relayer configuration +* The `adminSecret` must match the `LAUNCHTUBE_ADMIN_SECRET` environment variable + +## Responses + +All API responses use the standard Relayer envelope format: ` success, data, error, metadata `. + +### Success Response (HTTP 200) + +```json +{ + "success": true, + "data": { + "transactionId": "tx_123456", + "hash": "1234567890abcdef..." + }, + "error": null +} +``` + +***Response Fields:*** + +* `success`: `true` when the plugin executed successfully +* `data`: Contains the transaction result +* `transactionId`: The OpenZeppelin Relayer transaction ID +* `hash`: The Stellar transaction hash +* `error`: `null` on success + +### Error Response (HTTP 4xx) + +```json +{ + "success": false, + "data": { + "code": "INVALID_PARAMS", + "details": { "sim": false, "xdrProvided": false } + }, + "error": "Cannot pass `sim = false` without `xdr`", + "metadata": { + "logs": [ + { "level": "error", "message": "Cannot pass `sim = false` without `xdr`" } + ] + } +} +``` + +***Error Response Fields:*** + +* `success`: `false` when the plugin encountered an error +* `data`: Contains error details +* `code`: Error code (e.g., "INVALID_PARAMS", "LOCKED_CONFLICT") +* `details`: Additional context about the error +* `error`: Human-readable error message +* `metadata.logs`: Plugin execution logs (if `emit_logs` is enabled) + +### Common Error Codes + +* `INVALID_PARAMS`: Invalid parameter combination provided +* `LOCKED_CONFLICT`: Attempting to remove sequence accounts that are currently in use +* `MISSING_PARAM`: Required parameter is missing +* `AUTH_FAILED`: Authentication failed (invalid admin secret) + +## How It Works + +Launchtube processes transactions through the following workflow: + +1. ***Request Validation***: Validates input parameters and extracts Soroban data from XDR or func+auth +2. ***Sequence Account Acquisition***: Acquires an available sequence account from the pool using Redis locks +3. ***Authorization Checking***: Validates authorization entries and determines if simulation is possible +4. ***Simulation*** (if enabled): Simulates the transaction and rebuilds it with proper resource limits and fees +5. ***Fee Bumping***: Fund account wraps the transaction in a fee bump envelope +6. ***Submission***: Sends the transaction to the Stellar network via the Soroban RPC +7. ***Confirmation***: Returns the transaction ID and hash for tracking + +This architecture enables: + +* ***Concurrent Processing***: Multiple transactions can be processed in parallel using different sequence accounts +* ***Automatic Resource Management***: Simulation ensures transactions have sufficient resources +* ***Simplified User Experience***: Users don’t need to manage sequence numbers or fee bumping + +## Example Setup + +For a complete working example with Docker Compose, refer to the Launchtube plugin example in the OpenZeppelin Relayer repository: + +* ***Location***: [examples/launchtube-plugin-example](https://github.com/OpenZeppelin/openzeppelin-relayer/blob/0550c49444be585c6d40c43514758f57604d818b/examples/launchtube-plugin-example) +* ***Documentation***: [README.md](https://github.com/OpenZeppelin/openzeppelin-relayer/blob/0550c49444be585c6d40c43514758f57604d818b/examples/launchtube-plugin-example/README.md) + +The example includes: + +* Pre-configured Docker Compose setup +* Scripts for creating and funding accounts +* Complete configuration files +* Step-by-step setup instructions +* Local plugin development workflow + +## Additional Resources + +* ***Stellar SDK Documentation***: https://stellar.github.io/js-stellar-sdk/ +* ***Soroban Documentation***: https://soroban.stellar.org/docs diff --git a/content/relayer/configuration/index.mdx b/content/relayer/configuration/index.mdx index 4296c69a..9d06c4ef 100644 --- a/content/relayer/configuration/index.mdx +++ b/content/relayer/configuration/index.mdx @@ -21,7 +21,7 @@ The OpenZeppelin Relayer supports two configuration approaches: - Changes take effect immediately (no container restart required) - See the ***API Reference*** page for detailed endpoints documentation -See [Storage Configuration](/relayer/configuration/storage) for detailed information about how file-based and API-based configurations work together, storage behavior, and best practices. +See [Storage Configuration](./configuration/storage) for detailed information about how file-based and API-based configurations work together, storage behavior, and best practices. For quick setup examples with pre-configured files, see the [examples directory](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples) in our GitHub repository. @@ -42,12 +42,12 @@ This table lists the environment variables and their default values. | Environment Variable | Default Value | Accepted Values | Description | | --- | --- | --- | --- | | `RUST_LOG` | `info` | `info, debug, warn, error, trace` | Log level. | -| `REPOSITORY_STORAGE_TYPE` | `in-memory` | `in-memory, redis` | Type of storage used for storing repository config and resources. See [Storage Configuration](/relayer/configuration/storage) for detailed information. | -| `RESET_STORAGE_ON_START` | `false` | `bool` | Clears all resources from storage on startup and reloads entries from the config file. See [Storage Configuration](/relayer/configuration/storage) for usage details. | -| `TRANSACTION_EXPIRATION_HOURS` | `4` | `number` | Number of hours after which transactions in a final state are removed from storage. See [Storage Configuration](/relayer/configuration/storage) for more information. | +| `REPOSITORY_STORAGE_TYPE` | `in-memory` | `in-memory, redis` | Type of storage used for storing repository config and resources. See [Storage Configuration](./configuration/storage) for detailed information. | +| `RESET_STORAGE_ON_START` | `false` | `bool` | Clears all resources from storage on startup and reloads entries from the config file. See [Storage Configuration](./configuration/storage) for usage details. | +| `TRANSACTION_EXPIRATION_HOURS` | `4` | `number` | Number of hours after which transactions in a final state are removed from storage. See [Storage Configuration](./configuration/storage) for more information. | | `CONFIG_DIR` | `./config` | `` | Relative path of directory where config files reside | | `CONFIG_FILE_NAME` | `config.json` | `` | File Name of the configuration file. | -| `RATE_LIMIT_RPS` | `100` | `` | Rate limit for the API in requests per second. | +| `RATE_LIMIT_REQUESTS_PER_SECOND` | `100` | `` | Rate limit for the API in requests per second. | | `RATE_LIMIT_BURST_SIZE` | `300` | `` | Rate limit burst size. | | `API_KEY` | `` | `string`, | API key to use for authentication to the relayer server. Minimum length 32 characters. | | `WEBHOOK_SIGNING_KEY` | `` | `string` | Signing key to use for webhook notifications. Minimum length 32 characters. | @@ -56,10 +56,10 @@ This table lists the environment variables and their default values. | `LOG_MAX_SIZE (in bytes)` | `1073741824` | `` | Size after which logs needs to be rolled. | | `METRICS_ENABLED` | `false` | `bool` | Enable metrics server for external tools to scrape metrics. | | `METRICS_PORT` | `8081` | `` | Port to use for metrics server. | -| `REDIS_URL` | `redis://localhost:6379` | `` | Redis connection URL for the relayer. See [Storage Configuration](/relayer/configuration/storage) for Redis setup details. | -| `REDIS_CONNECTION_TIMEOUT_MS` | `10000` | `` | Connection timeout for Redis in milliseconds. See [Storage Configuration](/relayer/configuration/storage) for Redis configuration. | -| `REDIS_KEY_PREFIX` | `oz-relayer` | `string` | Redis key prefix for namespacing. See [Storage Configuration](/relayer/configuration/storage) for more information. | -| `STORAGE_ENCRYPTION_KEY` | `` | `string` | Encryption key used to encrypt data at rest in Redis storage. See [Storage Configuration](/relayer/configuration/storage) for security details. | +| `REDIS_URL` | `redis://localhost:6379` | `` | Redis connection URL for the relayer. See [Storage Configuration](./configuration/storage) for Redis setup details. | +| `REDIS_CONNECTION_TIMEOUT_MS` | `10000` | `` | Connection timeout for Redis in milliseconds. See [Storage Configuration](./configuration/storage) for Redis configuration. | +| `REDIS_KEY_PREFIX` | `oz-relayer` | `string` | Redis key prefix for namespacing. See [Storage Configuration](./configuration/storage) for more information. | +| `STORAGE_ENCRYPTION_KEY` | `` | `string` | Encryption key used to encrypt data at rest in Redis storage. See [Storage Configuration](./configuration/storage) for security details. | | `RPC_TIMEOUT_MS` | `10000` | `` | Sets the maximum time to wait for RPC connections before timing out. | | `PROVIDER_MAX_RETRIES` | `3` | `` | Maximum number of retry attempts for provider operations. | | `PROVIDER_RETRY_BASE_DELAY_MS` | `100` | `` | Base delay between retry attempts in milliseconds. | @@ -67,6 +67,15 @@ This table lists the environment variables and their default values. | `PROVIDER_MAX_FAILOVERS` | `3` | `` | Maximum number of failovers (switching to different providers). | | `ENABLE_SWAGGER` | `false` | `true, false` | Enable or disable Swagger UI for API documentation. | | `KEYSTORE_PASSPHRASE` | `` | `` | Passphrase for the keystore file used for signing transactions. | +| `BACKGROUND_WORKER_TRANSACTION_REQUEST_CONCURRENCY` | `50` | `` | Maximum number of concurrent transaction request jobs that can be processed simultaneously. | +| `BACKGROUND_WORKER_TRANSACTION_SENDER_CONCURRENCY` | `75` | `` | Maximum number of concurrent transaction submission jobs that can be processed simultaneously. | +| `BACKGROUND_WORKER_TRANSACTION_STATUS_CHECKER_CONCURRENCY` | `50` | `` | Maximum number of concurrent generic/default transaction status check jobs that can be processed simultaneously. This worker handles Solana and any future networks that don’t have dedicated status checkers. | +| `BACKGROUND_WORKER_TRANSACTION_STATUS_CHECKER_EVM_CONCURRENCY` | `100` | `` | Maximum number of concurrent EVM transaction status check invocations that can be processed simultaneously. EVM handles the highest volume (~75% of all status checks) and uses optimized retries (8-20s) to avoid triggering premature resubmission. | +| `BACKGROUND_WORKER_TRANSACTION_STATUS_CHECKER_STELLAR_CONCURRENCY` | `50` | `` | Maximum number of concurrent Stellar transaction status checks that can be processed simultaneously. Stellar status checker uses fast retries (2-3s) optimized for Stellar’s faster block times. | +| `BACKGROUND_WORKER_NOTIFICATION_SENDER_CONCURRENCY` | `30` | `` | Maximum number of concurrent notifications that can be processed simultaneously. | +| `BACKGROUND_WORKER_SOLANA_TOKEN_SWAP_REQUEST_CONCURRENCY` | `10` | `` | Maximum number of concurrent Solana token swap requests that can be processed simultaneously. Low volume worker. | +| `BACKGROUND_WORKER_TRANSACTION_CLEANUP_CONCURRENCY` | `1` | `` | Maximum number of concurrent transaction cleanup invocations that can be processed simultaneously. Defaults to 1 to avoid database conflicts. | +| `BACKGROUND_WORKER_RELAYER_HEALTH_CHECK_CONCURRENCY` | `10` | `` | Maximum number of concurrent relayer health check invocations that can be processed simultaneously. Low volume worker. | ### Environment configuration example @@ -78,7 +87,7 @@ CONFIG_DIR=./config CONFIG_FILE_NAME=config.json WEBHOOK_SIGNING_KEY=e1d42480-6f74-4d0b-85f4-b7f0bb690fae API_KEY=5eefd216-0e44-4ca7-b421-2925f90d30d5 -RATE_LIMIT_RPS=100 +RATE_LIMIT_REQUESTS_PER_SECOND=100 RATE_LIMIT_BURST_SIZE=300 METRICS_ENABLED=true METRICS_PORT=8081 @@ -130,7 +139,7 @@ For comprehensive details on configuring all supported signer types including: * CDP signers * Security best practices and troubleshooting -See the dedicated [Signers Configuration](/relayer/configuration/signers) guide. +See the dedicated [Signers Configuration](./configuration/signers) guide. @@ -218,16 +227,16 @@ Available configuration fields | network_type | String | Type of network the relayer will connect to (`evm`, `solana`) | | network | String | Network the relayer will connect to. Must match a network identifier defined in your network configuration files. See [Network Configuration](/relayer/network_configuration) for details on defining networks. | | custom_rpc_urls | list | Optional custom RPC URLs for the network. If provided, this will be used instead of the public RPC URLs. This is useful for using your own RPC node or a paid service provider. The first url of the list is going to be used as the default | -| policies | list | Overrides default policies. Please refer to the [`Policies`](/relayer/configuration#network-policies) table | +| policies | list | Overrides default policies. Please refer to the [`Policies`](./configuration#network-policies) table | Policies | Network type | Policy | Type | Description | | --- | --- | --- | --- | -| solana, evm | min_balance | unsigned 128 | Minimum balance (in lamports or wei) required for the relayer to operate. Optional. | -| solana | fee_payment_strategy | enum(user,relayer) | Specifies who pays the fee. "user" (default) means the sender pays; "relayer" means the relayer pays. For "user", RPC methods add an instruction to transfer SPL tokens (calculated from the current SOL price plus a configurable margin) from the user to the relayer, ensuring fees are sustainably covered in tokens rather than SOL. | -| solana | swap_config | SwapConfig | Optional object configuring automated token‐swaps on Solana. | -| solana | fee_margin_percentage | f32 | Additional margin percentage added to estimated transaction fees to account for price fluctuations. For example, a value of 10 will add 10% to estimated fees. Optional. | -| solana | max_allowed_fee_lamports | unsigned 64 | Maximum allowed fee (in lamports) for a transaction. Optional. | +| solana, evm | min_balance | `unsigned 128` | Minimum balance (in lamports or wei) required for the relayer to operate. Optional. | +| solana | fee_payment_strategy | `enum(user,relayer)` | Specifies who pays the fee. "user" (default) means the sender pays; "relayer" means the relayer pays. For "user", RPC methods add an instruction to transfer SPL tokens (calculated from the current SOL price plus a configurable margin) from the user to the relayer, ensuring fees are sustainably covered in tokens rather than SOL. | +| solana | swap_config | `SwapConfig` | Optional object configuring automated token‐swaps on Solana. | +| solana | fee_margin_percentage | `f32` | Additional margin percentage added to estimated transaction fees to account for price fluctuations. For example, a value of 10 will add 10% to estimated fees. Optional. | +| solana | max_allowed_fee_lamports | `unsigned 64` | Maximum allowed fee (in lamports) for a transaction. Optional. | | solana | allowed_tokens | `Vector` | List of allowed tokens. Only these tokens are supported if provided. Optional. | | solana | allowed_programs | `Vector` | List of allowed programs by their identifiers. Only these programs are supported if provided. | | solana | allowed_accounts | `Vector` | List of allowed accounts by their public keys. The relayer will only operate with these accounts if provided. | @@ -527,6 +536,6 @@ The OpenZeppelin Relayer supports two complementary approaches for configuration -See [Storage Configuration](/relayer/configuration/storage) for detailed information about how file-based and API-based configurations work together, storage behavior, and best practices. +See [Storage Configuration](./configuration/storage) for detailed information about how file-based and API-based configurations work together, storage behavior, and best practices. diff --git a/content/relayer/plugins/channels.mdx b/content/relayer/plugins/channels.mdx new file mode 100644 index 00000000..72e35f8b --- /dev/null +++ b/content/relayer/plugins/channels.mdx @@ -0,0 +1,621 @@ +--- +title: Channels +--- + +## Overview + +Channels is a plugin for OpenZeppelin Relayer that enables parallel transaction submission on Stellar using channel accounts with automatic fee bumping. Channel accounts provide unique sequence numbers for concurrent transaction processing, eliminating sequence number conflicts. + +The plugin features: + +* ***Parallel Transaction Processing***: Uses a pool of channel accounts for concurrent submissions +* ***Automatic Fee Bumping***: Dedicated fund account pays transaction fees +* ***Dynamic Pool Management***: Channel accounts acquired and released automatically +* ***Transaction Simulation***: Automatically simulates and builds transactions with proper resources +* ***Management API***: Dynamic configuration of channel accounts + +## Using the SDK Client + +The fastest way to interact with Channels is through the OpenZeppelin Relayer SDK, which provides a type-safe, unified interface for transaction submission and management operations. + +### Installation + +```bash +npm install @openzeppelin/relayer-sdk +# or +pnpm add @openzeppelin/relayer-sdk +``` + +### Client Setup + +The SDK supports two connection modes with identical APIs: + +```typescript +import { ChannelsClient, ChannelsRelayerClient } from '@openzeppelin/relayer-sdk'; + +// Direct HTTP connection to Channels service +const directClient = new ChannelsClient({ + baseUrl: 'https://channels.openzeppelin.com', // Channels service URL + apiKey: process.env.CHANNELS_API_KEY, // Service API key + adminSecret: process.env.CHANNELS_ADMIN, // Optional: for management + timeout: 30000, // Optional: request timeout +}); + +// Connection via OpenZeppelin Relayer plugin +const relayerClient = new ChannelsRelayerClient({ + pluginId: 'channels-plugin', // Plugin identifier + baseUrl: 'http://localhost:8080', // Optional: Relayer URL + apiKey: process.env.RELAYER_API_KEY, // Relayer API key + adminSecret: process.env.CHANNELS_ADMIN, // Optional: for management +}); +``` + +### Submitting Transactions + +Channels provides separate methods for different transaction types: + +```typescript +// Submit signed transaction (XDR) +const result = await client.submitTransaction({ + xdr: 'AAAAAgAAAABQEp+s8xGPrF...', // Complete signed envelope +}); + +// Submit Soroban transaction (func + auth) +const result = await client.submitSorobanTransaction({ + func: 'AAAABAAAAAEAAAAGc3ltYm9s...', // Host function XDR + auth: ['AAAACAAAAAEAAAA...', 'AAAACAAAAAEAAAB...'], // Detached auth entries +}); + +console.log('Transaction:', result.transactionId, result.hash, result.status); +``` + +### Managing Channel Accounts + +For managing channel accounts, the SDK provides the following methods: + +```typescript +// List configured channel accounts +const accounts = await client.listChannelAccounts(); +console.log('Channel accounts:', accounts.relayerIds); + +// Update channel accounts (requires adminSecret) +const result = await client.setChannelAccounts(['channel-001', 'channel-002', 'channel-003']); +console.log('Update successful:', result.ok, result.appliedRelayerIds); +``` + +### Example Scripts + +Complete working examples are available in the SDK repository: + +* [channels-direct-example.ts](https://github.com/OpenZeppelin/openzeppelin-relayer-sdk/blob/main/examples/clients/channels-direct-example.ts) - Direct HTTP connection +* [channels-relayer-example.ts](https://github.com/OpenZeppelin/openzeppelin-relayer-sdk/blob/main/examples/clients/channels-relayer-example.ts) - OpenZeppelin Relayer connection + +## Prerequisites + +* Node.js >= 18 +* pnpm >= 10 +* OpenZeppelin Relayer (installed and configured) + +## Example Setup + +For a complete working example with Docker Compose, refer to the Channels plugin example in the OpenZeppelin Relayer repository: + +* ***Location***: [examples/channels-plugin-example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/channels-plugin-example) +* ***Documentation***: [README.md](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/channels-plugin-example/README.md) + +## Installation + +Channels can be added to any OpenZeppelin Relayer installation. + +***Resources:*** + +* ***npm Package***: [@openzeppelin/relayer-plugin-channels](https://www.npmjs.com/package/@openzeppelin/relayer-plugin-channels) +* ***GitHub Repository***: https://github.com/OpenZeppelin/relayer-plugin-channels +* ***Example Setup***: [channels-plugin-example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/channels-plugin-example) + +### Install from npm + +```bash +# From the root of your Relayer repository +cd plugins +pnpm add @openzeppelin/relayer-plugin-channels +``` + +### Create the plugin wrapper + +Inside your Relayer, create a directory for the plugin and expose its handler: + +```bash +mkdir -p plugins/channels +``` + +Create `plugins/channels/index.ts`: + +```typescript +export { handler } from '@openzeppelin/relayer-plugin-channels'; +``` + +## Configuration + +### Plugin Registration + +Register the plugin in your `config/config.json` file: + +```json +{ + "plugins": [ + { + "id": "channels-plugin", + "path": "channels/index.ts", + "timeout": 60 + } + ] +} +``` + +### Environment Variables + +Configure the required environment variables: + +```bash +# Required environment variables +export STELLAR_NETWORK="testnet" # or "mainnet" +export SOROBAN_RPC_URL="https://soroban-testnet.stellar.org" +export FUND_RELAYER_ID="channels-fund" # ID of the fund relayer +export PLUGIN_ADMIN_SECRET="your-secret-here" # Required for management API + +# Optional environment variables +export LOCK_TTL_SECONDS=30 # Lock timeout (default: 30, range: 3-30) +export MAX_FEE=1000000 # Maximum fee in stroops (default: 1,000,000) +``` + +***Required Variables:*** + +* `STELLAR_NETWORK`: Either "testnet" or "mainnet" +* `SOROBAN_RPC_URL`: Stellar Soroban RPC endpoint URL +* `FUND_RELAYER_ID`: Relayer ID for the account that pays transaction fees + +***Optional Variables:*** + +* `PLUGIN_ADMIN_SECRET`: Secret for accessing the management API (required to manage channel accounts) +* `LOCK_TTL_SECONDS`: TTL for channel account locks in seconds (default: 30, range: 3-30) +* `MAX_FEE`: Maximum transaction fee in stroops (default: 1,000,000) + +### Relayer Configuration + +Channels requires two types of relayers: + +1. ***Fund Account***: The account that pays transaction fees (should have `concurrent_transactions: true` enabled) +2. ***Channel Accounts***: At least one channel account (recommended: 2 or more for better throughput) + +Configure relayers in your `config/config.json`: + +```json +{ + "relayers": [ + { + "id": "channels-fund", + "name": "Channels Fund Account", + "network": "testnet", + "paused": false, + "network_type": "stellar", + "signer_id": "channels-fund-signer", + "policies": { + "concurrent_transactions": true + } + }, + { + "id": "channel-001", + "name": "Channel Account 001", + "network": "testnet", + "paused": false, + "network_type": "stellar", + "signer_id": "channel-001-signer" + }, + { + "id": "channel-002", + "name": "Channel Account 002", + "network": "testnet", + "paused": false, + "network_type": "stellar", + "signer_id": "channel-002-signer" + } + ], + "notifications": [], + "signers": [ + { + "id": "channels-fund-signer", + "type": "local", + "config": { + "path": "config/keys/channels-fund.json", + "passphrase": { + "type": "env", + "value": "KEYSTORE_PASSPHRASE_FUND" + } + } + }, + { + "id": "channel-001-signer", + "type": "local", + "config": { + "path": "config/keys/channel-001.json", + "passphrase": { + "type": "env", + "value": "KEYSTORE_PASSPHRASE_CHANNEL_001" + } + } + }, + { + "id": "channel-002-signer", + "type": "local", + "config": { + "path": "config/keys/channel-002.json", + "passphrase": { + "type": "env", + "value": "KEYSTORE_PASSPHRASE_CHANNEL_002" + } + } + } + ], + "networks": "./config/networks", + "plugins": [ + { + "id": "channels", + "path": "channel/index.ts", + "timeout": 30, + "emit_logs": true, + "emit_traces": true + } + ] +} +``` + +***Important Configuration Notes:*** + +* ***Fund Account*** (`channels-fund`): Must have `"concurrent_transactions": true` in policies to enable parallel transaction processing +* ***Channel Accounts***: Create at least 2 for better throughput (you can add more as `channel-003`, etc.) +* ***Network***: Use `testnet` for testing or `mainnet` for production +* ***Signers***: Each relayer references a signer by `signer_id`, and signers are defined separately with keystore paths +* ***Keystore Files***: You’ll need to create keystore files for each account - see [OpenZeppelin Relayer documentation](https://docs.openzeppelin.com/relayer) for details on creating and managing keys +* ***Plugin Registration***: The plugin `id` should match what you use in environment variables and API calls + +After configuration, fund these accounts on-chain and register them with Channels (see "Initializing Channel Accounts" below). + +## Initializing Channel Accounts + +After configuring your relayers in `config.json` and funding the Stellar accounts, register them with Channels via the Management API: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/channels-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "setChannelAccounts", + "adminSecret": "your-secret-here", + "relayerIds": ["channel-001", "channel-002"] + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "ok": true, + "appliedRelayerIds": ["channel-001", "channel-002"] + }, + "error": null +} +``` + +This tells Channels which relayers to use as channel accounts. All relayer IDs must match your configured relayer IDs in `config.json`. + +Channels is now ready to serve Soroban transactions. + +## Automated Setup + +To skip the manual configuration steps, use the provided automation script. It automates the entire setup process: creating signers and relayers via the API, funding accounts on-chain, and registering them with Channels. + +### Prerequisites + +When using the automated setup, you only need to configure and fund the ***fund account***: + +```json +{ + "relayers": [ + { + "id": "channels-fund", + "chain": "stellar", + "signer": "channels-fund-signer", + "policies": { + "concurrent_transactions": true + } + } + ] +} +``` + +The script creates all channel account signers and relayers dynamically - no config.json entries needed for channel accounts. + +### Running the Script + +```bash +pnpm exec tsx ./scripts/create-channel-accounts.ts \ + --total 3 \ + --base-url http://localhost:8080 \ + --api-key \ + --funding-relayer channels-fund \ + --plugin-id channels-plugin \ + --plugin-admin-secret \ + --network testnet +``` + +### What the Script Does + +1. ***Creates channel account signers and relayers via API***: Following the naming pattern `channel-0001`, `channel-0002`, etc. +2. ***Funds channel accounts on-chain***: Submits funding transactions through the fund relayer and waits for confirmation +3. ***Registers with Channels***: Automatically calls the Management API to register all channel accounts + +### Script Options + +* `--total`: Number of channel accounts to create (recommended: 2-3 for testing, more for production) +* `--fix`: Audit and heal partially created state (use if the script was interrupted) +* `--dry-run`: Preview actions without making changes +* `--prefix`: Customize the naming prefix (default: `channel-`) +* `--starting-balance`: XLM amount for each account (default: 5) + +### Script Location + +* Example directory: [`scripts/create-channel-accounts.ts`](https://github.com/OpenZeppelin/relayer-plugin-channels/blob/main/scripts/create-channel-accounts.ts) + +## API Usage + +Channels is invoked by making POST requests to the plugin endpoint: + +```bash +POST /api/v1/plugins/{plugin-id}/call +``` + +### Submitting Transactions + +There are two ways to submit transactions to Channels: + +#### Option 1: Complete Transaction XDR + +Submit a complete, signed transaction envelope: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/channels-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "xdr": "AAAAAgAAAAA..." + } + }' +``` + +#### Option 2: Soroban Function + Auth + +Submit just the Soroban function and authorization entries: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/channels-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "func": "AAAABAAAAAEAAAAGc3ltYm9s...", + "auth": ["AAAACAAAAAEAAAA..."] + } + }' +``` + +### Parameters + +* `xdr` (string): Complete transaction envelope XDR (base64) - must be signed, not a fee-bump envelope +* `func` (string): Soroban host function XDR (base64) +* `auth` (array of strings): Array of Soroban authorization entry XDRs (base64) + +***Important Notes:*** + +* Provide either `xdr` OR `func`+`auth`, not both +* When using `xdr`, the transaction must be a regular signed transaction (not a fee-bump envelope) +* When using `func`+`auth`, Channels will build and simulate the transaction automatically +* Transactions are always submitted with fee bumping from the fund account + +### Generating XDR with Stellar SDK + +Use the `@stellar/stellar-sdk` to generate the required XDR values: + +#### Full Transaction Envelope XDR + +```typescript +import { Networks, TransactionBuilder, rpc } from '@stellar/stellar-sdk'; + +// Build your transaction +const tx = new TransactionBuilder(account, { + fee: '100', + networkPassphrase: Networks.TESTNET, +}) + .addOperation(/* Operation.invokeHostFunction from Contract.call(...) */) + .setTimeout(30) + .build(); + +// Sign the transaction +tx.sign(keypair); + +// Export base64 envelope XDR +const envelopeXdr = tx.toXDR(); +``` + +#### Soroban Function + Auth XDRs + +```typescript +// Build and simulate first to obtain auth +const baseTx = /* TransactionBuilder(...).addOperation(...).build() */; +const sim = await rpcServer.simulateTransaction(baseTx); + +// Apply simulation, then extract from the InvokeHostFunction operation +const assembled = rpc.assembleTransaction(baseTx, sim).build(); +const op = assembled.operations[0]; // Operation.InvokeHostFunction + +const funcXdr = op.func.toXDR("base64"); +const authXdrs = (op.auth ?? []).map(a => a.toXDR("base64")); +``` + +## Management API + +Channels provides a management API to dynamically configure channel accounts. This API requires authentication via the `PLUGIN_ADMIN_SECRET` environment variable. + +### List Channel Accounts + +Get the current list of configured channel accounts: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/channels-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "listChannelAccounts", + "adminSecret": "your-secret-here" + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "relayerIds": ["channel-001", "channel-002"] + }, + "error": null +} +``` + +### Set Channel Accounts + +Configure the channel accounts that Channels will use. This replaces the entire list: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/channels-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "setChannelAccounts", + "adminSecret": "your-secret-here", + "relayerIds": ["channel-001", "channel-002", "channel-003"] + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "ok": true, + "appliedRelayerIds": ["channel-001", "channel-002", "channel-003"] + }, + "error": null +} +``` + +***Important Notes:*** + +* You must configure at least one channel account before Channels can process transactions +* The management API will prevent removing accounts that are currently locked (in use). On failure it returns HTTP 409 with code `LOCKED_CONFLICT` and `details.locked` listing the blocked IDs +* All relayer IDs must exist in your OpenZeppelin Relayer configuration +* The `adminSecret` must match the `PLUGIN_ADMIN_SECRET` environment variable + +## Responses + +All API responses use the standard Relayer envelope format: ` success, data, error, metadata `. + +### Success Response (HTTP 200) + +```json +{ + "success": true, + "data": { + "transactionId": "tx_123456", + "status": "confirmed", + "hash": "1234567890abcdef..." + }, + "error": null +} +``` + +***Response Fields:*** + +* `success`: `true` when the plugin executed successfully +* `data`: Contains the transaction result +* `transactionId`: The OpenZeppelin Relayer transaction ID +* `status`: Transaction status (e.g., "confirmed") +* `hash`: The Stellar transaction hash +* `error`: `null` on success + +### Error Response (HTTP 4xx) + +```json +{ + "success": false, + "data": { + "code": "POOL_CAPACITY", + "details": {} + }, + "error": "Too many transactions queued. Please try again later", + "metadata": { + "logs": [ + { "level": "error", "message": "All channel accounts in use" } + ] + } +} +``` + +***Error Response Fields:*** + +* `success`: `false` when the plugin encountered an error +* `data`: Contains error details +* `code`: Error code (e.g., "POOL_CAPACITY", "LOCKED_CONFLICT") +* `details`: Additional context about the error +* `error`: Human-readable error message +* `metadata.logs`: Plugin execution logs (if `emit_logs` is enabled) + +### Common Error Codes + +* `CONFIG_MISSING`: Missing required environment variable +* `UNSUPPORTED_NETWORK`: Invalid network type +* `INVALID_PARAMS`: Invalid request parameters +* `INVALID_XDR`: Failed to parse XDR +* `INVALID_ENVELOPE_TYPE`: Not a regular transaction envelope (e.g., fee bump) +* `INVALID_TIME_BOUNDS`: TimeBounds too far in the future +* `NO_CHANNELS_CONFIGURED`: No channel accounts have been configured via management API +* `POOL_CAPACITY`: All channel accounts in use +* `RELAYER_UNAVAILABLE`: Relayer not found +* `SIMULATION_FAILED`: Transaction simulation failed +* `ONCHAIN_FAILED`: Transaction failed on-chain +* `WAIT_TIMEOUT`: Transaction wait timeout +* `MANAGEMENT_DISABLED`: Management API not enabled +* `UNAUTHORIZED`: Invalid admin secret +* `LOCKED_CONFLICT`: Cannot remove locked channel accounts + +## Additional Resources + +* ***Stellar SDK Documentation***: https://stellar.github.io/js-stellar-sdk/ +* ***Soroban Documentation***: https://soroban.stellar.org/docs +* ***OpenZeppelin Relayer Documentation***: https://docs.openzeppelin.com/relayer diff --git a/content/relayer/plugins.mdx b/content/relayer/plugins/index.mdx similarity index 96% rename from content/relayer/plugins.mdx rename to content/relayer/plugins/index.mdx index c529dc6f..f8f8b2c2 100644 --- a/content/relayer/plugins.mdx +++ b/content/relayer/plugins/index.mdx @@ -331,17 +331,17 @@ curl -X POST http://localhost:8080/api/v1/plugins/example-plugin/call \ 2. **API Response (Error)**: ```json -{ - "success": false, - "data": - { - "code": "MISSING_PARAM", - "details": { "field": "destinationAddress" } - }, - "metadata": { - "logs": [ { "level": "error", "message": "destinationAddress is required" } ] - }, - "error": "destinationAddress is required" +{ + "success": false, + "data": + { + "code": "MISSING_PARAM", + "details": { "field": "destinationAddress" } + }, + "metadata": { + "logs": [ { "level": "error", "message": "destinationAddress is required" } ] + }, + "error": "destinationAddress is required" } ``` @@ -397,14 +397,14 @@ export async function handler(context: PluginContext) { === Available Methods - `get(key: string): Promise` -- `set(key: string, value: unknown, opts?: { ttlSec?: number }): Promise` +- `set(key: string, value: unknown, opts?: ttlSec?: number ): Promise` - `del(key: string): Promise` - `exists(key: string): Promise` - `listKeys(pattern?: string, batch?: number): Promise` - `clear(): Promise` -- `withLock(key: string, fn: () => Promise, opts?: { ttlSec?: number; onBusy?: 'throw' | 'skip' }): Promise` +- `withLock(key: string, fn: () => Promise, opts?: ttlSec?: number; onBusy?: 'throw' | 'skip' ): Promise` -Keys must match `[A-Za-z0-9:_-]{1,512}` and are automatically namespaced per plugin. +Keys must match `[A-Za-z0-9:_-]1,512` and are automatically namespaced per plugin. == Migration from Legacy Patterns @@ -424,7 +424,7 @@ If you have existing plugins using `runPlugin()` or the two-parameter handler, m ``` import runPlugin, PluginAPI from "./lib/plugin"; -async function myPlugin(api: PluginAPI, params: any): Promise +async function myPlugin(api: PluginAPI, params: any): Promise // Your plugin logic return result; @@ -437,7 +437,7 @@ runPlugin(myPlugin); // ⚠️ Shows deprecation warning ``` import PluginAPI from "@openzeppelin/relayer-sdk"; -export async function handler(api: PluginAPI, params: any): Promise +export async function handler(api: PluginAPI, params: any): Promise // Same plugin logic - ⚠️ Deprecated, no KV access return result; @@ -448,7 +448,7 @@ export async function handler(api: PluginAPI, params: any): Promise ``` import PluginContext from "@openzeppelin/relayer-sdk"; -export async function handler(context: PluginContext): Promise +export async function handler(context: PluginContext): Promise const api, params, kv = context; // Same plugin logic plus KV access! return result; diff --git a/content/relayer/plugins/launchtube.mdx b/content/relayer/plugins/launchtube.mdx new file mode 100644 index 00000000..c80cf96b --- /dev/null +++ b/content/relayer/plugins/launchtube.mdx @@ -0,0 +1,499 @@ +--- +title: Launchtube +--- + +## Overview + +Launchtube is a plugin for OpenZeppelin Relayer that simplifies submitting Stellar Soroban transactions by automatically handling the complexity of getting transactions on-chain. + +The plugin features: + +* ***Automatic Fee Bumping***: Uses a dedicated fund account to pay transaction fees +* ***Sequence Number Management***: Maintains a pool of sequence accounts for concurrent transaction processing +* ***Transaction Simulation***: Automatically simulates and rebuilds transactions with proper resources +* ***Retry Logic***: Built-in error handling and retry mechanisms +* ***Management API***: Dynamic configuration of sequence accounts + +## Prerequisites + +* Node.js >= 18 +* pnpm >= 10 +* OpenZeppelin Relayer (installed and configured) +* Stellar testnet or mainnet account +* Funded Stellar accounts for the fund and sequence accounts + +## Installation + +Launchtube can be added to any OpenZeppelin Relayer installation. + +***Resources:*** + +* ***npm Package***: [@openzeppelin/relayer-plugin-launchtube](https://www.npmjs.com/package/@openzeppelin/relayer-plugin-launchtube) +* ***GitHub Repository***: https://github.com/OpenZeppelin/relayer-plugin-launchtube +* ***Example Setup***: [launchtube-plugin-example](https://github.com/OpenZeppelin/openzeppelin-relayer/blob/0550c49444be585c6d40c43514758f57604d818b/examples/launchtube-plugin-example) + +### Install from npm + +```bash +# From the root of your Relayer repository +cd plugins +pnpm add @openzeppelin/relayer-plugin-launchtube +``` + +### Create the plugin wrapper + +Inside your Relayer, create a directory for the plugin and expose its handler: + +```bash +mkdir -p plugins/launchtube +``` + +Create `plugins/launchtube/index.ts`: + +```typescript +export { handler } from '@openzeppelin/relayer-plugin-launchtube'; +``` + +## Configuration + +### Plugin Registration + +Register the plugin in your `config/config.json` file: + +```json +{ + "plugins": [ + { + "id": "launchtube-plugin", + "path": "launchtube/index.ts", + "timeout": 60 + } + ] +} +``` + +### Environment Variables + +Configure the required environment variables: + +```bash +# Required environment variables +export STELLAR_NETWORK="testnet" # or "mainnet" +export SOROBAN_RPC_URL="https://soroban-testnet.stellar.org" +export FUND_RELAYER_ID="launchtube-fund" # ID of the fund relayer +export LAUNCHTUBE_ADMIN_SECRET="your-secret-here" # Required for management API + +# Optional environment variables +export LOCK_TTL_SECONDS=30 # Lock timeout (default: 30, range: 10-30) +``` + +***Required Variables:*** + +* `STELLAR_NETWORK`: Either "testnet" or "mainnet" +* `SOROBAN_RPC_URL`: Stellar Soroban RPC endpoint URL +* `FUND_RELAYER_ID`: Relayer ID for the account that pays transaction fees + +***Optional Variables:*** + +* `LAUNCHTUBE_ADMIN_SECRET`: Secret for accessing the management API (required to manage sequence accounts) +* `LOCK_TTL_SECONDS`: TTL for sequence account locks in seconds (default: 30, range: 10-30) + +### Relayer Configuration + +Launchtube requires two types of relayers: + +1. ***Fund Account***: The account that pays transaction fees (should have `concurrent_transactions: true` enabled) +2. ***Sequence Accounts***: At least one sequence account (recommended: 2 or more for better throughput) + +Configure relayers in your `config/config.json`: + +```json +{ + "relayers": [ + { + "id": "launchtube-fund", + "chain": "stellar", + "signer": "launchtube-fund-signer", + "policies": { + "concurrent_transactions": true + } + }, + { + "id": "launchtube-seq-001", + "chain": "stellar", + "signer": "launchtube-seq-001-signer" + }, + { + "id": "launchtube-seq-002", + "chain": "stellar", + "signer": "launchtube-seq-002-signer" + } + ] +} +``` + + +The fund relayer should have `concurrent_transactions: true` enabled to allow parallel transaction processing. + + +After configuration, fund these accounts on-chain and register them with Launchtube (see "Initializing Sequence Accounts" below). + +## Initializing Sequence Accounts + +After configuring your relayers in `config.json` and funding the Stellar accounts, register them with Launchtube via the Management API: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "setSequenceAccounts", + "adminSecret": "your-secret-here", + "relayerIds": ["launchtube-seq-001", "launchtube-seq-002"] + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "appliedRelayerIds": ["launchtube-seq-001", "launchtube-seq-002"] + }, + "error": null +} +``` + +This tells Launchtube which relayers to use as sequence accounts. All relayer IDs must match your configured relayer IDs in `config.json`. + +Launchtube is now ready to serve Soroban transactions. + +## Automated Setup + +To skip the manual configuration steps, use the provided automation script. It automates the entire setup process: creating signers and relayers via the API, funding accounts on-chain, and registering them with Launchtube. + +### Prerequisites + +When using the automated setup, you only need to configure and fund the ***fund account***: + +```json +{ + "relayers": [ + { + "id": "launchtube-fund", + "chain": "stellar", + "signer": "launchtube-fund-signer", + "policies": { + "concurrent_transactions": true + } + } + ] +} +``` + +The script creates all sequence account signers and relayers dynamically - no config.json entries needed for sequence accounts. + +### Running the Script + +```bash +pnpm exec tsx ./scripts/create-sequence-accounts.ts \ + --total 3 \ + --base-url http://localhost:8080 \ + --api-key \ + --funding-relayer launchtube-fund \ + --plugin-id launchtube-plugin \ + --plugin-admin-secret \ + --network testnet +``` + +### What the Script Does + +1. ***Creates sequence account signers and relayers via API***: Following the naming pattern `lt-seq-0001`, `lt-seq-0002`, etc. +2. ***Funds sequence accounts on-chain***: Submits funding transactions through the fund relayer and waits for confirmation +3. ***Registers with Launchtube***: Automatically calls the Management API to register all sequence accounts + +### Script Options + +* `--total`: Number of sequence accounts to create (recommended: 2-3 for testing, more for production) +* `--fix`: Audit and heal partially created state (use if the script was interrupted) +* `--dry-run`: Preview actions without making changes +* `--prefix`: Customize the naming prefix (default: `lt-seq-`) +* `--starting-balance`: XLM amount for each account (default: 5) + +### Script Location + +* Example directory: [`scripts/create-sequence-accounts.ts`](https://github.com/OpenZeppelin/relayer-plugin-launchtube/blob/main/scripts/create-sequence-accounts.ts) + +## API Usage + +Launchtube is invoked by making POST requests to the plugin endpoint: + +```bash +POST /api/v1/plugins/{plugin-id}/call +``` + +### Submitting Transactions + +There are two ways to submit transactions to Launchtube: + +#### Option 1: Complete Transaction XDR + +Submit a complete, signed transaction envelope: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "xdr": "AAAAAgAAAAA...", + "sim": false + } + }' +``` + +#### Option 2: Soroban Function + Auth + +Submit just the Soroban function and authorization entries: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "func": "AAAABAAAAAEAAAAGc3ltYm9s...", + "auth": ["AAAACAAAAAEAAAA..."], + "sim": true + } + }' +``` + +### Parameters + +* `xdr` (string): Complete transaction envelope XDR (base64) +* `func` (string): Soroban host function XDR (base64) +* `auth` (array of strings): Array of Soroban authorization entry XDRs (base64) +* `sim` (boolean): Whether to simulate the transaction before submission + +***Important Notes:*** + +* Provide either `xdr` OR `func`+`auth`, not both +* When using `sim: true`, Launchtube will simulate the transaction and rebuild it with proper resource limits +* When using `sim: false` with `xdr`, you must provide a pre-assembled transaction with resource fees + +### Generating XDR with Stellar SDK + +Use the `@stellar/stellar-sdk` to generate the required XDR values: + +#### Full Transaction Envelope XDR + +```typescript +import { Networks, TransactionBuilder, rpc } from '@stellar/stellar-sdk'; + +// Build your transaction +const tx = new TransactionBuilder(account, { + fee: '100', + networkPassphrase: Networks.TESTNET, +}) + .addOperation(/* Operation.invokeHostFunction from Contract.call(...) */) + .setTimeout(30) + .build(); + +// Optional: pre-simulate to set resources/fees before signing +const sim = await rpcServer.simulateTransaction(tx); +const prepared = rpc.assembleTransaction(tx, sim).build(); +prepared.sign(keypair); + +// Export base64 envelope XDR +const envelopeXdr = prepared.toXDR(); +``` + +#### Soroban Function + Auth XDRs + +```typescript +// Build and simulate first to obtain auth +const baseTx = /* TransactionBuilder(...).addOperation(...).build() */; +const sim = await rpcServer.simulateTransaction(baseTx); + +// Apply simulation, then extract from the InvokeHostFunction operation +const assembled = rpc.assembleTransaction(baseTx, sim).build(); +const op = assembled.operations[0]; // Operation.InvokeHostFunction + +const funcXdr = op.func.toXDR("base64"); +const authXdrs = (op.auth ?? []).map(a => a.toXDR("base64")); +``` + +## Management API + +Launchtube provides a management API to dynamically configure sequence accounts. This API requires authentication via the `LAUNCHTUBE_ADMIN_SECRET` environment variable. + +### List Sequence Accounts + +Get the current list of configured sequence accounts: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "listSequenceAccounts", + "adminSecret": "your-secret-here" + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "relayerIds": ["launchtube-seq-001", "launchtube-seq-002"] + }, + "error": null +} +``` + +### Set Sequence Accounts + +Configure the sequence accounts that Launchtube will use. This replaces the entire list: + +```bash +curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "params": { + "management": { + "action": "setSequenceAccounts", + "adminSecret": "your-secret-here", + "relayerIds": ["launchtube-seq-001", "launchtube-seq-002", "launchtube-seq-003"] + } + } + }' +``` + +***Response:*** + +```json +{ + "success": true, + "data": { + "appliedRelayerIds": ["launchtube-seq-001", "launchtube-seq-002", "launchtube-seq-003"] + }, + "error": null +} +``` + +***Important Notes:*** + +* You must configure at least one sequence account before Launchtube can process transactions +* The management API will prevent removing accounts that are currently locked (in use). On failure it returns HTTP 409 with code `LOCKED_CONFLICT` and `details.locked` listing the blocked IDs +* All relayer IDs must exist in your OpenZeppelin Relayer configuration +* The `adminSecret` must match the `LAUNCHTUBE_ADMIN_SECRET` environment variable + +## Responses + +All API responses use the standard Relayer envelope format: ` success, data, error, metadata `. + +### Success Response (HTTP 200) + +```json +{ + "success": true, + "data": { + "transactionId": "tx_123456", + "hash": "1234567890abcdef..." + }, + "error": null +} +``` + +***Response Fields:*** + +* `success`: `true` when the plugin executed successfully +* `data`: Contains the transaction result +* `transactionId`: The OpenZeppelin Relayer transaction ID +* `hash`: The Stellar transaction hash +* `error`: `null` on success + +### Error Response (HTTP 4xx) + +```json +{ + "success": false, + "data": { + "code": "INVALID_PARAMS", + "details": { "sim": false, "xdrProvided": false } + }, + "error": "Cannot pass `sim = false` without `xdr`", + "metadata": { + "logs": [ + { "level": "error", "message": "Cannot pass `sim = false` without `xdr`" } + ] + } +} +``` + +***Error Response Fields:*** + +* `success`: `false` when the plugin encountered an error +* `data`: Contains error details +* `code`: Error code (e.g., "INVALID_PARAMS", "LOCKED_CONFLICT") +* `details`: Additional context about the error +* `error`: Human-readable error message +* `metadata.logs`: Plugin execution logs (if `emit_logs` is enabled) + +### Common Error Codes + +* `INVALID_PARAMS`: Invalid parameter combination provided +* `LOCKED_CONFLICT`: Attempting to remove sequence accounts that are currently in use +* `MISSING_PARAM`: Required parameter is missing +* `AUTH_FAILED`: Authentication failed (invalid admin secret) + +## How It Works + +Launchtube processes transactions through the following workflow: + +1. ***Request Validation***: Validates input parameters and extracts Soroban data from XDR or func+auth +2. ***Sequence Account Acquisition***: Acquires an available sequence account from the pool using Redis locks +3. ***Authorization Checking***: Validates authorization entries and determines if simulation is possible +4. ***Simulation*** (if enabled): Simulates the transaction and rebuilds it with proper resource limits and fees +5. ***Fee Bumping***: Fund account wraps the transaction in a fee bump envelope +6. ***Submission***: Sends the transaction to the Stellar network via the Soroban RPC +7. ***Confirmation***: Returns the transaction ID and hash for tracking + +This architecture enables: + +* ***Concurrent Processing***: Multiple transactions can be processed in parallel using different sequence accounts +* ***Automatic Resource Management***: Simulation ensures transactions have sufficient resources +* ***Simplified User Experience***: Users don’t need to manage sequence numbers or fee bumping + +## Example Setup + +For a complete working example with Docker Compose, refer to the Launchtube plugin example in the OpenZeppelin Relayer repository: + +* ***Location***: [examples/launchtube-plugin-example](https://github.com/OpenZeppelin/openzeppelin-relayer/blob/0550c49444be585c6d40c43514758f57604d818b/examples/launchtube-plugin-example) +* ***Documentation***: [README.md](https://github.com/OpenZeppelin/openzeppelin-relayer/blob/0550c49444be585c6d40c43514758f57604d818b/examples/launchtube-plugin-example/README.md) + +The example includes: + +* Pre-configured Docker Compose setup +* Scripts for creating and funding accounts +* Complete configuration files +* Step-by-step setup instructions +* Local plugin development workflow + +## Additional Resources + +* ***Stellar SDK Documentation***: https://stellar.github.io/js-stellar-sdk/ +* ***Soroban Documentation***: https://soroban.stellar.org/docs diff --git a/src/components/layout/docs-layout-client.tsx b/src/components/layout/docs-layout-client.tsx index b817c362..741b3cc3 100644 --- a/src/components/layout/docs-layout-client.tsx +++ b/src/components/layout/docs-layout-client.tsx @@ -43,7 +43,7 @@ export function DocsLayoutClient({ children }: DocsLayoutClientProps) { // Include shared paths in Polkadot tab only if coming from Polkadot context const polkadotUrls = isSharedPath && lastEcosystem === "polkadot" - ? new Set(["/substrate-runtimes", "/monitor"]) + ? new Set(["/substrate-runtimes", "/monitor", "/relayer"]) : new Set(["/substrate-runtimes"]); return [ diff --git a/src/navigation/ethereum-evm.json b/src/navigation/ethereum-evm.json index 0846c78a..fdd55793 100644 --- a/src/navigation/ethereum-evm.json +++ b/src/navigation/ethereum-evm.json @@ -1065,9 +1065,14 @@ "url": "/relayer/1.1.x/roadmap" }, { - "type": "page", + "type": "folder", "name": "Plugins", - "url": "/relayer/1.1.x/plugins" + "index": { + "type": "page", + "name": "Overview", + "url": "/relayer/1.1.x/plugins" + }, + "children": [] }, { "type": "page", diff --git a/src/navigation/polkadot.json b/src/navigation/polkadot.json index 70c1c520..1f90b4b1 100644 --- a/src/navigation/polkadot.json +++ b/src/navigation/polkadot.json @@ -358,6 +358,291 @@ "type": "separator", "name": "Open Source Tools" }, + { + "type": "folder", + "name": "Relayer", + "index": { + "type": "page", + "name": "Overview", + "url": "/relayer/1.1.x" + }, + "children": [ + { + "type": "page", + "name": "Quickstart", + "url": "/relayer/1.1.x/quickstart" + }, + { + "type": "folder", + "name": "Configuration", + "index": { + "type": "page", + "name": "Overview", + "url": "/relayer/1.1.x/configuration" + }, + "children": [ + { + "type": "page", + "name": "Signers", + "url": "/relayer/1.1.x/configuration/signers" + }, + { + "type": "page", + "name": "Network Configuration", + "url": "/relayer/1.1.x/network_configuration" + }, + { + "type": "page", + "name": "Storage Configuration", + "url": "/relayer/1.1.x/configuration/storage" + } + ] + }, + { + "type": "page", + "name": "Stellar Integration", + "url": "/relayer/1.1.x/stellar" + }, + { + "type": "folder", + "name": "API Reference", + "index": { + "type": "page", + "name": "Overview", + "url": "/relayer/1.1.x/api" + }, + "children": [ + { + "type": "folder", + "name": "Relayers", + "children": [ + { + "type": "page", + "name": "List Relayers", + "url": "/relayer/1.1.x/api/listRelayers" + }, + { + "type": "page", + "name": "Create Relayer", + "url": "/relayer/1.1.x/api/createRelayer" + }, + { + "type": "page", + "name": "Get Relayer", + "url": "/relayer/1.1.x/api/getRelayer" + }, + { + "type": "page", + "name": "Delete Relayer", + "url": "/relayer/1.1.x/api/deleteRelayer" + }, + { + "type": "page", + "name": "Update Relayer", + "url": "/relayer/1.1.x/api/updateRelayer" + }, + { + "type": "page", + "name": "Get Relayer Balance", + "url": "/relayer/1.1.x/api/getRelayerBalance" + }, + { + "type": "page", + "name": "RPC", + "url": "/relayer/1.1.x/api/rpc" + }, + { + "type": "page", + "name": "Sign", + "url": "/relayer/1.1.x/api/sign" + }, + { + "type": "page", + "name": "Sign Transaction", + "url": "/relayer/1.1.x/api/signTransaction" + }, + { + "type": "page", + "name": "Sign Typed Data", + "url": "/relayer/1.1.x/api/signTypedData" + }, + { + "type": "page", + "name": "Get Relayer Status", + "url": "/relayer/1.1.x/api/getRelayerStatus" + }, + { + "type": "page", + "name": "Send Transaction", + "url": "/relayer/1.1.x/api/sendTransaction" + }, + { + "type": "page", + "name": "List Transactions", + "url": "/relayer/1.1.x/api/listTransactions" + }, + { + "type": "page", + "name": "Get Transaction by Nonce", + "url": "/relayer/1.1.x/api/getTransactionByNonce" + }, + { + "type": "page", + "name": "Delete Pending Transactions", + "url": "/relayer/1.1.x/api/deletePendingTransactions" + }, + { + "type": "page", + "name": "Get Transaction by ID", + "url": "/relayer/1.1.x/api/getTransactionById" + }, + { + "type": "page", + "name": "Replace Transaction", + "url": "/relayer/1.1.x/api/replaceTransaction" + }, + { + "type": "page", + "name": "Cancel Transaction", + "url": "/relayer/1.1.x/api/cancelTransaction" + } + ] + }, + { + "type": "folder", + "name": "Plugins", + "children": [ + { + "type": "page", + "name": "Call Plugin", + "url": "/relayer/1.1.x/api/callPlugin" + } + ] + }, + { + "type": "folder", + "name": "Notifications", + "children": [ + { + "type": "page", + "name": "List Notifications", + "url": "/relayer/1.1.x/api/listNotifications" + }, + { + "type": "page", + "name": "Create Notification", + "url": "/relayer/1.1.x/api/createNotification" + }, + { + "type": "page", + "name": "Get Notification", + "url": "/relayer/1.1.x/api/getNotification" + }, + { + "type": "page", + "name": "Delete Notification", + "url": "/relayer/1.1.x/api/deleteNotification" + }, + { + "type": "page", + "name": "Update Notification", + "url": "/relayer/1.1.x/api/updateNotification" + } + ] + }, + { + "type": "folder", + "name": "Signers", + "children": [ + { + "type": "page", + "name": "List Signers", + "url": "/relayer/1.1.x/api/listSigners" + }, + { + "type": "page", + "name": "Create Signer", + "url": "/relayer/1.1.x/api/createSigner" + }, + { + "type": "page", + "name": "Get Signer", + "url": "/relayer/1.1.x/api/getSigner" + }, + { + "type": "page", + "name": "Delete Signer", + "url": "/relayer/1.1.x/api/deleteSigner" + }, + { + "type": "page", + "name": "Update Signer", + "url": "/relayer/1.1.x/api/updateSigner" + } + ] + }, + { + "type": "folder", + "name": "Metrics", + "children": [ + { + "type": "page", + "name": "Scrape Metrics", + "url": "/relayer/1.1.x/api/scrape_metrics" + }, + { + "type": "page", + "name": "List Metrics", + "url": "/relayer/1.1.x/api/list_metrics" + }, + { + "type": "page", + "name": "Metric Detail", + "url": "/relayer/1.1.x/api/metric_detail" + } + ] + }, + { + "type": "folder", + "name": "Health", + "children": [ + { + "type": "page", + "name": "Health", + "url": "/relayer/1.1.x/api/health" + } + ] + } + ] + }, + { + "type": "page", + "name": "Project Structure", + "url": "/relayer/1.1.x/structure" + }, + { + "type": "page", + "name": "Project Roadmap", + "url": "/relayer/1.1.x/roadmap" + }, + { + "type": "page", + "name": "Plugins", + "url": "/relayer/1.1.x/plugins" + }, + { + "type": "page", + "name": "Changelog", + "url": "/relayer/1.1.x/changelog" + }, + { + "type": "page", + "name": "Rust Book", + "url": "https://docs-v1-1--openzeppelin-relayer.netlify.app/openzeppelin_relayer/", + "external": true + } + ] + }, { "type": "folder", "name": "Monitor", diff --git a/src/navigation/stellar.json b/src/navigation/stellar.json index a9af8521..2fe4e815 100644 --- a/src/navigation/stellar.json +++ b/src/navigation/stellar.json @@ -530,9 +530,25 @@ "url": "/relayer/1.1.x/roadmap" }, { - "type": "page", + "type": "folder", "name": "Plugins", - "url": "/relayer/1.1.x/plugins" + "index": { + "type": "page", + "name": "Overview", + "url": "/relayer/1.1.x/plugins" + }, + "children": [ + { + "type": "page", + "name": "Launchtube", + "url": "/relayer/1.1.x/plugins/launchtube" + }, + { + "type": "page", + "name": "Channels", + "url": "/relayer/1.1.x/plugins/channels" + } + ] }, { "type": "page",