Skip to main content

Documentation Index

Fetch the complete documentation index at: https://v2-docs.exponent.finance/llms.txt

Use this file to discover all available pages before exploring further.

The Loopscale instructions module enables vault managers and allocators to interact with Loopscale through LoopscaleClient. Unlike the other integrations that start from createVaultSyncTransaction directly, Loopscale flows start from raw Loopscale API responses and then turn those responses into executable vault transactions. Loopscale exposes two distinct Strategy Vault surfaces:
  • Lender-side strategy flows — create, fund, update, withdraw from, and close a Loopscale strategy
  • Borrower-side loan flows — create a loan, deposit collateral, borrow principal, repay, withdraw collateral, and close the loan

How co-signing works

Loopscale transaction flow is slightly different from the other Strategy Vault integrations:
  1. Loopscale constructs the transaction flow through its API.
  2. The SDK turns that raw response into manager-facing vault transactions and signs them locally.
  3. If a transaction is flagged with requiresLoopscaleCoSign, send that transaction to client.coSign(...).
  4. Submit the returned co-signed transaction on-chain.
Only the wrapped Loopscale transaction belongs to Loopscale’s signing domain. Setup transactions and other local top-level transactions should be sent directly.
If you need more detail on Loopscale-specific mechanics, markets, or transaction endpoints, visit the Loopscale Docs.

Loopscale client

Create a client once and reuse it across your strategy or loan flow:
import { LoopscaleClient } from "@exponent-labs/exponent-sdk";

const client = new LoopscaleClient({
  connection,
  userWallet: vault.state.squadsVault,
});

Choose an execution path

Loopscale transaction methods return raw Loopscale responses. After each call, choose one of two execution paths.

Prepare appendable transactions

Use prepareVaultTransactions(...) when you want lower-level transaction pieces that you can compose yourself.
import { SQUADS_PROGRAM_ID } from "@exponent-labs/exponent-sdk";

const preparedTransactions = await client.prepareVaultTransactions({
  response,
  context: {
    connection,
    signer: wallet.publicKey,
    vaultPda: vault.state.squadsVault,
    vaultAddress: vault.selfAddress,
    squadsProgram: SQUADS_PROGRAM_ID,
  },
});
Each prepared entry contains:
  • setupInstructions — send these first when present
  • instructions — the main instruction list for the step
  • signers — any extra local signers required for the step
  • addressLookupTableAddresses — lookup tables needed to compile the transaction
  • requiresLoopscaleCoSign — whether the main transaction still needs client.coSign(...)

Build ready-to-send transactions

Use buildVaultTransactions(...) when you want locally signed VersionedTransactions that are ready to send.
import {
  LoopscaleClient,
  SQUADS_PROGRAM_ID,
  type LoopscaleTransactionResponse,
} from "@exponent-labs/exponent-sdk";

async function executeLoopscaleResponse(response: LoopscaleTransactionResponse) {
  const builtTransactions = await client.buildVaultTransactions({
    response,
    context: {
      connection,
      signer: wallet.publicKey,
      signers: [wallet],
      vaultPda: vault.state.squadsVault,
      vaultAddress: vault.selfAddress,
      squadsProgram: SQUADS_PROGRAM_ID,
    },
  });

  for (const builtTransaction of builtTransactions) {
    const transaction = builtTransaction.requiresLoopscaleCoSign
      ? await client.coSign(builtTransaction.transaction)
      : builtTransaction.transaction;

    await connection.sendTransaction(transaction, { skipPreflight: false });
  }
}
requiresLoopscaleCoSign is the exact boundary for Loopscale MPC signing. Only call coSign(...) for transactions flagged true.
buildVaultTransactions(...) requires context.signers[0] to match context.signer.
If you are already using VaultTransactionBuilder, pass the raw response to addLoopscaleResponse(...) and let result.send() handle any required Loopscale co-signing.

Create strategy

createStrategy(...) returns a raw Loopscale batch response. The response itself is enough to execute the create flow. If you want to keep the created strategy address for later calls, you can extract it from the response.
const response = await client.createStrategy({
  principalMint: new PublicKey("..."),
  lender: vault.state.squadsVault,
  amount: 0,
});

await executeLoopscaleResponse(response);

