# Vault Operations

> Always follow the **API-driven** pattern: obtain router metadata dynamically, construct contract instances with `dexFactory`/`routerFactory`, and avoid hardcoding addresses. This keeps the withdrawal flow compatible with future router updates.

## Mainnet workflow

1. Discover the vaults owed to the operator (e.g., via `stonApiClient.getWalletVaultsFee`).
2. Fetch router metadata for each vault using `getRouter(routerAddress)`.
3. Build the router contracts dynamically with `routerFactory` and request the vault instance.
4. Generate the withdrawal transaction parameters.

```typescript
import { Client, routerFactory } from "@ston-fi/sdk";
import { StonApiClient } from "@ston-fi/api";

const tonClient = new Client({
  endpoint: "https://toncenter.com/api/v2/jsonRPC",
  apiKey: process.env.TON_API_KEY,
});

const apiClient = new StonApiClient();

const userWalletAddress = "<your wallet address>";

// Retrieve pending vault fees for the wallet
const vaults = await apiClient.getWalletVaultsFee({
  walletAddress: userWalletAddress,
});

const messages = await Promise.all(
  vaults.map(async ({ routerAddress, assetAddress }) => {
    const routerMetadata = await apiClient.getRouter(routerAddress);
    const router = tonClient.open(routerFactory(routerMetadata));

    if (!("getVault" in router)) {
      throw new Error(
        `Vault contract is not supported on router version ${routerMetadata.majorVersion}.${routerMetadata.minorVersion}`,
      );
    }

    const tokenMinter =
      assetAddress === "EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c"
        ? routerMetadata.ptonMasterAddress
        : assetAddress;

    const vault = tonClient.open(
      await router.getVault({
        tokenMinter,
        user: userWalletAddress,
      }),
    );

    const txParams = await vault.getWithdrawFeeTxParams({
      queryId: 12345,
    });

    return {
      address: txParams.to.toString(),
      amount: txParams.value.toString(),
      payload: txParams.body?.toBoc().toString("base64"),
    };
  }),
);
```

Send the generated messages using your preferred wallet integration (see the [transaction sending guide](/developer-section/common/transaction-sending.md)).

> **TON fees**: TON does not have a jetton minter address. When `assetAddress` equals the canonical TON address (`EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c`), pass `routerMetadata.ptonMasterAddress` as the `tokenMinter` so the SDK resolves the correct proxy contract.

## Testnet withdrawal (manual setup)

Use testnet only when you must. Because `api.ston.fi` targets mainnet, you’ll have to hardcode contract addresses, mint or source the testnet jettons (e.g., TesREED), and deploy any supporting contracts yourself before attempting fee withdrawals.

```typescript
import { TonClient } from "@ton/ton";
import { DEX } from "@ston-fi/sdk";

const client = new TonClient({
  endpoint: "https://testnet.toncenter.com/api/v2/jsonRPC",
});

const router = client.open(
  DEX.v2_1.Router.CPI.create("kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v"), // CPI Router v2.1.0 (testnet)
);

const vault = client.open(
  await router.getVault({
    tokenMinter: "kQDLvsZol3juZyOAVG8tWsJntOxeEZWEaWCbbSjYakQpuYN5", // TesREED jetton (testnet)
    user: "<your testnet wallet>",
  }),
);

const txParams = await vault.getWithdrawFeeTxParams({
  queryId: 12345,
});
```

This manual approach is strictly **for testing**. Switch back to the API-driven workflow for mainnet deployments. For a comprehensive overview of referral fees, see the [Omniston referral fees guide](/developer-section/omniston/referral-fees.md#referral-fees-with-dex-v2) (while Omniston-focused, the linked section details the DEX V2 referral process).


---

# Agent Instructions: Querying This Documentation

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

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

```
GET https://docs.ston.fi/developer-section/dex/sdk/v2/vault-operations.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.
