@txtcel/mcp 0.1.3 → 0.3.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/dist/index.js CHANGED
@@ -47867,7 +47867,6 @@ function loadWallet() {
47867
47867
  var import_zorsh = __toESM(require_src2(), 1);
47868
47868
  import { Buffer as Buffer3 } from "buffer";
47869
47869
  var TAG_CONTENT = 1;
47870
- var TAG_ALLOC = 2;
47871
47870
  var TAG_THREAD = 3;
47872
47871
  var TAG_SETTINGS = 5;
47873
47872
  var TAG_ACCESS = 6;
@@ -47875,12 +47874,11 @@ var TAG_LIKES = 7;
47875
47874
  var TAG_ACCESS_ENTRY = 9;
47876
47875
  var TAG_FOLLOW_REGISTRY = 10;
47877
47876
  var TAG_FOLLOWER_SHARD = 11;
47878
- var KIND_TEXT = 0;
47879
47877
  var ACCESS_ALLOWED = 0;
47880
47878
  var ACCESS_DENIED = 1;
47881
47879
  var ACCESS_FEE_EXEMPT = 2;
47882
- var NEXT_ALLOC_INDEX = 31;
47883
- var CONTENT_SLOTS = 31;
47880
+ var KIND_TEXT = 0;
47881
+ var CONTENT_SLOTS = 32;
47884
47882
  var EXTEND_THRESHOLD = 16;
47885
47883
  var MAX_BODY_LEN = 8192;
47886
47884
  var MAX_TITLE_LEN = 64;
@@ -47954,9 +47952,7 @@ var ContentNodeSchema = import_zorsh.b.struct({
47954
47952
  var AllocNodeSchema = import_zorsh.b.struct({
47955
47953
  tag: import_zorsh.b.u8(),
47956
47954
  thread: Pubkey,
47957
- allocSeq: import_zorsh.b.u32(),
47958
- upperAllocSeq: import_zorsh.b.u32(),
47959
- nextAllocSeq: import_zorsh.b.u32()
47955
+ allocSeq: import_zorsh.b.u32()
47960
47956
  });
47961
47957
  var ThreadNodeSchema = import_zorsh.b.struct({
47962
47958
  tag: import_zorsh.b.u8(),
@@ -47993,7 +47989,7 @@ var AccessEntrySchema = import_zorsh.b.struct({
47993
47989
  var AllocLikesSchema = import_zorsh.b.struct({
47994
47990
  tag: import_zorsh.b.u8(),
47995
47991
  allocSeq: import_zorsh.b.u32(),
47996
- counts: import_zorsh.b.array(import_zorsh.b.u32(), NEXT_ALLOC_INDEX)
47992
+ counts: import_zorsh.b.array(import_zorsh.b.u32(), CONTENT_SLOTS)
47997
47993
  });
47998
47994
  var FollowRegistrySchema = import_zorsh.b.struct({
47999
47995
  tag: import_zorsh.b.u8(),
@@ -48029,7 +48025,6 @@ var FillSlotInstr = import_zorsh.b.struct({
48029
48025
  kind: import_zorsh.b.u16(),
48030
48026
  body: import_zorsh.b.bytes(),
48031
48027
  candidates: import_zorsh.b.vec(CandidateSlotSchema),
48032
- extend: import_zorsh.b.u8(),
48033
48028
  treasuryShardIdx: import_zorsh.b.u16(),
48034
48029
  authorFeeShardIdx: import_zorsh.b.u8(),
48035
48030
  replyAllocSeq: import_zorsh.b.u32(),
@@ -48048,7 +48043,8 @@ var InitThreadAccessInstr = import_zorsh.b.struct({
48048
48043
  var RequestAccessInstr = import_zorsh.b.struct({
48049
48044
  tag: import_zorsh.b.u8(),
48050
48045
  treasuryShardIdx: import_zorsh.b.u16(),
48051
- authorFeeShardIdx: import_zorsh.b.u8()
48046
+ authorFeeShardIdx: import_zorsh.b.u8(),
48047
+ maxFee: import_zorsh.b.u64()
48052
48048
  });
48053
48049
  var LikeContentInstr = import_zorsh.b.struct({
48054
48050
  tag: import_zorsh.b.u8(),
@@ -48062,7 +48058,8 @@ var AppendContentInstr = import_zorsh.b.struct({
48062
48058
  tag: import_zorsh.b.u8(),
48063
48059
  chunk: import_zorsh.b.bytes(),
48064
48060
  treasuryShardIdx: import_zorsh.b.u16(),
48065
- authorFeeShardIdx: import_zorsh.b.u8()
48061
+ authorFeeShardIdx: import_zorsh.b.u8(),
48062
+ maxFee: import_zorsh.b.u64()
48066
48063
  });
48067
48064
  var textDecoder = new TextDecoder();
48068
48065
  function decodeContent(pubkey, data) {
@@ -48083,17 +48080,6 @@ function decodeContent(pubkey, data) {
48083
48080
  text: raw.kind === KIND_TEXT ? textDecoder.decode(raw.body) : ""
48084
48081
  };
48085
48082
  }
48086
- function decodeAlloc(pubkey, data) {
48087
- const raw = AllocNodeSchema.deserialize(data);
48088
- if (raw.tag !== TAG_ALLOC) throw new Error("Invalid AllocNode tag");
48089
- return {
48090
- pubkey,
48091
- seed: raw.thread,
48092
- allocSeq: raw.allocSeq,
48093
- upperAllocSeq: raw.upperAllocSeq === INDEX_NONE ? null : raw.upperAllocSeq,
48094
- nextAllocSeq: raw.nextAllocSeq === INDEX_NONE ? null : raw.nextAllocSeq
48095
- };
48096
- }
48097
48083
  var textDecoder2 = new TextDecoder();
48098
48084
  function decodeThread(pubkey, data) {
48099
48085
  const raw = ThreadNodeSchema.deserialize(data);
@@ -48250,47 +48236,46 @@ function randomTreasuryShard() {
48250
48236
  function randomAuthorFeeShard() {
48251
48237
  return Math.floor(Math.random() * N_AUTHOR_FEE_SHARDS);
48252
48238
  }
48239
+ function assertOwnedTag(info, programId, tag, name) {
48240
+ if (!info.owner.equals(programId)) throw new Error(`${name} not owned by program`);
48241
+ if (info.data.length === 0 || info.data[0] !== tag) throw new Error(`Invalid ${name} data`);
48242
+ }
48243
+ async function fetchRequiredAccount(connection, programId, key, tag, name) {
48244
+ const info = await connection.getAccountInfo(key, "confirmed");
48245
+ if (!info) throw new Error(`${name} not found`);
48246
+ assertOwnedTag(info, programId, tag, name);
48247
+ return info;
48248
+ }
48249
+ async function fetchOptionalAccount(connection, programId, key, tag, name) {
48250
+ const info = await connection.getAccountInfo(key, "confirmed");
48251
+ if (!info) return null;
48252
+ assertOwnedTag(info, programId, tag, name);
48253
+ return info;
48254
+ }
48255
+ function isOwnedTag(info, programId, tag) {
48256
+ return info !== null && info.owner.equals(programId) && info.data.length > 0 && info.data[0] === tag;
48257
+ }
48253
48258
  async function loadThreadNode(connection, programId, pubkey) {
48254
- const info = await connection.getAccountInfo(pubkey, "confirmed");
48255
- if (!info) throw new Error("ThreadNode not found");
48256
- if (!info.owner.equals(programId)) throw new Error("ThreadNode not owned by program");
48257
- if (info.data.length === 0 || info.data[0] !== TAG_THREAD) throw new Error("Invalid ThreadNode data");
48259
+ const info = await fetchRequiredAccount(connection, programId, pubkey, TAG_THREAD, "ThreadNode");
48258
48260
  return decodeThread(pubkey.toBase58(), info.data);
48259
48261
  }
48260
- async function loadAllocNode(connection, programId, pubkey) {
48261
- const info = await connection.getAccountInfo(pubkey, "confirmed");
48262
- if (!info) throw new Error("AllocNode not found");
48263
- if (!info.owner.equals(programId)) throw new Error("AllocNode not owned by program");
48264
- if (info.data.length === 0 || info.data[0] !== TAG_ALLOC) throw new Error("Invalid AllocNode data");
48265
- return decodeAlloc(pubkey.toBase58(), info.data);
48266
- }
48267
48262
  async function loadContentNode(connection, programId, pubkey) {
48268
- const info = await connection.getAccountInfo(pubkey, "confirmed");
48269
- if (!info) throw new Error("ContentNode not found");
48270
- if (!info.owner.equals(programId)) throw new Error("ContentNode not owned by program");
48271
- if (info.data.length === 0 || info.data[0] !== TAG_CONTENT) throw new Error("Invalid ContentNode data");
48263
+ const info = await fetchRequiredAccount(connection, programId, pubkey, TAG_CONTENT, "ContentNode");
48272
48264
  return decodeContent(pubkey.toBase58(), info.data);
48273
48265
  }
48274
48266
  async function loadProgramSettings(connection, programId) {
48275
48267
  const settingsKey = deriveSettingsPda(programId);
48276
- const info = await connection.getAccountInfo(settingsKey, "confirmed");
48268
+ const info = await fetchOptionalAccount(connection, programId, settingsKey, TAG_SETTINGS, "ProgramSettings");
48277
48269
  if (!info) return null;
48278
- if (!info.owner.equals(programId)) throw new Error("ProgramSettings not owned by program");
48279
- if (info.data.length === 0 || info.data[0] !== TAG_SETTINGS) throw new Error("Invalid ProgramSettings data");
48280
48270
  return decodeSettings(settingsKey.toBase58(), info.data);
48281
48271
  }
48282
48272
  async function loadThreadAccess(connection, programId, accessKey) {
48283
- const info = await connection.getAccountInfo(accessKey, "confirmed");
48284
- if (!info) throw new Error("ThreadAccess not found");
48285
- if (!info.owner.equals(programId)) throw new Error("ThreadAccess not owned by program");
48286
- if (info.data.length === 0 || info.data[0] !== TAG_ACCESS) throw new Error("Invalid ThreadAccess data");
48273
+ const info = await fetchRequiredAccount(connection, programId, accessKey, TAG_ACCESS, "ThreadAccess");
48287
48274
  return decodeThreadAccess(accessKey.toBase58(), info.data);
48288
48275
  }
48289
48276
  async function loadAllocLikes(connection, programId, pubkey) {
48290
- const info = await connection.getAccountInfo(pubkey, "confirmed");
48277
+ const info = await fetchOptionalAccount(connection, programId, pubkey, TAG_LIKES, "AllocLikes");
48291
48278
  if (!info) return null;
48292
- if (!info.owner.equals(programId)) throw new Error("AllocLikes not owned by program");
48293
- if (info.data.length === 0 || info.data[0] !== TAG_LIKES) throw new Error("Invalid AllocLikes data");
48294
48279
  return decodeAllocLikes(pubkey.toBase58(), info.data);
48295
48280
  }
48296
48281
  async function loadAccessEntries(connection, programId, seed) {
@@ -48315,10 +48300,8 @@ async function loadAccessEntries(connection, programId, seed) {
48315
48300
  }
48316
48301
  async function loadFollowRegistry(connection, programId, owner) {
48317
48302
  const registryKey = deriveFollowRegistryPda(programId, owner);
48318
- const info = await connection.getAccountInfo(registryKey, "confirmed");
48303
+ const info = await fetchOptionalAccount(connection, programId, registryKey, TAG_FOLLOW_REGISTRY, "FollowRegistry");
48319
48304
  if (!info) return null;
48320
- if (!info.owner.equals(programId)) throw new Error("FollowRegistry not owned by program");
48321
- if (info.data.length === 0 || info.data[0] !== TAG_FOLLOW_REGISTRY) throw new Error("Invalid FollowRegistry data");
48322
48305
  return decodeFollowRegistry(registryKey.toBase58(), info.data);
48323
48306
  }
48324
48307
  async function loadFollowerCount(connection, programId, seed) {
@@ -48330,8 +48313,7 @@ async function loadFollowerCount(connection, programId, seed) {
48330
48313
  let total = 0n;
48331
48314
  for (let i = 0; i < infos.length; i++) {
48332
48315
  const info = infos[i];
48333
- if (!info || !info.owner.equals(programId)) continue;
48334
- if (info.data.length === 0 || info.data[0] !== TAG_FOLLOWER_SHARD) continue;
48316
+ if (!isOwnedTag(info, programId, TAG_FOLLOWER_SHARD)) continue;
48335
48317
  total += decodeFollowerShard(shardKeys[i].toBase58(), info.data).count;
48336
48318
  }
48337
48319
  return total;
@@ -48344,41 +48326,21 @@ async function loadThreadNodesBatched(connection, programId, channels) {
48344
48326
  const infos = await connection.getMultipleAccountsInfo(chunk, "confirmed");
48345
48327
  for (let j = 0; j < infos.length; j++) {
48346
48328
  const info = infos[j];
48347
- if (!info || !info.owner.equals(programId)) continue;
48348
- if (info.data.length === 0 || info.data[0] !== TAG_THREAD) continue;
48329
+ if (!isOwnedTag(info, programId, TAG_THREAD)) continue;
48349
48330
  const key = chunk[j].toBase58();
48350
48331
  result.set(key, decodeThread(key, info.data));
48351
48332
  }
48352
48333
  }
48353
48334
  return result;
48354
48335
  }
48355
- function buildCreateRootAllocInstruction(programId, payer, seed, messageFee = 0n, title = "") {
48356
- const titleBytes = new TextEncoder().encode(title);
48357
- if (titleBytes.length > MAX_TITLE_LEN) {
48358
- throw new Error(`Title is too long (max ${MAX_TITLE_LEN} bytes)`);
48359
- }
48360
- const threadAccount = deriveThreadPda(programId, seed);
48361
- const allocAccount = deriveAllocPda(programId, seed, 0);
48362
- const settingsAccount = deriveSettingsPda(programId);
48363
- const treasuryShardIdx = randomTreasuryShard();
48364
- const treasuryShard = deriveTreasuryShardPda(programId, treasuryShardIdx);
48365
- return new TransactionInstruction({
48366
- programId,
48367
- keys: [
48368
- { pubkey: payer, isSigner: true, isWritable: true },
48369
- { pubkey: threadAccount, isSigner: true, isWritable: true },
48370
- { pubkey: allocAccount, isSigner: false, isWritable: true },
48371
- { pubkey: settingsAccount, isSigner: false, isWritable: false },
48372
- { pubkey: treasuryShard, isSigner: false, isWritable: true },
48373
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48374
- ],
48375
- data: Buffer3.from(CreateRootAllocInstr.serialize({
48376
- tag: Instruction.CreateRootAlloc,
48377
- messageFee,
48378
- treasuryShardIdx,
48379
- title: titleBytes
48380
- }))
48381
- });
48336
+ function signerMeta(pubkey) {
48337
+ return { pubkey, isSigner: true, isWritable: true };
48338
+ }
48339
+ function writableMeta(pubkey) {
48340
+ return { pubkey, isSigner: false, isWritable: true };
48341
+ }
48342
+ function readonlyMeta(pubkey) {
48343
+ return { pubkey, isSigner: false, isWritable: false };
48382
48344
  }
48383
48345
  function buildFillSlotInstruction(opts) {
48384
48346
  const {
@@ -48391,7 +48353,6 @@ function buildFillSlotInstruction(opts) {
48391
48353
  bodyBytes,
48392
48354
  kind = KIND_TEXT,
48393
48355
  maxFee,
48394
- extendInfo,
48395
48356
  replyAllocSeq: replyAllocSeqRaw,
48396
48357
  replySlot: replySlotRaw
48397
48358
  } = opts;
@@ -48402,22 +48363,18 @@ function buildFillSlotInstruction(opts) {
48402
48363
  const accessAccount = deriveAccessPda(programId, seed);
48403
48364
  const entryAccount = deriveAccessEntryPda(programId, seed, payer);
48404
48365
  const keys = [
48405
- { pubkey: payer, isSigner: true, isWritable: true },
48406
- { pubkey: threadAccount, isSigner: false, isWritable: !!extendInfo },
48407
- { pubkey: settingsAccount, isSigner: false, isWritable: false },
48408
- { pubkey: treasuryShard, isSigner: false, isWritable: true },
48409
- { pubkey: authorFeeShard, isSigner: false, isWritable: true },
48410
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48366
+ signerMeta(payer),
48367
+ readonlyMeta(threadAccount),
48368
+ readonlyMeta(settingsAccount),
48369
+ writableMeta(treasuryShard),
48370
+ writableMeta(authorFeeShard),
48371
+ readonlyMeta(SystemProgram.programId)
48411
48372
  ];
48412
- for (const c of candidates) {
48413
- keys.push({ pubkey: c.pda, isSigner: false, isWritable: true });
48414
- }
48415
- keys.push({ pubkey: accessAccount, isSigner: false, isWritable: false });
48416
- keys.push({ pubkey: entryAccount, isSigner: false, isWritable: false });
48417
- if (extendInfo) {
48418
- keys.push({ pubkey: extendInfo.currentAllocPda, isSigner: false, isWritable: true });
48419
- keys.push({ pubkey: extendInfo.newAllocPda, isSigner: false, isWritable: true });
48373
+ for (const candidate of candidates) {
48374
+ keys.push(writableMeta(candidate.pda));
48420
48375
  }
48376
+ keys.push(readonlyMeta(accessAccount));
48377
+ keys.push(readonlyMeta(entryAccount));
48421
48378
  return new TransactionInstruction({
48422
48379
  programId,
48423
48380
  keys,
@@ -48425,8 +48382,7 @@ function buildFillSlotInstruction(opts) {
48425
48382
  tag: Instruction.FillSlot,
48426
48383
  kind,
48427
48384
  body: bodyBytes,
48428
- candidates: candidates.map((c) => ({ allocSeq: c.allocSeq, slot: c.slot })),
48429
- extend: extendInfo ? 1 : 0,
48385
+ candidates: candidates.map((candidate) => ({ allocSeq: candidate.allocSeq, slot: candidate.slot })),
48430
48386
  treasuryShardIdx,
48431
48387
  authorFeeShardIdx,
48432
48388
  replyAllocSeq: replyAllocSeqRaw ?? INDEX_NONE,
@@ -48435,26 +48391,58 @@ function buildFillSlotInstruction(opts) {
48435
48391
  }))
48436
48392
  });
48437
48393
  }
48438
- function buildAppendContentInstruction(programId, payer, contentAccount, threadAccount, settingsAccount, treasuryShard, authorFeeShard, chunk, treasuryShardIdx, authorFeeShardIdx) {
48394
+ function buildPrepareAllocInstruction(programId, payer, seed, allocSeq) {
48395
+ const currentAllocPda = deriveAllocPda(programId, seed, allocSeq);
48396
+ const newAllocPda = deriveAllocPda(programId, seed, allocSeq + 1);
48397
+ const threadAccount = deriveThreadPda(programId, seed);
48398
+ return new TransactionInstruction({
48399
+ programId,
48400
+ keys: [
48401
+ signerMeta(payer),
48402
+ writableMeta(currentAllocPda),
48403
+ writableMeta(newAllocPda),
48404
+ writableMeta(threadAccount),
48405
+ readonlyMeta(SystemProgram.programId)
48406
+ ],
48407
+ data: Buffer3.from(PrepareAllocInstr.serialize({
48408
+ tag: Instruction.PrepareAlloc,
48409
+ allocSeq
48410
+ }))
48411
+ });
48412
+ }
48413
+ function buildAppendContentInstruction(programId, payer, contentAccount, threadAccount, settingsAccount, treasuryShard, authorFeeShard, chunk, treasuryShardIdx, authorFeeShardIdx, maxFee) {
48439
48414
  return new TransactionInstruction({
48440
48415
  programId,
48441
48416
  keys: [
48442
- { pubkey: payer, isSigner: true, isWritable: true },
48443
- { pubkey: contentAccount, isSigner: false, isWritable: true },
48444
- { pubkey: threadAccount, isSigner: false, isWritable: false },
48445
- { pubkey: settingsAccount, isSigner: false, isWritable: false },
48446
- { pubkey: treasuryShard, isSigner: false, isWritable: true },
48447
- { pubkey: authorFeeShard, isSigner: false, isWritable: true },
48448
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48417
+ signerMeta(payer),
48418
+ writableMeta(contentAccount),
48419
+ readonlyMeta(threadAccount),
48420
+ readonlyMeta(settingsAccount),
48421
+ writableMeta(treasuryShard),
48422
+ writableMeta(authorFeeShard),
48423
+ readonlyMeta(SystemProgram.programId)
48449
48424
  ],
48450
48425
  data: Buffer3.from(AppendContentInstr.serialize({
48451
48426
  tag: Instruction.AppendContent,
48452
48427
  chunk,
48453
48428
  treasuryShardIdx,
48454
- authorFeeShardIdx
48429
+ authorFeeShardIdx,
48430
+ maxFee
48455
48431
  }))
48456
48432
  });
48457
48433
  }
48434
+ var TX_SIZE_LIMIT = 1232;
48435
+ var TX_OVERHEAD = 1 + 64 + 3 + 32;
48436
+ var FILL_SLOT_FIXED_OVERHEAD = 1 + 2 + 4 + 4 + 2 + 1 + 4 + 1 + 8;
48437
+ var CANDIDATE_SIZE = 4 + 1;
48438
+ var ACCOUNT_KEY_SIZE = 32;
48439
+ var FILL_SLOT_BASE_ACCOUNTS = 6;
48440
+ var FILL_SLOT_ACCESS_ACCOUNTS = 2;
48441
+ var PREPARE_ALLOC_MARGINAL_SIZE = 2 * ACCOUNT_KEY_SIZE + 1 + 1 + 5 + 1 + (1 + 4);
48442
+ var CONTENT_NODE_FIXED_SIZE = 89;
48443
+ var MAX_FEE_SLIPPAGE_NUM = 2n;
48444
+ var APPEND_INSTR_ACCOUNTS = 7;
48445
+ var APPEND_TX_OVERHEAD = TX_OVERHEAD + 1 + (APPEND_INSTR_ACCOUNTS + 1) * ACCOUNT_KEY_SIZE + 1 + 1 + 1 + APPEND_INSTR_ACCOUNTS + 2 + 1 + 4 + 2 + 1;
48458
48446
  function ensureTextBytes(text) {
48459
48447
  const bytes = new TextEncoder().encode(text);
48460
48448
  if (bytes.length === 0) {
@@ -48473,74 +48461,41 @@ function shuffle(arr) {
48473
48461
  }
48474
48462
  return result;
48475
48463
  }
48476
- var TX_SIZE_LIMIT = 1232;
48477
- var TX_OVERHEAD = 1 + 64 + 3 + 32;
48478
- var FILL_SLOT_FIXED_OVERHEAD = 1 + 2 + 4 + 4 + 1 + 2 + 1 + 4 + 1 + 8;
48479
- var CANDIDATE_SIZE = 4 + 1;
48480
- var ACCOUNT_KEY_SIZE = 32;
48481
- var FILL_SLOT_BASE_ACCOUNTS = 6;
48482
- var FILL_SLOT_ACCESS_ACCOUNTS = 2;
48483
- var CONTENT_NODE_FIXED_SIZE = 89;
48484
- var MAX_FEE_SLIPPAGE_NUM = 2n;
48485
- var APPEND_INSTR_ACCOUNTS = 7;
48486
- var APPEND_TX_OVERHEAD = TX_OVERHEAD + 1 + (APPEND_INSTR_ACCOUNTS + 1) * ACCOUNT_KEY_SIZE + 1 + 1 + 1 + APPEND_INSTR_ACCOUNTS + 2 + 1 + 4 + 2 + 1;
48487
- function estimateFillSlotTxSize(nCandidates, hasExtend, textLen) {
48488
- const nAccounts = FILL_SLOT_BASE_ACCOUNTS + nCandidates + FILL_SLOT_ACCESS_ACCOUNTS + (hasExtend ? 2 : 0);
48464
+ function estimateFillSlotTxSize(nCandidates, textLen) {
48465
+ const nAccounts = FILL_SLOT_BASE_ACCOUNTS + nCandidates + FILL_SLOT_ACCESS_ACCOUNTS;
48489
48466
  const accountsSize = 1 + (nAccounts + 1) * ACCOUNT_KEY_SIZE;
48490
48467
  const instrDataSize = FILL_SLOT_FIXED_OVERHEAD + textLen + nCandidates * CANDIDATE_SIZE;
48491
48468
  const instrOverhead = 1 + 1 + 1 + nAccounts + 2;
48492
48469
  return TX_OVERHEAD + accountsSize + instrOverhead + instrDataSize;
48493
48470
  }
48494
- function maxFillSlotTextLen(nCandidates, hasExtend) {
48495
- const withoutText = estimateFillSlotTxSize(nCandidates, hasExtend, 0);
48496
- return TX_SIZE_LIMIT - withoutText;
48471
+ function maxFillSlotTextLen(nCandidates, reserve = 0) {
48472
+ const withoutText = estimateFillSlotTxSize(nCandidates, 0);
48473
+ return TX_SIZE_LIMIT - withoutText - reserve;
48497
48474
  }
48498
48475
  function maxAppendChunkLen() {
48499
48476
  return TX_SIZE_LIMIT - APPEND_TX_OVERHEAD;
48500
48477
  }
48478
+ function pageCandidates(programId, seed, allocSeq) {
48479
+ return Array.from({ length: CONTENT_SLOTS }, (_, slot) => ({
48480
+ pda: deriveContentPda(programId, seed, allocSeq, slot),
48481
+ allocSeq,
48482
+ slot
48483
+ }));
48484
+ }
48501
48485
  async function buildSendMessageTransactions(connection, programId, payerKey, seed, text, replyTo) {
48502
48486
  const textBytes = ensureTextBytes(text);
48503
48487
  const threadPda = deriveThreadPda(programId, seed);
48504
48488
  const thread = await loadThreadNode(connection, programId, threadPda);
48505
48489
  const lastAllocSeq = thread.lastAllocSeq;
48506
- const lastAllocPda = deriveAllocPda(programId, seed, lastAllocSeq);
48507
- const lastAlloc = await loadAllocNode(connection, programId, lastAllocPda);
48508
- const lastAllocCandidates = Array.from({ length: CONTENT_SLOTS }, (_, slot) => ({
48509
- pda: deriveContentPda(programId, seed, lastAllocSeq, slot),
48510
- allocSeq: lastAllocSeq,
48511
- slot
48512
- }));
48513
- const lastInfos = await connection.getMultipleAccountsInfo(
48514
- lastAllocCandidates.map((c) => c.pda)
48515
- );
48516
- const freeInLast = lastAllocCandidates.filter((_, i) => lastInfos[i] === null);
48517
- let freeInNext = [];
48518
- if (freeInLast.length < DESIRED_CANDIDATES && lastAlloc.nextAllocSeq !== null) {
48519
- const nextSeq = lastAlloc.nextAllocSeq;
48520
- const nextAllocCandidates = Array.from({ length: CONTENT_SLOTS }, (_, slot) => ({
48521
- pda: deriveContentPda(programId, seed, nextSeq, slot),
48522
- allocSeq: nextSeq,
48523
- slot
48524
- }));
48525
- const nextInfos = await connection.getMultipleAccountsInfo(
48526
- nextAllocCandidates.map((c) => c.pda)
48527
- );
48528
- freeInNext = nextAllocCandidates.filter((_, i) => nextInfos[i] === null);
48529
- }
48530
- const allFree = [...freeInLast, ...freeInNext];
48531
- if (allFree.length === 0) throw new Error("No free slots in thread");
48532
- const selected = shuffle(allFree).slice(0, DESIRED_CANDIDATES);
48490
+ const windowCandidates = lastAllocSeq >= 1 ? [...pageCandidates(programId, seed, lastAllocSeq - 1), ...pageCandidates(programId, seed, lastAllocSeq)] : pageCandidates(programId, seed, lastAllocSeq);
48491
+ const windowInfos = await connection.getMultipleAccountsInfo(windowCandidates.map((c) => c.pda));
48492
+ const freeInWindow = windowCandidates.filter((_, i) => windowInfos[i] === null);
48533
48493
  const treasuryShardIdx = randomTreasuryShard();
48534
48494
  const authorFeeShardIdx = randomAuthorFeeShard();
48535
- const filledInLast = CONTENT_SLOTS - freeInLast.length;
48536
- let extendInfo;
48537
- if (filledInLast >= EXTEND_THRESHOLD && lastAlloc.nextAllocSeq === null) {
48538
- extendInfo = {
48539
- currentAllocPda: lastAllocPda,
48540
- newAllocPda: deriveAllocPda(programId, seed, lastAllocSeq + 1)
48541
- };
48542
- }
48543
- const maxFirst = maxFillSlotTextLen(selected.length, !!extendInfo);
48495
+ const extendInline = freeInWindow.length === 0;
48496
+ const selected = extendInline ? pageCandidates(programId, seed, lastAllocSeq + 1).slice(0, DESIRED_CANDIDATES) : shuffle(freeInWindow).slice(0, DESIRED_CANDIDATES);
48497
+ const reserve = extendInline ? PREPARE_ALLOC_MARGINAL_SIZE : 0;
48498
+ const maxFirst = maxFillSlotTextLen(selected.length, reserve);
48544
48499
  const firstChunkLen = Math.min(textBytes.length, maxFirst);
48545
48500
  const firstChunk = textBytes.subarray(0, firstChunkLen);
48546
48501
  const settings = await loadProgramSettings(connection, programId);
@@ -48560,38 +48515,86 @@ async function buildSendMessageTransactions(connection, programId, payerKey, see
48560
48515
  authorFeeShardIdx,
48561
48516
  bodyBytes: firstChunk,
48562
48517
  maxFee,
48563
- extendInfo,
48564
48518
  replyAllocSeq: replyTo?.allocSeq ?? null,
48565
48519
  replySlot: replyTo?.slot ?? null
48566
48520
  });
48567
- const transactions = [new Transaction().add(fillSlotIx)];
48521
+ const firstTx = new Transaction();
48522
+ if (extendInline) {
48523
+ firstTx.add(buildPrepareAllocInstruction(programId, payerKey, seed, lastAllocSeq));
48524
+ }
48525
+ firstTx.add(fillSlotIx);
48526
+ const transactions = [firstTx];
48568
48527
  if (firstChunkLen < textBytes.length) {
48569
48528
  const remaining = textBytes.subarray(firstChunkLen);
48570
48529
  const appendMax = maxAppendChunkLen();
48571
48530
  const contentPda = selected[0].pda;
48572
- const threadPda2 = deriveThreadPda(programId, seed);
48573
48531
  const settingsPda = deriveSettingsPda(programId);
48574
48532
  const treasuryShardPda = deriveTreasuryShardPda(programId, treasuryShardIdx);
48575
48533
  const authorFeeShardPda = deriveAuthorFeePda(programId, seed, authorFeeShardIdx);
48534
+ const emptyRent = BigInt(await connection.getMinimumBalanceForRentExemption(0, "confirmed"));
48535
+ const oneByteRent = BigInt(await connection.getMinimumBalanceForRentExemption(1, "confirmed"));
48536
+ const rentPerByte = oneByteRent - emptyRent;
48576
48537
  for (let offset2 = 0; offset2 < remaining.length; offset2 += appendMax) {
48577
48538
  const chunk = remaining.subarray(offset2, offset2 + appendMax);
48539
+ const appendBaseFee = BigInt(chunk.length) * rentPerByte * baseFeeBps / 10000n;
48540
+ const appendMaxFee = appendBaseFee * MAX_FEE_SLIPPAGE_NUM + 1n;
48578
48541
  const appendIx = buildAppendContentInstruction(
48579
48542
  programId,
48580
48543
  payerKey,
48581
48544
  contentPda,
48582
- threadPda2,
48545
+ threadPda,
48583
48546
  settingsPda,
48584
48547
  treasuryShardPda,
48585
48548
  authorFeeShardPda,
48586
48549
  chunk,
48587
48550
  treasuryShardIdx,
48588
- authorFeeShardIdx
48551
+ authorFeeShardIdx,
48552
+ appendMaxFee
48589
48553
  );
48590
48554
  transactions.push(new Transaction().add(appendIx));
48591
48555
  }
48592
48556
  }
48593
48557
  return transactions;
48594
48558
  }
48559
+ async function buildExtendAllocTransaction(connection, programId, payerKey, seed) {
48560
+ const threadPda = deriveThreadPda(programId, seed);
48561
+ const thread = await loadThreadNode(connection, programId, threadPda);
48562
+ const lastAllocSeq = thread.lastAllocSeq;
48563
+ const tailCandidates = pageCandidates(programId, seed, lastAllocSeq);
48564
+ const tailInfos = await connection.getMultipleAccountsInfo(tailCandidates.map((c) => c.pda));
48565
+ const freeOnPageN = tailInfos.filter((info) => info === null).length;
48566
+ const filled = CONTENT_SLOTS - freeOnPageN;
48567
+ if (filled < EXTEND_THRESHOLD) return null;
48568
+ return new Transaction().add(buildPrepareAllocInstruction(programId, payerKey, seed, lastAllocSeq));
48569
+ }
48570
+ function buildCreateRootAllocInstruction(programId, payer, seed, messageFee = 0n, title = "") {
48571
+ const titleBytes = new TextEncoder().encode(title);
48572
+ if (titleBytes.length > MAX_TITLE_LEN) {
48573
+ throw new Error(`Title is too long (max ${MAX_TITLE_LEN} bytes)`);
48574
+ }
48575
+ const threadAccount = deriveThreadPda(programId, seed);
48576
+ const allocAccount = deriveAllocPda(programId, seed, 0);
48577
+ const settingsAccount = deriveSettingsPda(programId);
48578
+ const treasuryShardIdx = randomTreasuryShard();
48579
+ const treasuryShard = deriveTreasuryShardPda(programId, treasuryShardIdx);
48580
+ return new TransactionInstruction({
48581
+ programId,
48582
+ keys: [
48583
+ signerMeta(payer),
48584
+ signerMeta(threadAccount),
48585
+ writableMeta(allocAccount),
48586
+ readonlyMeta(settingsAccount),
48587
+ writableMeta(treasuryShard),
48588
+ readonlyMeta(SystemProgram.programId)
48589
+ ],
48590
+ data: Buffer3.from(CreateRootAllocInstr.serialize({
48591
+ tag: Instruction.CreateRootAlloc,
48592
+ messageFee,
48593
+ treasuryShardIdx,
48594
+ title: titleBytes
48595
+ }))
48596
+ });
48597
+ }
48595
48598
  async function createRootAlloc(connection, programId, payer, messageFee = 0n, title = "", onSent) {
48596
48599
  const programKey = new PublicKey(programId);
48597
48600
  const threadKeypair = Keypair.generate();
@@ -48610,60 +48613,48 @@ async function createRootAlloc(connection, programId, payer, messageFee = 0n, ti
48610
48613
  allocPda: deriveAllocPda(programKey, seed, 0).toBase58()
48611
48614
  };
48612
48615
  }
48613
- function buildPrepareAllocInstruction(programId, payer, seed, allocSeq) {
48614
- const currentAllocPda = deriveAllocPda(programId, seed, allocSeq);
48615
- const newAllocPda = deriveAllocPda(programId, seed, allocSeq + 1);
48616
- const threadAccount = deriveThreadPda(programId, seed);
48616
+ function buildCloseAccountInstruction(programId, payer, targetAccount, likesAccount) {
48617
48617
  return new TransactionInstruction({
48618
48618
  programId,
48619
48619
  keys: [
48620
- { pubkey: payer, isSigner: true, isWritable: true },
48621
- { pubkey: currentAllocPda, isSigner: false, isWritable: true },
48622
- { pubkey: newAllocPda, isSigner: false, isWritable: true },
48623
- { pubkey: threadAccount, isSigner: false, isWritable: true },
48624
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48620
+ signerMeta(payer),
48621
+ writableMeta(targetAccount),
48622
+ writableMeta(likesAccount)
48625
48623
  ],
48626
- data: Buffer3.from(PrepareAllocInstr.serialize({
48627
- tag: Instruction.PrepareAlloc,
48628
- allocSeq
48629
- }))
48624
+ data: Buffer3.from(TagOnlyInstr.serialize({ tag: Instruction.CloseAccount }))
48630
48625
  });
48631
48626
  }
48632
- function buildSweepAuthorFeesInstruction(programId, seed, threadAccount, authorWallet, shardIndices) {
48633
- const keys = [
48634
- { pubkey: threadAccount, isSigner: false, isWritable: false },
48635
- { pubkey: authorWallet, isSigner: true, isWritable: true }
48636
- ];
48637
- for (const idx of shardIndices) {
48638
- keys.push({
48639
- pubkey: deriveAuthorFeePda(programId, seed, idx),
48640
- isSigner: false,
48641
- isWritable: true
48642
- });
48643
- }
48627
+ function buildLikeContentInstruction(programId, payer, seed, allocSeq, slot, maxFee) {
48628
+ const likesAccount = deriveLikesPda(programId, seed, allocSeq);
48629
+ const contentAccount = deriveContentPda(programId, seed, allocSeq, slot);
48630
+ const threadAccount = deriveThreadPda(programId, seed);
48631
+ const settingsAccount = deriveSettingsPda(programId);
48632
+ const treasuryShardIdx = randomTreasuryShard();
48633
+ const authorFeeShardIdx = randomAuthorFeeShard();
48634
+ const treasuryShard = deriveTreasuryShardPda(programId, treasuryShardIdx);
48635
+ const authorFeeShard = deriveAuthorFeePda(programId, seed, authorFeeShardIdx);
48644
48636
  return new TransactionInstruction({
48645
48637
  programId,
48646
- keys,
48647
- data: Buffer3.from(SweepAuthorFeesInstr.serialize({
48648
- tag: Instruction.SweepAuthorFees,
48649
- shardIndices: Uint8Array.from(shardIndices)
48638
+ keys: [
48639
+ signerMeta(payer),
48640
+ writableMeta(likesAccount),
48641
+ readonlyMeta(contentAccount),
48642
+ readonlyMeta(threadAccount),
48643
+ readonlyMeta(settingsAccount),
48644
+ writableMeta(treasuryShard),
48645
+ writableMeta(authorFeeShard),
48646
+ readonlyMeta(SystemProgram.programId)
48647
+ ],
48648
+ data: Buffer3.from(LikeContentInstr.serialize({
48649
+ tag: Instruction.LikeContent,
48650
+ allocSeq,
48651
+ slot,
48652
+ treasuryShardIdx,
48653
+ authorFeeShardIdx,
48654
+ maxFee
48650
48655
  }))
48651
48656
  });
48652
48657
  }
48653
- function buildCloseAccountInstruction(programId, payer, targetAccount, likesAccount) {
48654
- const keys = [
48655
- { pubkey: payer, isSigner: true, isWritable: true },
48656
- { pubkey: targetAccount, isSigner: false, isWritable: true }
48657
- ];
48658
- if (likesAccount) {
48659
- keys.push({ pubkey: likesAccount, isSigner: false, isWritable: true });
48660
- }
48661
- return new TransactionInstruction({
48662
- programId,
48663
- keys,
48664
- data: Buffer3.from(TagOnlyInstr.serialize({ tag: Instruction.CloseAccount }))
48665
- });
48666
- }
48667
48658
  function buildInitThreadAccessInstruction(programId, authority, seed, enabled) {
48668
48659
  const threadAccount = deriveThreadPda(programId, seed);
48669
48660
  const accessAccount = deriveAccessPda(programId, seed);
@@ -48672,11 +48663,11 @@ function buildInitThreadAccessInstruction(programId, authority, seed, enabled) {
48672
48663
  return new TransactionInstruction({
48673
48664
  programId,
48674
48665
  keys: [
48675
- { pubkey: authority, isSigner: true, isWritable: true },
48676
- { pubkey: threadAccount, isSigner: false, isWritable: false },
48677
- { pubkey: accessAccount, isSigner: false, isWritable: true },
48678
- { pubkey: treasuryShard, isSigner: false, isWritable: true },
48679
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48666
+ signerMeta(authority),
48667
+ readonlyMeta(threadAccount),
48668
+ writableMeta(accessAccount),
48669
+ writableMeta(treasuryShard),
48670
+ readonlyMeta(SystemProgram.programId)
48680
48671
  ],
48681
48672
  data: Buffer3.from(InitThreadAccessInstr.serialize({
48682
48673
  tag: Instruction.InitThreadAccess,
@@ -48689,8 +48680,8 @@ function buildSetThreadAccessInstruction(programId, authority, accessAccount, en
48689
48680
  return new TransactionInstruction({
48690
48681
  programId,
48691
48682
  keys: [
48692
- { pubkey: authority, isSigner: true, isWritable: true },
48693
- { pubkey: accessAccount, isSigner: false, isWritable: true }
48683
+ signerMeta(authority),
48684
+ writableMeta(accessAccount)
48694
48685
  ],
48695
48686
  data: Buffer3.from(SetThreadAccessInstr.serialize({
48696
48687
  tag: Instruction.SetThreadAccess,
@@ -48698,71 +48689,7 @@ function buildSetThreadAccessInstruction(programId, authority, accessAccount, en
48698
48689
  }))
48699
48690
  });
48700
48691
  }
48701
- function buildAddToWhitelistInstruction(programId, authority, seed, wallet) {
48702
- const accessAccount = deriveAccessPda(programId, seed);
48703
- const entryAccount = deriveAccessEntryPda(programId, seed, wallet);
48704
- return new TransactionInstruction({
48705
- programId,
48706
- keys: [
48707
- { pubkey: authority, isSigner: true, isWritable: true },
48708
- { pubkey: accessAccount, isSigner: false, isWritable: true },
48709
- { pubkey: entryAccount, isSigner: false, isWritable: true },
48710
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48711
- ],
48712
- data: Buffer3.from(WalletArgInstr.serialize({
48713
- tag: Instruction.AddToWhitelist,
48714
- wallet: wallet.toBytes()
48715
- }))
48716
- });
48717
- }
48718
- function buildRemoveFromWhitelistInstruction(programId, authority, seed, wallet) {
48719
- const accessAccount = deriveAccessPda(programId, seed);
48720
- const entryAccount = deriveAccessEntryPda(programId, seed, wallet);
48721
- return new TransactionInstruction({
48722
- programId,
48723
- keys: [
48724
- { pubkey: authority, isSigner: true, isWritable: true },
48725
- { pubkey: accessAccount, isSigner: false, isWritable: true },
48726
- { pubkey: entryAccount, isSigner: false, isWritable: true },
48727
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48728
- ],
48729
- data: Buffer3.from(WalletArgInstr.serialize({
48730
- tag: Instruction.RemoveFromWhitelist,
48731
- wallet: wallet.toBytes()
48732
- }))
48733
- });
48734
- }
48735
- function buildSetMessageFeeInstruction(programId, authority, threadAccount, fee) {
48736
- return new TransactionInstruction({
48737
- programId,
48738
- keys: [
48739
- { pubkey: authority, isSigner: true, isWritable: true },
48740
- { pubkey: threadAccount, isSigner: false, isWritable: true }
48741
- ],
48742
- data: Buffer3.from(FeeU64Instr.serialize({ tag: Instruction.SetMessageFee, fee }))
48743
- });
48744
- }
48745
- function buildSetLikeFeeInstruction(programId, authority, threadAccount, fee) {
48746
- return new TransactionInstruction({
48747
- programId,
48748
- keys: [
48749
- { pubkey: authority, isSigner: true, isWritable: true },
48750
- { pubkey: threadAccount, isSigner: false, isWritable: true }
48751
- ],
48752
- data: Buffer3.from(FeeU64Instr.serialize({ tag: Instruction.SetLikeFee, fee }))
48753
- });
48754
- }
48755
- function buildSetEntryFeeInstruction(programId, authority, accessAccount, fee) {
48756
- return new TransactionInstruction({
48757
- programId,
48758
- keys: [
48759
- { pubkey: authority, isSigner: true, isWritable: true },
48760
- { pubkey: accessAccount, isSigner: false, isWritable: true }
48761
- ],
48762
- data: Buffer3.from(FeeU64Instr.serialize({ tag: Instruction.SetEntryFee, fee }))
48763
- });
48764
- }
48765
- function buildRequestAccessInstruction(programId, payer, seed) {
48692
+ function buildRequestAccessInstruction(programId, payer, seed, maxFee) {
48766
48693
  const accessAccount = deriveAccessPda(programId, seed);
48767
48694
  const entryAccount = deriveAccessEntryPda(programId, seed, payer);
48768
48695
  const threadAccount = deriveThreadPda(programId, seed);
@@ -48774,127 +48701,111 @@ function buildRequestAccessInstruction(programId, payer, seed) {
48774
48701
  return new TransactionInstruction({
48775
48702
  programId,
48776
48703
  keys: [
48777
- { pubkey: payer, isSigner: true, isWritable: true },
48778
- { pubkey: accessAccount, isSigner: false, isWritable: true },
48779
- { pubkey: entryAccount, isSigner: false, isWritable: true },
48780
- { pubkey: threadAccount, isSigner: false, isWritable: false },
48781
- { pubkey: settingsAccount, isSigner: false, isWritable: false },
48782
- { pubkey: treasuryShard, isSigner: false, isWritable: true },
48783
- { pubkey: authorFeeShard, isSigner: false, isWritable: true },
48784
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48704
+ signerMeta(payer),
48705
+ writableMeta(accessAccount),
48706
+ writableMeta(entryAccount),
48707
+ readonlyMeta(threadAccount),
48708
+ readonlyMeta(settingsAccount),
48709
+ writableMeta(treasuryShard),
48710
+ writableMeta(authorFeeShard),
48711
+ readonlyMeta(SystemProgram.programId)
48785
48712
  ],
48786
48713
  data: Buffer3.from(RequestAccessInstr.serialize({
48787
48714
  tag: Instruction.RequestAccess,
48788
48715
  treasuryShardIdx,
48789
- authorFeeShardIdx
48790
- }))
48791
- });
48792
- }
48793
- function buildLikeContentInstruction(programId, payer, seed, allocSeq, slot, maxFee) {
48794
- const likesAccount = deriveLikesPda(programId, seed, allocSeq);
48795
- const contentAccount = deriveContentPda(programId, seed, allocSeq, slot);
48796
- const threadAccount = deriveThreadPda(programId, seed);
48797
- const settingsAccount = deriveSettingsPda(programId);
48798
- const treasuryShardIdx = randomTreasuryShard();
48799
- const authorFeeShardIdx = randomAuthorFeeShard();
48800
- const treasuryShard = deriveTreasuryShardPda(programId, treasuryShardIdx);
48801
- const authorFeeShard = deriveAuthorFeePda(programId, seed, authorFeeShardIdx);
48802
- return new TransactionInstruction({
48803
- programId,
48804
- keys: [
48805
- { pubkey: payer, isSigner: true, isWritable: true },
48806
- { pubkey: likesAccount, isSigner: false, isWritable: true },
48807
- { pubkey: contentAccount, isSigner: false, isWritable: false },
48808
- { pubkey: threadAccount, isSigner: false, isWritable: false },
48809
- { pubkey: settingsAccount, isSigner: false, isWritable: false },
48810
- { pubkey: treasuryShard, isSigner: false, isWritable: true },
48811
- { pubkey: authorFeeShard, isSigner: false, isWritable: true },
48812
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48813
- ],
48814
- data: Buffer3.from(LikeContentInstr.serialize({
48815
- tag: Instruction.LikeContent,
48816
- allocSeq,
48817
- slot,
48818
- treasuryShardIdx,
48819
48716
  authorFeeShardIdx,
48820
48717
  maxFee
48821
48718
  }))
48822
48719
  });
48823
48720
  }
48824
- function buildAddToBlacklistInstruction(programId, authority, seed, wallet) {
48721
+ function buildAclEntryInstruction(tag, programId, authority, seed, wallet) {
48825
48722
  const accessAccount = deriveAccessPda(programId, seed);
48826
48723
  const entryAccount = deriveAccessEntryPda(programId, seed, wallet);
48827
48724
  return new TransactionInstruction({
48828
48725
  programId,
48829
48726
  keys: [
48830
- { pubkey: authority, isSigner: true, isWritable: true },
48831
- { pubkey: accessAccount, isSigner: false, isWritable: true },
48832
- { pubkey: entryAccount, isSigner: false, isWritable: true },
48833
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48727
+ signerMeta(authority),
48728
+ writableMeta(accessAccount),
48729
+ writableMeta(entryAccount),
48730
+ readonlyMeta(SystemProgram.programId)
48834
48731
  ],
48835
48732
  data: Buffer3.from(WalletArgInstr.serialize({
48836
- tag: Instruction.AddToBlacklist,
48733
+ tag,
48837
48734
  wallet: wallet.toBytes()
48838
48735
  }))
48839
48736
  });
48840
48737
  }
48738
+ function buildAddToWhitelistInstruction(programId, authority, seed, wallet) {
48739
+ return buildAclEntryInstruction(Instruction.AddToWhitelist, programId, authority, seed, wallet);
48740
+ }
48741
+ function buildRemoveFromWhitelistInstruction(programId, authority, seed, wallet) {
48742
+ return buildAclEntryInstruction(Instruction.RemoveFromWhitelist, programId, authority, seed, wallet);
48743
+ }
48744
+ function buildAddToBlacklistInstruction(programId, authority, seed, wallet) {
48745
+ return buildAclEntryInstruction(Instruction.AddToBlacklist, programId, authority, seed, wallet);
48746
+ }
48841
48747
  function buildRemoveFromBlacklistInstruction(programId, authority, seed, wallet) {
48842
- const accessAccount = deriveAccessPda(programId, seed);
48843
- const entryAccount = deriveAccessEntryPda(programId, seed, wallet);
48748
+ return buildAclEntryInstruction(Instruction.RemoveFromBlacklist, programId, authority, seed, wallet);
48749
+ }
48750
+ function buildAddToFeeWhitelistInstruction(programId, authority, seed, wallet) {
48751
+ return buildAclEntryInstruction(Instruction.AddToFeeWhitelist, programId, authority, seed, wallet);
48752
+ }
48753
+ function buildRemoveFromFeeWhitelistInstruction(programId, authority, seed, wallet) {
48754
+ return buildAclEntryInstruction(Instruction.RemoveFromFeeWhitelist, programId, authority, seed, wallet);
48755
+ }
48756
+ var FEE_TAG = {
48757
+ base: Instruction.SetBaseFee,
48758
+ authorCut: Instruction.SetAuthorFeeCut,
48759
+ entryCut: Instruction.SetEntryCut,
48760
+ likeCut: Instruction.SetLikeCut
48761
+ };
48762
+ function buildSetMessageFeeInstruction(programId, authority, threadAccount, fee) {
48844
48763
  return new TransactionInstruction({
48845
48764
  programId,
48846
48765
  keys: [
48847
- { pubkey: authority, isSigner: true, isWritable: true },
48848
- { pubkey: accessAccount, isSigner: false, isWritable: true },
48849
- { pubkey: entryAccount, isSigner: false, isWritable: true },
48850
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48766
+ signerMeta(authority),
48767
+ writableMeta(threadAccount)
48851
48768
  ],
48852
- data: Buffer3.from(WalletArgInstr.serialize({
48853
- tag: Instruction.RemoveFromBlacklist,
48854
- wallet: wallet.toBytes()
48855
- }))
48769
+ data: Buffer3.from(FeeU64Instr.serialize({ tag: Instruction.SetMessageFee, fee }))
48856
48770
  });
48857
48771
  }
48858
- function buildAddToFeeWhitelistInstruction(programId, authority, seed, wallet) {
48859
- const accessAccount = deriveAccessPda(programId, seed);
48860
- const entryAccount = deriveAccessEntryPda(programId, seed, wallet);
48772
+ function buildSetLikeFeeInstruction(programId, authority, threadAccount, fee) {
48861
48773
  return new TransactionInstruction({
48862
48774
  programId,
48863
48775
  keys: [
48864
- { pubkey: authority, isSigner: true, isWritable: true },
48865
- { pubkey: accessAccount, isSigner: false, isWritable: true },
48866
- { pubkey: entryAccount, isSigner: false, isWritable: true },
48867
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48776
+ signerMeta(authority),
48777
+ writableMeta(threadAccount)
48868
48778
  ],
48869
- data: Buffer3.from(WalletArgInstr.serialize({
48870
- tag: Instruction.AddToFeeWhitelist,
48871
- wallet: wallet.toBytes()
48872
- }))
48779
+ data: Buffer3.from(FeeU64Instr.serialize({ tag: Instruction.SetLikeFee, fee }))
48873
48780
  });
48874
48781
  }
48875
- function buildRemoveFromFeeWhitelistInstruction(programId, authority, seed, wallet) {
48876
- const accessAccount = deriveAccessPda(programId, seed);
48877
- const entryAccount = deriveAccessEntryPda(programId, seed, wallet);
48782
+ function buildSetEntryFeeInstruction(programId, authority, accessAccount, fee) {
48878
48783
  return new TransactionInstruction({
48879
48784
  programId,
48880
48785
  keys: [
48881
- { pubkey: authority, isSigner: true, isWritable: true },
48882
- { pubkey: accessAccount, isSigner: false, isWritable: true },
48883
- { pubkey: entryAccount, isSigner: false, isWritable: true },
48884
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48786
+ signerMeta(authority),
48787
+ writableMeta(accessAccount)
48885
48788
  ],
48886
- data: Buffer3.from(WalletArgInstr.serialize({
48887
- tag: Instruction.RemoveFromFeeWhitelist,
48888
- wallet: wallet.toBytes()
48789
+ data: Buffer3.from(FeeU64Instr.serialize({ tag: Instruction.SetEntryFee, fee }))
48790
+ });
48791
+ }
48792
+ function buildSweepAuthorFeesInstruction(programId, seed, threadAccount, authorWallet, shardIndices) {
48793
+ const keys = [
48794
+ readonlyMeta(threadAccount),
48795
+ signerMeta(authorWallet)
48796
+ ];
48797
+ for (const idx of shardIndices) {
48798
+ keys.push(writableMeta(deriveAuthorFeePda(programId, seed, idx)));
48799
+ }
48800
+ return new TransactionInstruction({
48801
+ programId,
48802
+ keys,
48803
+ data: Buffer3.from(SweepAuthorFeesInstr.serialize({
48804
+ tag: Instruction.SweepAuthorFees,
48805
+ shardIndices: Uint8Array.from(shardIndices)
48889
48806
  }))
48890
48807
  });
48891
48808
  }
48892
- var FEE_TAG = {
48893
- base: Instruction.SetBaseFee,
48894
- authorCut: Instruction.SetAuthorFeeCut,
48895
- entryCut: Instruction.SetEntryCut,
48896
- likeCut: Instruction.SetLikeCut
48897
- };
48898
48809
  function buildFollowInstruction(tag, opts) {
48899
48810
  const { programId, user, seed } = opts;
48900
48811
  const threadAccount = deriveThreadPda(programId, seed);
@@ -48903,11 +48814,11 @@ function buildFollowInstruction(tag, opts) {
48903
48814
  return new TransactionInstruction({
48904
48815
  programId,
48905
48816
  keys: [
48906
- { pubkey: user, isSigner: true, isWritable: true },
48907
- { pubkey: followRegistry, isSigner: false, isWritable: true },
48908
- { pubkey: followerShard, isSigner: false, isWritable: true },
48909
- { pubkey: threadAccount, isSigner: false, isWritable: false },
48910
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }
48817
+ signerMeta(user),
48818
+ writableMeta(followRegistry),
48819
+ writableMeta(followerShard),
48820
+ readonlyMeta(threadAccount),
48821
+ readonlyMeta(SystemProgram.programId)
48911
48822
  ],
48912
48823
  data: Buffer3.from(TagOnlyInstr.serialize({ tag }))
48913
48824
  });
@@ -48981,6 +48892,18 @@ async function sendTransactions(payer, txs) {
48981
48892
  }
48982
48893
  return sigs;
48983
48894
  }
48895
+ async function trySendBestEffort(payer, tx) {
48896
+ try {
48897
+ const { connection } = loadConfig();
48898
+ tx.feePayer = payer.publicKey;
48899
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
48900
+ tx.recentBlockhash = blockhash;
48901
+ tx.sign(payer);
48902
+ return await connection.sendRawTransaction(tx.serialize());
48903
+ } catch {
48904
+ return null;
48905
+ }
48906
+ }
48984
48907
  function explorerTx(signature) {
48985
48908
  const { rpcUrl } = loadConfig();
48986
48909
  const cluster = rpcUrl.includes("devnet") ? "?cluster=devnet" : rpcUrl.includes("testnet") ? "?cluster=testnet" : rpcUrl.includes("localhost") || rpcUrl.includes("127.0.0.1") ? "?cluster=custom" : "";
@@ -49113,7 +49036,7 @@ function registerMessagingTools(server) {
49113
49036
  "send_message",
49114
49037
  {
49115
49038
  title: "Send message",
49116
- description: "Post a text message to a channel as the agent wallet. Handles fees, slot selection and chunking of long messages automatically.",
49039
+ description: "Post a text message to a channel as the agent wallet. Handles fees, slot selection and chunking of long messages automatically. Posting is decoupled from growing the alloc chain: after the post confirms, a best-effort page extension is fired when the tail page is filling up (its failure never affects the post).",
49117
49040
  inputSchema: {
49118
49041
  channel: channelArg,
49119
49042
  text: external_exports.string().min(1).describe("Message text (UTF-8, up to 8192 bytes)."),
@@ -49135,10 +49058,19 @@ function registerMessagingTools(server) {
49135
49058
  replyTo
49136
49059
  );
49137
49060
  const signatures = await sendTransactions(payer, txs);
49061
+ let extendSignature = null;
49062
+ try {
49063
+ const extendTx = await buildExtendAllocTransaction(connection, programId, payer.publicKey, seed);
49064
+ if (extendTx) extendSignature = await trySendBestEffort(payer, extendTx);
49065
+ } catch {
49066
+ extendSignature = null;
49067
+ }
49138
49068
  return jsonResult({
49139
49069
  channel: seedToAddress(seed),
49140
49070
  signatures,
49141
- explorer: signatures.map(explorerTx)
49071
+ explorer: signatures.map(explorerTx),
49072
+ extendSignature,
49073
+ extendExplorer: extendSignature ? explorerTx(extendSignature) : null
49142
49074
  });
49143
49075
  })
49144
49076
  );
@@ -49151,15 +49083,17 @@ function registerMessagingTools(server) {
49151
49083
  channel: channelArg,
49152
49084
  allocSeq: external_exports.number().int().describe("allocSeq of the target message."),
49153
49085
  slot: external_exports.number().int().describe("slot of the target message."),
49154
- text: external_exports.string().min(1).describe("Text chunk to append.")
49086
+ text: external_exports.string().min(1).describe("Text chunk to append."),
49087
+ maxFee: external_exports.union([external_exports.number(), external_exports.string()]).optional().describe("Max base fee in lamports willing to pay for the append (slippage cap). Default 100000000 (0.1 SOL).")
49155
49088
  }
49156
49089
  },
49157
- handler(async ({ channel, allocSeq, slot, text }) => {
49090
+ handler(async ({ channel, allocSeq, slot, text, maxFee }) => {
49158
49091
  const { programId } = loadConfig();
49159
49092
  const payer = loadWallet();
49160
49093
  const seed = resolveSeed(channel);
49161
49094
  const treasuryShardIdx = randomTreasuryShard();
49162
49095
  const authorFeeShardIdx = randomAuthorFeeShard();
49096
+ const cap = maxFee === void 0 ? 100000000n : toLamports(maxFee);
49163
49097
  const ix = buildAppendContentInstruction(
49164
49098
  programId,
49165
49099
  payer.publicKey,
@@ -49170,7 +49104,8 @@ function registerMessagingTools(server) {
49170
49104
  deriveAuthorFeePda(programId, seed, authorFeeShardIdx),
49171
49105
  new TextEncoder().encode(text),
49172
49106
  treasuryShardIdx,
49173
- authorFeeShardIdx
49107
+ authorFeeShardIdx,
49108
+ cap
49174
49109
  );
49175
49110
  const signature = await sendInstructions(payer, [ix]);
49176
49111
  return jsonResult({ signature, explorer: explorerTx(signature) });
@@ -49180,7 +49115,7 @@ function registerMessagingTools(server) {
49180
49115
  "prepare_alloc",
49181
49116
  {
49182
49117
  title: "Prepare alloc",
49183
- description: "Pre-create the next alloc page in a channel so there are free slots ahead of demand. Mostly useful for high-traffic channels.",
49118
+ description: "Manually pre-create the next alloc page (allocSeq + 1) in a channel so there are free slots ahead of demand. send_message already does this best-effort; use this to force-extend a high-traffic channel. Racy by design: it fails with InvalidAllocSeq if the chain tail moved on.",
49184
49119
  inputSchema: {
49185
49120
  channel: channelArg,
49186
49121
  allocSeq: external_exports.number().int().describe("Current alloc seq to extend from.")
@@ -49246,14 +49181,18 @@ function registerMessagingTools(server) {
49246
49181
  "request_access",
49247
49182
  {
49248
49183
  title: "Request channel access",
49249
- description: "Pay the entry fee to join a gated channel so the agent wallet can post in it.",
49250
- inputSchema: { channel: channelArg }
49184
+ description: "Pay the entry fee to join a gated channel so the agent wallet can post in it. The current on-chain entry fee is used as the slippage cap unless maxFee is given.",
49185
+ inputSchema: {
49186
+ channel: channelArg,
49187
+ maxFee: external_exports.union([external_exports.number(), external_exports.string()]).optional().describe("Max entry fee in lamports willing to pay (slippage cap). Defaults to the current on-chain entry fee.")
49188
+ }
49251
49189
  },
49252
- handler(async ({ channel }) => {
49253
- const { programId } = loadConfig();
49190
+ handler(async ({ channel, maxFee }) => {
49191
+ const { connection, programId } = loadConfig();
49254
49192
  const payer = loadWallet();
49255
49193
  const seed = resolveSeed(channel);
49256
- const ix = buildRequestAccessInstruction(programId, payer.publicKey, seed);
49194
+ const cap = maxFee === void 0 ? (await loadThreadAccess(connection, programId, deriveAccessPda(programId, seed))).entryFee : toLamports(maxFee);
49195
+ const ix = buildRequestAccessInstruction(programId, payer.publicKey, seed, cap);
49257
49196
  const signature = await sendInstructions(payer, [ix]);
49258
49197
  return jsonResult({ signature, explorer: explorerTx(signature) });
49259
49198
  })
@@ -49644,7 +49583,7 @@ function registerThreadAdminTools(server) {
49644
49583
  async function main() {
49645
49584
  const config2 = loadConfig();
49646
49585
  const wallet = loadWallet();
49647
- const server = new McpServer({ name: "txtcel-mcp", version: "0.1.3" });
49586
+ const server = new McpServer({ name: "txtcel-mcp", version: "0.2.0" });
49648
49587
  registerMessagingTools(server);
49649
49588
  registerFollowTools(server);
49650
49589
  registerReadTools(server);