Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

## [Unreleased]

### Added
- `Percentage` branded type: compile-time guarantee that a number is validated to [0, 100] (`src/types/escrow.ts`)
- `asPercentage()` runtime guard: validates and casts a number to `Percentage`, throws `RangeError` on NaN, Infinity, or out-of-range values (`src/types/escrow.ts`)
- `Distribution` type: `recipient: string`, `percentage: Percentage` (`src/types/escrow.ts`)
- `ReleaseParams` type: `escrowAccountId: string`, `distribution: Distribution[]` (`src/types/escrow.ts`)
- `ReleasedPayment` type: `recipient: string`, `amount: string` (`src/types/escrow.ts`)
- `ReleaseResult` type: `successful`, `txHash`, `ledger`, `payments: ReleasedPayment[]` (`src/types/escrow.ts`)
- Unit tests for all escrow release types in `tests/unit/types/escrow.test.ts`

## [0.1.0] - 2026-03-23

### Added
Expand Down
109 changes: 96 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"prebuild": "rm -rf dist",
"prebuild": "rimraf dist",
"test": "jest --testPathPattern=tests/unit --passWithNoTests",
"test:cov": "jest --testPathPattern=tests/unit --coverage --passWithNoTests",
"test:watch": "jest --testPathPattern=tests/unit --watch",
Expand All @@ -39,6 +39,7 @@
"jest": "^29.0.0",
"msw": "^2.0.0",
"prettier": "^3.0.0",
"rimraf": "^6.1.3",
"ts-jest": "^29.0.0",
"typescript": "^5.0.0"
},
Expand Down
9 changes: 8 additions & 1 deletion src/escrow/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
// escrow module — to be implemented
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function createEscrowAccount(..._args: unknown[]): unknown { return undefined; }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function lockCustodyFunds(..._args: unknown[]): unknown { return undefined; }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function anchorTrustHash(..._args: unknown[]): unknown { return undefined; }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function verifyEventHash(..._args: unknown[]): unknown { return undefined; }
33 changes: 32 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,33 @@
export const SDK_VERSION = '0.1.0';
// Exports will be added here as each module is implemented

// 1. Main class
export { StellarSDK } from './sdk';
export { StellarSDK as default } from './sdk';

// 2. Error classes
export {
SdkError,
ValidationError,
AccountNotFoundError,
EscrowNotFoundError,
InsufficientBalanceError,
HorizonSubmitError,
TransactionTimeoutError,
MonitorTimeoutError,
FriendbotError,
ConditionMismatchError,
} from './utils/errors';

// 3. Escrow types (canonical source for Signer + Thresholds)
export type { CreateEscrowParams, Signer, Thresholds, EscrowAccount } from './types/escrow';
export { EscrowStatus } from './types/escrow';

// 4. Network types (Signer + Thresholds excluded to avoid conflict)
export type { SDKConfig, KeypairResult, AccountInfo, BalanceInfo } from './types/network';

// 5. Transaction types
export type { SubmitResult, TransactionStatus } from './types/transaction';

// 6. Standalone functions
export { createEscrowAccount, lockCustodyFunds, anchorTrustHash, verifyEventHash } from './escrow';
export { buildMultisigTransaction } from './transactions';
1 change: 1 addition & 0 deletions src/sdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export class StellarSDK {}
3 changes: 2 additions & 1 deletion src/transactions/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
// transactions module — to be implemented
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function buildMultisigTransaction(..._args: unknown[]): unknown { return undefined; }
68 changes: 57 additions & 11 deletions src/types/escrow.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { Signer, Thresholds } from './network';

export { Signer, Thresholds };

export interface CreateEscrowParams {
adopterPublicKey: string;
ownerPublicKey: string;
Expand All @@ -7,17 +11,6 @@ export interface CreateEscrowParams {
metadata?: { adoptionId: string; petId: string };
}

export interface Signer {
publicKey: string;
weight: number;
}

export interface Thresholds {
low: number;
medium: number;
high: number;
}

export interface EscrowAccount {
accountId: string;
transactionHash: string;
Expand All @@ -34,3 +27,56 @@ export enum EscrowStatus {
SETTLED = 'SETTLED',
NOT_FOUND = 'NOT_FOUND',
}

// ---------------------------------------------------------------------------
// Branded type: Percentage
// Ensures Distribution.percentage is constrained to 0-100 at the type level.
// Use `asPercentage()` to create a validated value at runtime.
// ---------------------------------------------------------------------------

/** A number branded to signal it has been validated as 0 ≤ n ≤ 100. */
export type Percentage = number & { readonly __brand: 'Percentage' };

/**
* Validates and casts a plain number to a `Percentage` branded type.
* Rejects NaN, Infinity, -Infinity, and any value outside [0, 100].
* @throws {RangeError} if value is not a finite number in [0, 100].
*/
export function asPercentage(value: number): Percentage {
if (!Number.isFinite(value) || value < 0 || value > 100) {
throw new RangeError(
`Percentage must be between 0 and 100, got ${value}`,
);
}
return value as Percentage;
}

// ---------------------------------------------------------------------------
// Escrow release / settlement types (Issue #34)
// ---------------------------------------------------------------------------

/** A single recipient and their share of the escrow release. */
export interface Distribution {
recipient: string;
percentage: Percentage;
}

/** Parameters required to trigger an escrow release settlement. */
export interface ReleaseParams {
escrowAccountId: string;
distribution: Distribution[];
}

/** Recorded payment made to a recipient during settlement. */
export interface ReleasedPayment {
recipient: string;
amount: string;
}

/** Result returned after an escrow release transaction is submitted. */
export interface ReleaseResult {
successful: boolean;
txHash: string;
ledger: number;
payments: ReleasedPayment[];
}
28 changes: 27 additions & 1 deletion src/types/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,30 @@ export interface BalanceInfo {
accountId: string;
balance: string;
lastModifiedLedger: number;
}
}

export interface AnchorParams {
hash: string;
eventType: string;
metadata?: Record<string, unknown>;
}

export interface AnchorResult {
txHash: string;
ledger: number;
verified: boolean;
timestamp: Date;
}

export interface VerifyParams {
expectedHash: string;
transactionHash: string;
}

export interface VerifyResult {
isValid: boolean;
timestamp?: Date;
ledger?: number;
confirmations?: number;
reason?: string;
}
Loading
Loading