deploying a vault.md

For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.

Deploying a Vault

A vault is created in a single transaction against the Vault Deployer for the asset you want to hold. The deployer pulls your initial deposit and mints shares to your wallet in the same call.

Pick the right deployer

One deployer per asset:

Asset Deployer
USDC 0xe4D85722dB649d3890f9DBA4510F1A16AD942FD9
WBTC 0x55073cdF0b0E42C8B807A265EADff69973274a8d
WETH 0x6701241e65d4baFC399Ca4358E95960aF3C041C4

Read the current minimum initial deposit

Before deploying, read the minimum initial deposit from the deployer — it's configured per asset and can change, so read it on-chain rather than hard-coding.

const deployerAbi = [
  {type: 'function', stateMutability: 'view', name: 'minInitialDeposit',
   inputs: [], outputs: [{type: 'uint256'}]},
] as const

const minDeposit = await client.readContract({
  address: deployer, abi: deployerAbi, functionName: 'minInitialDeposit',
})

Approve the initial deposit

The deployer needs to be able to pull initialDeposit of the underlying asset from your wallet:

const assetAbi = [{
  type: 'function', stateMutability: 'nonpayable', name: 'approve',
  inputs: [{type: 'address'}, {type: 'uint256'}],
  outputs: [{type: 'bool'}],
}] as const

await walletClient.writeContract({
  address: underlyingToken,
  abi: assetAbi, functionName: 'approve',
  args: [deployer, initialDeposit],
})

If the underlying token supports EIP‑2612 permit (USDC does), you can skip the separate approval transaction by using deployVaultWithPermit instead — see below.

Call deployVault

const deployAbi = [{
  type: 'function', stateMutability: 'nonpayable', name: 'deployVault',
  inputs: [
    {name: 'initialDeposit',    type: 'uint256'},
    {name: 'maxManagementFee',  type: 'uint256'},
    {name: 'maxPerformanceFee', type: 'uint256'},
  ],
  outputs: [/* FusionInstance struct — read from the event instead */],
}] as const

// Fees are in basis points (100 bps = 1%). Default DCV terms are
// 0.25% management (25 bps) and 30% performance (3000 bps) — pass those
// as the slippage caps, unless you've agreed different terms with Tesseract.
const MAX_MGMT_FEE = 25n      // 0.25%
const MAX_PERF_FEE = 3000n    // 30%

const hash = await walletClient.writeContract({
  address: deployer,
  abi: deployAbi,
  functionName: 'deployVault',
  args: [initialDeposit, MAX_MGMT_FEE, MAX_PERF_FEE],
})

const receipt = await client.waitForTransactionReceipt({hash})

maxManagementFee and maxPerformanceFee are slippage guards — the transaction reverts if the deployer's currently configured fees exceed them. Use the agreed contractual values so an accidental on-chain fee bump can't deploy a vault with fees higher than what you signed up for.

Extract the vault address

The deployer emits a VaultDeployed event. Parse it from the receipt:

import {parseAbiItem, decodeEventLog} from 'viem'

const vaultDeployedEvent = parseAbiItem(
  'event VaultDeployed(address indexed plasmaVault, address indexed owner, address accessManager, address feeManager, address rewardsManager, address withdrawManager, address contextManager, address priceManager)'
)

const log = receipt.logs.find(l => l.address.toLowerCase() === deployer.toLowerCase())
const decoded = decodeEventLog({abi: [vaultDeployedEvent], data: log.data, topics: log.topics})

const vault = decoded.args.plasmaVault
const withdrawManager = decoded.args.withdrawManager

Save plasmaVault (the vault address you'll interact with for deposits / withdrawals) and withdrawManager (per-vault manager for scheduled withdrawals). Both are also discoverable later via the Public API.

Using permit (one transaction)

For underlying tokens that support EIP‑2612, you can skip the separate approval and deploy in a single transaction:

// 1. Generate a permit signature for `initialDeposit` to `deployer`
const {v, r, s, deadline} = await signPermit({...})

// 2. Call the permit variant
await walletClient.writeContract({
  address: deployer,
  abi: /* deployVaultWithPermit ABI */, 
  functionName: 'deployVaultWithPermit',
  args: [initialDeposit, mgmtFee, perfFee, deadline, v, r, s],
})

Once your vault exists, continue to Strategy Assignment to start managing it, or Deposits if you want to add more capital first.