const strategyAddress = await client.extractCreatedStrategyAddress(response);

Parameters

ParameterTypeDescription
principalMintPublicKey | stringMint of the strategy principal asset
lenderPublicKey | stringVault-owned lender address
amountbigint | numberInitial funding amount
collateralTermsArray<...>Optional collateral and APY configuration
marketInformationPublicKey | stringOptional external market information account
externalYieldSourceArgsTxnExternalYieldSourceArgsOptional external yield source configuration
A matching policy must exist before executing. See Policy Builders. See also Loopscale: Create Strategy for API details.

Deposit strategy

const response = await client.depositStrategy({
  strategy: strategyAddress,
  amount: 500_000_000,
});

await executeLoopscaleResponse(response);

Parameters

ParameterTypeDescription
strategyPublicKey | stringLoopscale strategy account
amountbigint | numberAmount of principal to deposit
A matching policy must exist before executing. See Policy Builders. See also Loopscale: Deposit Strategy for API details.

Update strategy

const response = await client.updateStrategy({
  strategy: strategyAddress,
  updateParams: {
    originationsEnabled: true,
    liquidityBuffer: 500,
  },
});

await executeLoopscaleResponse(response);
updateStrategy(...) also accepts collateralTerms when you need to change collateral matrices or duration / APY settings.

Parameters

ParameterTypeDescription
strategyPublicKey | stringLoopscale strategy account
collateralTermsStrategyCollateralUpdatesOptional collateral matrix updates
updateParamsEditStrategySettingsArgsOptional strategy settings updates
A matching policy must exist before executing. See Policy Builders. See also Loopscale: Update Strategy for API details.

Withdraw strategy

const response = await client.withdrawStrategy({
  strategy: strategyAddress,
  amount: 0,
  withdrawAll: true,
});

await executeLoopscaleResponse(response);

Parameters

ParameterTypeDescription
strategyPublicKey | stringLoopscale strategy account
amountbigint | numberAmount of principal to withdraw
withdrawAllbooleanWhether to withdraw the full position
A matching policy must exist before executing. See Policy Builders. See also Loopscale: Withdraw Strategy for API details.

Close strategy

const response = await client.closeStrategy({
  strategy: strategyAddress,
});

await executeLoopscaleResponse(response);

Parameters

ParameterTypeDescription
strategyPublicKey | stringLoopscale strategy account
A matching policy must exist before executing. See Policy Builders. See also Loopscale: Close Strategy for API details.

Create loan

createLoan(...) returns a raw Loopscale response plus a loanAddress string you can use immediately in follow-up calls.
const response = await client.createLoan({
  borrower: vault.state.squadsVault,
  depositCollateral: [{
    collateralAmount: 500_000_000,
    collateralAssetData: {
      Spl: { mint: new PublicKey("...").toBase58() },
    },
  }],
  principalRequested: [{
    ledgerIndex: 0,
    principalAmount: 100_000_000,
    principalMint: new PublicKey("..."),
    strategy: strategyAddress,
    durationIndex: 0,
    expectedLoanValues: {
      expectedApy: 0,
      expectedLqt: [0, 0, 0, 0, 0],
    },
  }],
});

const loanAddress = new PublicKey(response.loanAddress);

await executeLoopscaleResponse(response);

Parameters

ParameterTypeDescription
borrowerPublicKey | stringVault-owned borrower address
depositCollateralArray<...>Collateral legs to deposit into the loan
principalRequestedArray<...>Requested principal legs tied to strategies and durations
assetIndexGuidancenumber[]Optional asset index hints
loanNoncestringOptional loan nonce override
isLoopbooleanOptional loop-mode flag
A matching policy must exist before executing. See Policy Builders. See also Loopscale: Create Loan for API details.

Deposit collateral

const response = await client.depositCollateral({
  loan: loanAddress,
  depositMint: new PublicKey("..."),
  amount: 500_000_000,
  assetType: 0,
  assetIdentifier: new PublicKey("..."),
  expectedLoanValues: {
    expectedApy: 0,
    expectedLqt: [0, 0, 0, 0, 0],
  },
});

await executeLoopscaleResponse(response);

Parameters

