> ## 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.

# Deposit with Referral

> Deposit funds with a referrer who earns commission from a user's borrowing activity

## Deposit with Referral

A UserMetadata account stores the referrer information. The referrer must be included when the account is created. After it is set, the referrer cannot be changed.

<Tabs>
  <Tab title="TypeScript">
    <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,
          some,
        } from '@solana/kit';
        import {
          KaminoMarket,
          KaminoAction,
          VanillaObligation,
          PROGRAM_ID,
          parseKeypairFile,
          DEFAULT_RECENT_SLOT_DURATION_MS,
        } from '@kamino-finance/klend-sdk';
        import BN from 'bn.js';
        ```
      </Step>

      <Step>
        ### Setup Market and Check Existing Referrer

        Load the keypair, initialize the market, and check whether a referrer is already associated with the account.

        ```typescript theme={null}
        const KEYPAIR_FILE = '/path/to/your/keypair.json';
        const signer = await parseKeypairFile(KEYPAIR_FILE);

        const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
        const rpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');

        const marketPubkey = address('7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF'); // Main Market
        const market = await KaminoMarket.load(rpc, marketPubkey, DEFAULT_RECENT_SLOT_DURATION_MS);

        const usdcMint = address('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'); // USDC
        const depositAmount = new BN(1_000_000); // 1 USDC
        ```

        Check if a referrer is already associated with the account and set the referrer address.

        ```typescript theme={null}
        // Referrer address - UPDATE THIS with the referrer's wallet address
        const referrer = address('EZC9wzVCvihCsCHEMGADYdsRhcpdRYWzSCfqY');

        // Check if user already has UserMetadata with a different referrer
        const [, currentUserMetadata] = await market!.getUserMetadata(signer.address);
        const actualReferrer =
          currentUserMetadata && currentUserMetadata.referrer.toString() !== referrer.toString()
            ? currentUserMetadata.referrer
            : referrer;
        ```

        <Warning>
          Once a UserMetadata account has a referrer, it cannot be changed. If a referrer is already set, any new referrer value is ignored, and the original referrer continues to receive commission from the user's borrowing activity.
        </Warning>
      </Step>

      <Step>
        ### Build Deposit Instructions with Referrer

        Generate deposit instructions that include the referrer information.

        ```typescript theme={null}
        let depositAction = await KaminoAction.buildDepositTxns(
          market!,
          depositAmount,
          usdcMint,
          signer,
          new VanillaObligation(PROGRAM_ID),
          true,
          undefined,
          1_000_000,
          true,
          false,
          { skipInitialization: false, skipLutCreation: false },
          some(actualReferrer)
        );

        const hasSetup = (depositAction.setupIxs || []).length > 0;

        // Combine all instructions
        let instructions = [
          ...(depositAction.setupIxs || []),
          ...(depositAction.lendingIxs || []),
          ...(depositAction.cleanupIxs || []),
        ];
        ```

        <Tip>
          The referrer earns commission from the user's future borrowing activity. This creates a permanent, non-revocable link between the user and the referrer.
        </Tip>
      </Step>

      <Step>
        ### Send Initial Transaction

        Fetch the latest blockhash and build the transaction message.

        ```typescript theme={null}
        const signatures: string[] = [];

        try {
          const { value: depositBlockhash } = await rpc
            .getLatestBlockhash({ commitment: 'finalized' })
            .send();

          const depositTx = pipe(
            createTransactionMessage({ version: 0 }),
            (tx) => setTransactionMessageFeePayerSigner(signer, tx),
            (tx) => setTransactionMessageLifetimeUsingBlockhash(depositBlockhash, tx),
            (tx) => appendTransactionMessageInstructions(instructions, tx)
          );
        ```

        Sign and send the transaction.

        ```typescript theme={null}
          const depositSigned = await signTransactionMessageWithSigners(depositTx);
          const depositSignature = getSignatureFromTransaction(depositSigned);
          signatures.push(depositSignature);

          await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(depositSigned, {
            commitment: 'confirmed',
            skipPreflight: true,
          });

          console.log(`Deposit successful! Signature: ${depositSignature}`);
        ```
      </Step>

      <Step>
        ### Handle Retry Logic

        Detect and handle setup-related errors that may occur.

        ```typescript theme={null}
        } catch (error: unknown) {
          const errorMsg = error instanceof Error ? error.message : String(error);
          const is0x17a3 =
            errorMsg.includes('0x17a3') ||
            errorMsg.includes('6051') ||
            errorMsg.includes('IncorrectInstructionInPosition');

          if (hasSetup && is0x17a3) {
        ```

        <Warning>
          When setup instructions are included, the first transaction may fail with error 0x17a3. The retry logic handles this by waiting for blockchain state to settle, reloading the market, and rebuilding instructions.
        </Warning>

        Wait for blockchain state to settle and reload the market.

        ```typescript theme={null}
            await new Promise((resolve) => setTimeout(resolve, 2000));

            const reloadedMarket = await KaminoMarket.load(rpc, marketPubkey, DEFAULT_RECENT_SLOT_DURATION_MS);
        ```

        Rebuild the deposit instructions with the reloaded market state.

        ```typescript theme={null}
            depositAction = await KaminoAction.buildDepositTxns(
              reloadedMarket!,
              depositAmount,
              usdcMint,
              signer,
              new VanillaObligation(PROGRAM_ID),
              true,
              undefined,
              1_000_000,
              true,
              false,
              { skipInitialization: false, skipLutCreation: false },
              some(actualReferrer)
            );

            instructions = [
              ...(depositAction.setupIxs || []),
              ...(depositAction.lendingIxs || []),
              ...(depositAction.cleanupIxs || []),
            ];
        ```

        Fetch a fresh blockhash and build the retry transaction.

        ```typescript theme={null}
            const { value: retryBlockhash } = await rpc
              .getLatestBlockhash({ commitment: 'finalized' })
              .send();

            const retryTx = pipe(
              createTransactionMessage({ version: 0 }),
              (tx) => setTransactionMessageFeePayerSigner(signer, tx),
              (tx) => setTransactionMessageLifetimeUsingBlockhash(retryBlockhash, tx),
              (tx) => appendTransactionMessageInstructions(instructions, tx)
            );
        ```

        Sign and send the retry transaction.

        ```typescript theme={null}
            const retrySigned = await signTransactionMessageWithSigners(retryTx);
            const retrySignature = getSignatureFromTransaction(retrySigned);
            signatures.push(retrySignature);

            await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(retrySigned, {
              commitment: 'confirmed',
              skipPreflight: true,
            });

            console.log(
              `Deposit successful! Signature: ${retrySignature}\nSetup transaction: https://solscan.io/tx/${signatures[0]}`
            );
          } else {
            console.error('Deposit failed:', error);
          }
        }
        ```

        <Check>
          The deposit with referral is complete. The referrer is now permanently linked to the account and will earn commission from the user's future borrowing activity.
        </Check>
      </Step>
    </Steps>
  </Tab>

  <Tab title="Rust">
    <Info>
      `klend-interface` is a lightweight Rust instruction builder — it creates `Vec<Instruction>` with required refresh instructions prepended automatically. Use `ReserveInfo::from_account_data(pubkey, &data)` to construct reserve info from raw RPC account bytes. For obligation-based operations, `ObligationContext` is the recommended approach — it fetches the obligation and its associated reserves, then provides convenient `.deposit()`, `.borrow()`, `.repay()`, and `.withdraw()` methods.
    </Info>

    <Steps>
      <Step>
        ### Add Dependencies

        ```toml theme={null}
        [dependencies]
        klend-interface = "0.1.0"
        solana-pubkey = "2.1"
        solana-instruction = "2.1"
        solana-sdk = "~2.3"
        solana-client = "~2.3"
        spl-token = "7"
        spl-associated-token-account = "6"
        ```
      </Step>

      <Step>
        ### Set Up RPC Client

        ```rust theme={null}
        use solana_client::rpc_client::RpcClient;
        use solana_sdk::signer::keypair::read_keypair_file;
        use solana_sdk::signer::Signer;

        let rpc_client = RpcClient::new("https://api.mainnet-beta.solana.com");
        let signer = read_keypair_file("/path/to/your/keypair.json")
            .expect("Failed to read keypair file");
        let owner = signer.pubkey();
        ```
      </Step>

      <Step>
        ### Initialize User with Referrer

        Create the user metadata account linked to a referrer before depositing.

        ```rust theme={null}
        use klend_interface::helpers;
        use klend_interface::pda;
        use klend_interface::KLEND_PROGRAM_ID;
        use solana_pubkey::Pubkey;
        use std::str::FromStr;

        let referrer = Pubkey::from_str("EZC9wzVCvihCsCHEMGADYdsRhcpdRYWzSCfqY").unwrap();

        // Derive the referrer's user_metadata PDA
        let (referrer_user_metadata, _) = pda::user_metadata(&KLEND_PROGRAM_ID, &referrer);

        // Your user's address lookup table (created during onboarding)
        let user_lookup_table = Pubkey::from_str("YOUR_USER_LOOKUP_TABLE_PUBKEY").unwrap();

        let init_ixs = helpers::lifecycle::init_user(
            owner,
            owner,                         // fee payer
            user_lookup_table,
            Some(referrer_user_metadata),  // links to this referrer
        );
        ```

        <Warning>
          Once a UserMetadata account has a referrer, it cannot be changed. The original referrer permanently earns commission from the user's borrowing activity.
        </Warning>
      </Step>

      <Step>
        ### Build Obligation Context

        Derive the obligation PDA and build the context for depositing.

        ```rust theme={null}
        use klend_interface::ObligationContext;

        let lending_market = Pubkey::from_str("7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF").unwrap();
        let reserve_pubkey = Pubkey::from_str("D6q6wuQSrifJKZYpR1M8R4YawnLDtDsMmWM1NbBmgJ59").unwrap();

        // Derive the obligation PDA
        let (obligation_pubkey, _) = pda::obligation(
            &KLEND_PROGRAM_ID, 0, 0, &owner, &lending_market,
            &Pubkey::default(), &Pubkey::default(),
        );

        // Fetch the obligation account
        let obligation_data = rpc_client.get_account(&obligation_pubkey)?;

        // Discover which reserves the obligation references
        let reserve_addrs = ObligationContext::reserve_addresses_for_obligation(&obligation_data.data)?;

        // Fetch all reserve accounts in one RPC call
        let reserve_accounts = rpc_client.get_multiple_accounts(&reserve_addrs)?;

        // Build the context
        let reserves: Vec<(Pubkey, &[u8])> = reserve_addrs.iter()
            .zip(reserve_accounts.iter())
            .filter_map(|(addr, acc)| acc.as_ref().map(|a| (*addr, a.data.as_slice())))
            .collect();
        let ctx = ObligationContext::from_account_data(
            obligation_pubkey, &obligation_data.data, &reserves
        )?;
        ```
      </Step>

      <Step>
        ### Build Deposit Instructions

        ```rust theme={null}
        use spl_associated_token_account::get_associated_token_address;

        let liquidity_mint = Pubkey::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v").unwrap();
        let user_source_liquidity = get_associated_token_address(&owner, &liquidity_mint);

        let deposit_ixs = ctx.deposit(
            owner,
            &reserve_pubkey,
            user_source_liquidity,
            1_000_000, // 1 USDC
        )?;
        ```
      </Step>

      <Step>
        ### Send Transactions

        ```rust theme={null}
        use solana_sdk::transaction::Transaction;
        use solana_sdk::message::Message;

        // Transaction 1: Initialize user metadata with referrer
        let message = Message::new(&init_ixs, Some(&owner));
        let recent_blockhash = rpc_client.get_latest_blockhash()?;
        let tx = Transaction::new(&[&signer], message, recent_blockhash);
        rpc_client.send_and_confirm_transaction(&tx)?;

        // Transaction 2: Deposit with referral
        let message = Message::new(&deposit_ixs, Some(&owner));
        let recent_blockhash = rpc_client.get_latest_blockhash()?;
        let tx = Transaction::new(&[&signer], message, recent_blockhash);
        let signature = rpc_client.send_and_confirm_transaction(&tx)?;
        println!("Deposit with referral successful! Signature: {signature}");
        ```
      </Step>
    </Steps>
  </Tab>
</Tabs>
