@fepvenancio/stela-sdk
v0.11.0
Published
TypeScript SDK for the Stela P2P lending protocol on StarkNet
Maintainers
Readme
@fepvenancio/stela-sdk
TypeScript SDK for the Stela P2P lending protocol on StarkNet.
Stela enables peer-to-peer lending through on-chain inscriptions. Borrowers post collateral and request loans; lenders sign inscriptions to fund them. The protocol manages collateral locking via token-bound locker accounts, multi-lender share accounting through ERC1155 tokens, and automated liquidation of expired positions. An off-chain signing model allows gasless order creation and settlement through relayer bots.
Stack
- TypeScript (ESM + CJS dual build via tsup)
- starknet.js v6 — RPC calls, SNIP-12 typed data, Poseidon hashing
- Vitest for testing
Installation
npm install @fepvenancio/stela-sdk starknetpnpm add @fepvenancio/stela-sdk starknetstarknet is a peer dependency (^6.0.0).
Quick Start
Using the SDK Facade
import { StelaSdk } from '@fepvenancio/stela-sdk'
import { RpcProvider, Account } from 'starknet'
const provider = new RpcProvider({ nodeUrl: 'https://starknet-sepolia.public.blastapi.io' })
const account = new Account(provider, address, privateKey)
const sdk = new StelaSdk({ provider, account, network: 'sepolia' })
// Read an inscription
const inscription = await sdk.inscriptions.getInscription(1n)
// Check share balance
const shares = await sdk.shares.balanceOf(myAddress, 1n)
// Query the indexer API
const list = await sdk.api.listInscriptions({ status: 'open' })Using Individual Clients
import { InscriptionClient, ShareClient, STELA_ADDRESS } from '@fepvenancio/stela-sdk'
import { RpcProvider } from 'starknet'
const provider = new RpcProvider({ nodeUrl: 'https://starknet-sepolia.public.blastapi.io' })
const inscriptions = new InscriptionClient({
stelaAddress: STELA_ADDRESS.sepolia,
provider,
})
const data = await inscriptions.getInscription(1n)API Reference
StelaSdk
Main facade that wires together all clients.
const sdk = new StelaSdk({
provider, // StarkNet RPC provider
account?, // Account for write operations (optional for read-only)
network?, // 'sepolia' | 'mainnet' (default: 'sepolia')
apiBaseUrl?, // Custom indexer API URL
stelaAddress?, // Override contract address
})
sdk.inscriptions // InscriptionClient
sdk.shares // ShareClient
sdk.locker // LockerClient
sdk.api // ApiClientInscriptionClient
On-chain reads and transaction builders for the Stela protocol contract.
Read Methods
| Method | Returns | Description |
|--------|---------|-------------|
| getInscription(id) | StoredInscription | Fetch raw on-chain inscription data |
| getLocker(id) | string | Get the locker TBA address for an inscription |
| getInscriptionFee() | bigint | Current protocol inscription fee |
| convertToShares(id, percentage) | bigint | Convert a fill percentage to shares |
| getNonce(address) | bigint | Get the off-chain signing nonce for an address |
| getRelayerFee() | bigint | Current relayer fee (in BPS) |
| getTreasury() | string | Treasury contract address |
| isPaused() | boolean | Whether the protocol is paused |
| isOrderRegistered(orderHash) | boolean | Check if an off-chain order is registered |
| isOrderCancelled(orderHash) | boolean | Check if an order has been cancelled |
| getFilledBps(orderHash) | bigint | Get filled basis points for a signed order |
| getMakerMinNonce(maker) | string | Get minimum valid nonce for a maker |
Call Builders
Return a Call object for use with account.execute(). Bundle multiple calls (including ERC20 approvals) into a single transaction.
| Method | Description |
|--------|-------------|
| buildCreateInscription(params) | Create an inscription on-chain |
| buildSignInscription(id, bps) | Sign (fund) an inscription at a given BPS |
| buildCancelInscription(id) | Cancel an open inscription |
| buildRepay(id) | Repay a filled inscription |
| buildLiquidate(id) | Liquidate an expired inscription |
| buildRedeem(id, shares) | Redeem shares for underlying assets |
| buildSettle(params) | Settle an off-chain order (used by relayer bots) |
| buildBatchSettle(params) | Settle multiple off-chain orders atomically with 1 lender signature (BatchLendOffer) |
| buildFillSignedOrder(order, sig, fillBps) | Fill a signed order on-chain |
| buildCancelOrder(order) | Cancel a specific signed order |
| buildCancelOrdersByNonce(minNonce) | Bulk cancel orders below a nonce |
Execute Methods
Convenience wrappers that call account.execute() directly. Accept optional approvals for bundling ERC20 approves.
| Method | Description |
|--------|-------------|
| execute(calls) | Execute arbitrary calls via the connected account |
| createInscription(params, approvals?) | Create inscription with optional token approvals |
| signInscription(id, bps, approvals?) | Fund an inscription |
| cancelInscription(id) | Cancel an inscription |
| repay(id, approvals?) | Repay a loan |
| liquidate(id) | Liquidate an expired loan |
| redeem(id, shares) | Redeem ERC1155 shares |
| fillSignedOrder(order, sig, fillBps, approvals?) | Fill a signed order |
| cancelOrder(order) | Cancel a signed order |
| cancelOrdersByNonce(minNonce) | Bulk cancel by nonce |
ShareClient
Read-only client for ERC1155 share token queries. Inscription IDs are used as ERC1155 token IDs.
| Method | Returns | Description |
|--------|---------|-------------|
| balanceOf(account, inscriptionId) | bigint | Share balance for an account on an inscription |
| balanceOfBatch(accounts, ids) | bigint[] | Batch query multiple balances |
| isApprovedForAll(owner, operator) | boolean | Check operator approval |
LockerClient
Interact with collateral locker TBA (Token Bound Account) contracts.
| Method | Returns | Description |
|--------|---------|-------------|
| getLockerAddress(id) | string | Get locker TBA address for an inscription |
| isUnlocked(id) | boolean | Check if a locker is unlocked |
| getLockerState(id) | LockerState | Full locker state (address + unlock status) |
| getLockerBalance(id, tokenAddress) | bigint | ERC20 balance held by the locker |
| getLockerBalances(id, tokenAddresses) | Map<string, bigint> | Multiple ERC20 balances |
| buildLockerExecute(lockerAddr, calls) | Call | Build a governance call through the locker |
| executeThrough(id, call) | { transaction_hash } | Execute a single call through the locker |
| executeThroughBatch(id, calls) | { transaction_hash } | Execute multiple calls through the locker |
ApiClient
HTTP client for the Stela indexer API. Provides typed access to indexed data.
const api = new ApiClient({ baseUrl: 'https://stela-dapp.xyz/api' })| Method | Returns | Description |
|--------|---------|-------------|
| listInscriptions(params?) | ApiListResponse<InscriptionRow> | List inscriptions with filters (status, address, page, limit) |
| getInscription(id) | ApiDetailResponse<InscriptionRow> | Get a single inscription |
| getInscriptionEvents(id) | ApiListResponse<InscriptionEventRow> | Get events for an inscription |
| getTreasuryView(address) | ApiListResponse<TreasuryAsset> | Treasury asset balances |
| getLockers(address) | ApiListResponse<LockerInfo> | Locker info for an address |
| getShareBalances(address) | ApiListResponse<ShareBalance> | Share balances for an address |
Off-Chain Signing
Functions for creating SNIP-12 typed data for gasless order creation and settlement.
import {
getInscriptionOrderTypedData,
getLendOfferTypedData,
getBatchLendOfferTypedData,
hashAssets,
hashBatchEntries,
serializeSignature,
deserializeSignature,
} from '@fepvenancio/stela-sdk'| Function | Description |
|----------|-------------|
| getInscriptionOrderTypedData(params) | Build SNIP-12 typed data for a borrower's InscriptionOrder |
| getLendOfferTypedData(params) | Build SNIP-12 typed data for a lender's LendOffer |
| getBatchLendOfferTypedData(params) | Build SNIP-12 typed data for a lender's BatchLendOffer (multi-order settlement) |
| getCollectionLendOfferTypedData(params) | Build SNIP-12 typed data for a collection-wide lending offer |
| getCollectionBorrowAcceptanceTypedData(params) | Build SNIP-12 typed data for accepting a collection offer |
| getRenegotiationProposalTypedData(params) | Build SNIP-12 typed data for a renegotiation proposal |
| getCollateralSaleOfferTypedData(params) | Build SNIP-12 typed data for a collateral sale offer |
| getRefinanceOfferTypedData(params) | Build SNIP-12 typed data for a refinance offer |
| getRefinanceApprovalTypedData(params) | Build SNIP-12 typed data for approving a refinance |
| getTermsAcknowledgmentTypedData(params) | Build SNIP-12 typed data for terms acknowledgment |
| getCancelOrderTypedData(orderId, chainId) | Build SNIP-12 typed data for off-chain order cancellation |
| hashAssets(assets) | Poseidon hash of an asset array (matches Cairo's hash_assets()) |
| hashBatchEntries(entries) | Poseidon hash of BatchEntry array for batch_settle() |
| serializeSignature(sig) | Convert string[] signature to { r, s } for storage |
| deserializeSignature(stored) | Convert { r, s } back to string[] |
| getNonce(provider, stelaAddress, accountAddress) | Read on-chain nonce via RPC (uses latest block) |
Math Utilities
Share calculation helpers that mirror the on-chain math.
import {
convertToShares,
scaleByPercentage,
sharesToPercentage,
calculateFeeShares,
divCeil,
proRataInterest,
computeInterestRate,
computePositionValue,
computeSafePositionFloor,
} from '@fepvenancio/stela-sdk'| Function | Description |
|----------|-------------|
| convertToShares(percentage, totalSupply, currentIssuedPercentage) | Convert fill percentage to shares |
| scaleByPercentage(value, percentage) | Scale a value by basis points |
| sharesToPercentage(shares, totalSupply, currentIssuedPercentage) | Convert shares back to percentage |
| calculateFeeShares(shares, feeBps) | Calculate fee portion of shares |
| divCeil(a, b) | Ceiling division (rounds up) |
| proRataInterest(amount, elapsed, duration) | Pro-rata interest with ceiling rounding |
| computeInterestRate(debtAssets, interestAssets) | Interest/debt ratio (skips ERC721, returns null if zero debt) |
| shareProportionBps(shares, totalSupply) | Share proportion in basis points |
| proportionalAssetValue(assetValue, shares, totalSupply) | Asset value proportional to shares |
| computePositionValue(params) | Full position valuation (debt, interest, collateral) |
| accruedInterestWithBuffer(amount, elapsed, duration, buffer?) | Accrued interest + dust buffer |
| computeSafePositionFloor(params) | Safe minimum price for buying shares |
Event Parsing
Parse raw StarkNet events into typed SDK event objects.
import { parseEvent, parseEvents, SELECTORS } from '@fepvenancio/stela-sdk'| Export | Description |
|--------|-------------|
| SELECTORS | Map of event name to selector hash for all protocol events |
| parseEvent(raw) | Parse a single raw event into a typed StelaEvent |
| parseEvents(raws) | Parse an array of raw events |
Supported event types: InscriptionCreated, InscriptionSigned, InscriptionCancelled, InscriptionRepaid, InscriptionLiquidated, SharesRedeemed, TransferSingle, OrderSettled, OrderFilled, OrderCancelled, OrdersBulkCancelled.
Token Registry
Curated token list for StarkNet (mainnet + sepolia).
import { TOKENS, getTokensForNetwork, findTokenByAddress } from '@fepvenancio/stela-sdk'| Export | Description |
|--------|-------------|
| TOKENS | Full token list (ETH, STRK, USDC, USDT, WBTC, DAI, wstETH + testnet mocks) |
| getTokensForNetwork(network) | Filter tokens available on a specific network |
| findTokenByAddress(address) | Look up token info by contract address |
Utility Functions
import {
toU256, fromU256, inscriptionIdToHex, toHex,
formatAddress, normalizeAddress, addressesEqual,
parseAmount, formatTokenValue,
formatDuration, formatTimestamp, formatDurationHuman,
computeStatus, enrichStatus,
normalizeOrderData, parseOrderRow,
formatSig, parseSigToArray,
serializeAssetCalldata, parseAssetArray, parseInscriptionCalldata,
} from '@fepvenancio/stela-sdk'Address & Formatting
| Function | Description |
|----------|-------------|
| toU256(value) | Convert bigint to [low, high] string pair for Cairo u256 |
| fromU256({ low, high }) | Convert Cairo u256 back to bigint |
| inscriptionIdToHex(id) | Format inscription ID as hex string |
| toHex(value) | Convert bigint/number to hex string |
| formatAddress(address) | Shorten an address for display (0x1234...abcd) |
| normalizeAddress(address) | Pad and checksum an address |
| addressesEqual(a, b) | Case-insensitive address comparison |
| parseAmount(value, decimals) | Parse human-readable amount to bigint |
| formatTokenValue(value, decimals) | Format bigint token value for display |
| formatDuration(seconds) | Format seconds as compact duration (7d 0h) |
| formatTimestamp(timestamp) | Format unix timestamp as date string |
| formatDurationHuman(seconds) | Format seconds as human-readable (5 min, 2 hours, 7 days) |
Status Computation
| Function | Description |
|----------|-------------|
| computeStatus(input) | Derive inscription status from on-chain fields |
| enrichStatus(row) | Compute display status — distinguishes overdue, grace_period, auctioned from base expired |
| getStatusBadgeVariant(status) | Map any status to a StatusBadgeVariant for UI |
| getStatusLabel(status) | Human-readable label for any status (base + extended) |
| getOrderStatusBadgeVariant(status) | Badge variant for off-chain order status |
| getOrderStatusLabel(status) | Human-readable label for order status |
| inscriptionMatchesGroup(status, group) | Check if inscription status belongs to filter group (open/active/closed/all) |
| orderMatchesGroup(status, group) | Check if order status belongs to filter group |
Order Data Parsing
| Function | Description |
|----------|-------------|
| normalizeOrderData(raw) | Normalize camelCase/snake_case variants in order_data JSON |
| parseOrderRow(row) | Parse D1 order row — sanitizes assets, strips signature for non-pending orders |
Signature Utilities
| Function | Description |
|----------|-------------|
| formatSig(signature) | Format wallet signature (array or {r, s}) to string[] — converts bigints to hex |
| parseSigToArray(raw) | Parse stored signature (JSON array, JSON object, CSV, or string array) to string[] |
Calldata Serialization
| Function | Description |
|----------|-------------|
| serializeAssetCalldata(assets) | Serialize asset array to Cairo calldata [len, ...fields] |
| parseAssetArray(calldata, offset) | Deserialize asset array from RPC calldata |
| parseInscriptionCalldata(calldata) | Extract {debt, interest, collateral} from create_inscription/settle tx calldata |
| serializeSignatureCalldata(sig) | Serialize signature string to calldata [len, ...parts] |
Types
All exported types from the SDK:
Core Protocol Types
| Type | Description |
|------|-------------|
| Network | 'sepolia' \| 'mainnet' |
| AssetType | 'ERC20' \| 'ERC721' \| 'ERC1155' \| 'ERC4626' |
| InscriptionStatus | 'open' \| 'partial' \| 'filled' \| 'repaid' \| 'liquidated' \| 'expired' \| 'cancelled' |
| Call | StarkNet call object (contractAddress, entrypoint, calldata) |
| Asset | Token within an inscription (address, type, value, token_id) |
| InscriptionParams | Parameters for create_inscription |
| StoredInscription | Raw on-chain inscription data |
| Inscription | Parsed inscription with computed status |
| SignedOrder | Signed order for the matching engine |
| BatchEntry | Entry in a batch settle (order hash + fill BPS) |
| TokenInfo | Token metadata (symbol, name, decimals, addresses) |
| StoredSignature | Serialized signature for storage (r:s) |
API / D1 Row Types
| Type | Description |
|------|-------------|
| InscriptionRow | Inscription row (includes auction_started, auction_start_time) |
| InscriptionDetailResponse | Detail response extending InscriptionRow |
| AssetRow | Asset row (debt / interest / collateral) |
| ApiListResponse<T> | Paginated list response envelope |
| ApiDetailResponse<T> | Single item response envelope |
| TreasuryAsset | Treasury asset balance |
| ShareBalance | Share balance for an account |
| LockerInfo | Locker information |
| OrderStatus | 'pending' \| 'matched' \| 'settled' \| 'expired' \| 'cancelled' |
| OrderRow | Off-chain order row from D1 |
| OrderOfferRow | Lender offer row from D1 |
| CollectionOfferRow | Collection-wide offer row from D1 |
| RefinanceRow | Refinance offer row from D1 |
| RenegotiationRow | Renegotiation proposal row from D1 |
| CollateralSaleRow | Collateral sale offer row from D1 |
| ShareListingRow | Secondary market share listing row |
Order Book Types
| Type | Description |
|------|-------------|
| LendingLevel | Price level in lending order book (APR, amount, orders) |
| SwapLevel | Price level in swap order book (rate, amount, orders) |
| TokenDisplay | Token display info (address, symbol, decimals, logoUrl) |
| OrderBookResponse | Full order book response (pair, lending, swaps, recentFills) |
| DurationFilter | 'all' \| '7d' \| '30d' \| '90d' \| '180d' \| '365d' |
Status & UI Types
| Type | Description |
|------|-------------|
| EnrichedStatus | InscriptionStatus \| 'overdue' \| 'grace_period' \| 'auctioned' |
| StatusBadgeVariant | Union of all inscription + order status strings for badge rendering |
| StatusInput | Input for computeStatus() |
Order Parsing Types
| Type | Description |
|------|-------------|
| SerializedAsset | Asset in order_data JSON (string values) |
| RawOrderData | Flexible order_data with camelCase/snake_case variants |
| ParsedOrderData | Normalized order data output |
| StoredAsset | Asset shape for calldata serialization |
Preset Types
| Type | Description |
|------|-------------|
| DeadlinePreset | { label: string, seconds: number } |
| DurationPreset | { label: string, seconds: number } |
Event Types
| Type | Description |
|------|-------------|
| RawEvent | Raw StarkNet event (keys, data, tx_hash, block) |
| StelaEvent | Discriminated union of all protocol events |
| InscriptionCreatedEvent | InscriptionCreated event |
| InscriptionSignedEvent | InscriptionSigned event |
| InscriptionCancelledEvent | InscriptionCancelled event |
| InscriptionRepaidEvent | InscriptionRepaid event |
| InscriptionLiquidatedEvent | InscriptionLiquidated event |
| SharesRedeemedEvent | SharesRedeemed event |
| TransferSingleEvent | ERC1155 TransferSingle event |
| OrderSettledEvent | OrderSettled event |
| OrderFilledEvent | OrderFilled event |
| OrderCancelledEvent | OrderCancelled event |
| OrdersBulkCancelledEvent | OrdersBulkCancelled event |
| AuctionStartedEvent | AuctionStarted event |
| AuctionBidEvent | AuctionBid event |
Client Types
| Type | Description |
|------|-------------|
| LockerState | Locker address + unlock status |
| LockerCall | Call to execute through a locker |
| InscriptionClientOptions | Options for InscriptionClient |
| ShareClientOptions | Options for ShareClient |
| ApiClientOptions | Options for ApiClient |
| ListInscriptionsParams | Parameters for listInscriptions() |
| InscriptionEventRow | API response row for events |
| StelaSdkOptions | Options for StelaSdk |
| AssetValue | Asset with proportional value |
| AccruedInterestEntry | Interest with full + accrued amounts |
| PositionValue | Full position valuation result |
Constants
Protocol
| Export | Description |
|--------|-------------|
| STELA_ADDRESS | Contract addresses per network ({ sepolia, mainnet }) |
| resolveNetwork(raw?) | Validate/default network string |
| CHAIN_ID | SNIP-12 chain ID shortstrings per network |
| EXPLORER_TX_URL | Block explorer base URLs per network |
| MAX_BPS | 10_000n (100% in basis points) |
| VIRTUAL_SHARE_OFFSET | 1e16n (share calculation offset) |
| ASSET_TYPE_ENUM | AssetType to numeric enum mapping |
| ASSET_TYPE_NAMES | Numeric enum to AssetType mapping |
| GRACE_PERIOD | 86400n — 24h grace before auction |
| AUCTION_DURATION | 86400n — 24h Dutch auction |
| AUCTION_PENALTY_BPS | 500n — 5% penalty at auction start |
| AUCTION_RESERVE_BPS | 1000n — 10% auction floor |
| DEFAULT_DUST_BUFFER_SECONDS | 60n — dust buffer for position floor |
Status
| Export | Description |
|--------|-------------|
| VALID_STATUSES | Array of all valid inscription statuses |
| STATUS_LABELS | Human-readable inscription status labels |
| ORDER_STATUS_LABELS | Human-readable order status labels |
| INSCRIPTION_STATUS_GROUPS | Status → filter group mapping (open/active/closed) |
| ORDER_STATUS_GROUPS | Order status → filter group mapping |
| STATUS_DESCRIPTIONS | Detailed tooltip text for each status |
| CONCEPT_DESCRIPTIONS | Help text for protocol concepts (debt, interest, collateral, etc.) |
Trade Presets
| Export | Description |
|--------|-------------|
| SWAP_DEADLINE_PRESETS | DeadlinePreset[] — 5m to 30d |
| LEND_DEADLINE_PRESETS | DeadlinePreset[] — 7d to 90d |
| DURATION_PRESETS | DurationPreset[] — 1d to 1y |
| DURATION_RANGES | Record<DurationFilter, [min, max] \| null> — filter to seconds mapping |
Classes
| Export | Description |
|--------|-------------|
| ApiError | Error class thrown by ApiClient on HTTP failures |
ABIs
The package ships raw ABI JSON files in src/abi/:
| File | Contents |
|------|----------|
| src/abi/stela.json | Full Stela protocol ABI (IStelaProtocol + ERC1155 + Ownable) |
| src/abi/erc20.json | Minimal ERC20 ABI (approve, balanceOf, allowance) |
| src/abi/locker.json | Locker account ABI (execute, is_unlocked) |
Development
pnpm install
pnpm build # Build with tsup (ESM + CJS)
pnpm test # Run tests with vitest
pnpm test:watch # Watch mode
pnpm lint # Type-check with tsc --noEmitPublishing
pnpm build
npm publish --access publicRunning a Relayer
The Stela protocol is fully permissionless — anyone can run a relayer to settle matched orders and earn 0.05% on every settlement. See RELAYER.md for the SDK integration guide and the stela-relayer repo for a standalone implementation.
License
MIT
