> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gauntlet.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Single-Depositor Vaults

> Operating single-depositor vaults as a guardian -- setup, strategy execution, fee reporting, and emergency procedures

Single-depositor vaults are the simplest Aera V3 vault type: one owner deposits assets, and the guardian executes strategy operations on their behalf. For background on how the guardian role works at the protocol level, see the [Guardian Model](/guides/concepts/supported-protocols/aera-v3/guardian-model).

<Info>
  Before starting, make sure you have:

  * A wallet with guardian permissions on the target vault
  * [viem](https://viem.sh) 2.x installed (`npm install viem`) or [Foundry](https://book.getfoundry.sh/) for the `cast` CLI
  * Familiarity with the [Aera V3 Guardian Model](/guides/concepts/supported-protocols/aera-v3/guardian-model)
</Info>

## Setup

Configure your environment for interacting with the vault contract. The guardian needs both a signing wallet and a way to read on-chain state.

<CodeGroup>
  ```typescript viem theme={null}
  import {
    createPublicClient,
    createWalletClient,
    http,
  } from 'viem'
  import { privateKeyToAccount } from 'viem/accounts'
  import { mainnet } from 'viem/chains'

  const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY')

  const publicClient = createPublicClient({
    chain: mainnet,
    transport: http(process.env.RPC_URL_ETHEREUM!),
  })

  const walletClient = createWalletClient({
    account,
    chain: mainnet,
    transport: http(process.env.RPC_URL_ETHEREUM!),
  })

  // Vault and fee contract addresses — see Contract Reference for deployed addresses
  // https://docs.gauntlet.xyz/contract-reference/addresses
  const VAULT_ADDRESS = '0x...' as const
  const FEE_CALCULATOR_ADDRESS = '0x...' as const
  ```

  ```bash cast theme={null}
  # Set environment variables for all subsequent commands
  export RPC_URL_ETHEREUM="https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"
  export PRIVATE_KEY="0xYOUR_PRIVATE_KEY"

  # Vault and fee contract addresses — see Contract Reference for deployed addresses
  # https://docs.gauntlet.xyz/contract-reference/addresses
  export VAULT_ADDRESS="0x..."
  export FEE_CALCULATOR_ADDRESS="0x..."
  ```
</CodeGroup>

## Strategy Execution

Strategy execution is the guardian's core responsibility -- submitting operations that allocate capital, rebalance positions, and interact with DeFi protocols on behalf of the vault. Operations are submitted in batches and validated against the guardian's Merkle tree before execution.

### Submitting Operations

The `submit` function on `BaseVault` accepts an array of operations to execute atomically. Each operation specifies a target contract, calldata, and Merkle proof elements. For the full Operation struct and validation details, see the [BaseVault Contract Reference](/contract-reference/base-vault).

<CodeGroup>
  ```typescript viem theme={null}
  // Minimal ABI fragment for submit — see BaseVault Contract Reference for full ABI
  const baseVaultAbi = [
    {
      name: 'submit',
      type: 'function',
      stateMutability: 'nonpayable',
      inputs: [{
        name: 'operations',
        type: 'tuple[]',
        components: [
          { name: 'target', type: 'address' },
          { name: 'data', type: 'bytes' },
          { name: 'value', type: 'uint256' },
          { name: 'proof', type: 'bytes32[]' },
          { name: 'preHook', type: 'address' },
          { name: 'preHookData', type: 'bytes' },
          { name: 'postHook', type: 'address' },
          { name: 'postHookData', type: 'bytes' },
        ],
      }],
      outputs: [],
    },
  ] as const

  // Example: single operation targeting a DeFi protocol
  const operations = [
    {
      target: '0x...' as `0x${string}`,   // Target protocol contract
      data: '0x...' as `0x${string}`,     // Encoded function call
      value: 0n,
      proof: [] as `0x${string}`[],       // Merkle proof for this operation
      preHook: '0x0000000000000000000000000000000000000000' as `0x${string}`,
      preHookData: '0x' as `0x${string}`,
      postHook: '0x0000000000000000000000000000000000000000' as `0x${string}`,
      postHookData: '0x' as `0x${string}`,
    },
  ]

  const { request } = await publicClient.simulateContract({
    address: VAULT_ADDRESS,
    abi: baseVaultAbi,
    functionName: 'submit',
    args: [operations],
    account,
  })

  const txHash = await walletClient.writeContract(request)
  const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash, confirmations: 2 })

  console.log('Operations submitted in block:', receipt.blockNumber)
  ```

  ```bash cast theme={null}
  # For simple operations, use cast send directly.
  # Complex struct submissions (Operation[]) are typically scripted
  # rather than passed inline — use a Forge script or the viem approach above.

  cast send $VAULT_ADDRESS \
    "submit((address,bytes,uint256,bytes32[],address,bytes,address,bytes)[])" \
    "[($TARGET,$CALLDATA,0,[],0x0000000000000000000000000000000000000000,0x,0x0000000000000000000000000000000000000000,0x)]" \
    --rpc-url $RPC_URL \
    --private-key $PRIVATE_KEY
  ```
</CodeGroup>

<Info>
  Operations are validated against the guardian's Merkle tree on-chain. If an operation's proof is invalid or the target is not whitelisted, the entire batch reverts with `Vault__InvalidProof`. The vault owner sets the Merkle root via `setGuardianRoot`.
</Info>

### Reading Vault State

Monitor vault state between submissions to inform strategy decisions. The Gauntlet API provides pre-computed metrics, while on-chain reads give real-time contract state.

**Via API** -- For vault metrics including TVL, APY, and share price, use the Gauntlet API. See [Displaying Data](/guides/developer/earn/displaying-data) for complete API coverage and endpoint details.

**Via on-chain reads** -- Query the vault contract directly for current state:

<CodeGroup>
  ```typescript viem theme={null}
  const vaultReadAbi = [
    {
      name: 'guardian',
      type: 'function',
      stateMutability: 'view',
      inputs: [],
      outputs: [{ type: 'address' }],
    },
    {
      name: 'paused',
      type: 'function',
      stateMutability: 'view',
      inputs: [],
      outputs: [{ type: 'bool' }],
    },
    {
      name: 'owner',
      type: 'function',
      stateMutability: 'view',
      inputs: [],
      outputs: [{ type: 'address' }],
    },
  ] as const

  const [guardian, isPaused, owner] = await Promise.all([
    publicClient.readContract({
      address: VAULT_ADDRESS,
      abi: vaultReadAbi,
      functionName: 'guardian',
    }),
    publicClient.readContract({
      address: VAULT_ADDRESS,
      abi: vaultReadAbi,
      functionName: 'paused',
    }),
    publicClient.readContract({
      address: VAULT_ADDRESS,
      abi: vaultReadAbi,
      functionName: 'owner',
    }),
  ])

  console.log('Guardian:', guardian)
  console.log('Paused:', isPaused)
  console.log('Owner:', owner)
  ```

  ```bash cast theme={null}
  # Read guardian address
  cast call $VAULT_ADDRESS "guardian()(address)" --rpc-url $RPC_URL

  # Check pause status
  cast call $VAULT_ADDRESS "paused()(bool)" --rpc-url $RPC_URL

  # Read vault owner
  cast call $VAULT_ADDRESS "owner()(address)" --rpc-url $RPC_URL
  ```
</CodeGroup>

### Rebalancing

Rebalancing is submitting a new batch of operations that adjusts the vault's position allocations. A typical rebalance involves multiple chained operations -- for example, withdrawing from one protocol and depositing into another in a single atomic submission.

<CodeGroup>
  ```typescript viem theme={null}
  // Example: two-step rebalance — withdraw from Protocol A, deposit into Protocol B
  // Each operation needs its own Merkle proof from the guardian's tree

  const rebalanceOps = [
    {
      target: '0x...' as `0x${string}`,   // Protocol A — withdraw
      data: '0x...' as `0x${string}`,     // Encoded withdraw call
      value: 0n,
      proof: [] as `0x${string}`[],
      preHook: '0x0000000000000000000000000000000000000000' as `0x${string}`,
      preHookData: '0x' as `0x${string}`,
      postHook: '0x0000000000000000000000000000000000000000' as `0x${string}`,
      postHookData: '0x' as `0x${string}`,
    },
    {
      target: '0x...' as `0x${string}`,   // Protocol B — deposit
      data: '0x...' as `0x${string}`,     // Encoded deposit call
      value: 0n,
      proof: [] as `0x${string}`[],
      preHook: '0x0000000000000000000000000000000000000000' as `0x${string}`,
      preHookData: '0x' as `0x${string}`,
      postHook: '0x0000000000000000000000000000000000000000' as `0x${string}`,
      postHookData: '0x' as `0x${string}`,
    },
  ]

  const { request: rebalanceRequest } = await publicClient.simulateContract({
    address: VAULT_ADDRESS,
    abi: baseVaultAbi,
    functionName: 'submit',
    args: [rebalanceOps],
    account,
  })

  const rebalanceTx = await walletClient.writeContract(rebalanceRequest)
  await publicClient.waitForTransactionReceipt({ hash: rebalanceTx, confirmations: 2 })

  console.log('Rebalance submitted:', rebalanceTx)
  ```

  ```bash cast theme={null}
  # Multi-operation rebalance — typically scripted for complex batches.
  # For a simple two-operation rebalance:

  cast send $VAULT_ADDRESS \
    "submit((address,bytes,uint256,bytes32[],address,bytes,address,bytes)[])" \
    "[($PROTOCOL_A,$WITHDRAW_CALLDATA,0,[],0x0000000000000000000000000000000000000000,0x,0x0000000000000000000000000000000000000000,0x),($PROTOCOL_B,$DEPOSIT_CALLDATA,0,[],0x0000000000000000000000000000000000000000,0x,0x0000000000000000000000000000000000000000,0x)]" \
    --rpc-url $RPC_URL \
    --private-key $PRIVATE_KEY
  ```
</CodeGroup>

Operations within a batch execute sequentially, so the withdrawal completes before the deposit begins. If any operation fails or its Merkle proof is invalid, the entire batch reverts atomically. For details on operation chaining and callbacks, see the [Guardian Model](/guides/concepts/supported-protocols/aera-v3/guardian-model).

## Fee Reporting

Fee reporting ensures accurate fee accrual for the vault owner and fee recipient. The guardian periodically reports the vault's current value, which the fee calculator uses to compute management and performance fees.

### How Fees Work

Single-depositor vaults use the `DelayedFeeCalculator` for fee computation. Two fee types apply:

* **Management fee** -- A percentage of assets under management (AUM), accruing over time. The fee is proportional to the vault value and the time elapsed since the last report.
* **Performance fee** -- A percentage of gains above a high-water mark. Only accrues when the vault value exceeds its previous peak.

The `DelayedFeeCalculator` uses time-delayed accrual to prevent fee manipulation through short-term vault value changes. See the [Periphery Contract Reference](/contract-reference/periphery) for full function signatures.

### Reporting Fees

Report the current vault value to trigger fee calculation. This updates the fee calculator's internal state and starts the delayed accrual window.

<CodeGroup>
  ```typescript viem theme={null}
  const feeCalculatorAbi = [
    {
      name: 'reportValue',
      type: 'function',
      stateMutability: 'nonpayable',
      inputs: [{ name: 'vaultValue', type: 'uint256' }],
      outputs: [],
    },
  ] as const

  const feeVaultAbi = [
    {
      name: 'reportFees',
      type: 'function',
      stateMutability: 'nonpayable',
      inputs: [{ name: 'vaultValue', type: 'uint256' }],
      outputs: [],
    },
  ] as const

  // Step 1: Report value to the fee calculator
  const currentVaultValue = 1000000n * 10n ** 18n // Example: 1M tokens in 18 decimals

  const { request: reportValueReq } = await publicClient.simulateContract({
    address: FEE_CALCULATOR_ADDRESS,
    abi: feeCalculatorAbi,
    functionName: 'reportValue',
    args: [currentVaultValue],
    account,
  })
  await walletClient.writeContract(reportValueReq)

  // Step 2: Report fees to the vault
  const { request: reportFeesReq } = await publicClient.simulateContract({
    address: VAULT_ADDRESS,
    abi: feeVaultAbi,
    functionName: 'reportFees',
    args: [currentVaultValue],
    account,
  })
  await walletClient.writeContract(reportFeesReq)

  console.log('Fees reported for vault value:', currentVaultValue.toString())
  ```

  ```bash cast theme={null}
  # Report value to the fee calculator
  cast send $FEE_CALCULATOR_ADDRESS \
    "reportValue(uint256)" \
    1000000000000000000000000 \
    --rpc-url $RPC_URL \
    --private-key $PRIVATE_KEY

  # Report fees to the vault
  cast send $VAULT_ADDRESS \
    "reportFees(uint256)" \
    1000000000000000000000000 \
    --rpc-url $RPC_URL \
    --private-key $PRIVATE_KEY
  ```
</CodeGroup>

### Claiming Fees

The fee recipient claims accrued fees from the vault. Specify the token, amount, and recipient address.

<CodeGroup>
  ```typescript viem theme={null}
  const claimFeesAbi = [
    {
      name: 'claimFees',
      type: 'function',
      stateMutability: 'nonpayable',
      inputs: [
        { name: 'token', type: 'address' },
        { name: 'amount', type: 'uint256' },
        { name: 'recipient', type: 'address' },
      ],
      outputs: [],
    },
  ] as const

  const FEE_TOKEN = '0x...' as const       // Token to claim fees in
  const FEE_RECIPIENT = '0x...' as const   // Address to receive fees
  const claimAmount = 50000n * 10n ** 18n  // Amount to claim

  const { request: claimReq } = await publicClient.simulateContract({
    address: VAULT_ADDRESS,
    abi: claimFeesAbi,
    functionName: 'claimFees',
    args: [FEE_TOKEN, claimAmount, FEE_RECIPIENT],
    account,
  })

  const claimTx = await walletClient.writeContract(claimReq)
  await publicClient.waitForTransactionReceipt({ hash: claimTx, confirmations: 2 })

  console.log('Fees claimed:', claimTx)
  ```

  ```bash cast theme={null}
  cast send $VAULT_ADDRESS \
    "claimFees(address,uint256,address)" \
    $FEE_TOKEN \
    50000000000000000000000 \
    $FEE_RECIPIENT \
    --rpc-url $RPC_URL \
    --private-key $PRIVATE_KEY
  ```
</CodeGroup>

### Monitoring Accrued Fees

Check how much in fees has accrued and is available for claiming.

<CodeGroup>
  ```typescript viem theme={null}
  const accruedFeesAbi = [
    {
      name: 'accruedFees',
      type: 'function',
      stateMutability: 'view',
      inputs: [],
      outputs: [{ type: 'uint256' }],
    },
  ] as const

  const accrued = await publicClient.readContract({
    address: VAULT_ADDRESS,
    abi: accruedFeesAbi,
    functionName: 'accruedFees',
  })

  console.log('Accrued fees:', accrued.toString())
  ```

  ```bash cast theme={null}
  cast call $VAULT_ADDRESS "accruedFees()(uint256)" --rpc-url $RPC_URL
  ```
</CodeGroup>

## Emergency Procedures

Emergency procedures allow the guardian to halt vault operations immediately if a security concern arises. These are safety mechanisms -- use them when you detect anomalous conditions, compromised keys, or unexpected protocol behavior.

### Pausing the Vault

Calling `pause` immediately halts all guardian operations on the vault. Both the guardian and the vault owner can pause.

<CodeGroup>
  ```typescript viem theme={null}
  const pauseAbi = [
    {
      name: 'pause',
      type: 'function',
      stateMutability: 'nonpayable',
      inputs: [],
      outputs: [],
    },
  ] as const

  const { request: pauseReq } = await publicClient.simulateContract({
    address: VAULT_ADDRESS,
    abi: pauseAbi,
    functionName: 'pause',
    account,
  })

  const pauseTx = await walletClient.writeContract(pauseReq)
  await publicClient.waitForTransactionReceipt({ hash: pauseTx, confirmations: 2 })

  console.log('Vault paused:', pauseTx)
  ```

  ```bash cast theme={null}
  cast send $VAULT_ADDRESS "pause()" \
    --rpc-url $RPC_URL \
    --private-key $PRIVATE_KEY
  ```
</CodeGroup>

<Warning>
  Only the vault **owner** can call `unpause` to resume operations. As a guardian, once you pause the vault, you cannot unpause it yourself. Coordinate with the vault owner before pausing in non-emergency situations.
</Warning>

### Checking Pause Status

Verify whether the vault is currently paused before attempting operations.

<CodeGroup>
  ```typescript viem theme={null}
  const pausedAbi = [
    {
      name: 'paused',
      type: 'function',
      stateMutability: 'view',
      inputs: [],
      outputs: [{ type: 'bool' }],
    },
  ] as const

  const isPaused = await publicClient.readContract({
    address: VAULT_ADDRESS,
    abi: pausedAbi,
    functionName: 'paused',
  })

  console.log('Vault paused:', isPaused)
  ```

  ```bash cast theme={null}
  cast call $VAULT_ADDRESS "paused()(bool)" --rpc-url $RPC_URL
  ```
</CodeGroup>

### Guardian Whitelist Check

Verify that the guardian is still authorized on the vault's whitelist. Anyone can call this function -- if the guardian fails the whitelist check, they are removed from the vault.

<CodeGroup>
  ```typescript viem theme={null}
  const whitelistAbi = [
    {
      name: 'checkGuardianWhitelist',
      type: 'function',
      stateMutability: 'nonpayable',
      inputs: [],
      outputs: [],
    },
  ] as const

  const { request: whitelistReq } = await publicClient.simulateContract({
    address: VAULT_ADDRESS,
    abi: whitelistAbi,
    functionName: 'checkGuardianWhitelist',
    account,
  })

  const whitelistTx = await walletClient.writeContract(whitelistReq)
  await publicClient.waitForTransactionReceipt({ hash: whitelistTx, confirmations: 2 })

  console.log('Guardian whitelist check passed:', whitelistTx)
  ```

  ```bash cast theme={null}
  cast send $VAULT_ADDRESS "checkGuardianWhitelist()" \
    --rpc-url $RPC_URL \
    --private-key $PRIVATE_KEY
  ```
</CodeGroup>

## Related Pages

* [Multi-Depositor Vaults](/guides/guardian/multi-depositor-vaults) -- Guardian operations for shared liquidity pools with order solving
* [Custom Hooks](/guides/guardian/custom-hooks) -- Building and deploying custom hooks for vault operations
* [Guardian Model](/guides/concepts/supported-protocols/aera-v3/guardian-model) -- How the guardian role works at the protocol level
* [Hooks](/guides/concepts/supported-protocols/aera-v3/hooks) -- How hooks validate and extend guardian operations
* [BaseVault Contract Reference](/contract-reference/base-vault) -- Full ABI for submit, pause, and guardian functions
* [FeeVault Contract Reference](/contract-reference/fee-vault) -- Fee reporting and claiming functions
* [Periphery Contract Reference](/contract-reference/periphery) -- DelayedFeeCalculator and OracleRegistry
