|
| 1 | +--- |
| 2 | +title: Stellar Channels Guide |
| 3 | +--- |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +OpenZeppelin Stellar Channels Service is a managed infrastructure for submitting Stellar Soroban transactions with automatic parallel processing and fee management. The service handles all the complexity of transaction submission, allowing you to focus on building your application. |
| 8 | + |
| 9 | +**Key Benefits:** |
| 10 | + |
| 11 | +- **_Zero Infrastructure Management_**: No servers, relayers, or channel accounts to configure |
| 12 | +- **_Automatic Fee Payment_**: Gas fees paid by the service on your behalf |
| 13 | +- **_Parallel Processing_**: High throughput via managed pool of channel accounts |
| 14 | +- **_Simple Integration_**: Type-safe SDK with minimal setup |
| 15 | +- **_Free to Use_**: No credits, subscriptions, or payment systems |
| 16 | + |
| 17 | +## Service Endpoints |
| 18 | + |
| 19 | +- **Mainnet**: `https://channels.openzeppelin.com` |
| 20 | +- **Testnet**: `https://channels.openzeppelin.com/testnet` |
| 21 | + |
| 22 | +## Getting Started |
| 23 | + |
| 24 | +### 1. Get Your API Key |
| 25 | + |
| 26 | +Visit the service endpoint to generate an API key: |
| 27 | + |
| 28 | +- **Mainnet**: https://channels.openzeppelin.com/gen |
| 29 | +- **Testnet**: https://channels.openzeppelin.com/testnet/gen |
| 30 | + |
| 31 | +Save your API key securely - you'll need it for all requests. |
| 32 | + |
| 33 | +### 2. Install the Client |
| 34 | + |
| 35 | +```bash |
| 36 | +npm install @openzeppelin/relayer-plugin-channels |
| 37 | +# or |
| 38 | +pnpm add @openzeppelin/relayer-plugin-channels |
| 39 | +# or |
| 40 | +yarn add @openzeppelin/relayer-plugin-channels |
| 41 | +``` |
| 42 | + |
| 43 | +### 3. Initialize the Client |
| 44 | + |
| 45 | +```typescript |
| 46 | +import { ChannelsClient } from '@openzeppelin/relayer-plugin-channels'; |
| 47 | + |
| 48 | +const client = new ChannelsClient({ |
| 49 | + baseUrl: 'https://channels.openzeppelin.com/testnet', |
| 50 | + apiKey: process.env.CHANNELS_API_KEY, |
| 51 | +}); |
| 52 | +``` |
| 53 | + |
| 54 | +## Submitting Transactions |
| 55 | + |
| 56 | +The Channels service supports two transaction submission methods depending on your use case. |
| 57 | + |
| 58 | +### Method 1: Soroban Function + Auth (Recommended) |
| 59 | + |
| 60 | +This method is ideal when you want the service to handle transaction building and simulation. Submit the Soroban function and authorization entries, and the service builds the complete transaction using a channel account from the pool, enabling high-throughput parallel processing. |
| 61 | + |
| 62 | +```typescript |
| 63 | +import { Contract, Networks, SorobanRpc } from '@stellar/stellar-sdk'; |
| 64 | + |
| 65 | +// Initialize your contract |
| 66 | +const contract = new Contract('CA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUWDA'); |
| 67 | + |
| 68 | +// Build the transaction (don't sign yet) |
| 69 | +const rpc = new SorobanRpc.Server('https://soroban-testnet.stellar.org'); |
| 70 | +const source = await rpc.getAccount(sourceAddress); |
| 71 | + |
| 72 | +const tx = new TransactionBuilder(source, { |
| 73 | + fee: '100', |
| 74 | + networkPassphrase: Networks.TESTNET, |
| 75 | +}) |
| 76 | + .addOperation(contract.call('transfer' /* args */)) |
| 77 | + .setTimeout(30) |
| 78 | + .build(); |
| 79 | + |
| 80 | +// Simulate to get auth entries |
| 81 | +const simulation = await rpc.simulateTransaction(tx); |
| 82 | +const assembled = SorobanRpc.assembleTransaction(tx, simulation).build(); |
| 83 | + |
| 84 | +// Extract function and auth XDRs |
| 85 | +const op = assembled.operations[0]; |
| 86 | +const func = op.func.toXDR('base64'); |
| 87 | +const auth = (op.auth ?? []).map((a) => a.toXDR('base64')); |
| 88 | + |
| 89 | +// Submit to Channels |
| 90 | +const result = await client.submitSorobanTransaction({ |
| 91 | + func: func, |
| 92 | + auth: auth, |
| 93 | +}); |
| 94 | + |
| 95 | +console.log('Transaction submitted:', result.hash); |
| 96 | +console.log('Status:', result.status); |
| 97 | +``` |
| 98 | + |
| 99 | +**When to use this method:** |
| 100 | + |
| 101 | +- You want the service to handle transaction assembly |
| 102 | +- You're working with standard Soroban contract calls |
| 103 | +- You need automatic simulation and resource calculation |
| 104 | +- You need high-throughput parallel transaction processing |
| 105 | + |
| 106 | +### Method 2: Pre-Signed Transaction XDR |
| 107 | + |
| 108 | +This method gives you full control over the transaction structure. You build, sign, and submit a complete transaction envelope. |
| 109 | + |
| 110 | +```typescript |
| 111 | +import { Keypair, Networks, TransactionBuilder } from '@stellar/stellar-sdk'; |
| 112 | + |
| 113 | +// Build and sign your transaction |
| 114 | +const sourceKeypair = Keypair.fromSecret('S...'); |
| 115 | +const tx = new TransactionBuilder(source, { |
| 116 | + fee: '100', |
| 117 | + networkPassphrase: Networks.TESTNET, |
| 118 | +}) |
| 119 | + .addOperation(/* your operation */) |
| 120 | + .setTimeout(30) |
| 121 | + .build(); |
| 122 | + |
| 123 | +// Sign the transaction |
| 124 | +tx.sign(sourceKeypair); |
| 125 | + |
| 126 | +// Submit to Channels |
| 127 | +const result = await client.submitTransaction({ |
| 128 | + xdr: tx.toXDR(), // base64 envelope XDR |
| 129 | +}); |
| 130 | + |
| 131 | +console.log('Transaction submitted:', result.hash); |
| 132 | +console.log('Status:', result.status); |
| 133 | +``` |
| 134 | + |
| 135 | +**When to use this method:** |
| 136 | + |
| 137 | +- You need precise control over transaction structure |
| 138 | +- You're using advanced Stellar features |
| 139 | +- Your transaction is already signed by another system |
| 140 | + |
| 141 | +## Response Format |
| 142 | + |
| 143 | +All successful submissions return: |
| 144 | + |
| 145 | +```typescript |
| 146 | +{ |
| 147 | + transactionId: string; // Internal tracking ID |
| 148 | + hash: string; // Stellar transaction hash |
| 149 | + status: string; // Transaction status (e.g., "confirmed") |
| 150 | +} |
| 151 | +``` |
| 152 | + |
| 153 | +## Error Handling |
| 154 | + |
| 155 | +The SDK provides structured error handling with three error types: |
| 156 | + |
| 157 | +```typescript |
| 158 | +import { |
| 159 | + PluginTransportError, |
| 160 | + PluginExecutionError, |
| 161 | + PluginUnexpectedError, |
| 162 | +} from '@openzeppelin/relayer-plugin-channels'; |
| 163 | + |
| 164 | +try { |
| 165 | + const result = await client.submitSorobanTransaction({ func, auth }); |
| 166 | + console.log('Success:', result.hash); |
| 167 | +} catch (error) { |
| 168 | + if (error instanceof PluginTransportError) { |
| 169 | + // Network failures (connection, timeout, 5xx errors) |
| 170 | + console.error('Service unavailable:', error.message); |
| 171 | + console.error('Status:', error.statusCode); |
| 172 | + } else if (error instanceof PluginExecutionError) { |
| 173 | + // Transaction rejected (validation, simulation failure, on-chain failure) |
| 174 | + console.error('Transaction failed:', error.message); |
| 175 | + console.error('Error code:', error.errorDetails?.code); |
| 176 | + console.error('Details:', error.errorDetails?.details); |
| 177 | + } else if (error instanceof PluginUnexpectedError) { |
| 178 | + // Client-side errors (parsing, validation) |
| 179 | + console.error('Client error:', error.message); |
| 180 | + } |
| 181 | +} |
| 182 | +``` |
| 183 | + |
| 184 | +### Common Error Codes |
| 185 | + |
| 186 | +| Code | Description | Resolution | |
| 187 | +| --------------------- | ------------------------------------- | --------------------------------------------------------- | |
| 188 | +| `INVALID_PARAMS` | Invalid request parameters | Check that you're providing either `xdr` OR `func`+`auth` | |
| 189 | +| `INVALID_XDR` | Failed to parse XDR | Verify XDR is valid base64 and properly encoded | |
| 190 | +| `POOL_CAPACITY` | All channel accounts in use | Retry after a short delay | |
| 191 | +| `SIMULATION_FAILED` | Transaction simulation failed | Check contract address and function arguments | |
| 192 | +| `ONCHAIN_FAILED` | Transaction failed on-chain | Review transaction logic and on-chain state | |
| 193 | +| `INVALID_TIME_BOUNDS` | Transaction timeout too far in future | Set timeout to ≤30 seconds | |
| 194 | + |
| 195 | +## TypeScript Support |
| 196 | + |
| 197 | +The SDK is fully typed |
| 198 | + |
| 199 | +```typescript |
| 200 | +import type { |
| 201 | + ChannelsClient, |
| 202 | + ChannelsFuncAuthRequest, |
| 203 | + ChannelsXdrRequest, |
| 204 | + ChannelsTransactionResponse, |
| 205 | +} from '@openzeppelin/relayer-plugin-channels'; |
| 206 | + |
| 207 | +// All parameters and responses are fully typed |
| 208 | +const request: ChannelsFuncAuthRequest = { |
| 209 | + func: 'AAAABAAAAAEAAAAGc3ltYm9s...', |
| 210 | + auth: ['AAAACAAAAAEAAAA...'], |
| 211 | +}; |
| 212 | + |
| 213 | +const response: ChannelsTransactionResponse = await client.submitSorobanTransaction(request); |
| 214 | +``` |
| 215 | + |
| 216 | +## Support & Resources |
| 217 | + |
| 218 | +- **Stellar SDK Documentation**: https://stellar.github.io/js-stellar-sdk/ |
| 219 | +- **Channels Plugin**: https://github.com/OpenZeppelin/relayer-plugin-channels |
0 commit comments