New:Socket for Asana Is Now Available.Learn more
Get Started

@txtcel/protocol

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@txtcel/protocol

TypeScript SDK for the Txtcel Solana program: instruction builders, account codecs and PDA derivation.

Source
npmnpm
Version
0.2.0
Version published
Weekly downloads
15
-75.81%
Maintainers
1
Weekly downloads
 
Created
Source

@txtcel/protocol

TypeScript SDK for the Txtcel Solana program — a thin, framework-agnostic wrapper around the on-chain protocol. It gives you:

  • Instruction buildersbuild*Instruction(...) for every program instruction.
  • High-level helpersbuildSendMessageTransactions, createRootAlloc, …
  • PDA derivationderive*Pda(...) helpers that mirror the program's seeds.
  • Account codecs — borsh/zorsh schemas to decode on-chain accounts.
  • Account loadersload* helpers that fetch + decode in one call.
  • Constants & types — discriminator tags, seeds, instruction indices, layout sizes.

No React, no wallet adapter — just @solana/web3.js primitives, so it works in the browser, in Node scripts and in tests.

Note: This README is LLM-generated from the source and reviewed by hand. The code is the source of truth. A condensed, agent-oriented overview lives in llms.txt.

Install

npm install @txtcel/protocol @solana/web3.js

@solana/web3.js (v1) is a peer dependency — install it in your app.

Quick start

import { Connection, Keypair, PublicKey } from '@solana/web3.js'
import {
  createRootAlloc,
  buildSendMessageTransactions,
  loadThreadNode,
} from '@txtcel/protocol'

const connection = new Connection('https://api.mainnet-beta.solana.com')
const programId = '<YOUR_PROGRAM_ID>'
const payer = Keypair.generate()

// 1. Create a channel (thread). `seed` is the thread's 32-byte identity.
const { seed, threadPda } = await createRootAlloc(connection, programId, payer)

// 2. Post a message (auto-chunks if it exceeds one transaction).
const txs = await buildSendMessageTransactions(
  connection, new PublicKey(programId), payer.publicKey, seed, 'gm world',
)

// 3. Read the thread back.
const thread = await loadThreadNode(connection, new PublicKey(programId), new PublicKey(threadPda))

All builders take the programId explicitly — the SDK has no hard-coded address, so the same package works across clusters and deployments.

seed — most functions take a seed: Uint8Array. This is the thread's 32-byte identity (the thread account's pubkey bytes). createRootAlloc returns it; for an existing thread it's new PublicKey(threadPda).toBytes(). Child PDAs (alloc/content/access/likes/author-fee) all derive from it.

API reference

Everything is re-exported from the package root (src/index.ts).

High-level messaging helpers

createRootAlloc(connection, programId, payer, messageFee?, title?, onSent?)

Creates a new thread (channel) + root alloc and confirms it with a local Keypair payer. Generates a fresh thread keypair internally (it co-signs).

  • programId: string, payer: Keypair
  • messageFee: bigint = 0n — per-message author fee in lamports.
  • title: string = '' — channel title (≤ MAX_TITLE_LEN bytes).
  • onSent?: () => void — called right after the tx is submitted.
  • Returns { signature, seed, threadPda, allocPda }.

createRootAllocWithWallet(connection, programId, wallet, messageFee?, title?, onSent?)

Same as above but for a wallet-adapter style signer (WalletSigner = { publicKey, signTransaction }). The wallet is fee payer; the generated thread keypair is an extra signer.

buildSendMessageTransactions(connection, programId, payerKey, seed, text, replyTo?)

The recommended way to post a message. Returns an array of Transactions: the first is a FillSlot, followed by AppendContent txs when text is larger than one transaction. It:

  • loads the thread + last alloc, scans for free content slots (up to DESIRED_CANDIDATES = 3, looking into the next alloc if needed),
  • shuffles candidates to reduce write contention,
  • decides whether to auto-extend the alloc chain (EXTEND_THRESHOLD),
  • computes a maxFee slippage cap (≈ 2× base fee + message fee),
  • picks random treasury/author-fee shards.

