Get the ILN SDK installed, your wallet connected, and your first invoice submitted in minutes.
This guide covers
@iln/sdk(sdk/), the stable SDK recommended for integrators. There is also an experimental, not-yet-feature-complete@iln/sdk-next(packages/sdk) — seesdk-next-migration.mdif you're deciding which one to build against.
- Node.js 20.x or 22.x (active LTS). The SDK is tested against both versions in CI on every change; other versions may work but are not part of the guaranteed support matrix.
# npm
npm install @iln/sdk
# pnpm (recommended for the monorepo)
pnpm add @iln/sdk
# yarn
yarn add @iln/sdkThe SDK has a peer dependency on @stellar/stellar-sdk. Install it if you don't have it already:
npm install @stellar/stellar-sdkThe ILNSdk class is your single entry point for all contract interactions.
import { ILNSdk, ILN_TESTNET, createFreighterSigner } from "@iln/sdk";
const sdk = new ILNSdk({
...ILN_TESTNET, // contractId, rpcUrl, networkPassphrase
signer: createFreighterSigner(), // connects to the Freighter browser extension
});import { ILNSdk, ILN_TESTNET, createKeypairSigner } from "@iln/sdk";
const sdk = new ILNSdk({
...ILN_TESTNET,
signer: createKeypairSigner(process.env.STELLAR_SECRET_KEY!),
});
ILN_TESTNETexpands to:{ contractId: "CD3TE3IAHM737P236XZL2OYU275ZKD6MN7YH7PYYAXYIGEH55OPEWYJC", rpcUrl: "https://soroban-testnet.stellar.org", networkPassphrase: "Test SDF Network ; September 2015", }
Before sending any transactions on testnet, fund the account with the Stellar Friendbot:
curl "https://friendbot.stellar.org?addr=YOUR_PUBLIC_KEY"The submitInvoice call must be signed by the freelancer (payout recipient).
const invoiceId = await sdk.submitInvoice({
freelancer: "GFREELANCER...", // must match the signer's public key
payer: "GPAYER...",
amount: 10_000_000n, // BigInt, in smallest token units (~1 USDC)
dueDate: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60, // 7 days from now
discountRate: 300, // 300 bps = 3.00% yield for the LP
});
console.log("Invoice ID:", invoiceId); // bigint, e.g. 42nParameter notes:
| Parameter | Type | Notes |
|---|---|---|
amount |
bigint |
Smallest token unit. 10,000,000 ≈ 1 whole token |
dueDate |
number |
Unix timestamp in seconds. Must be 24 h – 365 days from now |
discountRate |
number |
Basis points (1–5000). 300 = 3%. This is the LP's yield |
Any LP can fund an open invoice. The fundInvoice call must be signed by the funder.
await sdk.fundInvoice({
funder: "GLP...", // must match the signer's public key
invoiceId: invoiceId, // bigint returned from submitInvoice
});
console.log("Invoice funded.");The LP's tokens are transferred to the contract and the freelancer receives the payout immediately minus the discount.
When the payer settles the invoice, call markPaid. This must be signed by the payer.
await sdk.markPaid({
invoiceId: invoiceId,
});
console.log("Invoice marked as paid.");The LP receives back the full invoice amount (principal + yield).
Read invoice state at any time — no signer required.
const invoice = await sdk.getInvoice(invoiceId);
console.log(invoice.status); // "Pending" | "Funded" | "Paid" | "Defaulted"
console.log(invoice.amount); // bigint
console.log(invoice.discountRate); // number (bps)
console.log(invoice.funder); // string | null
console.log(invoice.fundedAt); // number (unix seconds) | nullIf the payer does not settle by the dueDate, the LP can reclaim their principal:
await sdk.claimDefault({
funder: "GLP...",
invoiceId: invoiceId,
});// On-chain reputation score for any address (0–100+)
const score = await sdk.getReputation("GADDRESS...");
console.log("Reputation:", score);
// Protocol-wide statistics
const stats = await sdk.getStats();
console.log("Protocol stats:", stats);
// Protocol configuration (min amount, max discount, fee, etc.)
const config = await sdk.getProtocolConfig();
console.log("Min invoice amount:", config.minInvoiceAmount);
console.log("Protocol fee:", config.protocolFeeBps, "bps");The SDK supports Server-Sent Events (SSE) for watching an invoice or an address:
// Watch a specific invoice for state changes
const unsubInvoice = sdk.subscribeToInvoice(invoiceId, (event) => {
console.log("Invoice event:", event);
});
// Watch all events for an address (as freelancer, payer, or LP)
const unsubAddress = sdk.subscribeToAddress("GADDRESS...", (event) => {
console.log("Address event:", event);
});
// Stop listening
unsubInvoice();
unsubAddress();Save this as quickstart.ts and run with ts-node quickstart.ts (or compile first with tsc).
import { ILNSdk, ILN_TESTNET, createKeypairSigner } from "@iln/sdk";
const FREELANCER_SECRET = process.env.FREELANCER_SECRET!;
const PAYER_SECRET = process.env.PAYER_SECRET!;
const LP_SECRET = process.env.LP_SECRET!;
async function main() {
// --- Step 1: Freelancer submits invoice ---
const freelancerSdk = new ILNSdk({
...ILN_TESTNET,
signer: createKeypairSigner(FREELANCER_SECRET),
});
const invoiceId = await freelancerSdk.submitInvoice({
freelancer: (await freelancerSdk["signer"]!.getPublicKey()),
payer: "GPAYER...",
amount: 10_000_000n,
dueDate: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60,
discountRate: 300,
});
console.log("Submitted invoice:", invoiceId);
// --- Step 2: LP funds the invoice ---
const lpSdk = new ILNSdk({
...ILN_TESTNET,
signer: createKeypairSigner(LP_SECRET),
});
await lpSdk.fundInvoice({ funder: "GLP...", invoiceId });
console.log("Invoice funded.");
// --- Step 3: Payer marks invoice as paid ---
const payerSdk = new ILNSdk({
...ILN_TESTNET,
signer: createKeypairSigner(PAYER_SECRET),
});
await payerSdk.markPaid({ invoiceId });
console.log("Invoice paid.");
// --- Step 4: Verify final state ---
const invoice = await freelancerSdk.getInvoice(invoiceId);
console.log("Final status:", invoice.status); // "Paid"
}
main().catch(console.error);| Symptom | Likely Cause | Fix |
|---|---|---|
Freighter extension is not installed or not available |
Freighter not found in browser | Install the Freighter extension and unlock it |
Freighter is connected to a different Stellar network |
Freighter set to mainnet | Switch Freighter to Testnet in its network settings |
A transaction signer is required |
sdk.submitInvoice called without a signer in config |
Pass signer: createFreighterSigner() or createKeypairSigner(secret) to ILNSdk |
submitInvoice must be signed by the freelancer address |
Signer's public key ≠ freelancer param |
Ensure freelancer matches the account that will sign the transaction |
fundInvoice must be signed by the funder address |
Signer's public key ≠ funder param |
Ensure funder matches the account that will sign |
Insufficient balance |
Account has no testnet tokens | Fund via Friendbot: curl "https://friendbot.stellar.org?addr=YOUR_KEY" |
Transaction submission failed with status FAILED |
Contract error (e.g. InvalidDueDate, SelfInvoice) |
Read the error message; check parameter ranges in the invoice contract docs |
Simulation failed |
Invalid contract state or wrong params | Enable SDK debug logging: set ILN_SDK_DEBUG=true in env, then retry |
Network error / ECONNREFUSED |
RPC node unreachable | Check https://soroban-testnet.stellar.org is reachable; retry after a moment |
ILN_SDK_DEBUG=true ts-node quickstart.tsThe SDK logs transaction XDRs, simulation results, and polling status to stderr when this env var is set.
- SDK API Reference — full method signatures and types
- Invoice Contract Reference — all contract functions and errors
- Indexer Data Model — how to index ILN state with The Graph or a custom indexer
- SDK Trust Model — what the SDK validates vs. what it delegates to the RPC/signer