NestJS library for Soroban contract interaction: transaction building, simulation, fee estimation, signing, and submission. The frontend and third-party integrators use this instead of calling Soroban directly.
Stack: NestJS, TypeScript, Stellar SDK. Published as @tikka/sdk.
Consumers: Frontend (client), third-party developers.
- Customizable RpcService: Support for custom fetch clients, headers, and automatic failover across multiple nodes.
- Automatic RPC Retries: Built-in retry with exponential backoff for transient 429/5xx/timeout errors, with per-call opt-out.
- Contract Interaction: Type-safe transaction building and simulation for Soroban contracts.
- Wallet Integration: Unified
WalletAdapterinterface supporting Freighter, xBull, and Albedo. - Local Mocking Utilities:
MockWalletAdapterandMockRpcServicefor Storybook, UI tests, and offline development. - Modular Design: Domain-specific modules for Raffles, Tickets, and Users.
Consumers that only need to query raffle data (public dashboards, SSR pages, analytics) can import from the read-only sub-path. This avoids bundling wallet adapters and signing code, producing a significantly smaller bundle.
import { ReadOnlyRaffleService, ReadOnlyUserService, RpcService, resolveNetworkConfig } from '@tikka/sdk/read';
const networkConfig = resolveNetworkConfig('mainnet');
const rpcService = new RpcService(networkConfig);
const raffleService = new ReadOnlyRaffleService(rpcService, networkConfig);
const userService = new ReadOnlyUserService(rpcService, networkConfig);
// List all raffle IDs
const { value: allIds } = await raffleService.getAll();
// Fetch a single raffle
const { value: raffle } = await raffleService.getById(42);
// User participation profile
const { value: profile } = await userService.getProfile('GBIQ4VH3...');
// User raffle ID history
const { value: history } = await userService.getHistory('GBIQ4VH3...');| Export | Description |
|---|---|
ReadOnlyRaffleService |
getAll() — all raffle IDs; getById(id) — single raffle data |
ReadOnlyUserService |
getProfile(addr) — participation stats; getHistory(addr) — raffle IDs |
RpcService |
Soroban RPC client (simulate, getLedger) |
HorizonService |
Horizon account/fee queries |
resolveNetworkConfig, NetworkConfig |
Network configuration helpers |
ContractFn, RaffleStatus |
Contract constants |
ContractResponse |
Shared response envelope type |
| Read-only types | RaffleData, UserParticipation, AssetDescriptor, etc. |
| Utils | Formatting, validation, errors, retry, BigNumber |
The read-only bundle intentionally excludes all signing-related code:
ContractService(requires wallet + TransactionBuilder)TransactionLifecycle(requires wallet + signing)- All wallet adapters (Freighter, xBull, Albedo, LOBSTR, Rabet)
FeeEstimatorServicesep10auth helpers- Write-side service classes (
RaffleService,TicketService) - NestJS modules
cd sdk
pnpm run build:read # outputs to dist/read/src/network/— Customizable Soroban RPC and Horizon services.src/contract/— Core transaction logic and Soroban bindings.src/wallet/— Multi-wallet adapter system.src/modules/— Feature modules (Raffle, Ticket, User).src/utils/— Shared utilities for formatting, validation, and error handling.bin/tikka.cjs— Developer CLI for network testing and contract interaction.
The Tikka SDK includes a command-line interface for smoke testing, network configuration, and contract interaction.
After building the SDK:
npm run build
npm run cli -- --help-n, --network <type>— Target network:testnet(default) ormainnet-j, --json— Output results in JSON format (useful for scripting)--help— Show help for a command--version— Show CLI version
These commands don't require wallet signing and are safe for smoke testing:
Verify CLI configuration and SDK initialization.
tikka config-check # Check on testnet
tikka -n mainnet config-check # Check on mainnet
tikka config-check --json # Output as JSONGet estimated fees for a transaction.
tikka fee-quote CONTRACT_ABC123
tikka fee-quote CONTRACT_ABC123 --function transfer
tikka fee-quote CONTRACT_ABC123 --json
tikka -n mainnet fee-quote CONTRACT_ABC123Read contract data or state.
tikka read CONTRACT_ABC123
tikka read CONTRACT_ABC123 --key account_balance
tikka read CONTRACT_ABC123 --json
tikka -n mainnet read CONTRACT_ABC123List active raffles on the network.
tikka list # List on testnet
tikka list --limit 20 # Limit results
tikka list --json # Output as JSON
tikka -n mainnet list # List on mainnetGet contract status and network information.
tikka info # Get info on testnet
tikka info --json # Output as JSON
tikka -n mainnet info # Get info on mainnetThese commands require wallet integration for signing transactions:
Create a new raffle (interactive).
tikka create # Guided setup on testnet
tikka -n mainnet create # Guided setup on mainnetPurchase raffle tickets (interactive).
tikka buy # Purchase on testnet
tikka -n mainnet buy # Purchase on mainnet# Smoke test network configuration
cd sdk
npm run build
npm run cli -- config-check
# Get fee estimate for a contract (testnet)
npm run cli -- fee-quote CBVG2R3YLEDVGIQKHY6K2HGX55CPJMK5QX2YQE7WALLVIQG5IHVIGISQ
# Query contract state on mainnet
npm run cli -- -n mainnet read CBVG2R3YLEDVGIQKHY6K2HGX55CPJMK5QX2YQE7WALLVIQG5IHVIGISQ --json
# List raffles as JSON for automation
npm run cli -- list --json > raffles.json
# Get help for a specific command
npm run cli -- fee-quote --helpAll commands support the --json flag for machine-readable output:
# Output as JSON
$ tikka config-check --json
{
"version": "0.1.0",
"network": "testnet",
"rpcAvailable": true,
"contractAvailable": true,
"status": "OK"
}
# Errors also format as JSON
$ tikka fee-quote INVALID --json
{
"error": "Invalid contract ID"
}The CLI provides safe error messages that don't leak sensitive information:
- Read-only commands fail gracefully with helpful error messages
- Interactive commands confirm before executing wallet operations
- JSON output includes error details in structured format
- Invalid commands suggest the help flag for usage information
Full TypeDoc reference is auto-generated and hosted on GitHub Pages: crackedstudio.github.io/tikka
To build locally:
npm run docs # generates sdk/docs/
npm run docs:watch # rebuilds on file changeThe SDK includes SEP-10 helpers for building and verifying Stellar web authentication challenges.
import { buildChallenge } from '@tikka/sdk';
import { Networks } from '@stellar/stellar-sdk';
const challengeXdr = buildChallenge({
serverSecret: process.env.SEP10_SERVER_SECRET!,
clientAccount: clientPublicKey,
anchorDomain: 'example.com',
webAuthDomain: 'auth.example.com',
timeout: 300,
networkPassphrase: Networks.TESTNET,
});
return { xdr: challengeXdr };import { Sep10VerificationError, Sep10VerificationErrorCode, verifyResponse } from '@tikka/sdk';
import { Networks } from '@stellar/stellar-sdk';
const verifiedClient = await verifyResponse({
signedChallenge: responseXdr,
serverAccount: serverPublicKey,
clientAccount: clientPublicKey,
anchorDomain: 'example.com',
networkPassphrase: Networks.TESTNET,
nonceValidator: async (nonceBase64) => {
const key = `sep10:nonce:${nonceBase64}`;
const added = await redis.set(key, '1', { NX: true, EX: 300 });
return added === 'OK';
},
});
console.log('Authenticated client:', verifiedClient);try {
await verifyResponse(...);
} catch (err) {
if (err instanceof Sep10VerificationError) {
if (err.code === Sep10VerificationErrorCode.ChallengeExpired) {
// prompt client to request a new challenge
}
}
throw err;
}The docs are organized by module: Raffle · Ticket · Wallet · User · Network · Utils.
Full ecosystem spec: ../docs/ARCHITECTURE.md (section 2 — tikka-sdk).
All runnable examples live in examples/. They use MockWalletAdapter so they compile and run without a browser wallet — swap it for FreighterAdapter or another adapter when you need real signing.
Setup:
cp examples/.env.example examples/.env
# fill in TIKKA_PUBLIC_KEY and any other vars you needType-check all examples (no network required):
npm run examples:checkEnd-to-end walkthrough: bootstrap → create raffle → buy tickets → read state.
| Env var | Required | Default | Description |
|---|---|---|---|
TIKKA_NETWORK |
no | testnet |
testnet | mainnet | standalone |
TIKKA_PUBLIC_KEY |
no | mock key | Stellar G… address |
TIKKA_NETWORK=testnet npx ts-node examples/quickstart.tsCreate a new raffle on-chain with configurable price, asset, and duration.
| Env var | Required | Default | Description |
|---|---|---|---|
TIKKA_NETWORK |
no | testnet |
Network |
TIKKA_PUBLIC_KEY |
yes | — | Signer address |
TIKKA_TICKET_PRICE |
no | 1 |
Amount per ticket |
TIKKA_ASSET_CODE |
no | XLM |
Asset code |
TIKKA_ASSET_ISSUER |
no | "" |
Issuer for non-native assets |
TIKKA_MAX_TICKETS |
no | 50 |
Max tickets available |
TIKKA_DURATION_HOURS |
no | 24 |
Hours until raffle closes |
TIKKA_METADATA_CID |
no | "" |
IPFS CID for metadata |
TIKKA_PUBLIC_KEY=G... npx ts-node examples/create-raffle.ts
# USDC example:
TIKKA_ASSET_CODE=USDC TIKKA_ASSET_ISSUER=GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN \
TIKKA_PUBLIC_KEY=G... npx ts-node examples/create-raffle.tsPurchase tickets for an existing raffle, with pre-flight status checks.
| Env var | Required | Default | Description |
|---|---|---|---|
TIKKA_NETWORK |
no | testnet |
Network |
TIKKA_PUBLIC_KEY |
yes | — | Buyer address |
TIKKA_RAFFLE_ID |
yes | — | Numeric raffle ID |
TIKKA_QUANTITY |
no | 1 |
Number of tickets |
TIKKA_PUBLIC_KEY=G... TIKKA_RAFFLE_ID=1 npx ts-node examples/buy-tickets.tsPoll Soroban contract events (RaffleCreated, TicketPurchased, RaffleFinalized) with optional raffle filter. Runs until Ctrl+C.
Network required — connects to the Soroban RPC to stream events.
| Env var | Required | Default | Description |
|---|---|---|---|
TIKKA_NETWORK |
no | testnet |
Network |
TIKKA_RAFFLE_ID |
no | all | Filter events for one raffle |
TIKKA_POLL_MS |
no | 5000 |
Polling interval (ms) |
TIKKA_CONTRACT_ID |
no | built-in | Override contract address |
TIKKA_NETWORK=testnet npx ts-node examples/listen-events.ts
# filter to raffle #1:
TIKKA_RAFFLE_ID=1 npx ts-node examples/listen-events.tsCold-wallet / air-gapped signing flow. Step 1 builds an unsigned XDR; Step 3 submits a pre-signed XDR. Useful for multisig and hardware wallets.
Network required — Step 1 calls
simulateTransaction; Step 3 submits to the network.
| Env var | Required | Default | Description |
|---|---|---|---|
TIKKA_NETWORK |
no | testnet |
Network |
TIKKA_PUBLIC_KEY |
yes | — | Source account address |
TIKKA_SIGNED_XDR |
no | — | Provide to skip to Step 3 (submit) |
# Step 1 — build unsigned XDR:
TIKKA_PUBLIC_KEY=G... npx ts-node examples/offline-signing.ts
# Step 3 — submit after signing offline:
TIKKA_PUBLIC_KEY=G... TIKKA_SIGNED_XDR=<xdr> npx ts-node examples/offline-signing.tsBrowser-wallet integration demos. These require a browser environment (Albedo opens a popup; Rabet needs the extension installed).
Browser + wallet required — not runnable via
ts-nodein a plain terminal.
The SDK provides a unified interface for multiple Stellar wallets. All adapters implement the same WalletAdapter interface with getPublicKey() and signTransaction(xdr) methods.
| Wallet | Installation | Notes |
|---|---|---|
| Freighter | Browser extension | Most popular, requires extension |
| xBull | Browser extension / PWA | Mobile-friendly |
| Albedo | Web-based | No extension required, popup-based |
| LOBSTR | Browser extension | Large user base |
| Rabet | Browser extension | Lightweight, open-source |
Albedo is a web-based wallet that doesn't require browser extensions. It opens a popup window for authentication and transaction signing, making it ideal for users who prefer not to install extensions.
Key Features:
- No browser extension required
- Works in any modern browser
- Popup-based authentication
- Supports message signing for SIWS (Sign In With Stellar)
- Network switching support
Installation:
npm install @albedo-link/intentBasic Usage:
import { AlbedoAdapter } from '@tikka/sdk';
import { Networks } from '@stellar/stellar-sdk';
// Create adapter with network configuration
const adapter = new AlbedoAdapter({
networkPassphrase: Networks.TESTNET
});
// Check availability (always true in browser)
if (adapter.isAvailable()) {
// Get public key (opens Albedo popup)
const publicKey = await adapter.getPublicKey();
console.log('User public key:', publicKey);
// Sign transaction (opens Albedo popup)
const { signedXdr } = await adapter.signTransaction(xdr, {
networkPassphrase: Networks.TESTNET
});
// Sign message for authentication
const signature = await adapter.signMessage('Sign in to MyApp');
}Advanced Usage:
// Specify which account should sign (for multi-account users)
const { signedXdr } = await adapter.signTransaction(xdr, {
networkPassphrase: Networks.TESTNET,
accountToSign: 'GBQW4KLMRXIMSDWBEWX4AWQKWYW7R3E7SFPSHTUDTFFT22NNUC6COL72'
});
// Get configured network
const network = await adapter.getNetwork();
console.log('Network:', network);Error Handling:
import { TikkaSdkError, TikkaSdkErrorCode } from '@tikka/sdk';
try {
const publicKey = await adapter.getPublicKey();
} catch (err) {
if (err instanceof TikkaSdkError) {
switch (err.code) {
case TikkaSdkErrorCode.UserRejected:
console.log('User cancelled the request');
break;
case TikkaSdkErrorCode.WalletNotInstalled:
console.log('@albedo-link/intent package not installed');
break;
default:
console.log('Unknown error:', err.message);
}
}
}Complete Example: See examples/albedo-wallet.ts for a full working example.
Rabet is a lightweight, open-source browser extension wallet for Stellar. It provides a simple and secure way to manage Stellar assets and interact with dApps.
Key Features:
- Lightweight browser extension
- Open-source and community-driven
- Simple and intuitive interface
- Supports transaction signing
- Network switching support
Installation:
Rabet doesn't require an npm package - it uses the global window.rabet object injected by the browser extension.
Basic Usage:
import { RabetAdapter } from '@tikka/sdk';
import { Networks } from '@stellar/stellar-sdk';
// Create adapter with network configuration
const adapter = new RabetAdapter({
networkPassphrase: Networks.TESTNET
});
// Check if Rabet extension is installed
if (adapter.isAvailable()) {
// Get public key (prompts user to connect)
const publicKey = await adapter.getPublicKey();
console.log('User public key:', publicKey);
// Sign transaction
const { signedXdr } = await adapter.signTransaction(xdr, {
networkPassphrase: Networks.TESTNET
});
}Advanced Usage:
// Use different network
const { signedXdr } = await adapter.signTransaction(xdr, {
networkPassphrase: Networks.PUBLIC
});
// Get configured network
const network = await adapter.getNetwork();
console.log('Network:', network);Error Handling:
import { TikkaSdkError, TikkaSdkErrorCode } from '@tikka/sdk';
try {
const publicKey = await adapter.getPublicKey();
} catch (err) {
if (err instanceof TikkaSdkError) {
switch (err.code) {
case TikkaSdkErrorCode.UserRejected:
console.log('User cancelled the request');
break;
case TikkaSdkErrorCode.WalletNotInstalled:
console.log('Rabet extension not installed. Get it at https://rabet.io');
break;
default:
console.log('Unknown error:', err.message);
}
}
}Important Notes:
- Rabet requires a network passphrase for transaction signing
- Message signing is not supported by Rabet
- Users must have the Rabet browser extension installed from rabet.io
import { FreighterAdapter, XBullAdapter, AlbedoAdapter, LobstrAdapter, RabetAdapter } from '@tikka/sdk';
// Create adapter instance
const adapter = new FreighterAdapter({
networkPassphrase: Networks.TESTNET
});
// Check availability
if (adapter.isAvailable()) {
// Get public key
const publicKey = await adapter.getPublicKey();
// Sign transaction
const { signedXdr } = await adapter.signTransaction(xdr, {
networkPassphrase: Networks.TESTNET
});
}You can select adapters by name or auto-detect available wallets:
const adapters = {
freighter: new FreighterAdapter(),
xbull: new XBullAdapter(),
albedo: new AlbedoAdapter(),
lobstr: new LobstrAdapter(),
};
// Check which are available
const availableAdapters = Object.entries(adapters)
.filter(([, adapter]) => adapter.isAvailable())
.map(([name]) => name);
// Auto-select first available
const selectedAdapter = availableAdapters[0] ? adapters[availableAdapters[0]] : null;- Provide a fetch implementation explicitly when needed:
new RpcService(networkConfig, { endpoint, fetchClient: fetch })
- Wallet adapters that rely on browser extension globals are not available in React Native; use app-native signing or the mock adapter for local prototyping.
- Ensure required polyfills are present in your RN app entrypoint (
buffer,crypto, andstreamwhen your environment requires them).