# Overview

TownSquare is a high-performance cross-chain, non custodial lending and liquidity protocol, and institution-backed yield infrastructure powering RWAs and stablecoins. At its core, the stack providers open, non-custodial vault insfrastructure throught which independent curators may serve eligible users, subject to local law.

The infrastructure solves longstanding **accessibility** issues in finance that yield readily available product market fit:

* Institutional strategies and RWA yields offer higher-than-average returns but are **hard to access for retail**;
* Existing onchain lending-based yield curation offers 1-4% yield on average, whereas T-bill yield can offer on-par yield risk-off.
* Onchain infrastructure pffers efficient, transparant access to institutional strategies. TownSquare applies KYC/AML where required.

### Key functionalities

* **Institution-backed strategies and private yields:** trading strategies, private credit yields, proprietary yield sources are made accessible to retail on chains including BNBChain, Base, Monad and other performant chains.
* **Battle-tested curated yield vaults**: yield vaults on TownSquare can be customized for private credit lending-based, trading-based, and proprietary strategies, managed by institutional curators.
* **Lending & liquidity markets for the seamless crosschain liquidity:** users can collateralize their assets on Chain A to access liquidity on Chain B, supported on high-throughput chains includin BNBChain, Base, and Monad etc.
* **Yield loops**: Unlike existing protocols that only limit levered exposure to yield-bearing assets within a single chain, cross-chain loops on TownSquare can be achieved via collateralizing to borrow assets on a different chain that offers more attractive yields.
* **Isolated loan positions**: Isolated loan positions prevent risk contagion between collaterals during volatilities.
* **Capital-efficiency**: lending vault liquidity is directly utilized by loop strategy vaults, and unutilized liquidity is allocated to earn yield from external sources.


# Institution-backed yields

TownSquare introduces several sources of institution-backed yield to retail

* **Trading yield**

Hedge funds & trading teams can curate self-deployed vaults on TownSquare based on their factor trading, delta-neutral trading, and arbitrage opportunities. Non-delta-neutral strategies involve a maximum drawdown setting that stop losses in one-sided market. \
\
*\*Curator compensation, where applicable, is defined in each vault's own terms and is not offered to Korean users. TownSquare charges no performance fee and does not manage vault assets.*

* **Private credit yield**

Cash-flow businesses are traditionally available only to private equity funds. Private credit yields supported by private credits issued by cash-flow businesses via credible onchain issuers, can be offered on TownSquare vaults deployed by the asset managers, curated by the respective asset issuer and manager.&#x20;

* ***RWA yield\****

RWA vaults are curated via specialized asset issuer and management companies in case of reserve proof and security.

*\*TownSquare is not a bank, takes no deposits, and pays or guarantees no interest.*


# High-performance crosschain, non-custodial lending and liquidity protocol

TownSquare's lending and liquidity market allows for asset lending and liquidity provision across a variety of chains. Only performant chains are currently supported to offer the speed and performance on par with the transaction speed in TradFi engines.

TownSquare markets are distinctive in the following characteristics:

* **Decentralized and non-custorial**: the infrastructure lives onchain as autonomous smart contracts that execute transactions without owning the assets involved in the transactions.
* **Curated deployment**: pool/vault deployment requires governance approval and curator due diligence (KYC, sanctios screening); markets may be geo-restricted.
* **High-performant crosschain lending & liquidity**: asset lending & liquidity in DeFi is mostly restricted to single chain asset swaps, hampered by the performance differentials of different chains, resulting in inconsistent lending & liquidity user experience. TownSquare's lending & liquidity infrastructure supports sub-second asset borrows and lends between performant chains, currently including BNBChain, Base and Monad.
* **Crosschain pools**: Asset A on Chain 1 can be used to borrow Asset B on Chain 2, while keeping the yield on Chain 1 and borrowing on Asset C on Chain 1 simulataneously.
* **Crosschain liquidations**: supported by crosschain pools and performant chains, crosschain liquidations are possible to maximize liquidator inventory utilization.
* **High capital efficiency for correlated assets**: Efficiency Mode on TownSquare unlocks up to 95% loan-to-value ratio for assets in correlation, compared to the 85% efficiency offered in other markets.
* **RWA support**: RWAs supported on TownSquare yield vaults are supported seamless on TownSquare lending & liquidity markets, offering opportunities unavailable elsewhere.


# Lending

Lending on TownSquare allows you to deposit assets to be borrowed by others, earning you yields in base interest fee (base APY) & incentives.

Unlike a market model where liquidity is divided into several lend/borrow pairs for an asset, lending deposits on TownSquare pools can be borrowed against any collateral, increasing capital utilization and potential lending yield.

***

#### User guide

Refer to the [Deposit to Earn guide](/user-guide/deposit-to-earn) for how to lend and earn on TownSquare.


# Borrowing

Borrowing on TownSquare allows you to deposit collaterals into create loans to initiate borrowing. Unlike traditional borrow and lend markets where lending deposits and collateral deposits are mixed. Collateral deposits on TownSquare exist in separate pools from lending deposits, to increase security around risk contagion.

TownSquare lending markets have a highly customized borrowing experience, with key differentiators in the following aspects:

* **Multi-loan borrowing**: users can create multiple loans under their account, allowing for better risk segregation, unlike the traditional deposit-and-borrow model.
* **Multi-collateral loans**: each loan on TownSquare allows for multiple collaterals to be deposited, in both General Loans and in Efficiency Loans. This allows users to have reduced chances of liquidations when risk is spread across multiple collaterals.
* **General mode & Efficiency mode**: users can choose to create General Loans or Efficiency Loans for different borrowing strategies. General Loans allow for a diversified set of collateral assets to be deposited into the same loan, whereas Efficiency Loans only allow for correlated assets to be collateralized with an up to 95% LTV for enhanced capital efficiency and lower liquidation risks.

***

### General Loans

General loans allow for a mix of uncorrelated collaterals to be deposited into the loan. Each asset is marked at a collateral factor in the protocol. The collateral factor shows how much value of the asset could be borrowed against in a loan. In cases where multiple collaterals are in a loan, the asset with the highest collateral factor gets used first for borrowing.

### Efficiency Loans

Efficiency Loans allow correlated assets to be deposited and collateralized for a high LTV / collateral factor. Currently on TownSquare, there are two types of Efficiency Loans:

* MON Efficiency Loans: MON, wMON, shMON, sMON, gMON, and aprMON can be collateralized to borrow wMON and MON for 92.5% LTV.
* Stablecoin Efficiency Loans: USD1, AUSD, USDC, USDT, earnAUSD, sAUSD etc. can be collateralized to borrow these assets for 95% LTV.

***

#### User guide

Refer to the [Borrow page](/user-guide/borrow) for how to collateralize assets on TownSquare.


# Looping

Looping is a leveraged borrow strategy that allows users to farm yield of an yield-bearing asset at a leverage in a high LTV lend-borrow use case.

On TownSquare, Efficiency Loans are optimized for looping yield as the LTV gets up to 95% in Efficiency mode for correlated assets.

You can conduct looping with your MON Efficiency Loans or your Stable Efficiency Loans, both of which are recommended compared to do looping via General Loans.&#x20;

In the case of looping an LST as an example, you can

* **Collateralize & borrow**: create a MON Efficiency Loan and collateralize shMON LST to borrow MON
* **Get more collateral asset**: stake MON into more shMON (external to TownSquare)
* **Collateralize again (loop) to borrow more**: collateralize the newly staked shMON into the same loan to borrow more MON
* **Repeat the loop until the LTV is maxed out.**

The benefit of looping allows you to earn the shMON staking yield multiple times with correlated assets.\*\
\
*\*Leverage amplifies losses. Depeg, oracle failure, and liquidity shortfalls can cause liquidation and loss of principal.*

***

Automated loop vaults will be an available feature on TownSquare, allowing users to deposit and choose a leverage to loop without having to repeat the steps themselves.

More on this can be found at the [Automate Loop Vaults (coming soon) page](/automated-loop-vaults/leverage-higher-yield-with-your-yield-assets).


# Lending to institutions & RWA issuers

Vault deposits are represneted by non-transferable receipt tokens tracking a pro-rata claim on vault assets. These vaults are not offered or accessible in Korea; access is geo-fenced.

The yield could come from:

* Trading strategies from hedge funds, quant teams, and prop trading firms;
* Private credits of cash-flow (non-public) companies;
* RWAs such as tokenized commodities where banking interest yield is offered; etc.


# WrappedTLP & Credit Vault

* **WrappedTLP**

**WrappedTLP** converts the rebasing `TownSquareLPToken` (TLP) into a static-balance ERC-20. TLP's balance grows over time as yield accrues, which breaks compatibility with protocols that assume balances only change on transfer — AMMs, lending markets, and the like. WrappedTLP fixes this: your token balance stays constant, and accrued yield instead shows up as a growing *exchange rate* between wTLP and TLP.\
\
**Deployed Address**

