> For the complete documentation index, see [llms.txt](https://docs.townsq.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.townsq.xyz/technical-details/sdk-guide.md).

# SDK Guide

The TownSquare SDK (@townsq/mm-sdk) provides developers with tools to interact seamlessly with the protocol — creating accounts, managing loans, depositing, borrowing, repaying, and withdrawing assets — without directly calling smart contracts.

### Flow of transacting <a href="#flow-of-transacting" id="flow-of-transacting"></a>

To use the protocol you follow this order:

1. **Create an account** — One account per user. You need an account before you can open any loan.
2. **Create a loan (or initiate loan with deposit)** — Each loan has a **loan type** that defines which assets you can supply and borrow. Choose the type that matches your strategy:

<table><thead><tr><th width="67.445556640625">Step</th><th width="220.46240234375">Action</th><th>Description</th></tr></thead><tbody><tr><td>1</td><td><strong>Create account</strong></td><td><code>TSAccount.prepare.createAccount</code> → <code>TSAccount.write.createAccount</code>. Uses a unique nonce (4 bytes).</td></tr><tr><td>2a</td><td><strong>Create loan</strong> (empty)</td><td><code>TSLoan.prepare.createLoan</code> → <code>TSLoan.write.createLoan</code>. Opens a loan with a name and loan type; no deposit yet.</td></tr><tr><td>2b</td><td><strong>Initiate loan with deposit</strong></td><td><code>TSLoan.prepare.initiateLoanAccountWithDeposit</code> → <code>TSLoan.write.initiateLoanAccountWithDeposit</code>. Creates the loan and funds it in one transaction.</td></tr></tbody></table>

**Loan types** (choose one when creating the loan):

<table><thead><tr><th width="154.1378173828125">Loan type</th><th width="207.1993408203125">Constant</th><th>Use case</th></tr></thead><tbody><tr><td><strong>General</strong></td><td><code>MAINNET_LOAN_TYPE_ID.GENERAL</code></td><td>Accepts all supported tokens as collateral and for borrowing. Maximum flexibility.</td></tr><tr><td><strong>Stable Efficiency</strong></td><td><code>MAINNET_LOAN_TYPE_ID.STABLE_EFFICIENCY</code></td><td>Restricted to stablecoin-related assets (e.g. USDC, USDT, AUSD, USD1). Optimized for stablecoin strategies.</td></tr><tr><td><strong>Deposit (LPs)</strong></td><td><code>MAINNET_LOAN_TYPE_ID.DEPOSIT</code></td><td>Deposit-only: you can lend assets to pools but <strong>cannot borrow</strong>. Suited for LPs.</td></tr><tr><td><strong>MON Efficiency</strong></td><td><code>MAINNET_LOAN_TYPE_ID.MON_EFFICIENCY</code></td><td>Restricted to MON-related assets (e.g. MON, wMON, sMON, aprMON). Optimized for MON-focused strategies.</td></tr><tr><td><strong>BTC Efficiency</strong></td><td><code>MAINNET_LOAN_TYPE_ID.BTC_EFFICIENCY</code></td><td>Restricted to BTC-related assets (e.g. BTC, enzoBTC, wBTC). Optimized for BTC-focused strategies.</td></tr></tbody></table>

After the account and loan exist, you use **Deposit**, **Borrow**, **Repay**, **Withdraw,** as needed. The loan type does not change after creation.

### Installation

```bash
npm install @townsq/mm-sdk
```

or&#x20;

```bash
yarn add @townsq/mm-sdk
```

Peer dependency

* `viem` — used for chain config, wallet client, and RPC.

### Initialization

Before using the SDK, you need to configure the core instance and signer.

```typescript
import {
  TSCore,
  NetworkType,
  TS_CHAIN_ID,
  CHAIN_VIEM,
} from "@townsq/mm-sdk";
import type { TSCoreConfig } from "@townsq/mm-sdk";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";

const network = NetworkType.MAINNET;
const chain = TS_CHAIN_ID.MONAD;

const tsConfig: TSCoreConfig = { network, provider: { evm: {} } };
TSCore.init(tsConfig);
TSCore.setNetwork(network);

const provider = typeof window !== "undefined" ? window.ethereum : undefined;
if (!provider) throw new Error("MetaMask not installed");

const walletClient = createWalletClient({
  chain: CHAIN_VIEM[chain],
  transport: custom(provider),
});

// Ask user to connect and pick account
const [address] = await walletClient.requestAddresses();
if (!address) throw new Error("No account connected");

const signer = createWalletClient({
  chain: CHAIN_VIEM[chain],
  transport: custom(provider),
  account: address,
});

TSCore.setTSsSigner({ signer, tsChainId: chain });
```

* **Network:** `NetworkType.MAINNET`&#x20;
* **Chain:** Use `TS_CHAIN_ID` (e.g. `TS_CHAIN_ID.MONAD` for mainnet Monad). The signer’s `chain` must match the chain you intend to transact on.
* **Signer:** Any viem `WalletClient` with `chain` and `account` set. The SDK uses it for signing and for resolving the current address.

#### Reading Loan Information

The SDK provides utilities to fetch and compute detailed information about a loan, including collateral value, debt value, and health factors. This is useful for dashboards, liquidation bots, or analytics.

```typescript
import {
  NetworkType,
  TSCore,
  TSLoan,
  TS_CHAIN_ID,
  CHAIN_VIEM,
  TSPool,
  TSOracle,
  TSAccount,
  MAINNET_TS_TOKEN_ID,
  MAINNET_LOAN_TYPE_ID,
} from '@townsq/mm-sdk';

import type {
  TSCoreConfig,
  LoanId,
  TSTokenId,
  PoolInfo,
  EvmAddress,
} from '@townsq/mm-sdk';

import { createWalletClient, http } from 'viem';

 // 1. Initialize sdk
  const network = NetworkType.MAINNET;
  const chain = TS_CHAIN_ID.MONAD;

  const tsConfig: TSCoreConfig = {
    network,
    provider: { evm: {} },
  };

  TSCore.init(tsConfig);
  TSCore.setNetwork(network);

  const signer = createWalletClient({
    chain: CHAIN_VIEM[chain],
    transport: http(),
  });

  TSCore.setTSsSigner({ signer, tsChainId: chain });

 // 2. Resolve account ID from wallet address
const userAddress = "<USER_EVM_ADDRESS>" as EvmAddress;
const accountIds = await TSAccount.read.getAccountIdOfAddressOnChain(userAddress);
const accountId = accountIds[0].accountId;

// 3. Get loan IDs from your subgraph
const loanIdsSubgraphEndpoint = "https://api.goldsky.com/api/public/project_cmc8zodsr8oib01x145wf2hw1/subgraphs/mainnet-loanIds/1.0.0/gn";
const loanIds = await TSLoan.read.getLoanIds(accountId, loanIdsSubgraphEndpoint);

// 4. Pick a loan
const loanId = "<LOAN_ID>" as LoanId;

// 5. Load pool info for all mainnet tokens
const poolsInfo: Partial<Record<TSTokenId, PoolInfo>> = {};
await Promise.all(
  Object.values(MAINNET_TS_TOKEN_ID).map(async (tsTokenId) => {
    const poolInfo = await TSPool.read.poolInfo(tsTokenId);
    poolsInfo[tsTokenId] = poolInfo;
  })
);

// 6. Load loan type info
const loanTypeInfo = {
  [MAINNET_LOAN_TYPE_ID.GENERAL]: await TSLoan.read.loanTypeInfo(
    MAINNET_LOAN_TYPE_ID.GENERAL
  ),
};

// 7. Load oracle prices
const oraclePrices = await TSOracle.read.oraclePrices();

// 8. Fetch user loan and compute metrics
const userLoans = await TSLoan.read.userLoans([loanId]);
const userLoansInfo = TSLoan.util.userLoansInfo(
  userLoans,
  poolsInfo,
  loanTypeInfo,
  oraclePrices
);

console.dir(userLoansInfo, { depth: null });
```

#### Creating Account

Accounts are created on the chain selected at initialization. You need a **nonce** (unique 4-byte value) to avoid collisions.

```typescript
import { TSAccount, BYTES4_LENGTH, getRandomBytes } from "@townsq/mm-sdk";

// Nonce: 4 bytes (bytes4). Use a random value or a unique number encoded as 4-byte hex.
const nonce = getRandomBytes(BYTES4_LENGTH); // or your own bytes4

const createAccount = await TSAccount.prepare.createAccount(nonce, {
  adapterId: 1,
  returnAdapterId: 1,
});

const txnReceipt = await TSAccount.write.createAccount(nonce, createAccount);
console.log(txnReceipt);
```

The account will be created on the chain defined during initialization.

#### Creating a Loan

TownSquare supports multiple **loan types**:

<table><thead><tr><th width="185.18084716796875">Loan type</th><th width="357.5953369140625">Constant</th><th>Description</th></tr></thead><tbody><tr><td>Deposit-only</td><td><code>MAINNET_LOAN_TYPE_ID.DEPOSIT</code></td><td>No borrowing (1)</td></tr><tr><td>General</td><td><code>MAINNET_LOAN_TYPE_ID.GENERAL</code></td><td>All supported tokens (2)</td></tr><tr><td>MON Efficiency</td><td><code>MAINNET_LOAN_TYPE_ID.MON_EFFICIENCY</code></td><td>MON-related assets only (3)</td></tr><tr><td>Stable Efficiency</td><td><code>MAINNET_LOAN_TYPE_ID.STABLE_EFFICIENCY</code></td><td>Stablecoin-related (4)</td></tr><tr><td>BTC Efficiency</td><td><code>MAINNET_LOAN_TYPE_ID.BTC_EFFICIENCY</code></td><td>BTC-related assets (5)</td></tr></tbody></table>

```typescript
import { TSLoan, MAINNET_LOAN_TYPE_ID, BYTES4_LENGTH, getRandomBytes } from "@townsq/mm-sdk";
import { ethers } from "ethers";
import type { AccountId, LoanId, Nonce } from "@townsq/mm-sdk";

const accountId = "0x..." as AccountId;

function encodeLoanName(name: string): string {
  return ethers.encodeBytes32String(name);
}

const nonce = getRandomBytes(BYTES4_LENGTH) as Nonce;
const loanName = encodeLoanName("My loan");

const prepareCreateLoan = await TSLoan.prepare.createLoan(
  accountId,
  nonce,
  MAINNET_LOAN_TYPE_ID.DEPOSIT,
  loanName as any,
  { adapterId: 1, returnAdapterId: 1 }
);

const txnReceipt = await TSLoan.write.createLoan(
  accountId,
  nonce,
  MAINNET_LOAN_TYPE_ID.DEPOSIT,
  loanName as any,
  prepareCreateLoan
);
console.log(txnReceipt);
```

Use the loan type that matches the assets you want to deposit/borrow

### Create loan and deposit in one transaction

You can create a new loan and fund it in a single call using **initiateLoanAccountWithDeposit**. This is useful for “open account and deposit” flows.

```ts
import {
  TSLoan,
  MAINNET_LOAN_TYPE_ID,
  MAINNET_TS_TOKEN_ID,
  BYTES4_LENGTH,
  getRandomBytes,
} from "@townsq/mm-sdk";
import { ethers } from "ethers";
import { parseUnits } from "viem";
import type { AccountId, Nonce } from "@townsq/mm-sdk";

const accountId = "0x..." as AccountId;
const nonce = getRandomBytes(BYTES4_LENGTH) as Nonce;
const loanName = ethers.encodeBytes32String("loan from sdk");

const createLoanAndDeposit = await TSLoan.prepare.initiateLoanAccountWithDeposit(
  accountId,
  nonce,
  MAINNET_LOAN_TYPE_ID.DEPOSIT,
  loanName as any,
  MAINNET_TS_TOKEN_ID.USDC,
  parseUnits("0.01", 6),
  { adapterId: 1, returnAdapterId: 1 } as any
);

const txnReceipt = await TSLoan.write.initiateLoanAccountWithDeposit(
  accountId,
  nonce,
  MAINNET_LOAN_TYPE_ID.DEPOSIT,
  loanName as any,
  parseUnits("0.01", 6),
  true, // include token approval if needed
  createLoanAndDeposit
);
console.log(txnReceipt);
```

### Deposit

Deposit tokens into an existing loan. Use the correct **pool ID** (token) and **loan type** for the loan.

**Example (USDC on Monad):**

```typescript
import {
  TSLoan,
  MAINNET_TS_TOKEN_ID,
  MAINNET_LOAN_TYPE_ID,
  TS_CHAIN_ID,
} from "@townsq/mm-sdk";
import { parseUnits } from "viem";
import type { AccountId, LoanId, TSTokenId } from "@townsq/mm-sdk";

const accountId = "0x..." as AccountId;
const loanId = "0x..." as LoanId;
const poolId = MAINNET_TS_TOKEN_ID.USDC as TSTokenId;

const deposit = await TSLoan.prepare.deposit(
  accountId,
  loanId,
  MAINNET_LOAN_TYPE_ID.GENERAL,
  poolId,
  parseUnits("0.0003", 6),
  { adapterId: 1, returnAdapterId: 1 } as any
);

const txnReceipt = await TSLoan.write.deposit(
  accountId,
  loanId,
  parseUnits("0.0003", 6),
  true, // include approval tx if needed
  deposit
);
console.log(txnReceipt);
```

### Borrow

Borrow from a loan. Specify the **debt token** (pool), amount, and (for variable-rate) **max stable rate**. The borrowed asset is sent on the chosen **receiver chain** (`TS_CHAIN_ID`).

```typescript
import {
  TSLoan,
  MAINNET_TS_TOKEN_ID,
  TS_CHAIN_ID,
} from "@townsq/mm-sdk";
import { parseUnits } from "viem";
import type { AccountId, LoanId } from "@townsq/mm-sdk";

const accountId = "0x..." as AccountId;
const loanId = "0x..." as LoanId;
const poolId = MAINNET_TS_TOKEN_ID.aprMON;

const borrow = await TSLoan.prepare.borrow(
  accountId,
  loanId,
  poolId,
  parseUnits("0.0005", 18),
  0n, // maxStableRate
  TS_CHAIN_ID.MONAD,
  { adapterId: 1, returnAdapterId: 1 } as any
);

const txnReceipt = await TSLoan.write.borrow(
  accountId,
  loanId,
  poolId,
  parseUnits("0.0005", 18),
  0n,
  TS_CHAIN_ID.MONAD,
  borrow
);
console.log(txnReceipt);
```

### Repay

Repay debt in a given token. You pass the **borrow amount** (position) and the **repayment amount** (how much you are paying back).

```typescript
import {
  TSLoan,
  MAINNET_TS_TOKEN_ID,
  MAINNET_LOAN_TYPE_ID,
} from "@townsq/mm-sdk";
import { parseUnits } from "viem";
import type { AccountId, LoanId } from "@townsq/mm-sdk";

const accountId = "0x..." as AccountId;
const loanId = "0x..." as LoanId;
const poolId = MAINNET_TS_TOKEN_ID.aprMON;

const repay = await TSLoan.prepare.repay(
  accountId,
  loanId,
  MAINNET_LOAN_TYPE_ID.GENERAL,
  poolId,
  parseUnits("0.0004", 18), // amount to repay
  parseUnits("0.0001", 18), // maximum over repayment amount
  { adapterId: 1, returnAdapterId: 1 } as any
);

const txnReceipt = await TSLoan.write.repay(
  accountId,
  loanId,
  parseUnits("0.0004", 18),
  parseUnits("0.0001", 18),
  true, // include approval if needed
  repay
);
console.log(txnReceipt);
```

### Withdraw

Withdraw collateral from a loan. You specify the **token** (pool), **amount**, whether the amount is in underlying or “t” (wrapped) units, and the **receiver chain** (EVM chain ID).

```typescript
import {
  TSLoan,
  MAINNET_TS_TOKEN_ID,
  MAINNET_EVM_CHAIN_ID,
} from "@townsq/mm-sdk";
import { parseUnits } from "viem";
import type { AccountId, LoanId } from "@townsq/mm-sdk";

const accountId = "0x..." as AccountId;
const loanId = "0x..." as LoanId;

const withdraw = await TSLoan.prepare.withdraw(
  accountId,
  loanId,
  MAINNET_TS_TOKEN_ID.USDC,
  parseUnits("0.02", 6),
  true, // isTAmount
  MAINNET_EVM_CHAIN_ID.MONAD,
  { adapterId: 1, returnAdapterId: 1 } as any
);

const txnReceipt = await TSLoan.write.withdraw(
  accountId,
  loanId,
  MAINNET_TS_TOKEN_ID.USDC,
  parseUnits("0.02", 6),
  true,
  MAINNET_EVM_CHAIN_ID.MONAD,
  withdraw
);
console.log(txnReceipt);
```
