@titon-network/themis-sdk
v0.3.0
Published
TypeScript SDK for Themis — TON-native sealed-bid threshold-decryption protocol. Encrypt bids under Atlas's BLS group key, submit ciphertexts to a chamber, decode the post-reveal callback off-chain. Built on @titon-network/atlas-sdk + @titon-network/forge
Maintainers
Readme
@titon-network/themis-sdk
TypeScript SDK for Themis — TON-native sealed-bid threshold-decryption.
Latest: 0.1.0 on npm.
Audit status
🛡️ TSA-assisted manual audit — zero outstanding findings. Full report:
AUDIT.md.
| Layer | Result |
|---|---|
| TSA standard checkers (drain-check, bounce-check) over factory + chamber BoC | 0 outstanding findings; per-finding reproduction specs in tests/audit/ |
| Manual review against references/vulnerabilities.md | every receiver, every revert path |
| Contract test suite (pnpm test) | ~350 sandbox tests passing (Smoke / Factory / Chamber / FactoryFanout / RewardPool / MultiBidReveal / HotUpgrade / CryptoVectors / SchemaEvolution / SecurityChecklist / InvariantLint / GasSnapshot / BlsBindings / SealedAMMConsumer / Fuzz) |
| Cross-contract ABI byte-shape pin | tests/WireFormat.spec.ts + tests/OpcodeDrift.spec.ts green |
| Pool / key foundations | Built on the TSA-audited @titon-network/forgeton-sdk (zero findings) + @titon-network/atlas-sdk (zero findings) |
Live testnet: factory at 0QCfbRCmhSZxiMGlqyZoOGOoWClrSYGwimGTSGvOstI_kk69; reference SealedAMM chamber at 0QBsAuXDuxDsn83qmKPerqrwlt6MVIp_JctdbvksfpoMVxFg. Exposed as THEMIS_TESTNET in deployments.ts.
Live mainnet (factory only — chambers on demand): factory at UQCIYfoUB5QEVOwrclvN9LRsCxpntHSLPMYNiPn9TeO363Ht. Multi-op operator set live (n=2, threshold=2, epoch=1). Deployed 2026-05-18. Exposed as THEMIS_MAINNET in deployments.ts.
Building with an AI assistant? Point it at
AGENTS.mdfirst — that's the orientation map. Then load the matching playbook fromskills/(consumer integration, bidder UX, debug, etc.).
Install
npm install @titon-network/themis-sdk @ton/core @ton/ton
# Plus the sibling SDKs your dapp will exercise:
npm install @titon-network/atlas-sdk @titon-network/forgeton-sdkConnect — testnet vs mainnet (the only difference)
The SDK is network-agnostic. You pick the network in two lines: the
TonClient endpoint and which deployment constant you import.
// ── testnet ──────────────────────────────────────────────────────────
import { TonClient } from '@ton/ton';
import { ThemisFactory, THEMIS_TESTNET } from '@titon-network/themis-sdk';
const tonClient = new TonClient({
endpoint: 'https://testnet.toncenter.com/api/v2/jsonRPC',
});
const factory = tonClient.open(ThemisFactory.createFromAddress(THEMIS_TESTNET!.factory));
const { numChambers } = await factory.getNumChambers();
console.log(`connected to testnet — ${numChambers} chambers deployed`);For mainnet, change exactly two lines:
// ── mainnet ──────────────────────────────────────────────────────────
import { TonClient } from '@ton/ton';
import { ThemisFactory, THEMIS_MAINNET } from '@titon-network/themis-sdk';
const tonClient = new TonClient({
endpoint: 'https://toncenter.com/api/v2/jsonRPC', // ← no `testnet.`
});
const factory = tonClient.open(
ThemisFactory.createFromAddress(THEMIS_MAINNET!.factory), // ← was THEMIS_TESTNET
);That's it. The same npm package, same bytecode, same code paths work on
either network — only the TonClient endpoint and the deployment constant
change. Pointing at a custom deployment (your own factory, a private chain)
is just ThemisFactory.createFromAddress(Address.parse('EQ...')) — no
constant required.
Mainnet has no chambers yet — only the factory is deployed. Application developers spawn their own chambers via the permissionless + fee-gated
DeployChamber(~1.5 TON). See the Deploy a chamber example below.
Quick example — deploy a chamber for your consumer protocol
The mainnet factory is live. To build on Themis (MEV-resistant AMM swap, sealed-bid auction, confidential vote, …), deploy your own chamber bound to your consumer contract:
import { TonClient } from '@ton/ton';
import { Address, toNano } from '@ton/core';
import {
ThemisFactory, THEMIS_MAINNET, deployChamberOpts,
} from '@titon-network/themis-sdk';
const tonClient = new TonClient({ endpoint: 'https://toncenter.com/api/v2/jsonRPC' });
const factory = tonClient.open(ThemisFactory.createFromAddress(THEMIS_MAINNET!.factory));
const cfg = await factory.getConfig();
await factory.sendDeployChamber(walletSender, deployChamberOpts({
consumer: Address.parse('EQ...your-consumer-contract...'), // receives RevealCallback (0x95)
chamberOwner: Address.parse('EQ...your-admin-wallet...'), // can Pause / UpdateConfig
submitFee: toNano('0.05'),
revealerReward: toNano('0.1'),
commitDuration: 300, // 5-minute commit window
revealDuration: 120, // 2-minute reveal grace
callbackGas: toNano('0.3'),
}, cfg));
// Pick up the chamber address from the EvtChamberDeployed (0xB0) event on the factory.The same call works on testnet — swap THEMIS_MAINNET for THEMIS_TESTNET and
the testnet endpoint. See skills/themis-deploy-chamber.md
for the full walkthrough, and skills/themis-integrate-consumer.md
for the consumer-contract 0x95 RevealCallback receiver pattern.
What you can build with it
| Pattern | How | Skill |
|---------|-----|-------|
| Bidder UX — wallet / frontend submitting sealed bids | encryptBid → chamber.sendSubmitCiphertext | themis-bidder-flow |
| Dapp consumer — Tolk contract that settles revealed bids | Receiver at 0x95 RevealCallback + per-bid iteration | themis-integrate-consumer |
| Chamber deploy — stand up Themis for your protocol | factory.sendDeployChamber with your params | themis-deploy-chamber |
| Debug a revert — exit code → root cause → one-line fix | explainError(code) + symptom table | themis-debug |
Are you running an automaton operator? You don't import this SDK directly — the
@titon-network/automatondaemon embeds it. Add chamber addresses to your daemon'sconfig.themis.chambers(or the terraform module'sthemis_chambersknob) and the daemon serves them. See../../../automaton/CLAUDE.mdfor the operator-side surface.
Quick example — encrypt a bid
import { encryptBid } from '@titon-network/themis-sdk';
import { toNano } from '@ton/core';
const { groupPk } = await chamber.getGroupKey(); // 48-byte G1 from Atlas
const { roundId } = await chamber.getCurrentRound();
const cfg = await chamber.getConfig();
const { c1, aeadCell } = encryptBid({
plaintext: Buffer.from(JSON.stringify({ side: 'buy', size: 100 })),
groupPk,
chamber: chamber.address,
roundId,
});
await chamber.sendSubmitCiphertext(
walletSender,
cfg.submitFee + toNano('0.05'), // value
queryId,
c1,
aeadCell,
);Quick example — decode a RevealCallback
When the chamber dispatches your consumer's 0x95 RevealCallback, decode + decrypt in your off-chain settler (TVM has no ChaCha20 — see GUARANTEES.md for the rationale):
import { decodeRevealCallback, decryptBid } from '@titon-network/themis-sdk';
const decoded = decodeRevealCallback(messageBody);
// decoded = { roundId, chamber, bidCount, bids: [{ idx, sender, c1, D, aeadCt, ... }] }
for (const bid of decoded.bids) {
try {
const plaintext = decryptBid({
aeadCell: bid.aeadCt,
c1: bid.c1,
D: bid.D,
chamber: decoded.chamber,
roundId: decoded.roundId,
});
// …your settlement logic here.
} catch {
// Poly1305 MAC failed → dishonest D from operators for this bid.
// Skip. (Integrity-by-AEAD; see GUARANTEES.md "trust model".)
}
}Surface
ThemisFactory— contract wrapper for the singleton factory. Every send (sendDeployChamber,sendPause, …) + every getter (getOwnership,getNumChambers,getChildCodeHash,getGroupKey, …).ThemisChamber— contract wrapper for per-consumer chambers.sendSubmitCiphertext,sendRevealRound,sendAdvanceRound, owner-admin sends, plusgetCurrentRound,getOperator,getConfig,getGroupKey.- Crypto —
encryptBid/decryptBidfor bidders + consumers,groupDecryptfor operators,newEphemeral/bidderSharedSecretfor custom flows. ElGamal-on-G1 + ChaCha20-Poly1305 + HKDF-SHA256. - Operator helpers —
buildReveal({ chamber, roundId, groupEpoch, entries, groupKey })returns the three refssendRevealRoundneeds. PlussimulateDkg(n)+thresholdSign(dkg, msg)for multi-op tests. - Event decoder —
decodeEvent(cell)returns a typedThemisEventdiscriminated union.decodeEventsmaps batches;decodeRevealCallbacktyped-unwraps the 0x95 message body. - Opcode + error + schema-version constants — every
OP_*,EVT_*,E_*, and version byte is exported. Full tables:OPCODES.md+ERRORS.md.
Full surface inventory: AGENTS.md → "Public surface".
Documentation map
| File | When to read |
|------|--------------|
| AGENTS.md | First doc to load for any AI agent — quick reference table, protocol-in-one-page, writing-code guidance. |
| skills/ | Persona-grouped playbooks: integrate-consumer, bidder-flow, deploy-chamber, debug. |
| ERRORS.md | Every exit code with prose hint + fix-it suggestion. |
| OPCODES.md | Every inbound / outbound / event opcode. |
| GUARANTEES.md | What Themis does + does NOT promise. Read before mainnet. |
License
MIT — same as the rest of the titon.network workspace.