<table><thead><tr><th width="129.46484375">Network</th><th width="166.7890625">Market</th><th>Address</th></tr></thead><tbody><tr><td>Monad</td><td>USDC</td><td><code>0xf3D44492f11A2dC234Acba80a9E377aB5732b09D</code></td></tr><tr><td>Monad</td><td>WETH</td><td><code>0x01135C821a87154De3F338F1978811f2648E9570</code></td></tr><tr><td>Monad</td><td>cbBTC</td><td><code>0x72C486886d199984BDBCF278bA68358365B0f8b7</code></td></tr><tr><td>Monad</td><td>enzoBTC</td><td><code>0x4cC45f152448ef97a16eCAbc7a8AfC9DFD2cE73c</code></td></tr></tbody></table>

* **Credit vault**

**CreditVault** is the central custody and settlement contract for trader positions. It holds trader collateral, tracks per-trader / per-token positions, and settles trades executed through TownSquare's RFQ pools — with credit checks and risk limits handled off-chain and enforced on-chain via signatures.\
\
**DEPLOYED ADDRESS.**<br>

<table><thead><tr><th width="153.89453125">Network</th><th width="401.33984375">Address</th><th>Supported Market</th></tr></thead><tbody><tr><td>Monad</td><td><code>0xcD1D2D602C3e7394515DaAe96e4FFe16DE71e5B4</code></td><td>USDC, WETH, cbBTC</td></tr><tr><td>Monad</td><td><code>0x6B00868e2D1385b3804127827bBaB461d3E697E7</code></td><td>enzoBTC</td></tr></tbody></table>

<br>


# Leverage higher yield with your yield assets

TownSquare's Leveraged Yield Vaults (Loop Vaults) enable higher, super competitive yields for existing yield-bearing assets for correlated asset pairs.

*\*Leverage amplifies losses. Depeg, oracle failure, and liquidity shortfalls can cause liquidation and loss of principal.*

The Yield Vaults are an implementation of ERC-4626 in the EVM. The asset strategy exeuction is as follows:

* Initiate Leveraged Position
  * Users deposit collateral (e.g., PT-LST of asset x) into Loop Vaults to initiate leverage.
* Borrow Liquidity
  * Loop Vaults borrow correlated assets (e.g. asset x) from the Crosschain Money Market, subject to collateral factors.
* Increase exposure
  * Use the borrowed asset to acquire more of the PT of the asset to increase yield, requiring the borrow cost be lower than the PT yield.
* Position Management & Risk Mitigation
  * Health Factor: Ensures adequate collateralization.
  * Price Oracles: Feed real-time asset prices from the fixed rate asset protocol (e.g. Pendle, RateX)
  * Liquidation: Triggered if the health factor falls below the threshold; liquidators repay debt and claim collateral with a bonus.
  * Users can add collateral or repay loans to maintain safety.
* Close Position
  * Vault unwinds strategy, repays borrowed amount plus interest, and returns remaining assets (profit/loss) to the user.


# Loop strategies for yield-bearing assets

TownSquare's leveraged yield vaults accept yield-bearing assets and principal tokens (PT) as deposits for leverage strategies.

The loop strategies for these assets primarily depend on the following factors:

* LTV of the correlated assets
* Leverage amount
* Pricing of the deposit asset


# Primer on Yield Assets & Liquidity Incentive

Curve introduced the vote escrow mechanism (ve) to power the incentive for stableswap liquidity of stablecoin pairs. The concept of *ve* can be applied more generally to enhance the liquidity experience and added yield where it is needed.

Typically, the following areas are identified where liquidity *should be* incentivized, as utilization in these cases is high:

* **Stablecoin liquidity**

To become a medium of exchange, the issuer of a stablecoin is incentivized to enable as much liquidity for its stablecoin with other assets and media of exchange as possible.

Example: **Curve stableswap's veCVE**.

* **DEX Swap TVL**

Asset issuers with DEX pairs for their assets need liquidity for their pairs (e.g. xxx/ETH) to optimize trading experience for their asset holders.

**Example: Balancer BAL/ETH's veBAL**.

* **Yield trading liquidity**

Yield-bearing assets such as stETH, sUSDe want to incentivize their holders to lock in their yield-bearing assets with PT-YT yield trading.

Example: Pendle's vePENDLE for PT/YT pairs.

### veTOWN to incentivize liquidity for leveraged yield experience

TownSquare has found *ve* to be the perfect fit for liquidity & yield enhancement while maintaining a decentralized governance model of its institution-backed yield infrastructure.

TownSquare's vote escrow implementation leverages the success of all these examples to power liquidity for:

* Crosschain pools;

as well as added yield for&#x20;

* Institutional vaults.

In addition, the veTOWN will also dictate the governance of pool addition and vault addition as a governance utility for progressive protocol decentralization.


# TOWN Token

The TOWN token is the governance token of the TownSquare Protocol.

On a high-level, the Token is designed with the following utilities:

* Governance voting
* Gauge emission direction
* Staking for fee discounts

*Note: TOWN confers no dividend, profit-share, redemption, or revenue rights.*

For specific iterations or TOWN Token expected, please refer to the [veTOWN for Pools & Vaults](/vetown-governance/vetown-for-pools-and-vaults) section.


# veTOWN for Pools & Vaults

TownSquare is introducing a **vote-escrow (ve) incentives system** that directs emissions to two activities in each lending market:

* **Crosschain pools** — incentives for crosschain liquidity
* **Institutional vaults (yield curation)** — added yield for LPs.

The design mirrors **ve(3,3)** stacks (Velodrome/Aerodrome) and **classic ve model of Curve**, with similar components of `VotingEscrow`, `Voter`, `Minter`, `RewardsDistributor`, `Bribe`, and **add to LP pool gauges** with **SupplyGauge** and **BorrowGauge** for crosschain pools.

***

### High-Level Concepts

* **veToken (NFT lock):** Users lock the protocol token for a duration to receive vote power that **decays to zero** at unlock.
* **Epochs:** Fixed windows (default **7 days**) where votes are snapshotted and emissions are streamed to gauges.
* **Gauges:** Reward contracts that stream emissions to participants according to balances:
  * `SupplyGauge[asset]` → pays **lenders** by *scaled supply balance* (aToken-like).
  * `BorrowGauge[asset]` → pays **borrowers** by *scaled debt balance* (variableDebt-like).
* **Voter:** Aggregates epoch votes from ve holders and assigns each gauge a % of the weekly emission.
* **Bribes / Voting Rewards (optional):** Per-gauge reward channels paying ve voters who supported that gauge in the epoch.
* **Rewards Distributor (ve rebase):** Optional “locker rebase” stream to ve lockers (separate from gauge emissions).
* **Market Registry:** Whitelists which markets have gauges and sets risk params (caps, utilization floors).
* **Emission Policy:** Config for weekly budget, decay, and optional side multipliers (e.g., borrow 1.2×).

### Roles

* **User:** Locks tokens for ve; supplies/borrows assets; votes each epoch; claims rewards.
* **Protocol Gov/Multisig:** Lists markets; sets caps/allowlists; updates emission policy.
* **Partners:** Can add **bribes** to steer votes (with allowlisted tokens).
* **Keepers (optional):** Trigger epoch rollovers / batch distributions if not auto-pulled by claims.

***

## User Flows

#### Lock → Vote → Earn

1. **Lock**: User locks token → receives `veNFT(id, power)`.
2. **Vote**: User allocates vote weights across any combination of `SupplyGauge[asset]` and `BorrowGauge[asset]`.
3. **Epoch Start**: Votes snapshot → `Voter` computes per-gauge weight share.
4. **Stream**: `Minter` mints `E_epoch` → routes to `Voter` → each gauge receives `E_g` and streams linearly over the epoch.
5. **Accrual**:
   * Lenders accrue in `SupplyGauge` by *scaled aToken balance*.
   * Borrowers accrue in `BorrowGauge` by *scaled debt balance*.
6. **Claim**: Users call `claim()` on gauges (and `getReward()` on bribe contracts if they voted).

#### Supply/Borrow Balance Change

When a user supplies/withdraws or borrows/repays, the respective **scaled** balance changes. The gauge updates the user’s accrual using a cumulative **reward index**.

***

## Economics & Formulas

### Epoch Allocation

* Let `E_epoch` be emissions for the epoch.
* Let `w_g` be the vote weight for gauge `g`.
* **Gauge share:**

$$
E\_g = E\_{\text{epoch}} \cdot \frac{w\_g}{\sum\_{i} w\_i}
$$