replyTo?: { allocSeq: number; slot: number } | null threads the message as a reply. Throws 'No free slots in thread' / 'Text must not be empty' / 'Body is too long'.

buildSendMessageTransaction(...)deprecated

Returns only the first transaction. Use buildSendMessageTransactions for chunked sending.

type WalletSigner

{ publicKey: PublicKey; signTransaction: (tx: Transaction) => Promise<Transaction> }

Instruction builders

Each returns a TransactionInstruction you add to your own Transaction. They mirror the on-chain instructions 1:1 (see the program README for account semantics, fees and errors).

BuilderOn-chain instructionKey args
buildCreateRootAllocInstruction(programId, payer, seed, messageFee?, title?)CreateRootAllocthread keypair pubkey as seed, picks a random treasury shard.
buildFillSlotInstruction(opts)FillSlotsee options below.
buildPrepareAllocInstruction(programId, payer, seed, allocSeq)PrepareAllocpre-creates allocSeq + 1.
buildAppendContentInstruction(programId, payer, contentAccount, threadAccount, settingsAccount, treasuryShard, authorFeeShard, chunk, treasuryShardIdx, authorFeeShardIdx)AppendContentappend bytes to your message.
buildLikeContentInstruction(programId, payer, seed, allocSeq, slot, maxFee)LikeContentrandom shards chosen internally.
buildCloseAccountInstruction(programId, payer, targetAccount, likesAccount?)CloseAccountoptional likes account resets the slot counter.
buildSweepTreasuryInstruction(programId, treasuryWallet, shardIndices)SweepTreasury[settings, treasury, ...shards].
buildSweepAuthorFeesInstruction(programId, seed, threadAccount, authorWallet, shardIndices)SweepAuthorFeesauthor signs.
buildInitSettingsInstruction(programId, authority, treasury)InitSettingsderives ProgramData PDA; upgrade authority signs.
buildSetTreasuryInstruction(programId, authority, treasury)SetTreasuryadmin only.
buildSetAdminInstruction(programId, authority, newAdmin)SetAdminadmin only.
buildSetFeeInstruction(programId, authority, kind, feeBps)SetBaseFee/SetAuthorFeeCut/SetEntryCut/SetLikeCutkind: FeeKind.
buildSetBaseFeeInstruction(programId, authority, feeBps)SetBaseFeeadmin only.
buildSetAuthorFeeCutInstruction(programId, authority, feeBps)SetAuthorFeeCutadmin only.
buildSetEntryCutInstruction(programId, authority, feeBps)SetEntryCutadmin only.
buildSetLikeCutInstruction(programId, authority, feeBps)SetLikeCutadmin only.
buildSetMessageFeeInstruction(programId, authority, threadAccount, fee)SetMessageFeethread author.
buildSetLikeFeeInstruction(programId, authority, threadAccount, fee)SetLikeFeethread author.
buildSetEntryFeeInstruction(programId, authority, accessAccount, fee)SetEntryFeethread admin.
buildInitThreadAccessInstruction(programId, authority, seed, enabled)InitThreadAccessthread author.
buildSetThreadAccessInstruction(programId, authority, accessAccount, enabled)SetThreadAccessthread admin.
buildAddToWhitelistInstruction(programId, authority, seed, wallet)AddToWhitelistthread admin.
buildRemoveFromWhitelistInstruction(programId, authority, seed, wallet)RemoveFromWhitelistthread admin.
buildAddToBlacklistInstruction(programId, authority, seed, wallet)AddToBlacklistthread admin.
buildRemoveFromBlacklistInstruction(programId, authority, seed, wallet)RemoveFromBlacklistthread admin.
buildAddToFeeWhitelistInstruction(programId, authority, seed, wallet)AddToFeeWhitelistthread admin.
buildRemoveFromFeeWhitelistInstruction(programId, authority, seed, wallet)RemoveFromFeeWhitelistthread admin.
buildRequestAccessInstruction(programId, payer, seed)RequestAccesspay entry fee to join.

