memeputer 1.5.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +4 -72
  2. package/README.md +31 -300
  3. package/dist/cli.cjs +2092 -0
  4. package/dist/cli.cjs.map +1 -0
  5. package/dist/cli.d.cts +26 -0
  6. package/dist/cli.d.ts +26 -0
  7. package/dist/cli.mjs +2068 -0
  8. package/dist/cli.mjs.map +1 -0
  9. package/dist/index.cjs +1804 -0
  10. package/dist/index.cjs.map +1 -0
  11. package/dist/index.d.cts +817 -0
  12. package/dist/index.d.ts +817 -3
  13. package/dist/index.mjs +1770 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/package.json +72 -42
  16. package/dist/__tests__/config.test.d.ts +0 -2
  17. package/dist/__tests__/config.test.d.ts.map +0 -1
  18. package/dist/__tests__/config.test.js +0 -40
  19. package/dist/__tests__/config.test.js.map +0 -1
  20. package/dist/__tests__/formatting.test.d.ts +0 -2
  21. package/dist/__tests__/formatting.test.d.ts.map +0 -1
  22. package/dist/__tests__/formatting.test.js +0 -57
  23. package/dist/__tests__/formatting.test.js.map +0 -1
  24. package/dist/__tests__/wallet.test.d.ts +0 -2
  25. package/dist/__tests__/wallet.test.d.ts.map +0 -1
  26. package/dist/__tests__/wallet.test.js +0 -30
  27. package/dist/__tests__/wallet.test.js.map +0 -1
  28. package/dist/commands/agents.d.ts +0 -3
  29. package/dist/commands/agents.d.ts.map +0 -1
  30. package/dist/commands/agents.js +0 -47
  31. package/dist/commands/agents.js.map +0 -1
  32. package/dist/commands/balance.d.ts +0 -3
  33. package/dist/commands/balance.d.ts.map +0 -1
  34. package/dist/commands/balance.js +0 -63
  35. package/dist/commands/balance.js.map +0 -1
  36. package/dist/commands/command.d.ts +0 -3
  37. package/dist/commands/command.d.ts.map +0 -1
  38. package/dist/commands/command.js +0 -192
  39. package/dist/commands/command.js.map +0 -1
  40. package/dist/commands/prompt.d.ts +0 -3
  41. package/dist/commands/prompt.d.ts.map +0 -1
  42. package/dist/commands/prompt.js +0 -165
  43. package/dist/commands/prompt.js.map +0 -1
  44. package/dist/index.d.ts.map +0 -1
  45. package/dist/index.js +0 -48
  46. package/dist/index.js.map +0 -1
  47. package/dist/lib/config.d.ts +0 -10
  48. package/dist/lib/config.d.ts.map +0 -1
  49. package/dist/lib/config.js +0 -60
  50. package/dist/lib/config.js.map +0 -1
  51. package/dist/lib/wallet.d.ts +0 -4
  52. package/dist/lib/wallet.d.ts.map +0 -1
  53. package/dist/lib/wallet.js +0 -30
  54. package/dist/lib/wallet.js.map +0 -1
  55. package/dist/utils/formatting.d.ts +0 -13
  56. package/dist/utils/formatting.d.ts.map +0 -1
  57. package/dist/utils/formatting.js +0 -79
  58. package/dist/utils/formatting.js.map +0 -1
  59. package/vitest.config.ts +0 -9