ParameterTypeDescription
loanPublicKey | stringLoopscale loan account
depositMintPublicKey | stringMint being deposited as collateral
amountbigint | numberCollateral amount
assetTypenumberLoopscale asset type discriminator
assetIdentifierPublicKey | stringAsset identifier for the collateral leg
assetIndexGuidancenumber[]Optional asset index hints
expectedLoanValues{ expectedApy?: number; expectedLqt?: number[] }Optional loan-state hints
A matching policy must exist before executing. See Policy Builders. See also Loopscale: Deposit Collateral for API details.

Borrow principal

Before you request a borrow, inspect the available Loopscale lending options for the principal, collateral, and duration you want to use. In the SDK, the usual discovery calls are:
  • getQuotes(...) when you want a list of current lend orders for a principal / collateral / duration combination
  • getMaxQuote(...) when you already know the collateral input and want the top matching quote for that borrow
After you choose the strategy you want to borrow from, pass that strategy address into borrowPrincipal(...).
const response = await client.borrowPrincipal({
  loan: loanAddress,
  strategy: strategyAddress,
  borrowParams: {
    amount: 100_000_000,
    durationIndex: 0,
    expectedLoanValues: {
      expectedApy: 0,
      expectedLqt: [0, 0, 0, 0, 0],
    },
  },
});

await executeLoopscaleResponse(response);

Parameters

ParameterTypeDescription
loanPublicKey | stringLoopscale loan account
strategyPublicKey | stringStrategy providing the borrowed principal
borrowParams{ amount, durationIndex, expectedLoanValues? }Requested borrow amount and duration
refinanceParams{ ledgerIndex, durationIndex }Optional refinance target
isLoopbooleanOptional loop-mode flag
A matching policy must exist before executing. See Policy Builders. See also Loopscale: Borrow Principal for API details.
If you need more detail on Loopscale-specific market discovery and borrow selection, visit the Loopscale Docs. The official data endpoints are Get quotes and Get best quote.

Repay loan

repayLoanSimple(...) builds the raw repay_simple Loopscale response.
const response = await client.repayLoanSimple({
  loan: loanAddress,
  repayParams: {
    amount: 0,
    ledgerIndex: 0,
    repayAll: true,
  },
  strategy: strategyAddress,
});

await executeLoopscaleResponse(response);

Parameters

ParameterTypeDescription
loanPublicKey | stringLoopscale loan account
repayParams{ amount, ledgerIndex, repayAll }Repayment details for the loan
strategyPublicKey | stringStrategy used for the repayment flow
A matching policy must exist before executing. See Policy Builders. See also Loopscale: Repay Loan for API details.

Withdraw collateral

const response = await client.withdrawCollateral({
  loan: loanAddress,
  collateralMint: new PublicKey("..."),
  amount: 500_000_000,
  collateralIndex: 0,
  expectedLoanValues: {
    expectedApy: 0,
    expectedLqt: [0, 0, 0, 0, 0],
  },
});

await executeLoopscaleResponse(response);

Parameters

ParameterTypeDescription
loanPublicKey | stringLoopscale loan account
collateralMintPublicKey | stringMint to withdraw
amountbigint | numberCollateral amount to withdraw
collateralIndexnumberCollateral slot to withdraw from
expectedLoanValues{ expectedApy?: number; expectedLqt?: number[] }Loan-state hints required by the request
assetIndexGuidancenumber[]Optional asset index hints
A matching policy must exist before executing. See Policy Builders. See also Loopscale: Withdraw Collateral for API details.

Close loan

const response = await client.closeLoan({
  loan: loanAddress,
});

await executeLoopscaleResponse(response);

Parameters

ParameterTypeDescription
loanPublicKey | stringLoopscale loan account
A matching policy must exist before executing. See Policy Builders. See also Loopscale: Close Loan for API details.

Practical notes

  • createStrategy(...) can be executed directly from its raw response. Use extractCreatedStrategyAddress(...) only when you want to reference the created strategy later.
  • createLoan(...) returns a raw response plus loanAddress, so you can construct the loan public key immediately.
  • Only call coSign(...) for transactions flagged with requiresLoopscaleCoSign === true.
  • Split lender-side strategy actions and borrower-side loan actions into separate Loopscale policies when you configure the vault.