Skip to content

Commit 5bda763

Browse files
feat: Add Launctube plugin example (#414)
* chore: initial setup * feat: Use launchtube NPM package Signed-off-by: Dylan Kilkenny <dylankilkenny95@gmail.com> * fix: Use pnpm in readme Signed-off-by: Dylan Kilkenny <dylankilkenny95@gmail.com> * refactor: Add unique password per key Signed-off-by: Dylan Kilkenny <dylankilkenny95@gmail.com> * refactor: Add xdr example Signed-off-by: Dylan Kilkenny <dylankilkenny95@gmail.com> --------- Signed-off-by: Dylan Kilkenny <dylankilkenny95@gmail.com> Co-authored-by: Zeljko <zeljko89markovic@gmail.com>
1 parent 3732b01 commit 5bda763

8 files changed

Lines changed: 4249 additions & 0 deletions

File tree

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
# OpenZeppelin Relayer — LaunchTube Plugin Example
2+
3+
Run the LaunchTube plugin with OpenZeppelin Relayer to simplify Stellar Soroban transactions. LaunchTube handles fees, sequence numbers, simulation, and retries automatically.
4+
5+
## Quick Start
6+
7+
```bash
8+
# Clone and navigate to this example:
9+
git clone https://github.com/OpenZeppelin/openzeppelin-relayer
10+
cd openzeppelin-relayer/examples/launchtube-plugin-example
11+
12+
# Then follow the Setup steps below
13+
```
14+
15+
## Prerequisites
16+
17+
- Docker and Docker Compose
18+
- Rust (for generating keys and IDs)
19+
- Node.js >= 18 and pnpm >= 10
20+
21+
## Setup
22+
23+
You only need to:
24+
25+
1. Install and build the LaunchTube plugin
26+
2. Create the keys for LaunchTube accounts
27+
3. Set up environment variables
28+
4. Start Docker to get account addresses
29+
5. Fund accounts on testnet
30+
6. Restart the service
31+
32+
All configurations are pre-set for testnet use.
33+
34+
### 1. Install Dependencies
35+
36+
Install and build the LaunchTube plugin:
37+
38+
```bash
39+
# From this directory (examples/launchtube-plugin-example)
40+
cd launchtube
41+
pnpm install
42+
pnpm run build
43+
cd ..
44+
```
45+
46+
### 2. Create Keys and Configuration
47+
48+
LaunchTube requires two types of keys:
49+
50+
- **Fund account**: Pays transaction fees
51+
- **Sequence accounts**: Manage sequence numbers (at least 2 recommended)
52+
53+
From this directory (`examples/launchtube-plugin-example`), run these commands:
54+
55+
#### Create LaunchTube accounts
56+
57+
```bash
58+
# Replace each YOUR_PASSWORD with a unique strong password for each key
59+
# You will need to add these passwords to your .env file
60+
# Password must contain at least one uppercase letter, one lowercase letter,
61+
# one number, and one special character (e.g., MyPass123!)
62+
63+
# Create fund account (pays fees)
64+
cargo run --example create_key -- \
65+
--password YOUR_PASSWORD \
66+
--output-dir config/keys \
67+
--filename launchtube-fund.json
68+
69+
# Create first sequence account
70+
cargo run --example create_key -- \
71+
--password YOUR_PASSWORD \
72+
--output-dir config/keys \
73+
--filename launchtube-seq-001.json
74+
75+
# Create second sequence account (recommended for better throughput)
76+
cargo run --example create_key -- \
77+
--password YOUR_PASSWORD \
78+
--output-dir config/keys \
79+
--filename launchtube-seq-002.json
80+
```
81+
82+
#### Generate API credentials
83+
84+
```bash
85+
# Generate API key (save this output)
86+
cargo run --example generate_uuid
87+
88+
# Generate webhook signing key (save this output)
89+
cargo run --example generate_uuid
90+
```
91+
92+
#### Create environment file
93+
94+
Create `.env` in this directory:
95+
96+
```env
97+
REDIS_URL=redis://redis:6379
98+
KEYSTORE_PASSPHRASE_FUND=YOUR_PASSWORD
99+
KEYSTORE_PASSPHRASE_SEQ_001=YOUR_PASSWORD
100+
KEYSTORE_PASSPHRASE_SEQ_002=YOUR_PASSWORD
101+
WEBHOOK_SIGNING_KEY=<webhook_key_from_above>
102+
API_KEY=<api_key_from_above>
103+
```
104+
105+
### 3. Verify Configuration
106+
107+
The LaunchTube plugin and relayer configurations are already set up for testnet. The configurations include:
108+
109+
**`launchtube/config.json`** (pre-configured):
110+
111+
```json
112+
{
113+
"fundRelayerId": "launchtube-fund",
114+
"sequenceRelayerIds": ["launchtube-seq-001", "launchtube-seq-002"],
115+
"maxFee": 1000000,
116+
"network": "testnet",
117+
"rpcUrl": "https://soroban-testnet.stellar.org"
118+
}
119+
```
120+
121+
**`config/config.json`** (pre-configured):
122+
123+
- Three relayers defined: `launchtube-fund`, `launchtube-seq-001`, `launchtube-seq-002`
124+
- Corresponding signers pointing to the key files you'll create
125+
- Plugin registered as `launchtube-plugin`
126+
127+
> **Note**: If you need mainnet, update `network` in both config files and use mainnet RPC URL
128+
129+
### 4. (Optional) Configure Webhooks
130+
131+
For transaction notifications, edit `config/config.json`:
132+
133+
```json
134+
{
135+
"notifications": [
136+
{
137+
"url": "https://webhook.site/your-unique-id" // Get a test URL from webhook.site
138+
}
139+
]
140+
}
141+
```
142+
143+
### 5. Start the Service and Get Account Addresses
144+
145+
```bash
146+
docker compose up
147+
```
148+
149+
The relayer will start and display the public addresses for your accounts in the logs:
150+
151+
```
152+
relayer-1 | Syncing sequence for relayer: launchtube-fund (GCP7KWGZCDDVBFKANDJTA74H2HSORV34SMSQIPGZ3PK7V6OHKCFGRTF6)
153+
relayer-1 | Syncing sequence for relayer: launchtube-seq-001 (GCWFXU6HZNHLTXMHWZRPXYBZFOODJYRDZXFOPMUQN4S2JJGEZA2ZHA4B)
154+
relayer-1 | Syncing sequence for relayer: launchtube-seq-002 (GA7IXWK3VKF25JOXJZZ7XMFB3A3IPM5A66MW5DJ6FPOIWME4F66UK4HL)
155+
```
156+
157+
### 6. Fund Your Accounts on Testnet
158+
159+
In a new terminal, copy the addresses from the logs above and fund them:
160+
161+
```bash
162+
# Replace with your actual addresses from the logs
163+
curl "https://friendbot.stellar.org?addr=YOUR_FUND_ADDRESS" # fund account
164+
curl "https://friendbot.stellar.org?addr=YOUR_SEQ_001_ADDRESS" # seq-001
165+
curl "https://friendbot.stellar.org?addr=YOUR_SEQ_002_ADDRESS" # seq-002
166+
```
167+
168+
After funding, restart the service for it to recognize the funded accounts:
169+
170+
```bash
171+
# Stop the service with Ctrl+C, then restart
172+
docker compose up
173+
```
174+
175+
The relayer is now ready at `http://localhost:8080/api/v1` 🚀
176+
177+
## Usage
178+
179+
### Test Connection
180+
181+
```bash
182+
curl -X GET http://localhost:8080/api/v1/plugins \
183+
-H "Authorization: Bearer YOUR_API_KEY"
184+
```
185+
186+
### Submit Transactions
187+
188+
#### Option 1: Complete Transaction XDR
189+
190+
```bash
191+
curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \
192+
-H "Authorization: Bearer YOUR_API_KEY" \
193+
-H "Content-Type: application/json" \
194+
-d '{
195+
"params": {
196+
"xdr": "AAAAAgAAAAA...",
197+
"sim": false
198+
}
199+
}'
200+
```
201+
202+
#### Option 2: Soroban Function + Auth
203+
204+
```bash
205+
curl -X POST http://localhost:8080/api/v1/plugins/launchtube-plugin/call \
206+
-H "Authorization: Bearer YOUR_API_KEY" \
207+
-H "Content-Type: application/json" \
208+
-d '{
209+
"params": {
210+
"func": "AAAABAAAAAEAAAAGc3ltYm9s...",
211+
"auth": ["AAAACAAAAAEAAAA..."],
212+
"sim": true
213+
}
214+
}'
215+
```
216+
217+
**Parameters:**
218+
219+
- `xdr`: Complete transaction envelope XDR
220+
- `func`: Soroban host function XDR
221+
- `auth`: Array of authorization entry XDRs
222+
- `sim`: Simulate before submission (true/false)
223+
224+
> Use either `xdr` OR `func`+`auth`, not both
225+
226+
**Response:**
227+
228+
```json
229+
{
230+
"transactionId": "tx_123456",
231+
"status": "submitted",
232+
"hash": "1234567890abcdef..."
233+
}
234+
```
235+
236+
### Generating XDR for the Relayer
237+
238+
Use [@stellar/stellar-sdk](https://stellar.github.io/js-stellar-sdk/TransactionBuilder.html) to export either a full transaction envelope XDR, or Soroban `func` + `auth` XDRs.
239+
240+
#### Full transaction envelope XDR
241+
242+
```ts
243+
import { Networks, TransactionBuilder, rpc } from '@stellar/stellar-sdk';
244+
245+
// ...build your tx with TransactionBuilder and Contract.call(...)
246+
const tx = new TransactionBuilder(account, {
247+
fee: '100',
248+
networkPassphrase: Networks.TESTNET,
249+
})
250+
.addOperation(/* Operation.invokeHostFunction from Contract.call(...) */)
251+
.setTimeout(30)
252+
.build();
253+
254+
// Optional: pre-simulate to set resources/fees before signing
255+
const sim = await rpcServer.simulateTransaction(tx);
256+
const prepared = rpc.assembleTransaction(tx, sim).build();
257+
prepared.sign(keypair);
258+
259+
// Export base64 envelope XDR
260+
const envelopeXdr = prepared.toXDR();
261+
```
262+
263+
#### Soroban `func` + `auth` XDR
264+
265+
```ts
266+
// Build and simulate first to obtain auth
267+
const baseTx = /* TransactionBuilder(...).addOperation(...).build() */;
268+
const sim = await rpcServer.simulateTransaction(baseTx);
269+
270+
// Apply simulation, then extract from the single InvokeHostFunction op
271+
const assembled = rpc.assembleTransaction(baseTx, sim).build();
272+
const op = assembled.operations[0]; // Operation.InvokeHostFunction
273+
274+
const funcXdr = op.func.toXDR("base64");
275+
const authXdrs = (op.auth ?? []).map(a => a.toXDR("base64"));
276+
```
277+
278+
## How It Works
279+
280+
1. **Request Validation**: Validates input parameters and extracts Soroban data
281+
2. **Sequence Account Pool**: Acquires an available sequence account
282+
3. **Auth Checking**: Validates authorization entries
283+
4. **Simulation** (if enabled): Simulates transaction and rebuilds with proper resources
284+
5. **Fee Bumping**: Fund account wraps transaction with fee bump
285+
6. **Submission**: Sends to Stellar network
286+
287+
## Troubleshooting
288+
289+
### Common issues
290+
291+
- **Plugin not found**: Verify the plugin `id` and `path` in `examples/launchtube-plugin-example/config/config.json`.
292+
- **Missing LaunchTube config**: Ensure `examples/launchtube-plugin-example/launchtube/config.json` exists and is correctly filled.
293+
- **API authentication**: Ensure the `Authorization` header is present and the `API_KEY` is set in `.env`.
294+
- **Webhook not received**: Ensure the `notifications[0].url` is set to a reachable URL.
295+
296+
### View logs
297+
298+
```bash
299+
docker compose -f examples/launchtube-plugin-example/docker-compose.yaml logs -f relayer
300+
```
301+
302+
## Docker notes
303+
304+
This compose file mounts:
305+
306+
```yaml
307+
volumes:
308+
- ./config:/app/config/
309+
- ../../config/networks:/app/config/networks
310+
- ./launchtube:/app/plugins/launchtube
311+
```
312+
313+
The container image already includes the relayer and plugin runtime. You only need to mount your config and the LaunchTube plugin wrapper.
314+
315+
## Learn more
316+
317+
- LaunchTube plugin GitHub: `https://github.com/OpenZeppelin/relayer-plugin-launchtube`
318+
- LaunchTube on npm: `https://www.npmjs.com/package/@openzeppelin/relayer-plugin-launchtube`
319+
- OpenZeppelin Relayer docs: `https://docs.openzeppelin.com/relayer`

0 commit comments

Comments
 (0)