* **Optional side multiplier** (policy overlay):

  $$
  E'*g =
  \begin{cases}
  E\_g \cdot m*{\text{borrow}}, & \text{if } g \text{ is a BorrowGauge},\\\[4pt]
  E\_g \cdot m\_{\text{supply}}, & \text{if } g \text{ is a SupplyGauge}.
  \end{cases}
  $$

Then renormalize to keep the epoch budget constant:

$$
\tilde{E}*g ;=; \frac{E'*g}{\sum*{i} E'*i} \cdot E*{\text{epoch}}
\quad\text{so that}\quad
\sum*{g} \tilde{E}*g ;=; E*{\text{epoch}}.
$$

### Streaming Rate

* `rate_g = E_g / epochSeconds`
* Gauges stream linearly over the epoch (no compounding inside the epoch).

### Per-User Accrual (Index Model)

For each gauge:

* Maintain `index` and `userIndex[user]`.
* On time progress `dt`:

  $$
  \text{index} \mathrel{+}= \frac{\text{rate}\_g \cdot \Delta t}{\max{1,, \text{TotalScaledBalance}}}
  $$
* On user interaction:

  $$
  \begin{aligned}
  \text{accrued}\[u] &\mathrel{+}= \text{scaledBalance}\[u]\cdot\bigl(\text{index}-\text{userIndex}\[u]\bigr) \\
  \text{userIndex}\[u] &= \text{index}
  \end{aligned}
  $$
* **Scaled balances** come from your interest-bearing tokens:
  * Supply side: `aToken.scaledBalanceOf(user)`
  * Borrow side: `variableDebtToken.scaledBalanceOf(user)`

### Optional “Boost” for Lockers Who Use the Market

* Effective balance cap (Curve-style idea, optional):

  $$
  \text{effectiveBalance} ;=;
  \min!\left(
  \text{rawBalance}\cdot\Bigl(1 + k \cdot \frac{\text{veUser}}{\text{veOnGauge}}\Bigr),\ \text{cap}
  \right)
  $$
* Keep `cap` modest (e.g., 2.5×) to avoid runaway concentration.

***

## Contracts & Responsibilities

`VotingEscrow` (veNFT)

* **Core:** `createLock(amount, unlockTime)`, `increaseAmount`, `increaseUnlockTime`, `balanceOfNFT(tokenId, t)`, `merge/split (optional)`.
* **Voting Slope/Decay:** Time-weighted decay to enforce long-term alignment.
* **Events:** `LockCreated`, `LockIncreased`, `LockExtended`, `Withdrawn`.

`Voter`

* **Register Gauges:** `addGauge(gauge, type)` where `type ∈ {SUPPLY, BORROW}`.
* **Vote:** `vote(tokenId, gauges[], weights[])` (can be re-cast each epoch).
* **Distribute:** On epoch start, computes each gauge’s `E_g` (with policy multipliers) and calls `notifyRewardAmount` on gauges.
* **Voting Rewards:** Tracks who voted for which gauges for bribe/fee claims.
* **Guards:** Max % per gauge/asset; gauge kill/suspend; per-epoch vote limits.

`Minter`

* **Emission Budget:** Holds the epoch schedule and decay.
* **Mint & Route:** At epoch rollover, mints `E_epoch`, sends to `Voter`, optional % to `RewardsDistributor` (ve rebase).

`RewardsDistributor` (optional)

* **ve Rebase:** Streams a portion of newly emitted TOWN to ve lockers. Protocol fees and revenues are never distributed to TOWN or veTOWN holders or lockers.

`Bribe` / `FeesVotingReward` (optional but recommended)

* **Per-Gauge Vaults:** Accept **allowlisted** reward tokens.
* **Eligibility:** Only ve voters who voted for this gauge in the epoch can claim.
* **Use:** Partners can fund these to steer votes without protocol code changes.

`SupplyGauge[asset]` / `BorrowGauge[asset]`

* **Stake Model:** *No explicit staking*; the “stake” is the user’s **scaled** balance read from the market tokens.
* **Hooks:**
  * Option A (hook-based): Market tokens call `onBalanceChange(user, delta)` on mint/burn/transfer.
  * Option B (lazy): Settle on user actions + periodic keeper tick.
* **Core Methods:**
  * `notifyRewardAmount(amount)` — set epoch stream.
  * `claim(user)` — pull user’s accrued rewards.
  * `earned(user)` — view function.
* **Admin:** `setRewardToken`, `setBribeAddress`, `setMarketOracle` (if needed), `pause`.

`MarketRegistry`

* **Lists markets** eligible for gauges with risk flags.
* **Params per market:** `utilizationFloor`, `maxEmissionPct`, `sideCaps`, `isActive`.

`EmissionPolicy`

* **Global knobs:** `epochLength`, `baseWeeklyEmission`, `decayBps`, `supplyMultiplier`, `borrowMultiplier`, `sinkGauge` (if zero-vote fallback).

***

## Security & Risk Controls

* **Gauge Whitelist:** Only protocol-approved markets get gauges.
* **Bribe Token Allowlist:** Prevent malicious token griefing.
* **Caps:**
  * Per-gauge max share of weekly emissions.
  * Per-asset aggregate cap across its two gauges.
* **Utilization Floor:** Optionally require `U ≥ U_min` to accept votes for a gauge (avoid wasting emissions on dead markets).
* **Kill-Switch:** Ability to pause individual gauges and bribe vaults.
* **Reentrancy & Accounting:** Guard `claim()` and `notifyRewardAmount()`; use pull-based transfers.

***

## Parameters (suggested defaults, all configurable)

* `epochLength`: **7 days**
* `decayBps`: **100 bps** per epoch (example)
* `supplyMultiplier`: **1.0** (neutral)
* `borrowMultiplier`: **1.0–1.2** (policy lever)
* `maxEmissionPctPerGauge`: **15%**
* `maxEmissionPctPerAsset`: **25%** (supply+borrow combined)
* `utilizationFloor`: **10–20%** (if enabled)
* `bribeTokensAllowlist`: curated list (stablecoins + blue-chips)

***


# TOWN Vote Escrow V1

Vote Escrow V1 of TOWN enhances liquidity for asset deposits in different liquidity pools by creating vote escrow emission gauges for different pools. An example lending market goes as follows:

* Pool 1: USDC
* Pool 2: USD1
* Vault 1: U (United Stables)
* Vault 2: XAUE

For different participants in the ecosystem, there are different incentives, powered by the voting power of veTOWN and the emissions.

* Holders of TOWN: can lock TOWN and escrow into veTOWN, which can be used to vote for one or multiple Pools. TOWN holders receive linearly increasing voting power in veTOWN the longer it is locked, similar to how veCRV is determined by the lock time of CRV.
* veTOWN: voting power that is gained with locked TOWN that can be used to vote for Pools to receive emissions.
* Gauge (*g*) & Weight (*w*): each Gauge represents each Pool's voting power of veTOWN. Weight means how much of the total voting power a Gauge has, represented in a %.
* Emissions: Emissions of TOWN token are distributed to the Depositors of a Gauge based on how much veTOWN voting power a Pool receives. Pools 1-2 and Vaults 1-2 receive TOWN emissions pro rata from the veTOWN voting power it receives.:

  * Example: a total of 1000 veTOWN is voted into Pools 1-2 and Vaults 1-2. Pool 1 has 2500, Pool 2 has 2500, Vault 1 has 2500, and Vault 2 has 2500,. In an Epoch where there are a total Emission of 100,000 TOWN, Pool 1 would receive 25% of the emission, Pool 2 also 25%, Vault 1, 25%, Vault 2, 25%.
  * The emission calculation of each Gauge is as follows:

  $$
  E\_g = E\_{\text{epoch}} \cdot \frac{w\_g}{\sum\_i w\_i}
  $$
* Lenders: Lenders who deposit liquidity into the pools receive TOWN emissions based on how much of the Pool that the Lender's liquidity is.
* Borrowers: Borrowers of the Pool liquidity receive TOWN emissions based on how much of the Pool that the Borrower has borrowed.
* Epoch: a 7-day period by the end of which Emission tokens are distributed to a Pool's lenders and borrowers as claimable reward.


# TOWN Vote Escrow V2

In addition to the incentive distribution governance illustrated in V1, V2 brings forth the governance of additions of pools and vaults, as well as TOWN staking.

On a high level, veTOWN is used to determine additions as follows:

* veTOWN mirroring: veTOWN is mirrored into gveTOWN as a vote token to separate its V1 vote power & V2 vote utility;
* The gveTOWN vote token can be used to vote for additions of pools and assets, including the paramter settings of a pool.
* TOWN staking unlocks interest fee reduction and vault management fee discount features.

The benefit of V2 primarily revolves around:

* Allowing new curators to participate (pool/vault deployment requires governance approval and curator due diligence (KYC, sanctios screening); markets may be geo-restricted);
* Making the scrutiny of the asset and the curator's credentials public and more rigorous;
* Decentralize the governance of the protocol.
* Adding the additional utility of TOWN as a stakable token.

