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

# Create a Vault

> Create and configure Kamino vaults with custom fee structures to earn revenue

<Info>
  Learn how to create a Kamino vault with [custom fee structures](#configure-vault), deposit limits, and allocation strategies. The SDK manages vault initialization, metadata setup, and lookup table creation to optimize future transactions.
</Info>

## Creating a Vault

Initialize a new vault with custom configuration and prepare it for user deposits.

<Steps>
  <Step>
    ### Import Dependencies

    Import the required packages for Solana RPC communication, Kamino SDK operations, and Kit transaction building.

    ```typescript theme={null}
    import {
      createSolanaRpc,
      createSolanaRpcSubscriptions,
      address,
      pipe,
      createTransactionMessage,
      setTransactionMessageFeePayerSigner,
      setTransactionMessageLifetimeUsingBlockhash,
      appendTransactionMessageInstructions,
      signTransactionMessageWithSigners,
      sendAndConfirmTransactionFactory,
      getSignatureFromTransaction,
    } from '@solana/kit';
    import {
      KaminoManager,
      KaminoVaultConfig,
      getMedianSlotDurationInMsFromLastEpochs,
    } from '@kamino-finance/klend-sdk';
    import { parseKeypairFile } from '@kamino-finance/klend-sdk/dist/utils/signer.js';
    import { TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';
    import { Decimal } from 'decimal.js';
    ```
  </Step>

  <Step>
    ### Load Admin Keypair and Initialize Manager

    Load the admin keypair from file and initialize the Kamino manager with proper slot timing.

    ```typescript theme={null}
    const KEYPAIR_FILE = '/path/to/your/keypair.json';

    const adminSigner = await parseKeypairFile(KEYPAIR_FILE);

    const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
    const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');
    const slotDuration = await getMedianSlotDurationInMsFromLastEpochs();
    const kaminoManager = new KaminoManager(rpc, slotDuration);
    ```

    <Note>
      `parseKeypairFile` loads an existing keypair from a JSON file that will become the vault admin. The slot duration is used for accurate vault timing calculations.
    </Note>
  </Step>

  <Step>
    ### Configure Vault

    Create a vault configuration with the core identity and fee structure.

    ```typescript theme={null}
    const kaminoVaultConfig = new KaminoVaultConfig({
      admin: adminSigner,
      tokenMint: address('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
      tokenMintProgramId: TOKEN_PROGRAM_ADDRESS,
      performanceFeeRatePercentage: new Decimal(15.0),
      managementFeeRatePercentage: new Decimal(2.0),
      name: 'MyCustomVault',
      vaultTokenSymbol: 'USDC',
      vaultTokenName: 'MyCustomVaultToken',
      // Optional — defaults shown below:
      minDepositAmount: 1000,        // lamports (default: 1000)
      minWithdrawAmount: 10,         // lamports (default: 10)
      minInvestAmount: 0,            // lamports (default: 0)
      minInvestDelaySlots: 0,        // slots (default: 0)
      withdrawalPenaltyBps: 1,       // basis points (default: 1)
      withdrawalPenaltyLamports: 1,  // lamports (default: 1)
      crankFundFeePerReserve: 1,     // lamports (default: 1)
    });
    ```

    <Info>
      The operational parameters above are optional — omit them to use the defaults. All parameters are editable post-creation using specialized update methods. Unallocated weight and cap are set post-creation only — see the [Configure Vault Parameters](#configure-vault-parameters-post-creation) step below.
    </Info>

    <Expandable title="Vault Configuration Parameters">
      | Parameter                      | Type                | Default | Description                                                      |
      | ------------------------------ | ------------------- | ------- | ---------------------------------------------------------------- |
      | `admin`                        | `TransactionSigner` | —       | Keypair that owns and manages the vault                          |
      | `tokenMint`                    | `Address`           | —       | SPL token mint address (e.g., USDC)                              |
      | `tokenMintProgramId`           | `Address`           | —       | Token program address (usually `TOKEN_PROGRAM_ADDRESS`)          |
      | `performanceFeeRatePercentage` | `Decimal`           | —       | Percentage of earned yield collected as fees                     |
      | `managementFeeRatePercentage`  | `Decimal`           | —       | Annual percentage fee on total assets                            |
      | `name`                         | `string`            | —       | Vault name stored on-chain (max 40 characters)                   |
      | `vaultTokenSymbol`             | `string`            | —       | Symbol for vault share token (max 5 characters)                  |
      | `vaultTokenName`               | `string`            | —       | Display name for vault share token (max 10 characters)           |
      | `minDepositAmount`             | `number`            | 1000    | Optional. Minimum deposit per transaction (lamports)             |
      | `minWithdrawAmount`            | `number`            | 10      | Optional. Minimum withdrawal per transaction (lamports)          |
      | `minInvestAmount`              | `number`            | 0       | Optional. Minimum amount moved in a single investment (lamports) |
      | `minInvestDelaySlots`          | `number`            | 0       | Optional. Slots between investments in a reserve                 |
      | `withdrawalPenaltyBps`         | `number`            | 1       | Optional. Percentage-based withdrawal fee (basis points)         |
      | `withdrawalPenaltyLamports`    | `number`            | 1       | Optional. Absolute withdrawal fee (lamports)                     |
      | `crankFundFeePerReserve`       | `number`            | 1       | Optional. Fee per reserve to fund crank operations (lamports)    |
    </Expandable>
  </Step>

  <Step>
    ### Generate Vault Creation Instructions

    Pass the vault configuration to the Kamino manager to generate all required initialization instructions.

    ```typescript theme={null}
    const { vault: vaultSigner, initVaultIxs: instructions } = await kaminoManager.createVaultIxs(kaminoVaultConfig);

    const allInstructions = [
      ...instructions.createAtaIfNeededIxs,
      ...instructions.initVaultIxs,
      instructions.createLUTIx,
      instructions.initSharesMetadataIx,
      ...instructions.setFarmToVaultIxs,
    ];
    ```

    <Info>
      The `createVaultIxs` method returns a vault signer plus instruction bundles for creating associated token accounts, initializing the vault, creating a lookup table (LUT), initializing share token metadata, and registering the Vault Farm and First Loss Capital Farm addresses on the vault. The farm accounts themselves are created in separate transactions — see the farm creation step below.
    </Info>
  </Step>

  <Step>
    ### Build, Send, and Confirm Transaction

    Use Kit's functional pipe pattern to build, sign, and send the transaction. We define a reusable helper since vault creation involves multiple transactions:

    ```typescript theme={null}
    // Helper to build, sign, and send a transaction
    async function buildAndSendTx(ixs) {
      const { value: blockhash } = await rpc.getLatestBlockhash({ commitment: 'finalized' }).send();
      const signed = await signTransactionMessageWithSigners(
        pipe(
          createTransactionMessage({ version: 0 }),
          (tx) => setTransactionMessageFeePayerSigner(adminSigner, tx),
          (tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),
          (tx) => appendTransactionMessageInstructions(ixs, tx)
        )
      );
      await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signed, {
        commitment: 'confirmed',
        skipPreflight: true,
      });
    }

    await buildAndSendTx(allInstructions);
    console.log('Vault creation successful! Vault ID:', vaultSigner.address);

    // Populate LUT in background (optimizes future tx sizes, not required)
    setImmediate(async () => {
      try {
        await new Promise((resolve) => setTimeout(resolve, 2000));
        await buildAndSendTx(instructions.populateLUTIxs);
      } catch (error) {
        // LUT population failure doesn't affect vault functionality
      }
    });
    ```

    <Note>
      Kit's `pipe` function enables functional composition of transaction building steps. `signTransactionMessageWithSigners` automatically handles all required signers including the vault keypair embedded in the instructions.
    </Note>
  </Step>

  <Step>
    ### Create and Attach Farms

    The `createVaultIxs` method generates instructions for the **Vault Farm** (reward distribution), **First Loss Capital Farm** (loss-absorbing capital), and **Autocompound Farm**. Each farm requires two sequential transactions: setup and update.

    ```typescript theme={null}
    // Using buildAndSendTx helper from previous step
    for (const farm of instructions.createVaultFarms) {
      await buildAndSendTx(farm.setupFarmIxs);  // Create farm account
      await buildAndSendTx(farm.updateFarmIxs);  // Configure farm-side settings
      console.log('Farm created:', farm.farm.address);
    }
    ```

    <Info>
      Farm creation is ideally done during vault initialization. If skipped, farms can be created post-creation using `kaminoManager.createVaultFarmIxs(adminSigner, vault)` to create the farm account, then registered on the vault using `kaminoManager.updateVaultFarmIxs(vault, farm)` for the Vault Farm or `kaminoManager.updateVaultFirstLossCapitalFarmIxs(vault, farm)` for the FLC Farm. See [Farms & Rewards](/docs/curators/vaults/farms-and-rewards) for the full post-creation flow.
    </Info>
  </Step>

  <Step>
    ### Configure Vault Parameters (Post-Creation)

    After the vault is created, configure parameters that are only available post-creation (unallocated weight/cap), or update any parameter set during init. Each method returns `{ updateVaultConfigIx }` — send via `buildAndSendTx`.

    ```typescript theme={null}
    import { KaminoVault } from '@kamino-finance/klend-sdk';

    const vault = new KaminoVault(rpc, vaultSigner.address);

    // Unallocated weight/cap (post-creation only — not available in KaminoVaultConfig)
    const { updateVaultConfigIx: unallocWeightIx } =
      await kaminoManager.updateVaultUnallocatedWeightIxs(vault, 500, adminSigner);
    const { updateVaultConfigIx: unallocCapIx } =
      await kaminoManager.updateVaultUnallocatedTokensCapIxs(vault, 2000000, adminSigner);
    await buildAndSendTx([unallocWeightIx]);
    await buildAndSendTx([unallocCapIx]);

    // Any init parameter can also be updated post-creation:
    // await kaminoManager.updateVaultMinDepositAmountIxs(vault, 1000, adminSigner)
    // await kaminoManager.updateVaultMinWithdrawAmountIxs(vault, 10, adminSigner)
    // await kaminoManager.updateVaultMinInvestAmountIxs(vault, 0, adminSigner)
    // await kaminoManager.updateVaultMinInvestDelaySlotsIxs(vault, 0, adminSigner)
    // await kaminoManager.updateVaultWithdrawalPenaltyBpsIxs(vault, 1, adminSigner)
    // await kaminoManager.updateVaultWithdrawalPenaltyLamportsIxs(vault, 1, adminSigner)
    // await kaminoManager.updateVaultCrankFundFeePerReserveIxs(vault, 1, adminSigner)
    ```

    <Expandable title="Post-Creation Parameters">
      | Method                                    | Parameter                   | Default | Description                                                              |
      | ----------------------------------------- | --------------------------- | ------- | ------------------------------------------------------------------------ |
      | `updateVaultMinDepositAmountIxs`          | `minDepositAmount`          | 1000    | Minimum deposit per transaction (lamports)                               |
      | `updateVaultMinWithdrawAmountIxs`         | `minWithdrawAmount`         | 10      | Minimum withdrawal per transaction (lamports)                            |
      | `updateVaultMinInvestAmountIxs`           | `minInvestAmount`           | 0       | Minimum amount moved in a single investment (lamports)                   |
      | `updateVaultMinInvestDelaySlotsIxs`       | `minInvestDelaySlots`       | 0       | Slots between investments in a reserve                                   |
      | `updateVaultWithdrawalPenaltyBpsIxs`      | `withdrawalPenaltyBps`      | 1       | Percentage-based withdrawal fee (basis points); returned to vault        |
      | `updateVaultWithdrawalPenaltyLamportsIxs` | `withdrawalPenaltyLamports` | 1       | Absolute withdrawal fee (lamports); system takes MAX of BPS and lamports |
      | `updateVaultCrankFundFeePerReserveIxs`    | `crankFundFeePerReserve`    | 1       | Fee per reserve to fund crank operations (lamports)                      |
      | `updateVaultUnallocatedWeightIxs`         | `unallocatedWeight`         | 500     | Relative weight for idle funds in total weight calculation               |
      | `updateVaultUnallocatedTokensCapIxs`      | `unallocatedTokensCap`      | 2000000 | Maximum absolute amount to keep unallocated (lamports)                   |
    </Expandable>
  </Step>
</Steps>

#### Full Code Example

```typescript expandable theme={null}
import {
  createSolanaRpc,
  createSolanaRpcSubscriptions,
  address,
  pipe,
  createTransactionMessage,
  setTransactionMessageFeePayerSigner,
  setTransactionMessageLifetimeUsingBlockhash,
  appendTransactionMessageInstructions,
  signTransactionMessageWithSigners,
  sendAndConfirmTransactionFactory,
  getSignatureFromTransaction,
} from '@solana/kit';
import {
  KaminoManager,
  KaminoVault,
  KaminoVaultConfig,
  getMedianSlotDurationInMsFromLastEpochs,
} from '@kamino-finance/klend-sdk';
import { parseKeypairFile } from '@kamino-finance/klend-sdk/dist/utils/signer.js';
import { TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';
import { Decimal } from 'decimal.js';

const KEYPAIR_FILE = '/path/to/your/keypair.json';

const adminSigner = await parseKeypairFile(KEYPAIR_FILE);

const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');
const slotDuration = await getMedianSlotDurationInMsFromLastEpochs();
const kaminoManager = new KaminoManager(rpc, slotDuration);

// Step 1: Create the vault
const kaminoVaultConfig = new KaminoVaultConfig({
  admin: adminSigner,
  tokenMint: address('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
  tokenMintProgramId: TOKEN_PROGRAM_ADDRESS,
  performanceFeeRatePercentage: new Decimal(15.0),
  managementFeeRatePercentage: new Decimal(2.0),
  name: 'MyCustomVault',
  vaultTokenSymbol: 'USDC',
  vaultTokenName: 'MyCustomVaultToken',
  // Optional — defaults shown below:
  minDepositAmount: 1000,
  minWithdrawAmount: 10,
  minInvestAmount: 0,
  minInvestDelaySlots: 0,
  withdrawalPenaltyBps: 1,
  withdrawalPenaltyLamports: 1,
  crankFundFeePerReserve: 1,
});

// Helper to build, sign, and send a transaction
async function buildAndSendTx(ixs) {
  const { value: blockhash } = await rpc.getLatestBlockhash({ commitment: 'finalized' }).send();
  const signed = await signTransactionMessageWithSigners(
    pipe(
      createTransactionMessage({ version: 0 }),
      (tx) => setTransactionMessageFeePayerSigner(adminSigner, tx),
      (tx) => setTransactionMessageLifetimeUsingBlockhash(blockhash, tx),
      (tx) => appendTransactionMessageInstructions(ixs, tx)
    )
  );
  await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signed, {
    commitment: 'confirmed',
    skipPreflight: true,
  });
}

const { vault: vaultSigner, initVaultIxs: instructions } = await kaminoManager.createVaultIxs(kaminoVaultConfig);

// Step 1: Create the vault
await buildAndSendTx([
  ...instructions.createAtaIfNeededIxs,
  ...instructions.initVaultIxs,
  instructions.createLUTIx,
  instructions.initSharesMetadataIx,
  ...instructions.setFarmToVaultIxs,
]);
console.log('Vault created! Address:', vaultSigner.address);

// Step 2: Create and attach farms (Vault Farm + First Loss Capital Farm)
for (const farm of instructions.createVaultFarms) {
  await buildAndSendTx(farm.setupFarmIxs);
  await buildAndSendTx(farm.updateFarmIxs);
  console.log('Farm created:', farm.farm.address);
}

// Step 3: Populate LUT in background
setImmediate(async () => {
  try {
    await new Promise((resolve) => setTimeout(resolve, 2000));
    await buildAndSendTx(instructions.populateLUTIxs);
  } catch (error) {
    // LUT population failure doesn't affect vault functionality
  }
});

// Step 4: Configure vault parameters post-creation (unallocated weight/cap)
const vault = new KaminoVault(rpc, vaultSigner.address);

const { updateVaultConfigIx: unallocWeightIx } =
  await kaminoManager.updateVaultUnallocatedWeightIxs(vault, 500, adminSigner);
const { updateVaultConfigIx: unallocCapIx } =
  await kaminoManager.updateVaultUnallocatedTokensCapIxs(vault, 2000000, adminSigner);

// Build and send each config update as a transaction
await buildAndSendTx([unallocWeightIx]);
await buildAndSendTx([unallocCapIx]);
```
