Flash Loans
Veil supports native flash loans: borrow any amount up to the pool’s free liquidity, use the funds within the same Solana transaction, and return them — plus a fee — before the transaction ends. If repayment is missing or insufficient, the entire transaction reverts.
Fee: amount × flash_fee_bps / 10_000 (default 0.09%, configurable by admin)
Fee split: 90% to LPs (added to total_deposits) · 10% to protocol (accumulated_fees)
FlashBorrow and FlashRepay must appear in the same transaction. A FlashBorrow instruction without a FlashRepay in the same transaction will cause the transaction to revert on submission (the pool validator rejects state where flash_loan_amount > 0 at end-of-transaction through Solana’s atomic execution model).
FlashBorrow
Discriminator: 0x06
Transfer amount tokens from the pool vault to the borrower. Records the in-flight amount and fee in pool.flash_loan_amount. Rejects if another flash loan is already active on this pool.
Accounts
| # | Name | Flags | Description |
|---|---|---|---|
| 0 | borrower | signer, writable | — |
| 1 | borrower_token | writable | Receives the borrowed tokens |
| 2 | vault | writable | Pool vault (source) |
| 3 | pool | writable | flash_loan_amount is set to amount |
| 4 | pool_authority | readonly | PDA that signs the vault transfer |
| 5 | token_program | readonly | — |
Instruction Data
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | discriminator | 0x06 |
| 1 | 8 | amount | Tokens to borrow (u64 LE) |
Errors
FlashLoanActive— a flash loan is already in progress on this poolInsufficientLiquidity— requested amount exceeds available liquidityZeroAmount— amount is zeroPoolPaused— pool is paused
FlashRepay
Discriminator: 0x07
Return the borrowed tokens plus the fee. Distributes the fee between LPs and the protocol treasury, then clears pool.flash_loan_amount.
Accounts
| # | Name | Flags | Description |
|---|---|---|---|
| 0 | borrower | signer, writable | — |
| 1 | borrower_token | writable | Source token account (repays principal + fee) |
| 2 | vault | writable | Pool vault (destination) |
| 3 | pool | writable | — |
| 4 | token_program | readonly | — |
Instruction Data
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | discriminator | 0x07 |
| 1 | 8 | amount | Tokens to repay including fee (u64 LE) |
The required repayment is pool.flash_loan_amount + fee. Any amount below this returns FlashLoanRepayInsufficient.
Errors
FlashLoanNotActive— no flash loan is in progressFlashLoanRepayInsufficient—amount < principal + fee
Usage Pattern
import {
flashBorrowIx,
flashRepayIx,
} from './lib/veil/instructions';
const loanAmount = 1_000_000n; // 1 USDC (6 decimals)
const fee = (loanAmount * 9n) / 10_000n; // 0.09% = 900
const repayAmount = loanAmount + fee;
// Your custom instruction that uses the borrowed funds
const myInstruction = buildMyArbitrageInstruction(loanAmount);
const tx = new Transaction()
.add(flashBorrowIx(borrower, borrowerToken, vault, pool, poolAuthority, loanAmount))
.add(myInstruction)
.add(flashRepayIx(borrower, borrowerToken, vault, pool, repayAmount));
await sendAndConfirmTransaction(connection, tx, [borrowerKeypair]);Fee Calculation
function flashFee(amount: bigint, feeRateBps: bigint): bigint {
return (amount * feeRateBps) / 10_000n;
}
function flashFeeSplit(fee: bigint): { lp: bigint; protocol: bigint } {
const protocol = fee / 10n;
return { lp: fee - protocol, protocol };
}