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

# 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.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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/townsquare-vault/townsquare-vault-setup.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.
