> For the complete documentation index, see [llms.txt](https://docs.koo.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.koo.xyz/api/on-chain-deposit-withdraw-api.md).

# On-Chain Deposit / Withdraw API

This document explains how to call on-chain smart contracts to deposit and withdraw funds.&#x20;

***

### 1. Setup

Your program needs the following configuration (provided by the project team):

| Config             | Description                                                                                                                |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `CONTRACT_ADDRESS` | `0x73009aa79365635c6422EE67F1BB0BCA3213eF99` (on-chain smart contract address; single entry point)                         |
| `USDC_ADDRESS`     | `0xaf88d065e77c8cC2239327C5EDb3A432268e5831` (USDC token smart contract address; pass this value as the `asset` parameter) |
| `CHAIN_ID`         | Target chain ID (private chain before mainnet launch: `13579`)                                                             |
| `RPC_URL`          | `https://virtual.arbitrum.us-east.rpc.tenderly.co/d7189af4-c4ac-4ce6-845c-3a39bd6f4d7a`                                    |

Each trading account has an integer **`accountId`**. On the first deposit, an account is created automatically and an `accountId` is returned; use this ID for subsequent deposits, withdrawals, and backend order placement.

On-chain transactions must be signed by the **account holder wallet**. Deposits and withdrawals require **ETH** in the wallet for gas; **deposits** also require sufficient **USDC** balance.

***

### 2. General Conventions

#### 2.1 Amount Format

All amounts in the API use **USD notional with 18 decimal places**:

| Meaning | Value                              |
| ------- | ---------------------------------- |
| 100 USD | `100000000000000000000` (`100e18`) |
| 1 USD   | `1000000000000000000` (`1e18`)     |

ethers.js example: `parseUnits("100", 18)`.

#### 2.2 Approval Before Deposit

Before depositing, approve USDC for the smart contract address:

```solidity
IERC20(USDC_ADDRESS).approve(CONTRACT_ADDRESS, amount);
```

`amount` uses USDC native precision (6 decimal places). We recommend a one-time approval with `type(uint256).max`; after approval, subsequent deposits do not require approval again.

#### 2.3 Special Parameter Values

| Scenario                            | Value                                                  |
| ----------------------------------- | ------------------------------------------------------ |
| First deposit (auto-create account) | `accountId = type(uint256).max` (ethers: `MaxUint256`) |
| Withdraw full available balance     | `amount = type(uint256).max`                           |
| Withdraw to the caller              | `to = address(0)`                                      |

The value of `type(uint256).max` is `2^256 - 1`, written in hex as `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff` (64 `f` digits). In ethers.js you can use the constant `MaxUint256` directly without typing the literal value.

***

### 3. Deposit

#### 3.1 Interface

```solidity
function depositAsset(
    uint256 accountId,
    address asset,
    uint256 amount
) external returns (uint256 accountId);
```

#### 3.2 Usage

**First deposit (create account + fund; recommended)**

| Parameter   | Value                                     |
| ----------- | ----------------------------------------- |
| `accountId` | `MaxUint256`                              |
| `asset`     | `USDC_ADDRESS`                            |
| `amount`    | Deposit amount in USD (18 decimal places) |

After the transaction is mined, query and save `accountId` via the **ERC721Enumerable** interface:

```solidity
function balanceOf(address owner) external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
```

```typescript
const wallet = await signer.getAddress();
const count = await contract.balanceOf(wallet);

// Enumerate all accountIds held by this address
const accountIds: bigint[] = [];
for (let i = 0n; i < count; i++) {
  accountIds.push(await contract.tokenOfOwnerByIndex(wallet, i));
}

// On first creation with only one new NFT, the last item is the newly created accountId
const accountId = accountIds[accountIds.length - 1];
```

Each account creation mints a new account NFT; the global `accountId` (i.e. `tokenId`) **monotonically increases**. One wallet may hold multiple accounts; a newly created account id is usually greater than all previous ids.

**Deposit to an existing account**

| Parameter   | Value                                     |
| ----------- | ----------------------------------------- |
| `accountId` | Existing account ID                       |
| `asset`     | `USDC_ADDRESS`                            |
| `amount`    | Deposit amount in USD (18 decimal places) |

#### 3.3 Limits

* Deposit amount must be greater than 0
* Minimum deposit per transaction: **5 USD**

If the transaction is mined and does not revert, the deposit succeeded.

***

### 4. Withdraw

After initiating a withdrawal, USDC is automatically sent to the specified address.

#### 4.1 Interface

```solidity
function requestWithdraw(
    uint256 accountId,
    address asset,
    uint256 amount,
    address to
) external returns (uint256 requestId);
```

#### 4.2 Usage

| Parameter   | Value                                                                                                           |
| ----------- | --------------------------------------------------------------------------------------------------------------- |
| `accountId` | Account ID, i.e. tokenId                                                                                        |
| `asset`     | `USDC_ADDRESS`                                                                                                  |
| `amount`    | Withdrawal amount in USD (18 decimal places), or `MaxUint256` for the full withdrawable balance of this account |
| `to`        | Recipient address (see below)                                                                                   |

**`to` parameter**:

* Pass **`address(0)`** (zero address): USDC is sent to the **transaction sender** (`msg.sender`), i.e. the wallet that signed and initiated the withdrawal.
* Pass **any other valid address**: USDC is sent to that address; it must be a regular wallet or contract address that can receive ERC20 tokens.

**Suggested flow**: Call `getMaxWithdrawAmount(accountId)` first to get the withdrawable limit, then initiate the withdrawal.

#### 4.3 Fee

A fixed withdrawal fee of **1 USD** applies. Actual USDC received = withdrawal amount − fee.

#### 4.4 Limits

* Minimum withdrawal per transaction: **2 USD**, and must not exceed the withdrawable balance

If the transaction is mined and does not revert, the withdrawal succeeded. You may query the recipient’s USDC balance after about three seconds for further confirmation.

***

### 5. Query Interfaces

| Function                            | Description                                                   |
| ----------------------------------- | ------------------------------------------------------------- |
| `balanceOf(owner)`                  | Number of account NFTs held by an address                     |
| `tokenOfOwnerByIndex(owner, index)` | Enumerate the `accountId` at `index` for an address (0-based) |
| `getMaxWithdrawAmount(accountId)`   | Maximum withdrawable USD for an account (18 decimal places)   |

***

### 6. Common Failure Cases

| Symptom                                 | Suggested action                                                                                |
| --------------------------------------- | ----------------------------------------------------------------------------------------------- |
| Below minimum deposit/withdrawal amount | Increase the amount                                                                             |
| Exceeds withdrawable balance            | Query `getMaxWithdrawAmount` first; reduce withdrawal amount or free margin used by open orders |
| Withdrawal already in progress          | Wait for the previous withdrawal to complete before initiating another                          |
| Caller is not account holder            | Sign with the wallet that holds the account                                                     |
| Transaction reverted                    | Check that USDC approval is sufficient (deposit scenario)                                       |

***

### 7. Code Example (ethers v6)

```typescript
import { Contract, parseUnits, formatUnits, MaxUint256 } from "ethers";

// CONTRACT_ADDRESS, USDC_ADDRESS: see §1; abi / erc20Abi provided by the project team
const contract = new Contract(CONTRACT_ADDRESS, abi, signer);
const usdc = new Contract(USDC_ADDRESS, erc20Abi, signer);
const wallet = await signer.getAddress();

// ---------- First deposit (auto-create account) ----------
// 1. One-time USDC approval for the smart contract (only once; skip on later deposits)
await usdc.approve(CONTRACT_ADDRESS, MaxUint256);

// 2. Pass MaxUint256 as accountId to "create new account and deposit"; amount is USD with 18 decimals
const depositTx = await contract.depositAsset(
  MaxUint256,
  USDC_ADDRESS,
  parseUnits("100", 18) // Deposit 100 USD
);
await depositTx.wait(); // Mined without revert means deposit succeeded

// 3. Query accountId via ERC721Enumerable and save it
const count = await contract.balanceOf(wallet);
const accountId = await contract.tokenOfOwnerByIndex(wallet, count - 1n);
console.log("New account accountId:", accountId.toString());

// ---------- Deposit again to existing account ----------
// Use saved accountId; approve USDC first if not yet approved
const savedAccountId = accountId;
await contract.depositAsset(
  savedAccountId,
  USDC_ADDRESS,
  parseUnits("50", 18) // Deposit another 50 USD
);

// ---------- Withdraw ----------
const recipientAddress = wallet; // or any recipient address

// 1. Query withdrawable limit first to avoid revert from exceeding balance
const maxWithdraw = await contract.getMaxWithdrawAmount(savedAccountId);

// 2. Record recipient USDC balance before withdrawal (6 decimals) for reconciliation
const usdcBefore = await usdc.balanceOf(recipientAddress);

// 3. Initiate withdrawal; amount can be maxWithdraw or a specific USD amount (18 decimals)
const withdrawTx = await contract.requestWithdraw(
  savedAccountId,
  USDC_ADDRESS,
  maxWithdraw,
  recipientAddress
);
await withdrawTx.wait(); // Mined without revert means withdrawal succeeded

// 4. Optional: wait ~3 seconds, then query USDC balance again to confirm receipt (includes 1 USD fixed fee deduction)
await new Promise((resolve) => setTimeout(resolve, 3000));
const usdcAfter = await usdc.balanceOf(recipientAddress);
const receivedUsdc = usdcAfter - usdcBefore;
console.log("USDC received this time:", formatUnits(receivedUsdc, 6));
```

***

### 8. Integration Checklist

1. Configure `CONTRACT_ADDRESS`, `USDC_ADDRESS`, and RPC
2. First time: `approve` → `depositAsset(MaxUint256, …)` → query and save `accountId` via `balanceOf` / `tokenOfOwnerByIndex` → bind account on backend
3. Deposit: `approve` (if not approved) → `depositAsset(accountId, …)`
4. Withdraw: `getMaxWithdrawAmount` → `requestWithdraw` → transaction confirmation is sufficient (optional: check recipient USDC balance after 3 seconds)

***

### 9. Function Quick Reference

```solidity
function depositAsset(uint256 accountId, address asset, uint256 amount) external returns (uint256);
function requestWithdraw(uint256 accountId, address asset, uint256 amount, address to) external returns (uint256);

function getMaxWithdrawAmount(uint256 accountId) external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
```

Full ABI is provided by the project team.


---

# 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, and the optional `goal` query parameter:

```
GET https://docs.koo.xyz/api/on-chain-deposit-withdraw-api.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
