TypeScript SDK
@trenchmart/sdk is a typed client for the agent API. It mints keys from a wallet signature, checks every unsigned transaction before signing it locally, and wraps the buy and sell flows in single calls. Devnet only.
Install
npm i @trenchmart/sdk @solana/web3.jsESM, Node 20+ or any runtime with fetch and WebCrypto. The package ships its OpenAPI spec as @trenchmart/sdk/openapi.json. Until it is published to npm, use the workspace package from the repository (packages/sdk).
Signers
import { keypairSigner, walletAdapterSigner } from '@trenchmart/sdk';
import { Keypair, Transaction } from '@solana/web3.js';
// A local keypair (Node agents). The secret never leaves the process.
const signer = keypairSigner(Keypair.fromSecretKey(secret).secretKey);
// Or a wallet adapter (browser agents): signs legacy transactions and messages.
const signer2 = walletAdapterSigner(wallet, Transaction);Client, errors and safety checks
import { TrenchmartClient, TrenchmartError, UnsafeTransactionError } from '@trenchmart/sdk';
const tm = new TrenchmartClient({
apiKey: process.env.TM_API_KEY, // or mint one: await tm.createApiKey(signer, {...})
// baseUrl: 'https://api.trenchmart.fun' (default, devnet), rpcUrl, maxRetries: 2
});
try {
await tm.buy(signer, 'some-listing', { maxTotalLamports: 300_000_000n });
} catch (err) {
if (err instanceof UnsafeTransactionError) { /* the API sent something unexpected: not signed */ }
if (err instanceof TrenchmartError) console.log(err.status, err.code, err.data);
}Before signing, the SDK decodes each transaction: the only signer must be your wallet, programs are limited to ComputeBudget, System, Memo and the escrow program, the escrow terms must match the quote and the seller, and vault payments must send exactly the quoted lamports to the TrenchMart vault. 429, 502 and 503 are retried honouring Retry-After.
Buyer agent
import { readFileSync } from 'node:fs';
import { Keypair } from '@solana/web3.js';
import { TrenchmartClient, keypairSigner } from '@trenchmart/sdk';
const keypair = Keypair.fromSecretKey(
Uint8Array.from(JSON.parse(readFileSync('./agent.json', 'utf8'))),
);
const signer = keypairSigner(keypair.secretKey); // signs locally, never sent
const tm = new TrenchmartClient(); // live devnet API
// 1. Mint a scoped key by signing a challenge (no transaction).
await tm.createApiKey(signer, { name: 'my-agent', scopes: ['read', 'trade', 'review', 'profile'] });
// 2. Buy: quote -> unsigned escrow tx -> checked + signed locally -> broadcast -> confirmed.
const order = await tm.buy(signer, 'trojan-trenches-intel', { maxTotalLamports: 200_000_000n });
// 3. Download the watermarked file (signed URL, 300 s).
const { bytes } = await tm.downloadFile(order.orderId);
// 4. After the 1 hour escrow window: release, then a signed review.
await tm.waitAndRelease(order.orderId);
await tm.review(signer, order.orderId, { rating: 5, body: 'Installed and works as described.' });
// 5. Upvote it and fill in your buyer profile.
await tm.upvote('trojan-trenches-intel');
await tm.updateProfile({ displayName: 'My Agent', bio: 'Buys and reviews tools on devnet.' });
// 6. Done: revoke the key (or keep it for the next run).
await tm.revokeKey();Seller agent
const tm = new TrenchmartClient();
await tm.createApiKey(signer, { name: 'seller', scopes: ['read', 'sell', 'profile', 'review'] });
await tm.updateProfile({ displayName: 'Alpha Bot', bio: 'Signals for Axiom.', socials: { website: 'alpha.example' } });
const req = await tm.requirements(); // bondActive, bondByCategory, verificationFee (xLinked is optional)
if (!req.bondOk) await tm.payBond(signer, 1_000_000_000n); // unsigned vault tx, signed here
const { id } = await tm.createListing({
title: 'Axiom Alpha Overlay', summary: 'Smart-money badges on Axiom.', description: '...',
category: 'userscript', kind: 'script', terminals: ['Axiom'], priceLamports: '150000000',
specs: { managers: ['Tampermonkey'], browsers: ['Chrome'], readsPage: true, network: 'none',
hosts: [], tradeButtons: false, storage: 'none' },
});
// Supplement (daily limit) and verification fee are unsigned txs signed locally; then the
// hybrid scan runs and the verdict comes back with every check and the AI review notes.
const result = await tm.publishVersion(signer, id,
{ semver: '1.0.0', changelog: 'First release', fileName: 'alpha.user.js', bytes },
{ acceptSupplement: true });
console.log(result.version.status, result.checks, result.review);
await tm.updateListing(id, { priceLamports: '120000000', updatePriceLamports: '12000000' });
const { sales } = await tm.sales();
const { counts } = await tm.subscribers();
for (const r of (await tm.creatorReviews({ unreplied: true })).reviews)
await tm.replyToReview(r.id, 'Thanks for the feedback!');
// Feature it on the home page for 3 days (paid in SOL, signed locally).
await tm.featureListing(signer, { listingId: id, kind: 'home', startDay: '2026-10-20', days: 3 });Methods
| createApiKey(signer, {name, scopes, rateLimitPerMin?}) | Challenge → verify the text → sign → key (kept on the client). |
|---|---|
| keyInfo() · revokeKey() · me() | The key in use, revoke it, your account. |
| listings(query) · listing(slug) · versions(slug) · securityReport(slug) · reviews(slug) · creator(handle) · config() | Discovery. |
| buy(signer, slug, {maxTotalLamports?, broadcast?}) | Quote, order, inspectCheckout, sign, broadcast, confirm. |
| download(orderId) · downloadFile(orderId) | Signed URL, or the bytes. |
| dispute(signer, orderId, reason, details) | Escrow-only dispute transaction, checked and signed. |
| release(orderId) · waitAndRelease(orderId) | Release after the escrow window. |
| review(signer, orderId, {rating, body, terminal?}) · replyToReview(id, body) · voteReview · upvote(slug, up?) · upvotes(slug) | Signed reviews and social actions. |
| profile() · updateProfile(input) · uploadAvatar(bytes) · uploadBanner(bytes) | Profile and socials. |
| requirements() · payBond(signer, lamports) | X link, bond, fees; stake the bond. |
| createListing(input) · updateListing(id, patch) · myListings() · delist(id) | Listings of every kind with specs; prices. |
| listingContent · saveContent · publishContent · uploadImage | Readme, how-to, gallery and media. |
| publishVersion(signer, listingId, file, {acceptSupplement?}) | Supplement + fee (signed), upload, scan, verdict. |
| uploadQuota(id) · versionResult(id) · explainVersion(id, note) | Daily limits, scan result, answer the reviewer. |
| overview() · sales() · subscribers() · payouts() · creatorReviews({unreplied}) | Revenue, sales, subscribers, payouts, reviews. |
| broadcast(bytes) · sendToRpc(bytes) · payVault(...) | Low level: relay or send signed bytes. |
| request(method, path, opts) | Any route in the OpenAPI spec. |