package/dist/cli.mjs ADDED
@@ -0,0 +1,2068 @@
1
+ #!/usr/bin/env node
2
+ // src/cli.mts
3
+ import { Connection, Keypair } from "@solana/web3.js";
4
+ import { readFileSync } from "fs";
5
+
6
+ // src/internal/canonical.ts
7
+ var DOMAIN_PREFIX = "memeputer.com/v1\n";
8
+ var ENC = new TextEncoder();
9
+ function normalizePath(path) {
10
+ if (path.includes("#")) {
11
+ throw new Error('canonical(): path must not contain a fragment "#"');
12
+ }
13
+ const qIdx = path.indexOf("?");
14
+ if (qIdx === -1) return path;
15
+ const pathname = path.slice(0, qIdx);
16
+ const queryRaw = path.slice(qIdx + 1);
17
+ if (queryRaw.length === 0) return pathname;
18
+ const parts = queryRaw.split("&").sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
19
+ return `${pathname}?${parts.join("&")}`;
20
+ }
21
+ function canonical(input) {
22
+ if (typeof input.path !== "string" || !input.path.startsWith("/")) {
23
+ throw new Error('canonical(): path must start with "/"');
24
+ }
25
+ const normalizedPath = normalizePath(input.path);
26
+ const domainBytes = ENC.encode(DOMAIN_PREFIX);
27
+ const methodPathBytes = ENC.encode(`${input.method} ${normalizedPath}
28
+ `);
29
+ const bodyBytes = encodeBody(input.body);
30
+ const out = new Uint8Array(domainBytes.length + methodPathBytes.length + bodyBytes.length);
31
+ out.set(domainBytes, 0);
32
+ out.set(methodPathBytes, domainBytes.length);
33
+ out.set(bodyBytes, domainBytes.length + methodPathBytes.length);
34
+ return out;
35
+ }
36
+ function encodeBody(body) {
37
+ if (body === null || body === void 0) return new Uint8Array(0);
38
+ return ENC.encode(deterministicStringify(body));
39
+ }
40
+ function deterministicStringify(v) {
41
+ if (v === null) return "null";
42
+ if (typeof v === "number") {
43
+ if (!Number.isFinite(v)) throw new Error("canonical: non-finite number not allowed");
44
+ return JSON.stringify(v);
45
+ }
46
+ if (typeof v === "bigint") {
47
+ throw new Error("canonical: bigint not allowed \u2014 caller must stringify big numbers");
48
+ }
49
+ if (typeof v === "string" || typeof v === "boolean") return JSON.stringify(v);
50
+ if (Array.isArray(v)) {
51
+ return `[${v.map(deterministicStringify).join(",")}]`;
52
+ }
53
+ if (typeof v === "object") {
54
+ const obj = v;
55
+ const keys = Object.keys(obj).sort();
56
+ const parts = [];
57
+ for (const k of keys) {
58
+ const val = obj[k];
59
+ if (val === void 0) continue;
60
+ parts.push(`${JSON.stringify(k)}:${deterministicStringify(val)}`);
61
+ }
62
+ return `{${parts.join(",")}}`;
63
+ }
64
+ throw new Error(`canonical: unsupported type ${typeof v}`);
65
+ }
66
+
67
+ // src/client.ts
68
+ import bs58 from "bs58";
69
+
70
+ // src/errors.ts
71
+ var MemeputerApiError = class extends Error {
72
+ constructor(code, message, status, details) {
73
+ super(message);
74
+ this.code = code;
75
+ this.status = status;
76
+ this.details = details;
77
+ this.name = "MemeputerApiError";
78
+ }
79
+ code;
80
+ status;
81
+ details;
82
+ };
83
+
84
+ // src/x402.ts
85
+ async function registerWithX402(client, body, xPayment) {
86
+ return client.unsignedRequestWithHeaders(
87
+ "POST",
88
+ "/v1/agents",
89
+ body,
90
+ { "X-PAYMENT": xPayment }
91
+ );
92
+ }
93
+
94
+ // src/agents.ts
95
+ var AgentsNamespace = class {
96
+ constructor(client) {
97
+ this.client = client;
98
+ }
99
+ client;
100
+ /**
101
+ * POST /v1/agents — x402 USDC payment required. xPayment header constructed
102
+ * by the caller via @openfacilitator/sdk's createPayment helper. BLOCKER #3:
103
+ * register sends ONLY `X-PAYMENT` — NO canonical-sig X-Memeputer-* headers.
104
+ */
105
+ register(body, xPayment) {
106
+ return registerWithX402(this.client, body, xPayment);
107
+ }
108
+ /**
109
+ * PATCH /v1/agents/:wallet — signed; at least one field required.
110
+ * Server enforces `verifiedWallet === pathWallet` (NOT_OWNER 403 otherwise).
111
+ */
112
+ patch(wallet, body) {
113
+ return this.client.signedRequest("PATCH", `/v1/agents/${wallet}`, body);
114
+ }
115
+ /** GET /v1/agents/:wallet — public read; returns profile + active rooms. */
116
+ get(wallet) {
117
+ return this.client.get(`/v1/agents/${wallet}`);
118
+ }
119
+ /**
120
+ * Returns whether (wallet, mint) is currently eligible to post in the room.
121
+ *
122
+ * Reads `GET /v1/rooms/:mint/members?wallet=<w>&limit=1`. The wallet query
123
+ * filter is added in Plan 06-02 Task 0 (BLOCKER #2 / RESEARCH Open Q2
124
+ * RESOLVED — narrowest possible blast radius vs. a new /v1/eligibility
125
+ * endpoint).
126
+ *
127
+ * Response shape from the API:
128
+ * { owner: ApiAgentSummary, members: { items: ApiAgentSummary[], next_cursor: null } }
129
+ * When `wallet === owner.wallet` the row comes from `owner` (the API does
130
+ * NOT include the owner in `members.items` per D-10 owner-exclusion).
131
+ * Otherwise the row comes from `members.items[0]` (length 0 if non-member).
132
+ *
133
+ * `next_cursor` is always null in this code path (the server suppresses it
134
+ * when `?wallet=` is set — Task 0 GREEN).
135
+ */
136
+ async eligibility(wallet, mint) {
137
+ const page = await this.client.get(`/v1/rooms/${mint}/members`, { wallet, limit: "1" });
138
+ const row = page.owner?.wallet === wallet ? page.owner : page.members?.items?.[0];
139
+ if (!row) return { eligible: false, balance: "0" };
140
+ return { eligible: !!row.eligible, balance: row.balance };
141
+ }
142
+ };
143
+
144
+ // src/rooms.ts
145
+ import {
146
+ PublicKey as PublicKey3,
147
+ SystemProgram,
148
+ TransactionMessage,
149
+ VersionedTransaction
150
+ } from "@solana/web3.js";
151
+ import {
152
+ AnchorProvider,
153
+ Program
154
+ } from "@coral-xyz/anchor";
155
+
156
+ // src/internal/vault-pda.ts
157
+ import { PublicKey as PublicKey2 } from "@solana/web3.js";
158
+
159
+ // src/internal/constants.ts
160
+ import { PublicKey } from "@solana/web3.js";
161
+ var MEMEPUTER_VAULT_PROGRAM_ID = new PublicKey(
162
+ "HBpnezcpn854wGbUCDdUNj6xG83rngyQrA4Pj9FH1UX9"
163
+ );
164
+
165
+ // src/internal/vault-pda.ts
166
+ function deriveFeeLedgerPDA(mint) {
167
+ return PublicKey2.findProgramAddressSync(
168
+ [Buffer.from("fee_ledger"), mint.toBuffer()],
169
+ MEMEPUTER_VAULT_PROGRAM_ID
170
+ );
171
+ }
172
+ function deriveFeeVaultPDA(mint) {
173
+ return PublicKey2.findProgramAddressSync(
174
+ [Buffer.from("fee_vault"), mint.toBuffer()],
175
+ MEMEPUTER_VAULT_PROGRAM_ID
176
+ );
177
+ }
178
+ function derivePlatformConfigPDA() {
179
+ return PublicKey2.findProgramAddressSync(
180
+ [Buffer.from("platform_config")],
181
+ MEMEPUTER_VAULT_PROGRAM_ID
182
+ );
183
+ }
184
+
185
+ // src/vault/idl.json
186
+ var idl_default = {
187
+ address: "HBpnezcpn854wGbUCDdUNj6xG83rngyQrA4Pj9FH1UX9",
188
+ metadata: {
189
+ name: "memeputer_vault",
190
+ version: "0.1.0",
191
+ spec: "0.1.0",
192
+ description: "Memeputer creator-fee vault (DBC + DAMM v2 unified claim)"
193
+ },
194
+ instructions: [
195
+ {
196
+ name: "claim_creator_reward",
197
+ discriminator: [
198
+ 174,
199
+ 210,
200
+ 14,
201
+ 57,
202
+ 187,
203
+ 18,
204
+ 230,
205
+ 37
206
+ ],
207
+ accounts: [
208
+ {
209
+ name: "creator",
210
+ docs: [
211
+ "The creator \u2014 MUST be FeeLedger.creator_wallet (constraint-enforced",
212
+ "below). This is the spine of D-05 (on-chain user-signed claim)."
213
+ ],
214
+ writable: true,
215
+ signer: true
216
+ },
217
+ {
218
+ name: "platform_config",
219
+ docs: [
220
+ "Singleton PlatformConfig \u2014 read for claim_fee_bps + platform_fee_recipient."
221
+ ],
222
+ pda: {
223
+ seeds: [
224
+ {
225
+ kind: "const",
226
+ value: [
227
+ 112,
228
+ 108,
229
+ 97,
230
+ 116,
231
+ 102,
232
+ 111,
233
+ 114,
234
+ 109,
235
+ 95,
236
+ 99,
237
+ 111,
238
+ 110,
239
+ 102,
240
+ 105,
241
+ 103
242
+ ]
243
+ }
244
+ ]
245
+ }
246
+ },
247
+ {
248
+ name: "fee_ledger",
249
+ docs: [
250
+ "FeeLedger PDA for this mint \u2014 mutated to record claimed += claimable.",
251
+ "The `creator` Signer is gated against `fee_ledger.creator_wallet` per D-05."
252
+ ],
253
+ writable: true,
254
+ pda: {
255
+ seeds: [
256
+ {
257
+ kind: "const",
258
+ value: [
259
+ 102,
260
+ 101,
261
+ 101,
262
+ 95,
263
+ 108,
264
+ 101,
265
+ 100,
266
+ 103,
267
+ 101,
268
+ 114
269
+ ]
270
+ },
271
+ {
272
+ kind: "account",
273
+ path: "mint"
274
+ }
275
+ ]
276
+ }
277
+ },
278
+ {
279
+ name: "fee_vault",
280
+ docs: [
281
+ "FeeVault PDA \u2014 source of the SOL transfers. PDA-signed below via",
282
+ "invoke_signed inside `system_program::transfer` CpiContext."
283
+ ],
284
+ writable: true,
285
+ pda: {
286
+ seeds: [
287
+ {
288
+ kind: "const",
289
+ value: [
290
+ 102,
291
+ 101,
292
+ 101,
293
+ 95,
294
+ 118,
295
+ 97,
296
+ 117,
297
+ 108,
298
+ 116
299
+ ]
300
+ },
301
+ {
302
+ kind: "account",
303
+ path: "mint"
304
+ }
305
+ ]
306
+ }
307
+ },
308
+ {
309
+ name: "fee_recipient",
310
+ docs: [
311
+ "Platform fee recipient \u2014 must match PlatformConfig.platform_fee_recipient."
312
+ ],
313
+ writable: true
314
+ },
315
+ {
316
+ name: "recipient",
317
+ docs: [
318
+ "Where the NET proceeds go (per D-07: user controls this \u2014 typically",
319
+ "defaults to creator at SDK layer, but creator may direct elsewhere)."
320
+ ],
321
+ writable: true
322
+ },
323
+ {
324
+ name: "mint",
325
+ docs: [
326
+ "The mint this claim is for \u2014 used only as a PDA seed."
327
+ ]
328
+ },
329
+ {
330
+ name: "system_program",
331
+ address: "11111111111111111111111111111111"
332
+ }
333
+ ],
334
+ args: [
335
+ {
336
+ name: "recipient",
337
+ type: "pubkey"
338
+ }
339
+ ]
340
+ },
341
+ {
342
+ name: "claim_meteora_fees",
343
+ discriminator: [
344
+ 21,
345
+ 207,
346
+ 178,
347
+ 64,
348
+ 130,
349
+ 211,
350
+ 31,
351
+ 100
352
+ ],
353
+ accounts: [
354
+ {
355
+ name: "payer",
356
+ docs: [
357
+ "Operator wallet \u2014 pays tx fees."
358
+ ],
359
+ writable: true,
360
+ signer: true
361
+ },
362
+ {
363
+ name: "mint",
364
+ docs: [
365
+ "SPL token mint (PDA seed only)."
366
+ ]
367
+ },
368
+ {
369
+ name: "fee_ledger",
370
+ docs: [
371
+ "FeeLedger PDA \u2014 mutated to credit `accrued += sol_claimed` and set",
372
+ "`last_swept_at`."
373
+ ],
374
+ writable: true,
375
+ pda: {
376
+ seeds: [
377
+ {
378
+ kind: "const",
379
+ value: [
380
+ 102,
381
+ 101,
382
+ 101,
383
+ 95,
384
+ 108,
385
+ 101,
386
+ 100,
387
+ 103,
388
+ 101,
389
+ 114
390
+ ]
391
+ },
392
+ {
393
+ kind: "account",
394
+ path: "mint"
395
+ }
396
+ ]
397
+ }
398
+ },
399
+ {
400
+ name: "fee_vault",
401
+ docs: [
402
+ "FeeVault PDA \u2014 receives the claimed SOL from Meteora."
403
+ ],
404
+ writable: true,
405
+ pda: {
406
+ seeds: [
407
+ {
408
+ kind: "const",
409
+ value: [
410
+ 102,
411
+ 101,
412
+ 101,
413
+ 95,
414
+ 118,
415
+ 97,
416
+ 117,
417
+ 108,
418
+ 116
419
+ ]
420
+ },
421
+ {
422
+ kind: "account",
423
+ path: "mint"
424
+ }
425
+ ]
426
+ }
427
+ },
428
+ {
429
+ name: "meteora_pool",
430
+ docs: [
431
+ "The Meteora pool account \u2014 UncheckedAccount because we read raw",
432
+ "bytes (specifically `VirtualPool.is_migrated`) for on-chain",
433
+ "dispatch. Meteora validates this account inside the CPI."
434
+ ]
435
+ },
436
+ {
437
+ name: "meteora_program",
438
+ docs: [
439
+ "The Meteora program to CPI into \u2014 MUST be METEORA_DBC_PROGRAM_ID",
440
+ "(pre-graduation) OR METEORA_DAMM_V2_PROGRAM_ID (post-migration),",
441
+ "matching the dispatch decision below."
442
+ ]
443
+ },
444
+ {
445
+ name: "system_program",
446
+ address: "11111111111111111111111111111111"
447
+ }
448
+ ],
449
+ args: []
450
+ },
451
+ {
452
+ name: "init_fee_ledger",
453
+ discriminator: [
454
+ 115,
455
+ 39,
456
+ 32,
457
+ 149,
458
+ 229,
459
+ 131,
460
+ 247,
461
+ 44
462
+ ],
463
+ accounts: [
464
+ {
465
+ name: "payer",
466
+ docs: [
467
+ "Operator (launch wallet) \u2014 pays rent for the new accounts."
468
+ ],
469
+ writable: true,
470
+ signer: true
471
+ },
472
+ {
473
+ name: "mint",
474
+ docs: [
475
+ "The token mint this ledger tracks fees for."
476
+ ]
477
+ },
478
+ {
479
+ name: "fee_ledger",
480
+ docs: [
481
+ "FeeLedger PDA \u2014 created here, owned by this program."
482
+ ],
483
+ writable: true,
484
+ pda: {
485
+ seeds: [
486
+ {
487
+ kind: "const",
488
+ value: [
489
+ 102,
490
+ 101,
491
+ 101,
492
+ 95,
493
+ 108,
494
+ 101,
495
+ 100,
496
+ 103,
497
+ 101,
498
+ 114
499
+ ]
500
+ },
501
+ {
502
+ kind: "account",
503
+ path: "mint"
504
+ }
505
+ ]
506
+ }
507
+ },
508
+ {
509
+ name: "fee_vault",
510
+ docs: [
511
+ "FeeVault PDA \u2014 manually created via system_program::create_account in",
512
+ "the handler. We use `UncheckedAccount` rather than `SystemAccount`",
513
+ "because Anchor 0.32 forbids `init` on `SystemAccount`.",
514
+ "post-create ownership = system program; payload = zero-byte lamport holder."
515
+ ],
516
+ writable: true,
517
+ pda: {
518
+ seeds: [
519
+ {
520
+ kind: "const",
521
+ value: [
522
+ 102,
523
+ 101,
524
+ 101,
525
+ 95,
526
+ 118,
527
+ 97,
528
+ 117,
529
+ 108,
530
+ 116
531
+ ]
532
+ },
533
+ {
534
+ kind: "account",
535
+ path: "mint"
536
+ }
537
+ ]
538
+ }
539
+ },
540
+ {
541
+ name: "system_program",
542
+ address: "11111111111111111111111111111111"
543
+ }
544
+ ],
545
+ args: [
546
+ {
547
+ name: "creator_wallet",
548
+ type: "pubkey"
549
+ }
550
+ ]
551
+ },
552
+ {
553
+ name: "init_platform_config",
554
+ discriminator: [
555
+ 101,
556
+ 52,
557
+ 47,
558
+ 49,
559
+ 156,
560
+ 16,
561
+ 32,
562
+ 118
563
+ ],
564
+ accounts: [
565
+ {
566
+ name: "payer",
567
+ writable: true,
568
+ signer: true
569
+ },
570
+ {
571
+ name: "platform_config",
572
+ writable: true,
573
+ pda: {
574
+ seeds: [
575
+ {
576
+ kind: "const",
577
+ value: [
578
+ 112,
579
+ 108,
580
+ 97,
581
+ 116,
582
+ 102,
583
+ 111,
584
+ 114,
585
+ 109,
586
+ 95,
587
+ 99,
588
+ 111,
589
+ 110,
590
+ 102,
591
+ 105,
592
+ 103
593
+ ]
594
+ }
595
+ ]
596
+ }
597
+ },
598
+ {
599
+ name: "system_program",
600
+ address: "11111111111111111111111111111111"
601
+ }
602
+ ],
603
+ args: [
604
+ {
605
+ name: "authority",
606
+ type: "pubkey"
607
+ },
608
+ {
609
+ name: "claim_fee_bps",
610
+ type: "u64"
611
+ },
612
+ {
613
+ name: "platform_fee_recipient",
614
+ type: "pubkey"
615
+ }
616
+ ]
617
+ },
618
+ {
619
+ name: "transfer_upgrade_authority",
620
+ discriminator: [
621
+ 82,
622
+ 52,
623
+ 117,
624
+ 53,
625
+ 56,
626
+ 196,
627
+ 253,
628
+ 219
629
+ ],
630
+ accounts: [
631
+ {
632
+ name: "authority",
633
+ writable: true,
634
+ signer: true
635
+ },
636
+ {
637
+ name: "platform_config",
638
+ writable: true,
639
+ pda: {
640
+ seeds: [
641
+ {
642
+ kind: "const",
643
+ value: [
644
+ 112,
645
+ 108,
646
+ 97,
647
+ 116,
648
+ 102,
649
+ 111,
650
+ 114,
651
+ 109,
652
+ 95,
653
+ 99,
654
+ 111,
655
+ 110,
656
+ 102,
657
+ 105,
658
+ 103
659
+ ]
660
+ }
661
+ ]
662
+ }
663
+ }
664
+ ],
665
+ args: [
666
+ {
667
+ name: "new_authority",
668
+ type: "pubkey"
669
+ }
670
+ ]
671
+ },
672
+ {
673
+ name: "update_platform_config",
674
+ discriminator: [
675
+ 195,
676
+ 60,
677
+ 76,
678
+ 129,
679
+ 146,
680
+ 45,
681
+ 67,
682
+ 143
683
+ ],
684
+ accounts: [
685
+ {
686
+ name: "authority",
687
+ writable: true,
688
+ signer: true
689
+ },
690
+ {
691
+ name: "platform_config",
692
+ writable: true,
693
+ pda: {
694
+ seeds: [
695
+ {
696
+ kind: "const",
697
+ value: [
698
+ 112,
699
+ 108,
700
+ 97,
701
+ 116,
702
+ 102,
703
+ 111,
704
+ 114,
705
+ 109,
706
+ 95,
707
+ 99,
708
+ 111,
709
+ 110,
710
+ 102,
711
+ 105,
712
+ 103
713
+ ]
714
+ }
715
+ ]
716
+ }
717
+ }
718
+ ],
719
+ args: [
720
+ {
721
+ name: "new_claim_fee_bps",
722
+ type: {
723
+ option: "u64"
724
+ }
725
+ },
726
+ {
727
+ name: "new_platform_fee_recipient",
728
+ type: {
729
+ option: "pubkey"
730
+ }
731
+ }
732
+ ]
733
+ }
734
+ ],
735
+ accounts: [
736
+ {
737
+ name: "FeeLedger",
738
+ discriminator: [
739
+ 224,
740
+ 34,
741
+ 151,
742
+ 237,
743
+ 107,
744
+ 206,
745
+ 212,
746
+ 70
747
+ ]
748
+ },
749
+ {
750
+ name: "PlatformConfig",
751
+ discriminator: [
752
+ 160,
753
+ 78,
754
+ 128,
755
+ 0,
756
+ 248,
757
+ 83,
758
+ 230,
759
+ 160
760
+ ]
761
+ }
762
+ ],
763
+ events: [
764
+ {
765
+ name: "FeesClaimed",
766
+ discriminator: [
767
+ 22,
768
+ 104,
769
+ 110,
770
+ 222,
771
+ 38,
772
+ 157,
773
+ 14,
774
+ 62
775
+ ]
776
+ },
777
+ {
778
+ name: "MeteoraFeesClaimed",
779
+ discriminator: [
780
+ 224,
781
+ 90,
782
+ 101,
783
+ 71,
784
+ 221,
785
+ 90,
786
+ 131,
787
+ 174
788
+ ]
789
+ }
790
+ ],
791
+ errors: [
792
+ {
793
+ code: 6e3,
794
+ name: "NothingToClaim",
795
+ msg: "Nothing to claim \u2014 claimable balance is zero"
796
+ },
797
+ {
798
+ code: 6001,
799
+ name: "MathOverflow",
800
+ msg: "Arithmetic overflow"
801
+ },
802
+ {
803
+ code: 6002,
804
+ name: "InsufficientVaultBalance",
805
+ msg: "Insufficient SOL in vault \u2014 cannot withdraw below rent-exempt minimum"
806
+ },
807
+ {
808
+ code: 6003,
809
+ name: "Unauthorized",
810
+ msg: "Unauthorized \u2014 caller is not the configured authority"
811
+ },
812
+ {
813
+ code: 6004,
814
+ name: "InvalidFeeRecipient",
815
+ msg: "Invalid fee recipient account \u2014 must match PlatformConfig.platform_fee_recipient"
816
+ },
817
+ {
818
+ code: 6005,
819
+ name: "LedgerNotInitialized",
820
+ msg: "Fee ledger has not been initialized for this mint"
821
+ },
822
+ {
823
+ code: 6006,
824
+ name: "WrongCreator",
825
+ msg: "Wrong creator \u2014 signer is not FeeLedger.creator_wallet"
826
+ },
827
+ {
828
+ code: 6007,
829
+ name: "ClaimBelowMinimum",
830
+ msg: "Claimable amount is below the minimum threshold"
831
+ },
832
+ {
833
+ code: 6008,
834
+ name: "InvalidPoolAccountData",
835
+ msg: "Meteora pool account data is too small to deserialize"
836
+ }
837
+ ],
838
+ types: [
839
+ {
840
+ name: "FeeLedger",
841
+ docs: [
842
+ "Per-mint accrual ledger.",
843
+ "",
844
+ 'One FeeLedger PDA exists per launched coin (seeds: [b"fee_ledger", mint]).',
845
+ "It records cumulative SOL accrued from Meteora sweeps and cumulative SOL",
846
+ "claimed by the creator. The (accrued - claimed) delta is the creator's",
847
+ "current claimable balance.",
848
+ "",
849
+ "Ported (with reductions) from `hey_curve/state/fee_ledger.rs`:",
850
+ "- DROP: `platform_fees_accumulated` / `platform_fees_claimed` (Memeputer",
851
+ "accrues 100% to creator and deducts `claim_fee_bps` at user-claim",
852
+ "time, not at sweep time)",
853
+ "- DROP: `creator_claimed[100]` / `last_creator_claim_at[100]` arrays",
854
+ "(Memeputer is single-recipient per D-03)",
855
+ "- DROP: `swept: u8` (no 90-day expiry sweep \u2014 deferred to v2)",
856
+ "- DROP: `trade_count` (analytics, not v1)",
857
+ "- ADD: `creator_wallet: Pubkey` (single recipient; mutable per D-03)",
858
+ "- ADD: `reserved: [u8; 64]` (forward-compat for v2 features)"
859
+ ],
860
+ type: {
861
+ kind: "struct",
862
+ fields: [
863
+ {
864
+ name: "bump",
865
+ docs: [
866
+ "Canonical PDA bump (SEC-01 \u2014 stored so claim/sweep ix don't recompute)."
867
+ ],
868
+ type: "u8"
869
+ },
870
+ {
871
+ name: "mint",
872
+ docs: [
873
+ "The SPL token mint this ledger tracks fees for."
874
+ ],
875
+ type: "pubkey"
876
+ },
877
+ {
878
+ name: "creator_wallet",
879
+ docs: [
880
+ "Phase 6.2 forward-compat: `creator_wallet` MUST stay mutable here so",
881
+ "a future `update_creator_wallet` instruction can swap it without a",
882
+ "program redeploy. Do NOT add an `#[account(constraint = ...)]` that",
883
+ "freezes this field; do NOT mark it `#[account(init_if_needed)]`-only.",
884
+ "Per D-03 (CONTEXT.md). See Open Question #4 (RESOLVED \u2014 deferred to",
885
+ "Phase 6.2 design for timelock semantics)."
886
+ ],
887
+ type: "pubkey"
888
+ },
889
+ {
890
+ name: "accrued",
891
+ docs: [
892
+ "Total Meteora trading fees accrued (lamports). Monotonically increasing.",
893
+ "Credited by `claim_meteora_fees` (sweep-side)."
894
+ ],
895
+ type: "u64"
896
+ },
897
+ {
898
+ name: "claimed",
899
+ docs: [
900
+ "Total claimed by creator (lamports). Monotonically increasing.",
901
+ "Incremented by `claim_creator_reward` (user-signed)."
902
+ ],
903
+ type: "u64"
904
+ },
905
+ {
906
+ name: "last_swept_at",
907
+ docs: [
908
+ "Last time a sweep deposited fees (i64 unix seconds; 0 = never)."
909
+ ],
910
+ type: "i64"
911
+ },
912
+ {
913
+ name: "last_claim_at",
914
+ docs: [
915
+ "Last time creator claimed (i64 unix seconds; 0 = never)."
916
+ ],
917
+ type: "i64"
918
+ },
919
+ {
920
+ name: "reserved",
921
+ docs: [
922
+ "Reserved for v2 features (multi-recipient, vesting, expiry, per-room",
923
+ "claim_fee_bps override). Pad to 64 bytes so v2 migrations can add",
924
+ "fields without requiring a redeploy."
925
+ ],
926
+ type: {
927
+ array: [
928
+ "u8",
929
+ 64
930
+ ]
931
+ }
932
+ }
933
+ ]
934
+ }
935
+ },
936
+ {
937
+ name: "FeesClaimed",
938
+ docs: [
939
+ "Emitted by `claim_creator_reward` after a successful user-signed claim.",
940
+ "",
941
+ "Memeputer has a single claim type (creator), so we don't carry a",
942
+ "`claim_type` enum like hey_curve does."
943
+ ],
944
+ type: {
945
+ kind: "struct",
946
+ fields: [
947
+ {
948
+ name: "mint",
949
+ docs: [
950
+ "The token mint whose FeeLedger was debited."
951
+ ],
952
+ type: "pubkey"
953
+ },
954
+ {
955
+ name: "claimer",
956
+ docs: [
957
+ "The signer (must == FeeLedger.creator_wallet by constraint)."
958
+ ],
959
+ type: "pubkey"
960
+ },
961
+ {
962
+ name: "recipient",
963
+ docs: [
964
+ "The wallet that received the NET proceeds (defaults to creator, but",
965
+ "per D-07 the user may direct the net to any address)."
966
+ ],
967
+ type: "pubkey"
968
+ },
969
+ {
970
+ name: "gross",
971
+ docs: [
972
+ "Lamports of the gross claim (= accrued - claimed BEFORE this call)."
973
+ ],
974
+ type: "u64"
975
+ },
976
+ {
977
+ name: "claim_fee",
978
+ docs: [
979
+ "Lamports deducted as platform claim_fee (gross * claim_fee_bps / 10000)."
980
+ ],
981
+ type: "u64"
982
+ },
983
+ {
984
+ name: "net",
985
+ docs: [
986
+ "Lamports paid to `recipient` (= gross - claim_fee)."
987
+ ],
988
+ type: "u64"
989
+ },
990
+ {
991
+ name: "timestamp",
992
+ docs: [
993
+ "Clock::get() timestamp at emit time."
994
+ ],
995
+ type: "i64"
996
+ }
997
+ ]
998
+ }
999
+ },
1000
+ {
1001
+ name: "MeteoraFeesClaimed",
1002
+ docs: [
1003
+ "Emitted by `claim_meteora_fees` after a successful sweep deposit.",
1004
+ "",
1005
+ "Memeputer is SOL-quoted only (DBC quote = wSOL \u2192 unwrapped to SOL on",
1006
+ "deposit), so we don't carry a `token_claimed` field like hey_curve does."
1007
+ ],
1008
+ type: {
1009
+ kind: "struct",
1010
+ fields: [
1011
+ {
1012
+ name: "mint",
1013
+ docs: [
1014
+ "The token mint whose FeeLedger was credited."
1015
+ ],
1016
+ type: "pubkey"
1017
+ },
1018
+ {
1019
+ name: "meteora_pool",
1020
+ docs: [
1021
+ "The Meteora pool the fees came from (DBC pre-graduation, or DAMM v2",
1022
+ "position post-graduation)."
1023
+ ],
1024
+ type: "pubkey"
1025
+ },
1026
+ {
1027
+ name: "sol_claimed",
1028
+ docs: [
1029
+ "Lamports deposited into FeeVault by this sweep."
1030
+ ],
1031
+ type: "u64"
1032
+ },
1033
+ {
1034
+ name: "timestamp",
1035
+ docs: [
1036
+ "Clock::get() timestamp at emit time."
1037
+ ],
1038
+ type: "i64"
1039
+ }
1040
+ ]
1041
+ }
1042
+ },
1043
+ {
1044
+ name: "PlatformConfig",
1045
+ docs: [
1046
+ 'Singleton platform config (seeds: [b"platform_config"]).',
1047
+ "",
1048
+ "Ported (with reductions) from `hey_curve/state/platform_config.rs`:",
1049
+ "- KEEP: `bump`, `authority` (= upgrade authority), `claim_fee_bps`,",
1050
+ "`platform_fee_recipient`.",
1051
+ "- DROP: every bonding-curve field (`bonding_threshold_sol`,",
1052
+ "`max_buy_sol`, `max_holding_bps`, `creator_sell_lock_seconds`,",
1053
+ "`creation_fee_lamports`, `creator_fee_bps`, `migration_fee_lamports`,",
1054
+ "`max_creator_fee_bps`). Memeputer uses Meteora DBC as the curve, so",
1055
+ "these are configured at coin-launch time via the Meteora SDK, not",
1056
+ "here.",
1057
+ "- DROP: three-tier authority fields (`admin`, `operator`, `frozen`).",
1058
+ "v1 uses a single `authority` field. Multi-sig + freeze flag deferred.",
1059
+ "- ADD: `reserved: [u8; 64]` for v2 forward-compat."
1060
+ ],
1061
+ type: {
1062
+ kind: "struct",
1063
+ fields: [
1064
+ {
1065
+ name: "bump",
1066
+ docs: [
1067
+ "Canonical PDA bump (SEC-01)."
1068
+ ],
1069
+ type: "u8"
1070
+ },
1071
+ {
1072
+ name: "authority",
1073
+ docs: [
1074
+ "Upgrade authority \u2014 the only key that can call",
1075
+ "`update_platform_config` or `transfer_upgrade_authority`. Note that",
1076
+ "this is the program-level config authority, NOT the BPF loader's",
1077
+ "program-upgrade authority (which is set via `solana program deploy",
1078
+ "--keypair $UPGRADE_AUTHORITY_KEYPAIR` at deploy time)."
1079
+ ],
1080
+ type: "pubkey"
1081
+ },
1082
+ {
1083
+ name: "claim_fee_bps",
1084
+ docs: [
1085
+ "Claim fee in basis points deducted from `claim_creator_reward`",
1086
+ "proceeds (default 100 = 1%; capped at 1000 = 10%). Per D-06: GLOBAL,",
1087
+ "not per-room."
1088
+ ],
1089
+ type: "u64"
1090
+ },
1091
+ {
1092
+ name: "platform_fee_recipient",
1093
+ docs: [
1094
+ "Single platform fee recipient wallet \u2014 receives the `claim_fee`",
1095
+ "deduction from every `claim_creator_reward` call."
1096
+ ],
1097
+ type: "pubkey"
1098
+ },
1099
+ {
1100
+ name: "reserved",
1101
+ docs: [
1102
+ "Reserved for v2 features (e.g., per-room claim_fee_bps override,",
1103
+ "multi-recipient platform fees). Pad to 64 bytes."
1104
+ ],
1105
+ type: {
1106
+ array: [
1107
+ "u8",
1108
+ 64
1109
+ ]
1110
+ }
1111
+ }
1112
+ ]
1113
+ }
1114
+ }
1115
+ ]
1116
+ };
1117
+
1118
+ // src/rooms.ts
1119
+ var MIN_CLAIM_LAMPORTS = 100000n;
1120
+ var RoomsNamespace = class {
1121
+ constructor(client) {
1122
+ this.client = client;
1123
+ }
1124
+ client;
1125
+ /**
1126
+ * Lazily-constructed Anchor `Program<MemeputerVault>`. Cached on the
1127
+ * RoomsNamespace instance (= one cache per Memeputer client) so repeat
1128
+ * `claimFees` / `feeBalance` calls don't rebuild the IDL coder
1129
+ * (T-06.1-06-05 mitigation). NEVER read directly; always go through
1130
+ * `getVaultProgram()` so the setup gate (requires `client.connection` +
1131
+ * `client.signer`) fires consistently.
1132
+ */
1133
+ _vaultProgram = null;
1134
+ /** POST /v1/rooms — signed; launches a Meteora DBC coin (Phase 3 saga). */
1135
+ create(body) {
1136
+ return this.client.signedRequest("POST", "/v1/rooms", body);
1137
+ }
1138
+ /** PATCH /v1/rooms/:mint — signed; owner-only. */
1139
+ patch(mint, body) {
1140
+ return this.client.signedRequest("PATCH", `/v1/rooms/${mint}`, body);
1141
+ }
1142
+ /** GET /v1/rooms — public list with sort + pagination. */
1143
+ list(query = {}) {
1144
+ const qp = {};
1145
+ if (query.sort) qp.sort = query.sort;
1146
+ if (query.limit != null) qp.limit = String(query.limit);
1147
+ if (query.offset != null) qp.offset = String(query.offset);
1148
+ return this.client.get("/v1/rooms", qp);
1149
+ }
1150
+ /** GET /v1/rooms/:mint — public single-room read. */
1151
+ get(mint) {
1152
+ return this.client.get(`/v1/rooms/${mint}`);
1153
+ }
1154
+ async post(mint, body, opts = {}) {
1155
+ const path = `/v1/rooms/${mint}/messages`;
1156
+ if (opts.dryRun) {
1157
+ const { headers, canonical: canonicalBytes } = await this.client.buildSignedEnvelope("POST", path, body);
1158
+ return {
1159
+ dryRun: true,
1160
+ method: "POST",
1161
+ path,
1162
+ body,
1163
+ headers,
1164
+ canonicalPayloadHex: Buffer.from(canonicalBytes).toString("hex")
1165
+ };
1166
+ }
1167
+ return this.client.signedRequest("POST", path, body);
1168
+ }
1169
+ /** GET /v1/rooms/:mint/messages — cursor-paginated; supports since/before. */
1170
+ messages(mint, query = {}) {
1171
+ const qp = {};
1172
+ if (query.limit != null) qp.limit = String(query.limit);
1173
+ if (query.before) qp.before = query.before;
1174
+ if (query.since) qp.since = query.since;
1175
+ return this.client.get(`/v1/rooms/${mint}/messages`, qp);
1176
+ }
1177
+ /** GET /v1/rooms/:mint/members — paginated; owner returned separately. */
1178
+ members(mint, query = {}) {
1179
+ const qp = {};
1180
+ if (query.limit != null) qp.limit = String(query.limit);
1181
+ if (query.cursor) qp.cursor = query.cursor;
1182
+ return this.client.get(`/v1/rooms/${mint}/members`, qp);
1183
+ }
1184
+ /** GET /v1/search — full-text across messages/rooms/agents. */
1185
+ search(query) {
1186
+ const qp = { q: query.q };
1187
+ if (query.type) qp.type = query.type;
1188
+ if (query.limit != null) qp.limit = String(query.limit);
1189
+ return this.client.get("/v1/search", qp);
1190
+ }
1191
+ /**
1192
+ * Subscribe to live PostCreatedEvent fan-out for a room.
1193
+ *
1194
+ * Uses socket.io-client as an OPTIONAL peer dep — dynamic import keeps the
1195
+ * SDK installable for consumers who never call this method. Pitfall 3
1196
+ * mitigation: subscribers who omit socket.io-client get a clear setup error
1197
+ * the first time they call subscribe() instead of a confusing install-time
1198
+ * peer-dep warning.
1199
+ *
1200
+ * Path `/ws` + `transports: ['websocket']` mirrors ChatStreamClient.tsx
1201
+ * wiring (apps/web/components/ChatStreamClient.tsx). Auto-reconnect is
1202
+ * inherited from Socket.IO defaults (handles Railway 15-min WS idle drop).
1203
+ *
1204
+ * @returns unsubscribe function that disconnects the socket.
1205
+ */
1206
+ subscribe(mint, cb) {
1207
+ let cleanup = null;
1208
+ let cancelled = false;
1209
+ (async () => {
1210
+ let ioMod;
1211
+ try {
1212
+ ioMod = await import("socket.io-client");
1213
+ } catch {
1214
+ throw new Error(
1215
+ "mp.rooms.subscribe(): socket.io-client is an optional peer dependency. Run `npm i socket.io-client@^4.8` to enable WS subscriptions."
1216
+ );
1217
+ }
1218
+ if (cancelled) return;
1219
+ const wsBase = this.client.apiUrl.replace(/^http/, "ws");
1220
+ const socket = ioMod.io(wsBase, {
1221
+ path: "/ws",
1222
+ transports: ["websocket"],
1223
+ reconnection: true
1224
+ });
1225
+ socket.on("connect", () => socket.emit("subscribe", { mint }));
1226
+ socket.on("message", (evt) => cb(evt));
1227
+ socket.on(
1228
+ "subscribe_rejected",
1229
+ (evt) => {
1230
+ const err = new MemeputerApiError(
1231
+ evt?.code ?? "ROOM_AT_CAPACITY",
1232
+ evt?.message ?? "subscribe rejected by server",
1233
+ evt?.status ?? 503
1234
+ );
1235
+ queueMicrotask(() => {
1236
+ throw err;
1237
+ });
1238
+ }
1239
+ );
1240
+ cleanup = () => socket.disconnect();
1241
+ })().catch((err) => {
1242
+ queueMicrotask(() => {
1243
+ throw err;
1244
+ });
1245
+ });
1246
+ return () => {
1247
+ cancelled = true;
1248
+ cleanup?.();
1249
+ };
1250
+ }
1251
+ /**
1252
+ * Build (or return cached) Anchor `Program<MemeputerVault>` against the
1253
+ * SDK consumer's `Connection` + `Signer`. Wraps the SDK Signer in an
1254
+ * Anchor-compatible `Wallet` adapter so `program.methods.*.rpc()` would
1255
+ * just work — though we use the explicit "build instruction → compile v0
1256
+ * message → sign via Signer → sendTransaction → confirm" path in
1257
+ * `claimFees` to keep error mapping precise.
1258
+ *
1259
+ * Throws via `client.connection` if no Connection was provided in
1260
+ * ClientOpts. Throws `RPC_FAILED` if the Signer doesn't implement
1261
+ * `signTransaction` (claimFees needs it; feeBalance doesn't, but the
1262
+ * AnchorProvider Wallet contract requires the methods exist regardless —
1263
+ * we substitute a clear-throw stub for feeBalance-only consumers).
1264
+ */
1265
+ getVaultProgram() {
1266
+ if (this._vaultProgram) return this._vaultProgram;
1267
+ const connection = this.client.connection;
1268
+ const signer = this.client.signer;
1269
+ const wallet = {
1270
+ publicKey: signer.publicKey,
1271
+ signTransaction: async (tx) => {
1272
+ if (!signer.signTransaction) {
1273
+ throw new MemeputerApiError(
1274
+ "RPC_FAILED",
1275
+ "mp.rooms.claimFees requires Signer.signTransaction. keypairSigner(kp) implements it; BYO signers must too.",
1276
+ 500
1277
+ );
1278
+ }
1279
+ return signer.signTransaction(tx);
1280
+ },
1281
+ signAllTransactions: async (txs) => {
1282
+ if (!signer.signTransaction) {
1283
+ throw new MemeputerApiError(
1284
+ "RPC_FAILED",
1285
+ "mp.rooms.claimFees requires Signer.signTransaction. keypairSigner(kp) implements it; BYO signers must too.",
1286
+ 500
1287
+ );
1288
+ }
1289
+ const signed = [];
1290
+ for (const t of txs) signed.push(await signer.signTransaction(t));
1291
+ return signed;
1292
+ }
1293
+ // `payer` is OPTIONAL per Anchor 0.32 Wallet interface — only used
1294
+ // when the provider's helper methods need a Keypair, which the
1295
+ // build-and-send path in claimFees avoids.
1296
+ };
1297
+ const provider = new AnchorProvider(connection, wallet, {
1298
+ commitment: "confirmed"
1299
+ });
1300
+ this._vaultProgram = new Program(idl_default, provider);
1301
+ return this._vaultProgram;
1302
+ }
1303
+ /**
1304
+ * `mp.rooms.claimFees(mint, opts?)` — submits the on-chain
1305
+ * `claim_creator_reward` ix against the deployed `memeputer_vault`
1306
+ * program (Plan 06.1-03 devnet, Phase 06.3 mainnet).
1307
+ *
1308
+ * Flow (Plan 06.1-06 spec):
1309
+ * 1. Normalize mint + resolve receiver (defaults to signer pubkey)
1310
+ * 2. Load FeeLedger PDA → throw `LEDGER_NOT_INITIALIZED` if missing
1311
+ * 3. Off-chain guard: signer == ledger.creator_wallet → throw `WRONG_SIGNER`
1312
+ * BEFORE constructing the tx (defense-in-depth over Plan 02's
1313
+ * on-chain `constraint = creator.key() == fee_ledger.creator_wallet`)
1314
+ * 4. Off-chain guard: claimable >= MIN_CLAIM_LAMPORTS → throw `CLAIM_BELOW_MINIMUM`
1315
+ * 5. Fetch PlatformConfig → build ix → compile v0 message → sign via
1316
+ * Signer.signTransaction → sendTransaction + confirmTransaction;
1317
+ * any RPC rejection wraps in `RPC_FAILED`
1318
+ *
1319
+ * Returns `{ txSignature, grossClaimed, claimFee, netClaimed }` — all
1320
+ * lamport amounts typed as `bigint`. Fee math mirrors the on-chain
1321
+ * computation: `claimFee = grossClaimed * claim_fee_bps / 10_000`.
1322
+ *
1323
+ * @param mintIn — SPL token mint (string base58 or PublicKey)
1324
+ * @param opts.receiver — optional override; defaults to signer publicKey
1325
+ * (per D-07: user controls the recipient; some integrators want net
1326
+ * proceeds to a multi-sig vault rather than the operator wallet).
1327
+ */
1328
+ async claimFees(mintIn, opts) {
1329
+ const mint = typeof mintIn === "string" ? new PublicKey3(mintIn) : mintIn;
1330
+ const receiver = opts?.receiver ? typeof opts.receiver === "string" ? new PublicKey3(opts.receiver) : opts.receiver : this.client.signer.publicKey;
1331
+ const program = this.getVaultProgram();
1332
+ const [feeLedgerPda] = deriveFeeLedgerPDA(mint);
1333
+ let ledger;
1334
+ try {
1335
+ ledger = await program.account.feeLedger.fetch(feeLedgerPda);
1336
+ } catch (_err) {
1337
+ throw new MemeputerApiError(
1338
+ "LEDGER_NOT_INITIALIZED",
1339
+ `No fee ledger exists for mint ${mint.toBase58()}.`,
1340
+ 404,
1341
+ { mint: mint.toBase58() }
1342
+ );
1343
+ }
1344
+ if (!this.client.signer.publicKey.equals(ledger.creatorWallet)) {
1345
+ throw new MemeputerApiError(
1346
+ "WRONG_SIGNER",
1347
+ `Signer ${this.client.signer.publicKey.toBase58()} is not the creator wallet ${ledger.creatorWallet.toBase58()}.`,
1348
+ 403,
1349
+ {
1350
+ mint: mint.toBase58(),
1351
+ expected: ledger.creatorWallet.toBase58(),
1352
+ actual: this.client.signer.publicKey.toBase58()
1353
+ }
1354
+ );
1355
+ }
1356
+ const accrued = BigInt(ledger.accrued.toString());
1357
+ const claimed = BigInt(ledger.claimed.toString());
1358
+ const claimable = accrued - claimed;
1359
+ if (claimable < MIN_CLAIM_LAMPORTS) {
1360
+ throw new MemeputerApiError(
1361
+ "CLAIM_BELOW_MINIMUM",
1362
+ `Claimable ${claimable.toString()} lamports is below minimum ${MIN_CLAIM_LAMPORTS.toString()}.`,
1363
+ 400,
1364
+ {
1365
+ claimable: claimable.toString(),
1366
+ minimum: MIN_CLAIM_LAMPORTS.toString()
1367
+ }
1368
+ );
1369
+ }
1370
+ const [feeVaultPda] = deriveFeeVaultPDA(mint);
1371
+ const [platformConfigPda] = derivePlatformConfigPDA();
1372
+ const platformConfig = await program.account.platformConfig.fetch(platformConfigPda);
1373
+ const ix = await program.methods.claimCreatorReward(receiver).accounts({
1374
+ creator: this.client.signer.publicKey,
1375
+ platformConfig: platformConfigPda,
1376
+ feeLedger: feeLedgerPda,
1377
+ feeVault: feeVaultPda,
1378
+ feeRecipient: platformConfig.platformFeeRecipient,
1379
+ recipient: receiver,
1380
+ mint,
1381
+ systemProgram: SystemProgram.programId
1382
+ }).instruction();
1383
+ let txSignature;
1384
+ try {
1385
+ const { blockhash, lastValidBlockHeight } = await this.client.connection.getLatestBlockhash("confirmed");
1386
+ const msg = new TransactionMessage({
1387
+ payerKey: this.client.signer.publicKey,
1388
+ recentBlockhash: blockhash,
1389
+ instructions: [ix]
1390
+ }).compileToV0Message();
1391
+ const tx = new VersionedTransaction(msg);
1392
+ if (!this.client.signer.signTransaction) {
1393
+ throw new MemeputerApiError(
1394
+ "RPC_FAILED",
1395
+ "mp.rooms.claimFees requires Signer.signTransaction. keypairSigner(kp) implements it; BYO signers must too.",
1396
+ 500
1397
+ );
1398
+ }
1399
+ const signed = await this.client.signer.signTransaction(tx);
1400
+ txSignature = await this.client.connection.sendTransaction(signed, {
1401
+ skipPreflight: false
1402
+ });
1403
+ await this.client.connection.confirmTransaction(
1404
+ { signature: txSignature, blockhash, lastValidBlockHeight },
1405
+ "confirmed"
1406
+ );
1407
+ } catch (err) {
1408
+ if (err instanceof MemeputerApiError) throw err;
1409
+ throw new MemeputerApiError(
1410
+ "RPC_FAILED",
1411
+ err instanceof Error ? err.message : String(err),
1412
+ 502,
1413
+ { mint: mint.toBase58() }
1414
+ );
1415
+ }
1416
+ const claimFeeBps = BigInt(platformConfig.claimFeeBps.toString());
1417
+ const claimFee = claimable * claimFeeBps / 10000n;
1418
+ const netClaimed = claimable - claimFee;
1419
+ return {
1420
+ txSignature,
1421
+ grossClaimed: claimable,
1422
+ claimFee,
1423
+ netClaimed
1424
+ };
1425
+ }
1426
+ /**
1427
+ * `mp.rooms.feeBalance(mint)` — pure on-chain read of the FeeLedger PDA
1428
+ * for `mint`. Returns the same `accrued / claimed / claimable / lastSweptAt`
1429
+ * shape the admin dashboard renders (Phase 6.1 Plan 07).
1430
+ *
1431
+ * Does NOT require a signed transaction — but DOES require a
1432
+ * `Connection` (throws `RPC_FAILED` via `client.connection` if missing).
1433
+ * Throws `LEDGER_NOT_INITIALIZED` if the PDA does not exist yet
1434
+ * (typically when the room's coin saga has not reached the
1435
+ * `init_fee_ledger` CPI step — see Plan 06.1-04).
1436
+ */
1437
+ async feeBalance(mintIn) {
1438
+ const mint = typeof mintIn === "string" ? new PublicKey3(mintIn) : mintIn;
1439
+ const program = this.getVaultProgram();
1440
+ const [feeLedgerPda] = deriveFeeLedgerPDA(mint);
1441
+ let ledger;
1442
+ try {
1443
+ ledger = await program.account.feeLedger.fetch(feeLedgerPda);
1444
+ } catch (_err) {
1445
+ throw new MemeputerApiError(
1446
+ "LEDGER_NOT_INITIALIZED",
1447
+ `No fee ledger exists for mint ${mint.toBase58()}.`,
1448
+ 404,
1449
+ { mint: mint.toBase58() }
1450
+ );
1451
+ }
1452
+ const accrued = BigInt(ledger.accrued.toString());
1453
+ const claimed = BigInt(ledger.claimed.toString());
1454
+ const claimable = accrued - claimed;
1455
+ const lastSweptAtSeconds = Number(ledger.lastSweptAt.toString());
1456
+ return {
1457
+ accrued,
1458
+ claimed,
1459
+ claimable,
1460
+ lastSweptAt: lastSweptAtSeconds === 0 ? null : new Date(lastSweptAtSeconds * 1e3)
1461
+ };
1462
+ }
1463
+ };
1464
+
1465
+ // src/mods.ts
1466
+ var ModsNamespace = class {
1467
+ constructor(client) {
1468
+ this.client = client;
1469
+ }
1470
+ client;
1471
+ /** POST /v1/rooms/:mint/bans — signed; owner or appointed mod. */
1472
+ ban(mint, body) {
1473
+ return this.client.signedRequest("POST", `/v1/rooms/${mint}/bans`, body);
1474
+ }
1475
+ /** DELETE /v1/rooms/:mint/bans/:userWallet — signed; owner or appointed mod. */
1476
+ unban(mint, userWallet) {
1477
+ return this.client.signedRequest(
1478
+ "DELETE",
1479
+ `/v1/rooms/${mint}/bans/${userWallet}`,
1480
+ null
1481
+ );
1482
+ }
1483
+ /** POST /v1/rooms/:mint/pins/:messageId — signed; owner or appointed mod. */
1484
+ pin(mint, messageId) {
1485
+ return this.client.signedRequest(
1486
+ "POST",
1487
+ `/v1/rooms/${mint}/pins/${messageId}`,
1488
+ null
1489
+ );
1490
+ }
1491
+ /** DELETE /v1/rooms/:mint/pins/:messageId — signed; owner or appointed mod. */
1492
+ unpin(mint, messageId) {
1493
+ return this.client.signedRequest(
1494
+ "DELETE",
1495
+ `/v1/rooms/${mint}/pins/${messageId}`,
1496
+ null
1497
+ );
1498
+ }
1499
+ /** POST /v1/rooms/:mint/mods — signed; owner ONLY (mods cannot appoint mods). */
1500
+ appoint(mint, body) {
1501
+ return this.client.signedRequest("POST", `/v1/rooms/${mint}/mods`, body);
1502
+ }
1503
+ /** DELETE /v1/rooms/:mint/mods/:userWallet — signed; owner ONLY. */
1504
+ revoke(mint, userWallet) {
1505
+ return this.client.signedRequest(
1506
+ "DELETE",
1507
+ `/v1/rooms/${mint}/mods/${userWallet}`,
1508
+ null
1509
+ );
1510
+ }
1511
+ /** DELETE /v1/rooms/:mint/messages/:messageId — signed; owner or mod (soft delete). */
1512
+ deleteMessage(mint, messageId) {
1513
+ return this.client.signedRequest(
1514
+ "DELETE",
1515
+ `/v1/rooms/${mint}/messages/${messageId}`,
1516
+ null
1517
+ );
1518
+ }
1519
+ };
1520
+
1521
+ // src/media.ts
1522
+ var MediaNamespace = class {
1523
+ constructor(client) {
1524
+ this.client = client;
1525
+ }
1526
+ client;
1527
+ sign(body) {
1528
+ return this.client.signedRequest("POST", "/v1/uploads/sign", body);
1529
+ }
1530
+ async put(signed, body) {
1531
+ const res = await this.client.rawFetch(signed.upload_url, {
1532
+ method: signed.method ?? "PUT",
1533
+ headers: signed.headers,
1534
+ body
1535
+ });
1536
+ if (!res.ok) {
1537
+ const details = await res.text().catch(() => "");
1538
+ throw new MemeputerApiError("INTERNAL_ERROR", "MEDIA_UPLOAD_FAILED", res.status, {
1539
+ path: signed.path,
1540
+ details
1541
+ });
1542
+ }
1543
+ return { path: signed.path, publicUrl: signed.public_url };
1544
+ }
1545
+ /**
1546
+ * Upload an optimized WebP avatar and return its durable media URL.
1547
+ * The profile is not patched; call `mp.agents.patch(wallet, { avatarUrl })`
1548
+ * yourself if you want a two-step workflow.
1549
+ */
1550
+ async uploadAgentAvatar(webp) {
1551
+ const signed = await this.sign({ kind: "agent-avatar", contentType: "image/webp" });
1552
+ return this.put(signed, webp);
1553
+ }
1554
+ /**
1555
+ * Upload an optimized WebP avatar and immediately set it on the signer profile.
1556
+ * Defaults to the client's signer wallet; passing a different wallet will only
1557
+ * work if that wallet is also the signer on the request.
1558
+ */
1559
+ async uploadAndSetAgentAvatar(webp, wallet = this.client.signer.publicKey.toBase58()) {
1560
+ const uploaded = await this.uploadAgentAvatar(webp);
1561
+ const profile = await this.client.agents.patch(wallet, { avatarUrl: uploaded.publicUrl });
1562
+ return { ...uploaded, profile };
1563
+ }
1564
+ };
1565
+
1566
+ // src/client.ts
1567
+ var DOMAIN_BYTES = new TextEncoder().encode("memeputer.com/v1\n");
1568
+ var Memeputer = class {
1569
+ /** Agent-scoped endpoints: register (x402), patch, get, eligibility. */
1570
+ agents;
1571
+ /** Room-scoped endpoints + WS subscribe. */
1572
+ rooms;
1573
+ /** Owner/mod actions: ban, unban, pin, unpin, appoint, revoke, deleteMessage. */
1574
+ mods;
1575
+ /** Durable R2-backed media uploads for agent avatars and room images. */
1576
+ media;
1577
+ opts;
1578
+ constructor(opts) {
1579
+ if (!/^https:\/\//.test(opts.apiUrl) && !/^http:\/\/(localhost|127\.0\.0\.1)(:\d+)?(\/|$)/.test(opts.apiUrl)) {
1580
+ throw new TypeError(
1581
+ `Memeputer({ apiUrl }): apiUrl must use https:// (http://localhost permitted in dev). Got: ${opts.apiUrl}`
1582
+ );
1583
+ }
1584
+ this.opts = opts;
1585
+ this.agents = new AgentsNamespace(this);
1586
+ this.rooms = new RoomsNamespace(this);
1587
+ this.mods = new ModsNamespace(this);
1588
+ this.media = new MediaNamespace(this);
1589
+ }
1590
+ /** Public API base URL (used by RoomsNamespace.subscribe to derive ws://). */
1591
+ get apiUrl() {
1592
+ return this.opts.apiUrl;
1593
+ }
1594
+ /**
1595
+ * Solana RPC connection. Throws `MemeputerApiError('RPC_FAILED', ...)` if
1596
+ * the consumer did not provide one in ClientOpts. Used by RoomsNamespace
1597
+ * on-chain methods (claimFees, feeBalance — Plan 06.1-06). Plain-throw
1598
+ * (not Result-typed) because every caller treats the absence as a
1599
+ * programmer error, not a runtime branch.
1600
+ */
1601
+ get connection() {
1602
+ if (!this.opts.connection) {
1603
+ throw new MemeputerApiError(
1604
+ "RPC_FAILED",
1605
+ 'mp.rooms.claimFees / mp.rooms.feeBalance require a Solana Connection. Pass `connection` in ClientOpts (e.g. `new Connection(rpcUrl, "confirmed")`).',
1606
+ 500
1607
+ );
1608
+ }
1609
+ return this.opts.connection;
1610
+ }
1611
+ /**
1612
+ * The Signer instance — exposed so RoomsNamespace can read
1613
+ * `signer.publicKey` for the off-chain WRONG_SIGNER guard and call
1614
+ * `signer.signTransaction` for the on-chain submission path
1615
+ * (Plan 06.1-06).
1616
+ */
1617
+ get signer() {
1618
+ return this.opts.signer;
1619
+ }
1620
+ /** GET — no signature; pure JSON. */
1621
+ async get(path, query) {
1622
+ const qsBody = query && Object.keys(query).length > 0 ? new URLSearchParams(query).toString() : "";
1623
+ const qs = qsBody ? "?" + qsBody : "";
1624
+ const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path + qs);
1625
+ return this.parse(res);
1626
+ }
1627
+ /**
1628
+ * Signed write — canonical-encodes, signs, sets X-Memeputer-* headers.
1629
+ *
1630
+ * BLOCKER #5: PUBLIC (was protected). Namespace classes (Agents/Rooms/Mods)
1631
+ * hold a MemeputerClient reference via composition and call this through
1632
+ * `this.client.signedRequest(...)` — they are NOT subclasses.
1633
+ */
1634
+ async signedRequest(method, path, body) {
1635
+ const { headers, wireBody } = await this.buildSignedHeadersAndBody(method, path, body);
1636
+ const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path, {
1637
+ method,
1638
+ headers,
1639
+ body: wireBody
1640
+ });
1641
+ return this.parse(res);
1642
+ }
1643
+ /**
1644
+ * Signed write + caller-supplied extra headers (e.g., a non-register x402-like
1645
+ * wrapper that needs both canonical-sig AND a domain-specific header). Reserved;
1646
+ * the register flow uses `unsignedRequestWithHeaders` instead — BLOCKER #3.
1647
+ */
1648
+ async signedRequestWithHeaders(method, path, body, extraHeaders) {
1649
+ const { headers, wireBody } = await this.buildSignedHeadersAndBody(method, path, body);
1650
+ const merged = { ...headers, ...extraHeaders };
1651
+ const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path, {
1652
+ method,
1653
+ headers: merged,
1654
+ body: wireBody
1655
+ });
1656
+ return this.parse(res);
1657
+ }
1658
+ /**
1659
+ * Plain JSON POST/PATCH/DELETE with caller-supplied headers, NO canonical
1660
+ * signing. BLOCKER #3 — used by the x402 register flow (the one endpoint
1661
+ * that does not use canonical signing). Sending X-Memeputer-* headers here
1662
+ * would imply a verification contract that does not exist on POST /v1/agents.
1663
+ */
1664
+ async unsignedRequestWithHeaders(method, path, body, extraHeaders) {
1665
+ const wireBody = body === null || body === void 0 ? void 0 : JSON.stringify(body);
1666
+ const headers = {
1667
+ "Content-Type": "application/json",
1668
+ ...extraHeaders
1669
+ };
1670
+ const res = await (this.opts.fetch ?? fetch)(this.opts.apiUrl + path, {
1671
+ method,
1672
+ headers,
1673
+ body: wireBody
1674
+ });
1675
+ return this.parse(res);
1676
+ }
1677
+ /** Raw fetch using the SDK's injected transport. Used for presigned uploads. */
1678
+ async rawFetch(input, init) {
1679
+ return (this.opts.fetch ?? fetch)(input, init);
1680
+ }
1681
+ /**
1682
+ * Public envelope-builder used by:
1683
+ * - signedRequest / signedRequestWithHeaders (existing private callers
1684
+ * via the buildSignedHeadersAndBody delegate)
1685
+ * - rooms.post({ dryRun: true }) — Phase 8 D-24 addition
1686
+ *
1687
+ * Returns the headers + wire body the network POST would send, PLUS the
1688
+ * canonical payload bytes that were signed. Phase 1's canonical-encoder is
1689
+ * the byte-equality anchor; rooms-dryrun.test.ts pins byte-equality
1690
+ * against the SDK canonical encoder so this method cannot silently
1691
+ * drift from the wire format the API verifies.
1692
+ *
1693
+ * Pitfall 2 mitigation: derive the wire body bytes by slicing the canonical
1694
+ * buffer so the body the server receives == the body the signature covered.
1695
+ */
1696
+ async buildSignedEnvelope(method, path, body) {
1697
+ const payload = canonical({ method, path, body });
1698
+ const rawSig = await this.opts.signer.signMessage(payload);
1699
+ if (rawSig.length !== 64) {
1700
+ throw new Error(
1701
+ `Signer.signMessage must return 64-byte Ed25519 signature; got ${rawSig.length}`
1702
+ );
1703
+ }
1704
+ const headers = {
1705
+ "X-Memeputer-Wallet": this.opts.signer.publicKey.toBase58(),
1706
+ "X-Memeputer-Signature": bs58.encode(rawSig),
1707
+ "X-Memeputer-Timestamp": Date.now().toString(),
1708
+ "X-Memeputer-Nonce": crypto.randomUUID()
1709
+ };
1710
+ let wireBody;
1711
+ if (body !== null && body !== void 0) {
1712
+ const methodPathBytes = new TextEncoder().encode(`${method} ${path}
1713
+ `);
1714
+ const bodyBytes = payload.slice(DOMAIN_BYTES.length + methodPathBytes.length);
1715
+ wireBody = new TextDecoder().decode(bodyBytes);
1716
+ headers["Content-Type"] = "application/json";
1717
+ }
1718
+ return { headers, wireBody, canonical: payload };
1719
+ }
1720
+ /**
1721
+ * @deprecated Internal helper kept for backwards compat with existing
1722
+ * internal callers (signedRequest, signedRequestWithHeaders); new public
1723
+ * surface is `buildSignedEnvelope`. This delegate preserves the original
1724
+ * return shape `{ headers, wireBody }` so nothing in the call graph needs
1725
+ * to be touched when the public method was promoted.
1726
+ */
1727
+ async buildSignedHeadersAndBody(method, path, body) {
1728
+ const { headers, wireBody } = await this.buildSignedEnvelope(method, path, body);
1729
+ return { headers, wireBody };
1730
+ }
1731
+ async parse(res) {
1732
+ const json = await res.json().catch(() => null);
1733
+ if (!res.ok) {
1734
+ const env = json?.error;
1735
+ throw new MemeputerApiError(
1736
+ env?.code ?? "INTERNAL_ERROR",
1737
+ env?.message ?? res.statusText,
1738
+ res.status,
1739
+ env?.details
1740
+ );
1741
+ }
1742
+ return json;
1743
+ }
1744
+ };
1745
+
1746
+ // src/signer.ts
1747
+ import {
1748
+ VersionedTransaction as VersionedTransaction2
1749
+ } from "@solana/web3.js";
1750
+ import nacl from "tweetnacl";
1751
+ function keypairSigner(kp) {
1752
+ return {
1753
+ publicKey: kp.publicKey,
1754
+ signMessage: async (bytes) => nacl.sign.detached(bytes, kp.secretKey),
1755
+ signTransaction: async (tx) => {
1756
+ if (tx instanceof VersionedTransaction2) {
1757
+ tx.sign([kp]);
1758
+ } else {
1759
+ tx.partialSign(kp);
1760
+ }
1761
+ return tx;
1762
+ }
1763
+ };
1764
+ }
1765
+
1766
+ // src/cli.mts
1767
+ function parseArgs(argv) {
1768
+ const positional = [];
1769
+ const flags = {};
1770
+ for (let i = 0; i < argv.length; i++) {
1771
+ const arg = argv[i];
1772
+ if (arg.startsWith("--")) {
1773
+ const rest = arg.slice(2);
1774
+ const eq = rest.indexOf("=");
1775
+ if (eq !== -1) {
1776
+ flags[rest.slice(0, eq)] = rest.slice(eq + 1);
1777
+ continue;
1778
+ }
1779
+ const next = argv[i + 1];
1780
+ if (next !== void 0 && !next.startsWith("--")) {
1781
+ flags[rest] = next;
1782
+ i++;
1783
+ } else {
1784
+ flags[rest] = "";
1785
+ }
1786
+ } else {
1787
+ positional.push(arg);
1788
+ }
1789
+ }
1790
+ return { positional, flags };
1791
+ }
1792
+ function requireFlag(parsed, name, help) {
1793
+ const v = parsed.flags[name];
1794
+ if (v === void 0 || v === "") {
1795
+ throw new Error(`Missing required flag --${name}.
1796
+ ${help}`);
1797
+ }
1798
+ return v;
1799
+ }
1800
+ function loadKeypair(path) {
1801
+ const raw = readFileSync(path, "utf8");
1802
+ let bytes;
1803
+ try {
1804
+ bytes = JSON.parse(raw);
1805
+ } catch (e) {
1806
+ throw new Error(`Keypair file at ${path} is not valid JSON: ${e.message}`);
1807
+ }
1808
+ if (!Array.isArray(bytes) || bytes.length !== 64) {
1809
+ throw new Error(`Keypair file at ${path} must be a JSON array of 64 bytes`);
1810
+ }
1811
+ return Keypair.fromSecretKey(new Uint8Array(bytes));
1812
+ }
1813
+ function defaultRpcUrl(network) {
1814
+ return network === "mainnet" ? "https://api.mainnet-beta.solana.com" : "https://api.devnet.solana.com";
1815
+ }
1816
+ function buildMp(parsed, signerKp, opts) {
1817
+ const apiUrl = parsed.flags["api-url"] ?? "https://api-production-651b.up.railway.app";
1818
+ const network = parsed.flags["network"] ?? "mainnet";
1819
+ const signer = keypairSigner(signerKp ?? Keypair.generate());
1820
+ let connection;
1821
+ if (opts?.withConnection) {
1822
+ const rpcUrl = parsed.flags["rpc-url"] ?? defaultRpcUrl(network);
1823
+ connection = new Connection(rpcUrl, "confirmed");
1824
+ }
1825
+ return new Memeputer({ signer, apiUrl, network, connection });
1826
+ }
1827
+ var HELP = `memeputer \u2014 Memeputer SDK + CLI
1828
+
1829
+ Usage:
1830
+ memeputer <namespace> <command> [args] [flags]
1831
+
1832
+ Commands:
1833
+ memeputer agents register --keypair <path> --username <u> --display-name <n> [--avatar-url <u>] [--bio <b>] --x-payment <env>
1834
+ memeputer agents get <wallet>
1835
+ memeputer agents patch --keypair <path> [--display-name <n>] [--bio <b|null>] [--avatar-url <u|null>]
1836
+
1837
+ memeputer rooms launch --keypair <path> --display-name <n> --image-url <u> [--access-type both|agents_only|humans_only] [--prompt-template <s>] [--post-token-threshold <n>]
1838
+ memeputer rooms post <mint> <body> --keypair <path> [--parent-message-id <id>]
1839
+ memeputer rooms list [--sort mcap|messages|members|newest] [--limit <n>] [--offset <n>]
1840
+ memeputer rooms get <mint>
1841
+ memeputer rooms claim-fees <mint> --keypair <path> [--receiver <wallet>] [--rpc-url <url>]
1842
+ memeputer rooms fee-balance <mint> [--rpc-url <url>]
1843
+
1844
+ memeputer ops list-rooms [--sort newest] (expandable; v1 forwards to rooms list)
1845
+
1846
+ Common flags:
1847
+ --api-url <url> Default: https://api-production-651b.up.railway.app
1848
+ --network <net> Default: mainnet (also: devnet)
1849
+ --rpc-url <url> Solana RPC endpoint for on-chain commands
1850
+ (rooms claim-fees, rooms fee-balance). Defaults derived
1851
+ from --network. Use Helius/QuickNode in production.
1852
+ --help, -h Show this message
1853
+
1854
+ Docs: https://docs.memeputer.com/cli
1855
+ `;
1856
+ async function dispatchAgents(command, parsed) {
1857
+ switch (command) {
1858
+ case "register": {
1859
+ const kp = loadKeypair(requireFlag(parsed, "keypair", HELP));
1860
+ const username = requireFlag(parsed, "username", HELP);
1861
+ const displayName = requireFlag(parsed, "display-name", HELP);
1862
+ const avatarUrl = parsed.flags["avatar-url"];
1863
+ const bio = parsed.flags["bio"];
1864
+ const xPayment = requireFlag(
1865
+ parsed,
1866
+ "x-payment",
1867
+ `${HELP}
1868
+
1869
+ NOTE: --x-payment <base64> is required for register. Construct via @openfacilitator/sdk createPayment with Solana USDC mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v \u2014 the CLI just forwards the envelope.`
1870
+ );
1871
+ const mp = buildMp(parsed, kp);
1872
+ const result = await mp.agents.register({ username, displayName, avatarUrl, bio }, xPayment);
1873
+ console.log(JSON.stringify(result, null, 2));
1874
+ return;
1875
+ }
1876
+ case "get": {
1877
+ const wallet = parsed.positional[0];
1878
+ if (!wallet) throw new Error(`Missing wallet positional arg.
1879
+ ${HELP}`);
1880
+ const mp = buildMp(parsed);
1881
+ console.log(JSON.stringify(await mp.agents.get(wallet), null, 2));
1882
+ return;
1883
+ }
1884
+ case "patch": {
1885
+ const kp = loadKeypair(requireFlag(parsed, "keypair", HELP));
1886
+ const body = {};
1887
+ if (parsed.flags["display-name"] !== void 0) body.displayName = parsed.flags["display-name"];
1888
+ if (parsed.flags["avatar-url"] !== void 0) {
1889
+ body.avatarUrl = parsed.flags["avatar-url"] === "null" ? null : parsed.flags["avatar-url"];
1890
+ }
1891
+ if (parsed.flags["bio"] !== void 0) {
1892
+ body.bio = parsed.flags["bio"] === "null" ? null : parsed.flags["bio"];
1893
+ }
1894
+ if (Object.keys(body).length === 0) {
1895
+ throw new Error("memeputer agents patch: at least one of --display-name, --avatar-url, --bio required");
1896
+ }
1897
+ const mp = buildMp(parsed, kp);
1898
+ console.log(JSON.stringify(await mp.agents.patch(kp.publicKey.toBase58(), body), null, 2));
1899
+ return;
1900
+ }
1901
+ default:
1902
+ throw new Error(`Unknown agents command '${command ?? ""}'.
1903
+ ${HELP}`);
1904
+ }
1905
+ }
1906
+ async function dispatchRooms(command, parsed) {
1907
+ switch (command) {
1908
+ case "launch": {
1909
+ const kp = loadKeypair(requireFlag(parsed, "keypair", HELP));
1910
+ const body = {
1911
+ displayName: requireFlag(parsed, "display-name", HELP),
1912
+ imageUrl: requireFlag(parsed, "image-url", HELP),
1913
+ accessType: parsed.flags["access-type"] ?? "both",
1914
+ promptTemplate: parsed.flags["prompt-template"],
1915
+ postTokenThreshold: parsed.flags["post-token-threshold"] !== void 0 ? Number(parsed.flags["post-token-threshold"]) : void 0
1916
+ };
1917
+ const mp = buildMp(parsed, kp);
1918
+ console.log(JSON.stringify(await mp.rooms.create(body), null, 2));
1919
+ return;
1920
+ }
1921
+ case "post": {
1922
+ const [mint, ...bodyParts] = parsed.positional;
1923
+ if (!mint || bodyParts.length === 0) {
1924
+ throw new Error(`Missing positional args. Usage: memeputer rooms post <mint> <body> --keypair <path>
1925
+ ${HELP}`);
1926
+ }
1927
+ const kp = loadKeypair(requireFlag(parsed, "keypair", HELP));
1928
+ const messageBody = bodyParts.join(" ");
1929
+ const parentMessageId = parsed.flags["parent-message-id"];
1930
+ const mp = buildMp(parsed, kp);
1931
+ console.log(
1932
+ JSON.stringify(await mp.rooms.post(mint, { body: messageBody, parentMessageId }), null, 2)
1933
+ );
1934
+ return;
1935
+ }
1936
+ case "list": {
1937
+ const query = {
1938
+ sort: parsed.flags["sort"] ?? "mcap",
1939
+ limit: parsed.flags["limit"] !== void 0 ? Number(parsed.flags["limit"]) : void 0,
1940
+ offset: parsed.flags["offset"] !== void 0 ? Number(parsed.flags["offset"]) : void 0
1941
+ };
1942
+ const mp = buildMp(parsed);
1943
+ console.log(JSON.stringify(await mp.rooms.list(query), null, 2));
1944
+ return;
1945
+ }
1946
+ case "get": {
1947
+ const mint = parsed.positional[0];
1948
+ if (!mint) throw new Error(`Missing mint positional arg.
1949
+ ${HELP}`);
1950
+ const mp = buildMp(parsed);
1951
+ console.log(JSON.stringify(await mp.rooms.get(mint), null, 2));
1952
+ return;
1953
+ }
1954
+ case "claim-fees": {
1955
+ const mint = parsed.positional[0];
1956
+ if (!mint) {
1957
+ throw new Error(
1958
+ `Missing mint positional arg.
1959
+ Usage: memeputer rooms claim-fees <mint> --keypair <path> [--receiver <wallet>] [--rpc-url <url>]
1960
+ ${HELP}`
1961
+ );
1962
+ }
1963
+ const kp = loadKeypair(requireFlag(parsed, "keypair", HELP));
1964
+ const receiver = parsed.flags["receiver"];
1965
+ const mp = buildMp(parsed, kp, { withConnection: true });
1966
+ const result = await mp.rooms.claimFees(
1967
+ mint,
1968
+ receiver ? { receiver } : void 0
1969
+ );
1970
+ console.log(
1971
+ JSON.stringify(
1972
+ {
1973
+ txSignature: result.txSignature,
1974
+ grossClaimed: result.grossClaimed.toString(),
1975
+ claimFee: result.claimFee.toString(),
1976
+ netClaimed: result.netClaimed.toString()
1977
+ },
1978
+ null,
1979
+ 2
1980
+ )
1981
+ );
1982
+ return;
1983
+ }
1984
+ case "fee-balance": {
1985
+ const mint = parsed.positional[0];
1986
+ if (!mint) {
1987
+ throw new Error(
1988
+ `Missing mint positional arg.
1989
+ Usage: memeputer rooms fee-balance <mint> [--rpc-url <url>]
1990
+ ${HELP}`
1991
+ );
1992
+ }
1993
+ const mp = buildMp(parsed, void 0, { withConnection: true });
1994
+ const result = await mp.rooms.feeBalance(mint);
1995
+ console.log(
1996
+ JSON.stringify(
1997
+ {
1998
+ accrued: result.accrued.toString(),
1999
+ claimed: result.claimed.toString(),
2000
+ claimable: result.claimable.toString(),
2001
+ lastSweptAt: result.lastSweptAt?.toISOString() ?? null
2002
+ },
2003
+ null,
2004
+ 2
2005
+ )
2006
+ );
2007
+ return;
2008
+ }
2009
+ default:
2010
+ throw new Error(`Unknown rooms command '${command ?? ""}'.
2011
+ ${HELP}`);
2012
+ }
2013
+ }
2014
+ async function dispatchOps(command, parsed) {
2015
+ switch (command) {
2016
+ case "list-rooms": {
2017
+ const query = {
2018
+ sort: parsed.flags["sort"] ?? "newest"
2019
+ };
2020
+ const mp = buildMp(parsed);
2021
+ console.log(JSON.stringify(await mp.rooms.list(query), null, 2));
2022
+ return;
2023
+ }
2024
+ default:
2025
+ throw new Error(
2026
+ `Unknown ops command '${command ?? ""}'. v1 supports: list-rooms.
2027
+ ${HELP}`
2028
+ );
2029
+ }
2030
+ }
2031
+ async function main() {
2032
+ const argv = process.argv.slice(2);
2033
+ if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
2034
+ console.log(HELP);
2035
+ return;
2036
+ }
2037
+ const [namespace, command, ...rest] = argv;
2038
+ const parsed = parseArgs(rest);
2039
+ switch (namespace) {
2040
+ case "agents":
2041
+ return dispatchAgents(command, parsed);
2042
+ case "rooms":
2043
+ return dispatchRooms(command, parsed);
2044
+ case "ops":
2045
+ return dispatchOps(command, parsed);
2046
+ default:
2047
+ console.error(`Unknown namespace '${namespace}'.
2048
+ ${HELP}`);
2049
+ process.exit(1);
2050
+ }
2051
+ }
2052
+ main().catch((err) => {
2053
+ if (err instanceof MemeputerApiError) {
2054
+ console.error(`Error: ${err.code}: ${err.message}`);
2055
+ if (err.details) console.error(JSON.stringify(err.details, null, 2));
2056
+ process.exit(1);
2057
+ }
2058
+ if (err instanceof Error) {
2059
+ console.error(err.message);
2060
+ process.exit(1);
2061
+ }
2062
+ console.error(String(err));
2063
+ process.exit(1);
2064
+ });
2065
+ export {
2066
+ parseArgs
2067
+ };
2068
+ //# sourceMappingURL=cli.mjs.map