Specifics of V2 will be shared post-V1 launch.


# TownSquare Points & Partner Points

TownSquare lending & liquidity markets run on top of a mix of TownSquare points and Partner protocol points.

*\*For the non-TownSquare points, the reward format shoukld be consulted with the team behind the partner protocol.*

***

#### TownSquare Points

TownSquare Points are rewarded to the users with the following qualifying activities:

* Lending of:
  * MON, wMON
  * MON LSTs (shMON, sMON, gMON, aprMON)
  * Stablecoins (USD1, AUSD, USDC, earnAUSD, USDT)
  * *RWAs\**
  * Bluechip assets (wBTC, wETH)
* Borrowing of:
  * MON, wMON
  * Stablecoins (USD1, AUSD, USDC, earnAUSD, USDT)
  * *RWAs\**
  * Bluechip assets (wBTC, wETH)
* Deposits of:
  * U on BNBChain
  * USD1 on BNBChain, Monad
  * USDC on Base, Monad
  * AUSD on Monad

*\*Security-type RWAs are supported only where lawful, via licensed issuers, and are geo-fenced from Korean users.*

#### Partner Protocol Points

The following protocols are partnering with TownSquare to offer their points:

* World Liberty Financial: 2x points for USD1 lends, collaterals and deposits.
* United Stables: 2x points for U deposits.
* Fastlane: 2x boosted points for collateralizing shMON to borrow
* Kintsu: 2x boosted points for collateralizing sMON to borrow
* Magma: 3x boosted points for collateralizing gMON to borrow
* Upshift: 4x boosted points for lending earnAUSD & collateralizing earnAUSD & sAUSD to borrow.

***

#### Incentive APY

Incentive rewards are variable, not guaranteed, and may change or end at any time. No dollar value is promised.

***

#### Distribution

Points are non-transferable activity records with no monetary value and no right to any token distribution. Any furure benefit is discretionary.

Partner protocol points' reward rules should be consulted with the respective protocol. TownSquare protocol does not have information on the timeline nor format of the value realization of those points.


# Creating Account

In TownSquare, accounts act as your personal hub for managing deposits, loans, and other activities across supported blockchains. Every account is uniquely tied to your wallet, enabling smooth and secure operations whether you’re on a single chain or moving between multiple networks.

To set up your account:

1. Locate the connect button from the top right corner of the website and click on **Connect Wallet**

<figure><img src="/files/YtDR7YKDzFtlJPIOGTif" alt=""><figcaption></figcaption></figure>

2. A modal to create account pops up, select the network you would like to create your account on.

<figure><img src="/files/4qyNUC25LlltHrPCDho4" alt=""><figcaption></figcaption></figure>

3. Confirm by clicking **Create Account**, then complete the transaction to create account in your wallet.

Once done, your account is ready to handle assets and interact with all available features.


# Link or Invite Address

You can connect additional networks to your account for easier management across chains.

1. Click on the connect wallet button and select **Account** from the drop down

<figure><img src="/files/f3heL6e7VBmbw7TWPpDU" alt=""><figcaption></figcaption></figure>

2. Click on **Register new wallet** button and select the network you would like to invite. Click on continue and complete the transaction in your wallet.&#x20;

<figure><img src="/files/LGk5erFNaXdErVE4uQD7" alt=""><figcaption></figcaption></figure>

3. Sign the transaction, switch to the network to be invited and accept the invite and complete the trasactionn in your wallet

<figure><img src="/files/7ZzZluNxV21WNyxwha8H" alt=""><figcaption></figcaption></figure>

3. You can also click on **Pending Invites** to accept pending invites.

<figure><img src="/files/3b0ZbhgeNiYbHNLw97up" alt=""><figcaption></figcaption></figure>


# Deposit to Earn

The Deposit to Earn feature allows you to supply assets into liquidity pools and earn interest over time. By participating, you become a lender — your deposited tokens are made available to borrowers.

1. Click on **Earn** and select from the list of pools asset you would like to deposit

<figure><img src="/files/mm2RVSUv3N6h1IEHEwMB" alt=""><figcaption></figcaption></figure>

2. Enter the amount you want to deposit, confirm the deposit and sign the transaction with your connected wallet.

<figure><img src="/files/WOATdgfnNgSgZg69SxI5" alt=""><figcaption></figcaption></figure>


# Withdraw Deposits

When you withdraw, you take back the tokens you’ve supplied to the protocol, along with any interest earned while they were deposited.

1. Go to **Earn** and select from the list of pools you have supplied to the asset you want to withdraw. Click **Withdraw**

<figure><img src="/files/LjIYt4KasxvdY7sbUnc3" alt=""><figcaption></figcaption></figure>

2. Input the amount you want to withdraw. Click **Withdraw** and sign with your wallet

<figure><img src="/files/kQRjm0uRpASEIWObx8PS" alt=""><figcaption></figcaption></figure>


# Borrow


# Adding Collateral

1. On the top right corner of the borrow section, click on **Create a new loan**

<figure><img src="/files/ARAoANlpcSeyApLmIR0z" alt=""><figcaption></figcaption></figure>

2. Input the name of your loan, choose the type of loan you want, click on Continue

<figure><img src="/files/HZEtRmVhLCg1xJEbfRt2" alt=""><figcaption></figcaption></figure>

3. You can choose Create loan and add collateral later or you select Create Loan. Selecting an asset to add to the loan while creating a loan bundles the two actions in a single transaction.

<figure><img src="/files/RTGXn6dzfipEiXqFVnts" alt=""><figcaption></figcaption></figure>

4. Before borrowing an asset you need collateral. Click **Add Collateral** to collaterize an asset

<figure><img src="/files/RPKj200un0kYqLjr0Ffi" alt=""><figcaption></figcaption></figure>

5. Input the amount you want to collaterize and sign with your connected wallet


# Borrow an Asset

1. To borrow an asset click on **Available to Borrow** from the available to borrow table

<figure><img src="/files/LnrpR7IGljBKi7IoOy3q" alt=""><figcaption></figcaption></figure>

2. Input the amount you want to borrow and sign with your wallet

<figure><img src="/files/UmIJiOoD2KN2Jlcsaa7I" alt=""><figcaption></figcaption></figure>


# Withdraw Collateral

1. To withdraw your collateral, select the loan you want to withdraw from and click withdraw

<figure><img src="/files/DCPsByJr3d5fbcIbRwcV" alt=""><figcaption></figcaption></figure>

2. Input the amount you want to withdraw and sign with your connected wallet.

<figure><img src="/files/IonWjrUb7W7qICpJTHMW" alt=""><figcaption></figcaption></figure>


# Project Roadmap

#### Q2-Q3 2026

* Lendign & liquidity protocol & institutional vault:
  * TownSquare V2 User Interface update;
  * Base & BNB Chain lending & liquidity pool deployments;
  * $150M-$200M liquidity in lending & liquidity protocol;
  * Institution-backed yield vault rollout preparation ($150M RWA vaults — private credit);
  * Institutional trading yield vaults for wETH, cbBTC, MON, USDC;
