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