Production-ready TypeScript building blocks for the parts of Stellar/Soroban backend development that every serious project ends up rebuilding from scratch.
Every backend talking to Stellar/Soroban eventually hits the same five problems, usually in this order:
- Your RPC node starts rate-limiting you because you're calling
getContractDataon a loop. - You add a cache, and now you have stale-data bugs.
- You need to submit a batch of transactions and some inevitably fail, so you write retry logic — badly, under deadline pressure.
- You wire up a Horizon event stream and get bitten by a duplicate event on reconnect.
- You deploy a contract and realize you never actually verified the WASM you're uploading matches what you built.
Most teams solve each of these individually, inside their own app — which means the retry logic, the cache invalidation, and the idempotency checks are all under-tested and never looked at again. This package pulls those five problems out into small, independently-tested modules, so you can pick the ones you need instead of writing your own version of each.
This is a toolkit, not a framework. Each module works standalone. Import what you need.
Building on Stellar means wiring together RPC calls, Soroban contract reads, Horizon event streams, and WASM deployments. The plumbing is repetitive — this SDK packages it into well-tested, composable TypeScript modules so you can focus on your contract logic.
| Module | Description |
|---|---|
contractCache |
Two-tier LRU + Redis cache for Soroban contract state reads |
rpcRateLimiter |
Token-bucket rate limiter for Stellar RPC / Horizon API calls |
transactionBatcher |
Concurrent Soroban transaction submission with exponential backoff |
horizonEventHandler |
Secure, idempotent handler for Horizon streaming events |
wasmPipeline |
Streaming WASM validation and hash pipeline for Soroban contract uploads |
npm install soroban-ts-sdk
# or
pnpm add soroban-ts-sdkPeer dependencies (install separately based on what you use):
npm install @stellar/stellar-sdk ioredisAvoid hammering your RPC node with repeated getContractData calls on the same key:
import { ContractCache } from 'soroban-ts-sdk';
import { Contract, SorobanRpc } from '@stellar/stellar-sdk';
const server = new SorobanRpc.Server('https://soroban-testnet.stellar.org');
const cache = new ContractCache({ maxSize: 500, defaultTtlLedgers: 5 });
const balance = await cache.getOrFetch(
contractId,
'balance',
[new Address(userAddress)],
(key) => server.getContractData(contractId, key, SorobanRpc.Durability.Persistent)
);Respect Stellar RPC and Horizon rate limits without dropping requests:
import { RpcRateLimiter } from 'soroban-ts-sdk';
import Redis from 'ioredis';
const redis = new Redis();
const limiter = RpcRateLimiter.create('soroban-rpc', redis, {
maxTokens: 100,
refillRate: 100 / 60,
windowSeconds: 60,
});
app.use('/rpc', limiter.middleware());Submit multiple Soroban transactions concurrently with automatic retry:
import { TransactionBatcher } from 'soroban-ts-sdk';
const batcher = new TransactionBatcher({
maxConcurrency: 5,
batchSize: 10,
retryInterval: 1000,
maxRetries: 3,
});
const txEnvelopes = [...];
const results = await batcher.submit(txEnvelopes, (xdr) =>
server.sendTransaction(xdr)
);
results.forEach((r) => {
if (r.status === 'fulfilled') console.log('hash:', r.result.hash);
else console.error('failed:', r.error.message);
});Process Stellar Horizon payment, ledger, and contract events with idempotency:
import { HorizonEventHandler } from 'soroban-ts-sdk';
const handler = HorizonEventHandler.create({
secret: process.env.HORIZON_WEBHOOK_SECRET!,
onEvent: async (event) => {
if (event.type === 'payment') {
await processPayment(event);
}
},
});
app.post('/horizon/events', handler.middleware());Hash, validate, and prepare a Soroban contract WASM before deploying:
import { WasmPipeline } from 'soroban-ts-sdk';
const pipeline = new WasmPipeline({ sandboxDir: './contracts/target' });
const result = await pipeline.process('my_contract.wasm');
console.log('SHA-256:', result.sha256);
console.log('Size: ', result.totalBytes, 'bytes');
console.log('Valid: ', result.integrityVerified);typescript-backend-utils/
├── src/
│ ├── contractCache.ts # Soroban contract state LRU+Redis cache
│ ├── rpcRateLimiter.ts # Token-bucket rate limiter for RPC/Horizon
│ ├── transactionBatcher.ts # Concurrent transaction submission + retry
│ ├── horizonEventHandler.ts # Horizon streaming event handler
│ ├── wasmPipeline.ts # WASM streaming hash + validation pipeline
│ └── index.ts # Barrel export
├── tests/
│ ├── contractCache.test.ts
│ ├── rpcRateLimiter.test.ts
│ ├── transactionBatcher.test.ts
│ ├── horizonEventHandler.test.ts
│ └── wasmPipeline.test.ts
├── .github/workflows/
│ └── ci.yml # Build + test on every push/PR
├── package.json
├── tsconfig.json
├── CONTRIBUTING.md
└── SECURITY.md
- Node.js 20+
- npm / pnpm / yarn
- (Optional) Redis for rate limiter and cache tests
git clone https://github.com/eogenyi23-creator/typescript-backend-utils
cd typescript-backend-utils
npm installnpm run buildnpm testnpm run lintContributions are welcome! See CONTRIBUTING.md for guidelines.
Issues tagged good first issue are beginner-friendly starting points.
MIT — see LICENSE.