Thank you for helping build the VeriTix SDK! This document describes how to pick up a stub module and implement it correctly.
- Prerequisites
- Development Setup
- Project Structure
- Implementing a Module Stub
- Writing Tests
- Code Style
- Submitting a Pull Request
| Tool | Version |
|---|---|
| Node.js | ≥ 20 |
| npm | ≥ 10 |
| A Stellar Testnet account | Stellar Laboratory |
# 1. Fork and clone the repo
git clone https://github.com/veritix/contract-sdk.git
cd contract-sdk
# 2. Use the pinned Node.js version (.nvmrc)
nvm use
# 3. Install dependencies (run npm ci after nvm use)
npm ci
# 4. Copy the env example and fill in your values
cp .env.example .env
# 5. Build to verify the TypeScript compiles
npm run build
# 6. Run the existing test suite
npm testsrc/
client.ts ← Main VeriTixClient class
modules/ ← One file per contract feature area
types/index.ts ← Shared TypeScript interfaces (do not edit lightly)
utils/
errors.ts ← VeriTixError + parseSorobanError
network.ts ← getTestnetConfig, getMainnetConfig, getHorizonUrl
transaction.ts ← buildContractCall, simulateTransaction, submitTransaction
index.ts ← Public barrel export
tests/ ← Jest test files mirroring src/modules/
Each method in src/modules/*.ts currently contains a // TODO: implement comment and throws new Error('not implemented'). Here is the standard pattern to follow when implementing one:
All write operations go through three utility functions that live in src/utils/transaction.ts:
buildContractCall → simulateTransaction → submitTransaction
These must be completed before module write methods can work.
// Example: EscrowModule.getEscrow
async getEscrow(id: bigint): Promise<EscrowRecord | null> {
const account = await this.server.getAccount(this.config.sourceAddress);
const tx = await buildContractCall(
this.server,
account,
this.config.contractId,
'get_escrow',
[nativeToScVal(id, { type: 'u64' })],
this.config.networkPassphrase,
);
const { transaction } = await simulateTransaction(this.server, tx);
// Parse the ScVal return value into an EscrowRecord
// Return null if the contract returns void / None
...
}// Example: EscrowModule.createEscrow
async createEscrow(params: CreateEscrowParams): Promise<TransactionResult> {
if (!this.keypair) throw new Error('keypair required for write operations');
const account = await this.server.getAccount(this.keypair.publicKey());
const tx = await buildContractCall(...);
const { transaction } = await simulateTransaction(this.server, tx);
return submitTransaction(this.server, transaction, this.keypair);
}Catch raw RPC errors and pass them through parseSorobanError:
} catch (err) {
throw parseSorobanError(err);
}- Tests live in
tests/and mirrorsrc/modules/. - Each stub test already exists; replace the
rejects.toThrow('not implemented')assertion with real expectations. - Use Jest mocks to avoid hitting the live network in unit tests.
- Integration tests (hitting Testnet) should be placed in a separate
tests/integration/directory and skipped in CI unlessINTEGRATION=trueis set.
Run tests:
npm test # unit tests only
npm test -- --watch # watch mode- Prettier handles formatting:
npm run format - ESLint handles linting:
npm run lint - All public API methods must have JSDoc comments with
@param,@returns, and@throwstags. - Prefer
bigintfor token amounts and IDs; never usenumberfor amounts. - Use
_prefixfor intentionally unused parameters (satisfiesno-unused-vars).
- Branch from
main:git checkout -b feat/implement-token-module - Implement and test your change.
- Update
CHANGELOG.md— if your PR modifies any file undersrc/, you must add an entry under the[Unreleased]section. CI will fail the build ifsrc/changes are detected without a correspondingCHANGELOG.mdupdate. Use the following format:Use## [Unreleased] ### Added - Brief description of the new feature or fix (#PR-number)
### Addedfor new features,### Fixedfor bug fixes,### Changedfor non-breaking changes, and### Removedfor removed functionality. - Run
npm run build && npm test && npm run lint— all must pass. - Open a PR against
mainwith a clear description of what was implemented. - Reference the relevant module in the PR title, e.g.
feat(token): implement mint and burn.
Happy building! 🚀
This project uses Changesets for versioning and changelog generation.
- After making changes, run
npm run changesetand follow the prompts. - Commit the generated
.changeset/*.mdfile alongside your code changes. - When merged to
main, the Changeset PR bot will open a "Version Packages" PR automatically. - Merging that PR bumps versions and publishes to npm via the release workflow.