TypeScript SDK for the Sharpy advanced split payment protocol on Stellar Soroban.
- Overview
- Installation
- Quick Start
- Configuration
- API Reference
- Networks
- Error Handling
- Development
- Contributing
- License
The Sharpy SDK provides a typed TypeScript interface to the Sharpy Soroban smart contract. It handles transaction construction, simulation, signing via Freighter, and submission to the Stellar network.
The SDK is designed for use in browser environments with the Freighter wallet extension. All on-chain amounts use bigint (stroops) to avoid floating-point precision issues.
npm install @stellar-sharpy/sdkOr directly from GitHub:
npm install https://github.com/stellar-sharpy/sharpy-sdk.gitimport { SharpyClient, connectWallet, parseAmount, NETWORKS } from "@stellar-sharpy/sdk";
// Connect Freighter wallet
const publicKey = await connectWallet();
// Initialize client with testnet config
const client = new SharpyClient(NETWORKS.testnet);
// Create an invoice splitting 1000 USDC between two recipients
const { invoiceId, txHash } = await client.createInvoice({
creator: publicKey,
recipients: [
{ address: "GABC...RECIPIENT1", amount: parseAmount("600") },
{ address: "GDEF...RECIPIENT2", amount: parseAmount("400") },
],
token: "USDC_CONTRACT_ADDRESS",
deadline: deadlineFromDays(7),
});
console.log(`Invoice #${invoiceId} created: ${txHash}`);
// Pay toward the invoice
await client.pay(publicKey, invoiceId, parseAmount("1000"));
// Fetch invoice status
const invoice = await client.getInvoice(invoiceId);
console.log(invoice.status); // "Released"interface SharpyClientConfig {
rpcUrl: string; // Soroban RPC endpoint
networkPassphrase: string; // Stellar network passphrase
contractId: string; // Deployed Sharpy contract ID
}Use the pre-built NETWORKS constants for testnet and mainnet configuration:
import { NETWORKS } from "@stellar-sharpy/sdk";
const client = new SharpyClient(NETWORKS.testnet);
// or
const client = new SharpyClient(NETWORKS.mainnet);Creates a single invoice on-chain.
const { invoiceId, txHash } = await client.createInvoice({
creator: string, // Payer's public key — must sign
recipients: RecipientAmount[], // Array of { address, amount }
token: string, // Token contract address
deadline: number, // Unix timestamp
escrowEnabled?: boolean, // Default: false
escrowReleaseDelay?: number, // Seconds to hold funds (if escrow enabled)
splitRules?: SplitRule[], // Per-recipient split rules (optional)
});Returns: { invoiceId: number, txHash: string }
Creates up to 10 invoices in a single transaction.
const { invoiceIds, txHash } = await client.createBatch(publicKey, [
{ recipients, token, deadline },
{ recipients, token, deadline },
]);Returns: { invoiceIds: number[], txHash: string }
Creates a recurring invoice that auto-generates the next invoice upon release.
const { invoiceId, txHash } = await client.createRecurring({
creator: string,
recipients: RecipientAmount[],
token: string,
deadline: number,
recurrenceInterval: number, // Seconds between invoices
maxRecurrences: number, // 0 = infinite
});Returns: { invoiceId: number, txHash: string }
Pay toward an invoice. Transfers tokens from the payer's wallet to the contract.
const { txHash } = await client.pay(publicKey, invoiceId, parseAmount("500"));Returns: { txHash: string }
Constraints:
- Invoice must be in
Pendingstatus - Amount must not exceed the remaining unfunded balance
- Invoice must not be past its deadline
Releases an escrow-held invoice once the delay period has passed.
const { txHash } = await client.releaseEscrow(publicKey, invoiceId);Returns: { txHash: string }
Refunds all payers after the deadline has passed and the invoice is not fully funded.
const { txHash } = await client.refund(publicKey, invoiceId);Returns: { txHash: string }
Creator cancels an invoice. Refunds all payments if any have been made.
const { txHash } = await client.cancelInvoice(publicKey, invoiceId);Returns: { txHash: string }
Fetches the full invoice state from the contract. Does not require a wallet.
const invoice = await client.getInvoice(1);
console.log(invoice.status); // "Pending" | "Released" | "Refunded" | "Cancelled"
console.log(invoice.funded); // bigint — total stroops funded
console.log(invoice.recipients); // string[]
console.log(invoice.amounts); // bigint[]Returns: Invoice
Returns the full audit trail for an invoice.
const log = await client.getAuditLog(1);
// [{ action: "pay", actor: "GABC...", timestamp: 1700000000 }, ...]Returns: AuditEntry[]
Returns the ID of the next invoice in a recurring chain, or null if none.
const nextId = await client.getNextRecurring(1);Returns: number | null
Connects the Freighter browser extension and returns the user's public key.
const publicKey = await connectWallet();Throws if Freighter is not installed or the user denies access.
Returns the connected wallet's public key, or null if not connected.
const key = await getWalletPublicKey();Signs a transaction XDR string using Freighter and returns the signed XDR.
const signed = await signTransaction(xdr, NETWORKS.testnet.networkPassphrase);Converts a human-readable USDC string to stroops (bigint). USDC has 7 decimal places.
parseAmount("100") // 1_000_000_000n
parseAmount("1.5") // 15_000_000n
parseAmount("0.0000001") // 1nConverts stroops (bigint) to a human-readable USDC string.
formatAmount(1_000_000_000n) // "100"
formatAmount(15_000_000n) // "1.5"Returns a Unix timestamp days days from now.
deadlineFromDays(7) // Unix timestamp 7 days from nowReturns true if the deadline has already passed.
isExpired(invoice.deadline) // booleanReturns true if the string is a valid Stellar public key.
isValidAddress("GABC...") // booleanTruncates a Stellar address for display.
truncateAddress("GABCDEF...XYZ") // "GABC...RXYZ"Builds a Stellar Expert explorer URL.
explorerUrl("testnet", txHash, "tx")
explorerUrl("mainnet", contractId, "contract")interface RecipientAmount {
address: string;
amount: bigint; // in stroops
}
interface Invoice {
version: number;
creator: string;
recipients: string[];
amounts: bigint[];
tokens: string[];
deadline: number;
funded: bigint;
status: "Pending" | "Released" | "Refunded" | "Cancelled";
escrowEnabled: boolean;
escrowReleaseDelay: number;
completionTime?: number;
}
interface AuditEntry {
action: string;
actor: string;
timestamp: number;
}
interface BatchInvoiceParams {
recipients: RecipientAmount[];
token: string;
deadline: number;
}
type SplitRule =
| { type: "Fixed"; amount: bigint }
| { type: "Percentage"; bps: number }
| { type: "Tiered"; threshold: bigint; bps: number };import { NETWORKS } from "@stellar-sharpy/sdk";
NETWORKS.testnet = {
rpcUrl: "https://soroban-testnet.stellar.org",
networkPassphrase: "Test SDF Network ; September 2015",
contractId: "CAYTIFPD6RFWVHMK5SPPUUIWWAAANHKOJB6GOAJS5SR5MBKZMEY2UODZ",
}
NETWORKS.mainnet = {
rpcUrl: "https://mainnet.sorobanrpc.com",
networkPassphrase: "Public Global Stellar Network ; September 2015",
contractId: "", // coming soon
}All async methods throw a standard Error with a descriptive message. Wrap calls in try/catch:
try {
const { txHash } = await client.pay(publicKey, invoiceId, amount);
} catch (err) {
console.error(err.message);
// Examples:
// "invoice is not pending"
// "payment exceeds remaining balance"
// "Simulation failed: ..."
// "Transaction failed: ERROR"
}- Node.js 20+
- npm 9+
git clone https://github.com/stellar-sharpy/sharpy-sdk.git
cd sharpy-sdk
npm installnpm run build # Outputs ESM, CJS, and TypeScript declarations to dist/
npm run dev # Watch modenpm run lint # tsc --noEmitSee CONTRIBUTING.md.
MIT