buildFillSlotInstruction(opts) options

{
  programId: PublicKey
  payer: PublicKey
  seed: Uint8Array
  candidates: Array<{ allocSeq: number; slot: number; pda: PublicKey }>
  treasuryShardIdx: number
  authorFeeShardIdx: number
  bodyBytes: Uint8Array          // opaque payload; UTF-8 for the default text kind
  kind?: number                  // message-type discriminator, defaults to KIND_TEXT
  maxFee: bigint                 // slippage cap on base + author fee
  extendInfo?: { currentAllocPda: PublicKey; newAllocPda: PublicKey }
  replyAllocSeq?: number | null
  replySlot?: number | null
}

The builder appends the mandatory access + per-wallet entry PDAs (derived for payer) at fixed positions, and the two extend accounts when extendInfo is set.

type FeeKind

'base' | 'authorCut' | 'entryCut' | 'likeCut' — selects which platform fee buildSetFeeInstruction updates.

PDA derivation

All mirror the program's seeds exactly (see the program README PDA table).

FunctionReturns
deriveSettingsPda(programId)global settings PDA.
deriveThreadPda(programId, seed)the thread account address (= new PublicKey(seed); thread is a full-address account, not a PDA).
deriveAllocPda(programId, seed, allocSeq)alloc node PDA.
deriveContentPda(programId, seed, allocSeq, slot)content (message) PDA.
deriveAccessPda(programId, seed)ThreadAccess PDA.
deriveAccessEntryPda(programId, seed, wallet)per-wallet AccessEntry PDA.
deriveLikesPda(programId, seed, allocSeq)AllocLikes PDA.
deriveTreasuryShardPda(programId, shard)treasury vault shard PDA.
deriveAuthorFeePda(programId, seed, shard)author-fee vault shard PDA.
deriveProgramDataPda(programId)BPF loader ProgramData account (holds upgrade authority).
randomTreasuryShard()random index in [0, N_TREASURY_SHARDS).
randomAuthorFeeShard()random index in [0, N_AUTHOR_FEE_SHARDS).

Helper seed encoders u16Seed(n) / u32Seed(n) are also exported.

Account loaders

Fetch (getAccountInfo, 'confirmed') + validate owner/tag + decode in one call.

LoaderReturnsMissing account
loadThreadNode(connection, programId, pubkey)ThreadNodeDatathrows.
loadAllocNode(connection, programId, pubkey)AllocNodeDatathrows.
loadContentNode(connection, programId, pubkey)ContentNodeDatathrows.
loadProgramSettings(connection, programId)ProgramSettingsData | nullreturns null.
loadThreadAccess(connection, programId, accessKey)ThreadAccessDatathrows.
loadAllocLikes(connection, programId, pubkey)AllocLikesData | nullreturns null.
loadAccessEntries(connection, programId, seed){ whitelist, blacklist, feeExempt } of base58 stringsscans program accounts by tag + seed.

Account codecs

Pure decoders (Uint8Array -> typed data), used by the loaders but exported for when you already have raw account data (e.g. from getProgramAccounts or a websocket subscription).

  • decodeContent(pubkey, data) → ContentNodeData
  • decodeAlloc(pubkey, data) → AllocNodeData
  • decodeThread(pubkey, data) → ThreadNodeData
  • decodeSettings(pubkey, data) → ProgramSettingsData
  • decodeThreadAccess(pubkey, data) → ThreadAccessData
  • decodeAccessEntry(pubkey, data) → AccessEntryData
  • decodeAllocLikes(pubkey, data) → AllocLikesData

The raw zorsh schemas (ContentNodeSchema, FillSlotInstr, …) live in codec/schemas.ts if you need to (de)serialize manually.

Types

