Skip to content

TypeScript SDK ​

@alula/client-sdk is the high-level TypeScript client for the Alula protocol. It wraps the generated Soroban contract bindings (@alula/market-sdk and the Aqua/Soroswap swap providers) behind an ergonomic, service-oriented API, handling bigint conversion, result unwrapping, decimal scaling, and transaction assembly for you. It is the same integration layer the Alula web app is built on.

If you are integrating from Rust or working directly against the contract ABI, see the Smart Contract API and the Developer Quickstart instead.

Installation ​

bash
pnpm add @alula/client-sdk @stellar/stellar-sdk

@stellar/stellar-sdk is a peer dependency — the SDK returns and consumes its transaction and account types.

Creating a client ​

AlulaClient is created through an async factory. Construction reads the market's oracle decimals from the contract, so you must await it.

typescript
import { AlulaClient } from '@alula/client-sdk'

const client = await AlulaClient.create({
  publicKey: 'GABC...',                 // connected wallet address (optional)
  marketContractId: 'CBP7...',          // market contract for this network
  opts: {
    rpc: 'public',                      // 'devnet' | 'testnet' | 'public'
    // horizonRpcUrl / sorobanRpcUrl are optional overrides
  },
})

A convenience factory is also available when you only have an address:

typescript
const client = await AlulaClient.fromAddress(address, marketContractId, { rpc: 'public' })

INFO

publicKey is optional — read-only calls (querying pools, market state, obligations) work without a connected wallet. A wallet is only required to sign and send transactions.

Services ​

Every capability hangs off a service on the client:

ServiceAccessPurpose
Marketclient.marketRead market and pool state, oracle prices, withdrawal simulation — see Reading Market State
Obligationclient.obligationRead a user's obligation (positions) — see Reading Market State
Lendingclient.lendingDeposit, withdraw, add/remove collateral — see Supply & Borrow
Borrowingclient.borrowingBorrow and repay — see Supply & Borrow
Multiplyclient.multiplyOpen and close leveraged positions — see Multiply
Swapclient.swapQuote routes and execute swaps — see Swap
Walletclient.walletBalances and trustlines — see Wallet & Trustlines

Core patterns ​

These conventions apply across every service. The individual pages assume you have read this section.

Build vs. execute ​

Every state-changing operation comes in two forms:

  • buildXTx(...) returns an unsigned AssembledTransaction you can inspect, simulate, or estimate fees on before committing.
  • The convenience method (deposit, borrow, openPosition, …) builds, signs, and submits in one call.
typescript
// Inspect first
const tx = await client.lending.buildDepositTx(user, poolAddress, '100', assetDecimals)
const feeXlm = client.lending.getTransactionFee(tx, assetDecimals)

// Or do everything at once
await client.lending.deposit(user, poolAddress, 100, assetDecimals, kit)

Wallet kit injection ​

The SDK does not depend on any specific wallet library. Signing methods accept a kit argument — any object exposing a signTransaction(xdr, opts) method that returns { signedTxXdr } satisfies the required SigningKit shape. This works with Stellar Wallets Kit, custom signers, or test doubles.

typescript
interface SigningKit {
  signTransaction: (
    xdr: string,
    opts?: Record<string, unknown>,
  ) => Promise<{ signedTxXdr: string }>
}

Obligations ​

Every user-scoped operation takes an ObligationKey, not a plain address:

typescript
import type { ObligationKey } from '@alula/market-sdk'

const user: ObligationKey = { user: 'GABC...', seed: undefined } // standard obligation

The seed distinguishes obligation types (standard, earn, multiply). See Concepts and the User Operations reference for details.

Amounts and decimals ​

You pass human-readable amounts (strings or numbers) plus the token's decimal precision; the SDK scales them to on-chain i128 values internally. Utility helpers are exported for the reverse direction:

typescript
import { amountToBigInt, bigintToNumber } from '@alula/client-sdk'

amountToBigInt('100', 7)          // 1000000000n
bigintToNumber(1000000000n, 7)    // '100'

Maxing out with withBuffer ​

Withdraw, borrow, and remove-collateral accept a withBuffer flag. When true, the SDK submits MAX_I128 so the contract releases the maximum currently allowed (capped by health and liquidity), avoiding dust left behind by rounding between quote and execution.

typescript
// Withdraw everything available
await client.lending.withdraw(user, poolAddress, 0, assetDecimals, kit, /* withBuffer */ true)

Preview objects ​

Multiply and swap operations return rich preview objects describing fees, slippage, minimum outputs, and price impact before you commit. Render these to the user, then pass the same parameters to the execute call. See Multiply and Swap.

Debug logging ​

Signing methods take an options argument that defaults to { debug: true }, which logs the transaction, signed XDR, and result to the console. Pass { debug: false } to silence this in production.

typescript
await client.lending.deposit(user, poolAddress, 100, assetDecimals, kit, { debug: false })

Error handling ​

Contract failures surface as errors carrying a Soroban contract error code. Use getErrorMessage to turn them into human-readable strings mapped from the protocol's error codes:

typescript
import { getErrorMessage } from '@alula/client-sdk'

try {
  await client.borrowing.borrow(user, poolAddress, 500, assetDecimals, kit, false)
} catch (err) {
  console.error(getErrorMessage(err)) // e.g. "Health factor is below the required threshold..."
}