# 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);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.townsq.xyz/technical-details/sdk-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