type ContentNodeData = {
  pubkey: string
  allocSeq: number
  slot: number
  seed: Uint8Array          // owning thread's identity bytes
  author: string
  createdAt: string         // ISO-8601
  replyAllocSeq: number | null
  replySlot: number | null
  contentKind: number       // KIND_* discriminator
  body: Uint8Array          // opaque, raw bytes
  text: string              // decoded UTF-8 when contentKind === KIND_TEXT
}

type AllocNodeData = {
  pubkey: string; seed: Uint8Array; allocSeq: number
  upperAllocSeq: number | null; nextAllocSeq: number | null
}

type ThreadNodeData = {
  pubkey: string; seed: Uint8Array; allocCount: number; lastAllocSeq: number
  author: string; messageFee: bigint; likeFee: bigint; title: string
}

type ProgramSettingsData = {
  pubkey: string; admin: string; treasury: string
  baseFeeBps: number; authorFeeCutBps: number; entryCutBps: number; likeCutBps: number
}

type ThreadAccessData = {
  pubkey: string; seed: Uint8Array; enabled: boolean; admin: string
  entryFee: bigint; whitelistCount: number
}

type AccessEntryData = { pubkey: string; seed: Uint8Array; wallet: string; status: number }

type AllocLikesData = { pubkey: string; allocSeq: number; counts: number[] }

Constants

GroupConstants
Account tagsTAG_CONTENT, TAG_ALLOC, TAG_THREAD, TAG_SETTINGS, TAG_ACCESS, TAG_LIKES, TAG_ACCESS_ENTRY
Content kindsKIND_TEXT
Access statusACCESS_ALLOWED, ACCESS_DENIED, ACCESS_FEE_EXEMPT
Layout sizesCHILDREN_LEN, NEXT_ALLOC_INDEX, CONTENT_SLOTS, EXTEND_THRESHOLD, MAX_BODY_LEN, MAX_TITLE_LEN, INDEX_NONE, PUBKEY_SIZE
ShardsN_TREASURY_SHARDS, N_AUTHOR_FEE_SHARDS
SeedsSEED_SETTINGS, SEED_ALLOC, SEED_CONTENT, SEED_ACCESS, SEED_LIKES, SEED_TREASURY_SHARD, SEED_AUTHOR_FEE, SEED_ACL
MiscInstruction (variant index map), BPF_LOADER_UPGRADEABLE_ID, DESIRED_CANDIDATES, BPS_DIVISOR, TX_POLL_TIMEOUT_MS, TX_POLL_INTERVAL_MS

The Instruction object maps instruction names to their on-chain variant index (e.g. Instruction.FillSlot === 1), matching ProgramInstruction in the program's instruction.rs.

Recipes

Post a message and send it

import { Connection, PublicKey } from '@solana/web3.js'
import { buildSendMessageTransactions } from '@txtcel/protocol'

const txs = await buildSendMessageTransactions(
  connection, programId, wallet.publicKey, seed, longText,
)
for (const tx of txs) {
  tx.feePayer = wallet.publicKey
  tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash
  const signed = await wallet.signTransaction(tx)
  await connection.sendRawTransaction(signed.serialize())
}

Gate a thread, then whitelist a wallet

import { Transaction } from '@solana/web3.js'
import {
  buildInitThreadAccessInstruction,
  buildAddToWhitelistInstruction,
} from '@txtcel/protocol'

const tx = new Transaction()
  .add(buildInitThreadAccessInstruction(programId, author, seed, true))
  .add(buildAddToWhitelistInstruction(programId, author, seed, memberWallet))

Sweep platform revenue

import { buildSweepTreasuryInstruction } from '@txtcel/protocol'

const ix = buildSweepTreasuryInstruction(programId, treasuryWallet, [0, 1, 2])

Build

npm install
npm run build      # tsup -> dist (ESM + CJS + .d.ts)
npm run typecheck

Publishing

npm run build
npm publish        # publishConfig.access is already "public"

License

MIT

Keywords

solana

FAQs

Package last updated on 22 Jun 2026

Related posts