* Partnerships:
  * $100M USD1 liquidity pipeline announced;
  * Institutional trading partnership with [Native, PMM network powered by institutional traders](https://x.com/native_fi);
  * Partnership with Blockbooster, institutional RWA asset issuer in private credits & real estate; etc.
* Protocol governance & vote escrow:
  * V1 governance: Vote escrow release for pool gauges on Monad, Base, and BNBChain.

#### Q4 2026

* Lending & liquidity protocol & institutional vault:
  * BNB Chain trading-backed yield vault launch with Aster & United Stables; United Stables ($U) yield vault launch;
  * Yield vaults for BNB, AUSD;
  * RWA vaults for real estate.
* Protocol governance:
  * V2 governance:&#x20;
    * Token-based voting release for protocol governance, curated institutional vault addition, and altcoin lending & liquidity listing.
    * Staking: TOWN staking for reduced interest fees and vault management fees.


# Interest Rate Framework

The lending protocol uses a dynamic rate model that automatically adjusts to balance supply and borrowing activity. This mechanism ensures lenders receive competitive yields while borrowers face fair costs, keeping the overall system healthy and efficient.

### Core Concepts

#### Utilization Ratio (U)

This ratio shows how much of the pool’s liquidity is currently borrowed relative to the total deposits.

$$
U=Total Supplied / Total Borrowed​
$$

A higher utilization means most funds are borrowed, which generally drives interest rates upward.

**Variable Rate:** Moves up or down based on the utilization ratio.

**Interest Rate Calculations**

**Variable Borrow Interest Rate** (i<sub>vb</sub>):

The variable interest rate depends on the utilization ratio and is calculated using the following formulas:

$$
\text{If } U\_t < U\_{opt} : \quad i\_{vb}(t) = R\_{v0} + \frac{U\_t}{U\_{opt}} \times R\_{v1}
$$

$$
\text{If } U\_t \geq U\_{opt} : \quad
i\_{vb}(t) = R\_{v0} + R\_{v1} + \frac{U\_t - U\_{opt}}{1 - U\_{opt}} \times R\_{v2}
$$

**Deposit Interest Rate**

The deposit interest rate, earned by depositors, is derived from the interest paid by borrowers.

$$
i\_d(t) = U\_t \times i\_b(t) \times (1 - RR)
$$

( RR ) is the retention rate, representing the protocol's fee.

i<sub>b</sub>(t) is the overall borrow interest rate, which is a weighted average of variable and stable borrow rates.

**Borrow Interest Amount**

The stable borrow interest amount is the sum of the stable borrowed amounts multiplied by their respective interest rates.

$$
O\_{sb}(t) = \sum B\_i \times i\_{sb}(i)
$$

### Collateral and Borrowing Limits

#### Collateral Factor (CF)

Each asset has a **collateral factor (CF)**, which sets the maximum amount you can borrow against it, expressed as a percentage of its value.

**Example:**\
If USDC has a CF of **80%**, depositing **$10 USDC** allows you to borrow up to **$8 worth** of assets.

#### Borrowable Amount (BA)

The total borrowing capacity is calculated from the collateral you provide, its market price, and the asset’s collateral factor:

$$
BA(t) = \sum \left( A^t\_i \times P\_i \times CF\_i \right)
$$

#### Borrow Factor (BF)

Some assets are treated as riskier when borrowed, so they are adjusted with a **borrow factor (BF)**. This increases the effective exposure when borrowing volatile assets.

**Example:**\
If BTC has a **BF of 110%**, then borrowing **$10 BTC** is treated as **$11 of risk exposure** in the system.


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


# Liquidation

Welcome to the how-to guide on liquidations on the TownSquare money market protocol.

In TownSquare, liquidation plays a critical role in protecting the system from bad debt. When borrowers’ positions become under-collateralized — often due to market fluctuations or growing interest — a liquidation mechanism is triggered to restore balance and secure the lending pool.\
\
**What Is Liquidation?**

Liquidation is the process where unhealthy loans (i.e., those that no longer meet collateral requirements) are partially or fully repaid by external actors known as **liquidators**. In return for taking on this risk, liquidators receive a portion of the borrower’s collateral as an incentive.

#### When Is a Loan Eligible for Liquidation?

A loan enters liquidation when the **borrowed value** exceeds the borrowable amount, typically due to:

* A drop in the value of the collateral,
* An increase in the borrowed amount from interest accrual,
* Or both.

In simple terms, if the collateral no longer provides enough safety buffer, the position is considered risky and can be liquidated.

#### Who Can Liquidate?

Anyone can act as a liquidator — including bots, users, or third-party services — as long as they can detect under-collateralized positions and have enough collateral in their own loan to cover the debt they intend to repay. This ensures that the liquidation can be executed fully and safely, preserving the stability of the protocol.

#### Liquidation Rewards, Debt Transfer & Protocol Fees

In the TownSquare protocol, a loan can contain **multiple borrowed assets** and **multiple collateral assets**, potentially **across different supported chains**.\
Liquidators therefore have the flexibility to decide:

* Which borrowed asset from the violator’s loan they want to repay
* Which collateral asset from the violator’s loan they want to seize

**How It Works**

1. **Asset Selection**
   * The violator may have borrowed assets like USDC, WETH, and MON, and collateral assets like ETH and DAI.
   * The liquidator must specify:
     * The **borrowed asset** they are repaying.
     * The **collateral asset** they are receiving.
2. **Debt Transfer**
   * When a liquidator repays part of a violator’s debt, the repaid amount is transferred from the violator’s loan to the liquidator’s loan. Since TownSquare supports cross-chain loans, this debt transfer is tracked within the protocol’s unified loan system, meaning the liquidator’s loan position can now include assets seized from the violator, regardless of the chain they reside on.
3. **Collateral Seizure with Bonus**
   * The liquidator receives in their loan the specified collateral from the violator’s loan.
   * A **liquidation bonus** ensures the collateral’s value exceeds the repayment.
   * Example: With a **10% bonus**, repaying $50 USDC earns $55 worth of ETH collateral.
4. **Profit & Fees**
   * The liquidator’s profit is the difference between collateral received and debt repaid.
   * The protocol takes a **liquidation fee** from the collateral seized.
   * Example: If $60 ETH is seized with a **15% fee**, $9 goes to the protocol, $51 stays with the liquidator.


# SDK Guide: Performing a Liquidation

The **@townsq/mm-sdk** is a lightweight JavaScript/TypeScript SDK designed for interacting with the liquidation functionality of the **TownSquare Lending Protocol**.

It enables developers to:

* Identify under-collateralized (liquidatable) loans.
* Compute loan health and repayment requirements.
* Trigger on-chain liquidation transactions.

**Note:** This SDK currently focuses only on liquidation. Support for borrowing, supplying, and other protocol actions will be added in future releases.

Installation

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

or

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

### Understanding TownSquare Liquidation

Before diving into code, it’s important to understand **how liquidation works in TownSquare**.

#### 1. What Makes a Loan Liquidatable?

A loan becomes liquidatable when its **borrowed value (with interest)** is greater than or equal to its **collateral value**.

Formally checked as:

```typescript
dn.gte(
  violatorLoanInfo.totalEffectiveBorrowBalanceValue,
  violatorLoanInfo.totalEffectiveCollateralBalanceValue,
);
```

If the above condition is true → the loan is unsafe → liquidators may step in.

#### 2. Liquidator Requirements

To liquidate, a **liquidator must already have a loan** opened within the protocol.

* This loan provides the funds for repayment.
* The liquidator’s loan must hold enough collateral of the repayment token.
* If insufficient, the liquidation transaction will fail.

#### 3. Loan Types & Efficiencies

TownSquare supports **three loan types**, each with specific efficiency rules:

* **General Loans** → Accept all supported tokens.
* **Stable Efficiency** → Restricted to stable coins related assets
* **MON Efficiency Loans** → You can only borrow or supply MON-related assets with this loan

When computing loan health or preparing a liquidation, always check the `loanTypeId` to apply the correct efficiency rules.

### Reading Loans

When a liquidator service starts, it is recommended to **read all existing loans into memory** (or preferably into a persistent database).

* This gives a **snapshot** of the system’s state.
* From here, you will only need to process incremental updates from events, rather than repeatedly querying the entire chain.
* Without this snapshot, you may miss loans that are already unhealthy at startup.

#### Updating Loan State with Events

The **LoanManager contract** is the central source of truth for loan changes. It emits events for every important action that affects loan health. Liquidators must listen to these events and update their internal state accordingly.

**Events to track:**

* **Deposit**

  ```ts
  {
    loanId: string;
    poolId: number;
    amount: bigint;
    fAmount: bigint;
  }
  ```

  *Indicates collateral was added to the loan.*
* **Borrow**

  ```ts
  {
    loanId: string;
    poolId: number;
    amount: bigint;
    isStableBorrow: boolean;
    stableInterestRate: bigint;
  }
  ```

  *Indicates debt was added to the loan. If `isStableBorrow` is true, the borrow position is fixed-rate.*
* **Withdraw**

  ```ts
  {
    loanId: string;
    poolId: number;
    amount: bigint;
    fAmount: bigint;
  }
  ```

  *Indicates collateral was removed from the loan.*
* **Repay**

  ```ts
  {
    loanId: string;
    poolId: number;
    principalPaid: bigint;
    interestPaid: bigint;
    excessPaid: bigint;
  }
  ```

  *Indicates loan debt was reduced. This directly improves loan health.*
* **Liquidate**

  ```ts
  {
    violatorLoanId: string;
    liquidatorLoanId: string;
    colPoolId: number;
    borPoolId: number;
    repayBorrowBalance: bigint;
    liquidatorCollateralFAmount: bigint;
    reserveCollateralFAmount: bigint;
  }
  ```

  *Indicates a liquidation occurred. The violator’s loan was partially or fully closed, and the liquidator’s loan received seized collateral.*

When a liquidator service starts, it is recommended to **read all existing loans into memory** (or preferably into a persistent database).

* This gives a **snapshot** of the system’s state.
* From here, you will only need to process incremental updates from events, rather than repeatedly querying the entire chain.
* Without this snapshot, you may miss loans that are already unhealthy at startup.

When an event is received (e.g., `Borrow`, `Deposit`, `Repay`, `Withdraw`, `Liquidate`), liquidators should recompute the full loan state before persisting it in their database. This ensures loan health metrics are always accurate.

Example (computing `loanInfo` after receiving a `loanId` from an event):

```typescript

const poolsInfo: Partial<Record<TSTokenId, PoolInfo>> = {};
await Promise.all(
  Object.values(TESTNET_TS_TOKEN_ID).map(async (tsTokenId) => {
    const poolInfo = await TSPool.read.poolInfo(tsTokenId);
    poolsInfo[tsTokenId] = poolInfo;
  }),
);

const loanId = event.args.violatorLoanId as LoanId;

const loanTypeMap = Object.entries(TESTNET_LOAN_TYPE_ID).reduce(
  (acc, [key, value]) => {
    acc[key as any] = value;
    return acc;
  },
  {} as Record<number, LoanTypeId>,
);

try {
  const oraclePrices = await TSOracle.read.oraclePrices();
  const userLoans = await TSLoan.read.userLoans([loanId]);
  const loan = userLoans?.get(loanId);

  if (!loan) return;

  const loanType = Object.values(loanTypeMap).find((type) => type === loan.loanTypeId);

  if (!loanType || loanType === TESTNET_LOAN_TYPE_ID.DEPOSIT) return;

  const loanTypeInfo = {
    [loanType]: await TSLoan.read.loanTypeInfo(loanType),
  };

  const loanInfo = TSLoan.util.userLoansInfo(
    userLoans,
    poolsInfo,
    loanTypeInfo,
    oraclePrices,
  )[loanId];

  if (!loanInfo) return;

  // At this point, `loanInfo` contains all computed fields needed
  // to evaluate liquidation eligibility and can be stored in your DB.
} catch (e) {
  console.error("Failed to compute loan info", e);
}
```

#### Updating Loan State with Events

The **LoanManager contract** is the central source of truth for loan changes. It emits events for every important action that affects loan health. Liquidators must listen to these events and update their internal state accordingly.

### **Step-by-Step: Liquidate a Loan**

**1. Initialize the SDK and set network**

<pre class="language-typescript"><code class="lang-typescript"><strong>import { TSCore, NetworkType, TS_CHAIN_ID } from 'townsq-mm-sdk';
</strong>
TSCore.init({ network: NetworkType.TESTNET, provider: { evm: {} } });
TSCore.setNetwork(NetworkType.TESTNET);
</code></pre>

2. **Set up your signer**

```typescript
import { createWalletClient, http } from 'viem';
import { CHAIN_VIEM } from '@townsq/mm-sdk';

const signer = createWalletClient({
  chain: CHAIN_VIEM[TS_CHAIN_ID.MONAD_TESTNET],
  transport: http(), // You can pass your rpc (optional)
});

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

```

3. **Resolve Account ID from Address**

```typescript
import { TSAccount } from '@townsq/mm-sdk';

const accountId = await TSAccount.read.getAccountIdOfAddressOnChain(
  '0xYourEvmAddressHere'
);

```

**4. Check if the Loan is Liquidatable**

```typescript
import { TSLoan, TSPool, TSOracle, TESTNET_TS_TOKEN_ID, TESTNET_LOAN_TYPE_ID } from 'townsq-mm-sdk';
import * as dn from 'dnum';

const violatorLoanId = '0x...'; // loan ID to liquidate

// Fetch oracle price and violator loan
const [oraclePrices, userLoans] = await Promise.all([
  TSOracle.read.oraclePrices(),
  TSLoan.read.userLoans([violatorLoanId]),
]);

const poolsInfo = await Promise.all(
  Object.values(TESTNET_TS_TOKEN_ID).map(async (tokenId) => ({
    tokenId,
    pool: await TSPool.read.poolInfo(tokenId),
  }))
);

// Prepare loan info
const userGeneralLoansInfo = TSLoan.util.userLoansInfo(
  userLoans,
  Object.fromEntries(poolsInfo.map(({ tokenId, pool }) => [tokenId, pool])),
  {
    [TESTNET_LOAN_TYPE_ID.GENERAL]: await TSLoan.read.loanTypeInfo(TESTNET_LOAN_TYPE_ID.GENERAL),
  },
  oraclePrices
);

const loanInfo = userGeneralLoansInfo[violatorLoanId];

if (dn.lt(
  loanInfo.totalEffectiveBorrowBalanceValue,
  loanInfo.totalEffectiveCollateralBalanceValue
)) {
  console.log("Loan is healthy — not eligible for liquidation.");
  return;
}

```

**5. Prepare Liquidation Transaction**

**Important:** The liquidator’s loan must have sufficient collateral\
If the liquidator's loan doesn’t hold sufficient collateral to cover for the amount the liquidator wants to pay, the transaction will fail.

```typescript
import { TSLoan, convertToGenericAddress, ChainType, parseUnits } from '@townsq/mm-sdk'

const prepareLiquidationCall = await TSLoan.prepare.liquidate(
  accountId,
  '0xYourLiquidatorLoanId',
  violatorLoanId,
  TESTNET_TS_TOKEN_ID.USDC,  // Repay in USDC
  TESTNET_TS_TOKEN_ID.WETH,   // Receive WETH as collateral
  parseUnits('100', 18),      // Amount to repay
  parseUnits('0', 18),        // Minimum collateral to seize
  convertToGenericAddress('0xYourAddressAssociatedToYourAccount', ChainType.EVM)
);

```

**6. Execute the Liquidation**

```typescript
const result = await TSLoan.write.liquidate(accountId, prepareLiquidationCall);
console.log(`Transaction hash: ${result}`);
```


# Configurations

### Loan Type Configuration

<details>

<summary>General Loan Configuration</summary>

<table><thead><tr><th width="148.6640625">Asset</th><th width="149.66015625">Collateral Factor</th><th>Borrow Factor</th><th>Liquidation Bonus</th><th>Liquidation Fee</th></tr></thead><tbody><tr><td><strong>MON</strong></td><td>75%</td><td>100%</td><td>8%</td><td>10%</td></tr><tr><td><strong>WMON</strong></td><td>75%</td><td>100%</td><td>8%</td><td>10%</td></tr><tr><td><strong>USDC</strong></td><td>80%</td><td>100%</td><td>4%</td><td>10%</td></tr><tr><td><strong>USDT</strong></td><td>80%</td><td>100%</td><td>5%</td><td>10%</td></tr><tr><td><strong>WETH</strong></td><td>80%</td><td>100%</td><td>5%</td><td>10%</td></tr><tr><td><strong>WBTC</strong></td><td>75%</td><td>100%</td><td>8%</td><td>10%</td></tr><tr><td><strong>shMON</strong></td><td>75%</td><td>100%</td><td>10%</td><td>10%</td></tr><tr><td><strong>gMON</strong></td><td>75%</td><td>100%</td><td>10%</td><td>10%</td></tr><tr><td><strong>sMON</strong></td><td>75%</td><td>100%</td><td>10%</td><td>10%</td></tr><tr><td><strong>aprMON</strong></td><td>75%</td><td>100%</td><td>10%</td><td>10%</td></tr><tr><td>USD1</td><td>80%</td><td>100%</td><td>6%</td><td>10%</td></tr><tr><td>AUSD</td><td>80%</td><td>100%</td><td>4%</td><td>10%</td></tr><tr><td>sAUSD</td><td>80%</td><td>100%</td><td>6%</td><td>10%</td></tr><tr><td>earnAUSD</td><td>80%</td><td>100%</td><td>6%</td><td>10%</td></tr></tbody></table>

</details>

*\*Security-type RWA markets are geo-fenced and not accessible to Korean users. Parameters are shown for completeness and do not constitute an offer in Korea.*

### POOL CONFIGURATION

<details>

<summary>Pool Config</summary>

<table><thead><tr><th width="148.55859375" align="center">Asset</th><th width="104.55078125" align="center">Optml Util. %</th><th width="93.9453125" align="center">RR</th><th width="121.79296875" align="center">Variable Int 0</th><th width="101.51171875" align="center">Variable Int 1</th><th align="center">Variable Int 2</th></tr></thead><tbody><tr><td align="center"><strong>MON</strong></td><td align="center">75%</td><td align="center">10%</td><td align="center">0%</td><td align="center">5.5%</td><td align="center">200%</td></tr><tr><td align="center">WMON</td><td align="center">75%</td><td align="center">10%</td><td align="center">0%</td><td align="center">5%</td><td align="center">150%</td></tr><tr><td align="center">USDC</td><td align="center">80%</td><td align="center">10%</td><td align="center">0%</td><td align="center">7%</td><td align="center">30%</td></tr><tr><td align="center">USDT</td><td align="center">80%</td><td align="center">10%</td><td align="center">0%</td><td align="center">7%</td><td align="center">30%</td></tr><tr><td align="center"><strong>WETH</strong></td><td align="center">70%</td><td align="center">10%</td><td align="center">0%</td><td align="center">2.5%</td><td align="center">200%</td></tr><tr><td align="center"><strong>WBTC</strong></td><td align="center">80%</td><td align="center">10%</td><td align="center">0%</td><td align="center">4%</td><td align="center">200%</td></tr><tr><td align="center"><strong>shMON</strong></td><td align="center">75%</td><td align="center">10%</td><td align="center">0%</td><td align="center">5%</td><td align="center">135%</td></tr><tr><td align="center"><strong>gMON</strong></td><td align="center">75%</td><td align="center">10%</td><td align="center">0%</td><td align="center">5%</td><td align="center">135%</td></tr><tr><td align="center"><strong>sMON</strong></td><td align="center">75%</td><td align="center">10%</td><td align="center">0%</td><td align="center">5%</td><td align="center">135%</td></tr><tr><td align="center"><strong>aprMON</strong></td><td align="center">75%</td><td align="center">10%</td><td align="center">0%</td><td align="center">5%</td><td align="center">135%</td></tr><tr><td align="center">USD1</td><td align="center">80%</td><td align="center">10%</td><td align="center">0%</td><td align="center">7%</td><td align="center">300%</td></tr><tr><td align="center">AUSD</td><td align="center">80%</td><td align="center">10%</td><td align="center">0%</td><td align="center">7%</td><td align="center">40%</td></tr><tr><td align="center">sAUSD</td><td align="center">80%</td><td align="center">10%</td><td align="center">0%</td><td align="center">7%</td><td align="center">40%</td></tr><tr><td align="center">earnAUSD</td><td align="center">80%</td><td align="center">10%</td><td align="center">0%</td><td align="center">7%</td><td align="center">50%</td></tr></tbody></table>

</details>

*\*Security-type RWA markets are geo-fenced and not accessible to Korean users. Parameters are shown for completeness and do not constitute an offer in Korea.*


# Oracle

Each asset pool in the protocol has an oracle node that reports market prices for collateralization. Oracle nodes are registered in the protocol and can be simple (like Chainlink or Redstone feeds) or composite (combining multiple sources with circuit breakers or reducers). Nodes can reference other nodes as parents, creating flexible oracle configurations. Authorized roles assign nodes to pools and manage the oracle system. When prices are needed, the system processes the assigned node to return the current price feed.

<details>

<summary>Pool Oracle</summary>

<table><thead><tr><th width="179.29296875">Asset</th><th>Oracle</th></tr></thead><tbody><tr><td>MON</td><td><a href="https://monadvision.com/address/0xBcD78f76005B7515837af6b50c7C52BCf73822fb">Chainlink MON/USD</a><br><a href="https://monadvision.com/address/0x1C9582E87eD6E99bc23EC0e6Eb52eE9d7C0D6bcd">RedStone MON/USD</a></td></tr><tr><td>WMON</td><td><a href="https://monadvision.com/address/0xBcD78f76005B7515837af6b50c7C52BCf73822fb">Chainlink MON/USD</a><br><a href="https://monadvision.com/address/0x1C9582E87eD6E99bc23EC0e6Eb52eE9d7C0D6bcd">RedStone MON/USD</a></td></tr><tr><td>USDC</td><td><a href="https://monadvision.com/address/0xf5F15f188AbCB0d165D1Edb7f37F7d6fA2fCebec">Chainlink USDC/USD</a><br><a href="https://monadvision.com/address/0x7A9b672fc20b5C89D6774514052b3e0899E5E263">Redstone USDC/USD</a></td></tr><tr><td>USDT</td><td><a href="https://monadvision.com/address/0x1a1Be4c184923a6BFF8c27cfDf6ac8bDE4DE00FC">Chainlink USDT/USD</a><br><a href="https://monadvision.com/address/0x90196F6D52fce394C79D1614265d36D3F0033Ccf">Redstone USDT/USD</a></td></tr><tr><td>shMON</td><td><a href="https://monadvision.com/address/0xBcD78f76005B7515837af6b50c7C52BCf73822fb">Chainlink MON/USD</a>  <a href="https://monadvision.com/address/0x54a1020D118B9BeF3F3A4ec8E24AeEc9DFdBe4c3">Chainlink shMON/MON</a><br><a href="https://monadvision.com/address/0xAd1A270a3F7FF685B90445d9da3EE7Eb22F8A1Ec">Redstone shMON/MON</a></td></tr><tr><td>gMON</td><td><a href="https://monadvision.com/address/0xBcD78f76005B7515837af6b50c7C52BCf73822fb">Chainlink MON/USD</a> <a href="https://monadvision.com/address/0xf97dfEd6Aa4cc387aBC5d47F0062A91CB4E4A755">Chainlink gMON/MON</a><br><a href="https://monadvision.com/address/0x8C9f39f0D08EE284a4Fe0198524fE7C28630CEAb">Redstone gMON/USD</a></td></tr><tr><td>sMON</td><td><a href="https://monadvision.com/address/0xBcD78f76005B7515837af6b50c7C52BCf73822fb">Chainlink MON/USD</a> <a href="https://monadvision.com/address/0x056d0eF95A4e046D028b00E6eC00bB4A8b1eBb96">Chainlink sMON/MON</a><br><a href="https://monadvision.com/address/0xE77456457619ad1948336FBaBC3883cB965b50D1">Redstone sMON/MON</a></td></tr><tr><td>aprMON</td><td><a href="https://monadvision.com/address/0xBcD78f76005B7515837af6b50c7C52BCf73822fb">Chainlink MON/USD</a> <a href="https://monadvision.com/address/0xc744776cAF11982a4c632121E0f6E2543f42FA47">Chainlink aprMON/MON</a><br><a href="https://monadvision.com/address/0x096073133355F874A7D0a857Ffac314dda4e0551">Redstone aprMON/MON</a></td></tr><tr><td>AUSD</td><td><a href="https://monadvision.com/address/0xE20751C7B5867bCBef815ffc1b284c3f412a9e13">Chainlink AUSD/USD</a><br><a href="https://monadvision.com/address/0xFFD1339908E0deBE2416E03df0843B896b8944Fe">Redstone AUSD/USD</a></td></tr><tr><td>sAUSD</td><td>VaultAssetToSharesNode</td></tr><tr><td>earnAUSD</td><td><a href="https://monadvision.com/address/0xacC0a0cF13571d30B4b8637996F5D6D774d4fd62">Stock Price Feed</a><br><a href="https://monadvision.com/address/0xD1136a8C1E5a8Ff204d6875b51158b5Ef1858f60">Redstone earnAUSD/USD</a></td></tr><tr><td>USD1</td><td><a href="https://monadvision.com/address/0xa63564f2A626f69130C1CCA87f984351B26Cf2f1">Chainlink USD1/USD</a></td></tr><tr><td>WETH</td><td><a href="https://monadvision.com/address/0x1B1414782B859871781bA3E4B0979b9ca57A0A04">Chainlink ETH/USD</a><br><a href="https://monadvision.com/address/0xc44be6D00307c3565FDf753e852Fc003036cBc13">Redstone ETH/USD</a></td></tr><tr><td>WBTC</td><td><a href="https://monadvision.com/address/0x2D1Df1bD061AAc38C22407AD69d69bCC3C62edBD">Chainlink WBTC/USD<br></a><a href="https://monadvision.com/address/0x98ECE0D516f891a35278E3186772fb1545b274eB">Redstone WBTC/USD</a></td></tr></tbody></table>

</details>

*\*Security-type RWA markets are geo-fenced and not accessible to Korean users. Parameters are shown for completeness and do not constitute an offer in Korea.*


# Vaults & Curators

### Live vaults

#### **Monad**

**Curator: Native**

* Vaults: cbBTC, wETH, USDC, enzoBTC
* Addresses:&#x20;
  * 0xcD1D2D602C3e7394515DaAe96e4FFe16DE71e5B4
  * 0x6B00868e2D1385b3804127827bBaB461d3E697E7\ <br>


# Audits

TownSquare is going through periodic audits and has gone thru 2 audits in Q3 2025.

* Q3 2025 audits: <https://github.com/TowneSquare/audit-reports-2025Q3>

Further audits will be published here.


# TownSquare Vault Setup

## TownSqVault — Deploy, Deposit, Withdraw

This repo contains `TownSqVault`, an upgradeable ERC-4626 vault that supplies deposits into TownSq via `spokeTokenOperations.topup(...)`, and uses a **2-step withdrawal** flow:

* Step 1: `initiateWithdraw(shares, receiver, owner)` initiates the withdrawal and burns shares.
* Step 2: After a timelock (currently **6 hours**), `withdraw(owner)` finalizes by transferring assets to `receiver` (or mints shares back if transfer fails).

***

### What gets deployed

`TownSqVault` is written as an **upgradeable** contract and is intended to be used behind a proxy.

* **Implementation**: `src/TownSqVault.sol`
* **Proxy pattern**: `TransparentUpgradeableProxy`
* **Initializer**: `TownSqVault.initialize(...)` (called once via proxy constructor or upgrade tooling)

***

### Deployment (Foundry)

#### Prerequisites

* **Foundry** installed (`forge`, `cast`)
* RPC URL for your target network (the script is currently set up for **chainId = 143**)
* A deployer private key loaded in your environment (examples use Foundry’s `--sender` + broadcast key)

#### Parameters you must supply

`TownSqVault.initialize(...)` requires:

* **Owner**: `_owner` (admin of vault settings like fees)
* **TownSq addresses**:
  * `_spokeOperations`
  * `_spokeTokenOperations`
  * `_assetHubPool`
  * `_loanManager`
* **Asset**: `_asset` (ERC20 address; current script uses USDC)
* **Pool Id**: `_assetPoolId` (vault collateral pool id in TownSq)
* **Timelock**: `initialTimelock` (vault’s configurable timelock param; separate from withdrawal init timelock)
* **Chain / loan identity**:
  * `chainId`
  * `vaultLoanTypeId`
  * `nonce`
  * `loanName`
* **ERC20 share token metadata**:
  * `_name`
  * `_symbol`
* **Messaging params**: `_vaultMessageParams` (`Messages.MessageParams`)

#### Example values (from `script/TownSqVault.s.sol`)

Your script currently uses (chain 143):

* `initialOwner`: `0xa031f11d7CDF039eeF0e73E47Bd5B487f3659B65`
* `USDC`: `0x754704Bc059F8C67012fEd69BC8A327a5aafb603`
* `spokeOperations`: `0x63CB1CF5aCCbCC57e0cCa047bE9673EA5022b8DB`
* `spokeTokenOperations`: `0xA457235B68606a7921b7c525D92e9592e793b4C0`
* `assetHubPool`: `0xdb4E67F878289A820046f46f6304fd6Ee1449281`
* `loanManager`: `0xC4C20EFbEfA4Bde14091a3040d112cF981d8B2DB`
* `vaultAssetPoolId`: `10`
* `vaultLoanTypeId`: `1`
* `nonce`: `0x00000001`
* `loanName`: `"USDC VAULT"`
* `name/symbol`: `"USDC VAULT"` / `"vUSDC"`
* `messageParams`: `{ adapterId: 1, returnAdapterId: 1, receiverValue: 0, gasLimit: 5_000_000, returnGasLimit: 5_000_000 }`

#### Deploying a new proxy (recommended pattern)

The canonical flow is:

1. Deploy `TownSqVault` implementation.
2. Encode `initializerData = abi.encodeCall(TownSqVault.initialize, (...))`.
3. Deploy `TransparentUpgradeableProxy(impl, admin, initializerData)`.

You already do this in tests (`test/TownSqVault.t.sol`).

#### Running the script

There is a working Foundry invocation note at the bottom of `script/TownSqVault.s.sol`:

```bash
forge script --chain 143 script/TownSqVault.s.sol:TownSqVaultScript \
  --rpc-url "$MONAD_RPC_URL" \
  --broadcast --slow -g 300 -vvvv \
  --interactives 1 \
  --sender 0xa031f11d7CDF039eeF0e73E47Bd5B487f3659B65
```

Notes:

* `--broadcast` writes transactions + receipts into `broadcast/...`.
* If you’re deploying a **new** vault proxy, ensure your script uses the proxy-constructor deployment pattern (implementation + `TransparentUpgradeableProxy`) and not only an upgrade call.

***

### Depositing

#### User flow (ERC-4626 deposit)

To deposit `assets` of the underlying token:

1. Approve the vault to spend your asset.
2. Call `deposit(assets, receiver)` on the vault.

In Solidity terms:

* **User** calls `TownSqVault.deposit(assets, receiver)`
* Vault:
  * accrues interest (`_accrueInterest()`)
  * calculates shares using `lastTotalAssets` and current `totalSupply()`
  * transfers the ERC20 `assets` into the vault (ERC-4626 `_deposit`)
  * supplies to TownSq by calling `spokeTokenOperations.topup(...)`
  * updates `lastTotalAssets += assets`

#### Example (cast)

Replace:

* `$VAULT` with the proxy address
* `$ASSET` with the underlying asset (e.g. USDC)
* `$AMOUNT` with the amount in smallest units (USDC has 6 decimals)

```bash
cast send "$ASSET" "approve(address,uint256)" "$VAULT" "$AMOUNT" --rpc-url "$MONAD_RPC_URL" --private-key "$PK"
cast send "$VAULT" "deposit(uint256,address)" "$AMOUNT" "$(cast wallet address --private-key "$PK")" --rpc-url "$MONAD_RPC_URL" --private-key "$PK"
```

#### Shares / decimals note

The vault’s share token uses **18 decimals** (ERC20 default), and computes an internal `decimalOffset` as:

* `decimalOffset = 18 - ERC20(asset).decimals()`

So for USDC (6 decimals), `decimalOffset = 12`. This is why in tests a USDC deposit can mint “18-decimal shares”.

***

### Withdrawing (2-step flow)

Withdrawals are not done via the standard ERC-4626 `withdraw(...)` / `redeem(...)` path in this contract. Instead, the user must use:

1. `initiateWithdraw(shares, receiver, owner)`
2. wait **6 hours**
3. `withdraw(owner)`

#### Step 1 — initiateWithdraw

Call:

* `initiateWithdraw(uint256 shares, address receiver, address owner)`

What it does:

* Reverts if `owner` already has a pending withdrawal (`ErrorsLib.AlreadyInitiated()`).
* Accrues interest.
* Converts `shares → assets` using current totals (rounding up).
* Calls `_withdrawFromTownSq(assets, owner, receiver)`:
  * Ensures requested assets don’t exceed vault’s expected supply.
  * Enforces liquidity via `_withdrawable()` (deposit minus borrows from `assetHubPool`).
  * Stores `initiatedWithdrawal[owner] = { receiver, assets, shares, timeInitiated, true }`
  * Burns the owner’s shares immediately.
  * Updates `lastTotalAssets` down.
  * Calls `spokeOperations.withdraw(...)` to start withdrawing from TownSq.

If TownSq liquidity is insufficient, this step can revert with:

* `ErrorsLib.NotEnoughLiquidity()`

#### Step 2

The finalize call requires a minimum delay:

* `ConstantsLib.WITHDRAW_INITIALIZATION_TIMELOCK = 6 hours`

If you call finalize too early:

* `ErrorsLib.WithdrawTimeLockNotExpired()`

#### Step 3 — withdraw (finalize)

Call:

* `withdraw(address owner)`

What it does:

* Reverts if there is no initiated withdrawal (`ErrorsLib.NoInitiatedWithdrawal()`).
* Reverts if 6 hours hasn’t elapsed.
* Attempts to transfer `assets` to `receiver` via `safeTransferExternal(receiver, assets)`.
  * If the transfer **succeeds**: emits `EventsLib.AssetWithdraw(owner, receiver, assets)` and clears the pending state.
  * If the transfer **fails** (caught by `try/catch`): it **mints back** the burned shares to `receiver` and clears the pending state.

This “mint-back” fallback ensures the user isn’t stuck permanently if the final transfer cannot be executed for some reason.

#### Example (cast)

Assuming you want to withdraw *all shares* you own:

```bash
# shares = vault share-token balance
SHARES=$(cast call "$VAULT" "balanceOf(address)(uint256)" "$(cast wallet address --private-key "$PK")" --rpc-url "$MONAD_RPC_URL")

# initiate withdraw
cast send "$VAULT" "initiateWithdraw(uint256,address,address)" \
  "$SHARES" \
  "$(cast wallet address --private-key "$PK")" \
  "$(cast wallet address --private-key "$PK")" \
  --rpc-url "$MONAD_RPC_URL" --private-key "$PK"

# ... wait 6 hours ...

# finalize
cast send "$VAULT" "withdraw(address)" "$(cast wallet address --private-key "$PK")" \
  --rpc-url "$MONAD_RPC_URL" --private-key "$PK"
```

***

### Operational notes

* **Liquidity matters**: `initiateWithdraw` enforces available liquidity using `assetHubPool` totals. If TownSq has outstanding borrows, withdrawals may revert until liquidity is available.
* **Interest + fees**: the vault accrues interest on interaction (`deposit`, `initiateWithdraw`) via `_accrueInterest()`. If `fee != 0`, it mints fee shares to `feeRecipient`.
* **Timelock variables**:
  * `WITHDRAW_INITIALIZATION_TIMELOCK` is a hard-coded constant (6 hours).
  * `timelock` is a separate vault parameter set in `initialize()` via `_setTimelock(initialTimelock)` and bounded for non-zero values.


