Last Updated: May 2026 For: External testers, developers, grant reviewers Network: Stacks Mainnet only
FlashStack supports canonical sBTC flash loans — borrow real Bitcoin (backed 1:1) with zero collateral, execute any on-chain strategy, repay in the same atomic transaction.
| Asset | Core Contract | Status |
|---|---|---|
| STX | flashstack-stx-core |
Active |
| sBTC (canonical) | flashstack-sbtc-core |
Active |
This guide covers the sBTC path. For STX testing see docs/TESTING_GUIDE_STX.md.
All contracts are under deployer SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5.
| Contract | Role | Explorer |
|---|---|---|
flashstack-sbtc-core |
sBTC flash loan engine — holds reserve, executes loans | View |
sbtc-flash-receiver-trait |
Interface all sBTC receivers must implement | View |
sbtc-test-receiver |
Basic receiver — borrows canonical sBTC and repays, no strategy | View |
Canonical sBTC token: SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token
- You need a Leather or Xverse wallet with a small amount of STX for gas (~0.01 STX covers all tests)
- You do NOT need sBTC in your wallet — the sBTC reserve lives in
flashstack-sbtc-core. Your wallet only pays the Stacks tx fee. - All testing is on Stacks Mainnet. There is no testnet deployment.
- Flash loans are atomic — if the receiver fails to repay, the entire transaction reverts. You lose only the gas fee (~$0.001). No other funds are at risk.
- Any new receiver contract you deploy must be whitelisted by the admin via
add-approved-receiverbefore it can borrow. Contact Matt to get whitelisted.
What it tests: Core sBTC flash loan mechanic — borrow canonical sBTC, repay in same tx.
Via the frontend (easiest):
- Go to flashstack.vercel.app/flash-loan
- Click the sBTC Flash Loan tab
- Connect your wallet (Mainnet)
- Enter amount:
0.00010000(10,000 sats — safe, within reserve) - Receiver: sBTC Test Receiver (default)
- Click Execute sBTC Flash Loan
- Approve in Leather/Xverse
Via Hiro Explorer (direct contract call):
- Go to flashstack-sbtc-core on Explorer
- Click Call contract → connect wallet
- Select function:
flash-loan - Parameters:
amount: u10000 receiver: SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5.sbtc-test-receiver - Submit
What success looks like:
- Transaction confirmed (green checkmark)
- Result:
(ok true) - Tokens Transferred shows two sBTC movements:
SP20...core → SP20...sbtc-test-receiver(loan sent)SP20...sbtc-test-receiver → SP20...core(principal + fee repaid)
get-statsonflashstack-sbtc-coreshowstotal-loansincremented by 1
What failure looks like:
| Error | Meaning | Fix |
|---|---|---|
(err u303) |
Reserve too low | Reduce loan amount below current reserve |
(err u306) |
Receiver not whitelisted | Contact Matt to whitelist |
(err u301) |
Zero amount | Use a positive amount |
(err u304) |
Exceeds max loan | Max is 10,000,000 sats (0.1 BTC) |
(err u300) |
Protocol paused | Contact Matt |
All read-only functions are free — no wallet, no gas.
Go to the flashstack-sbtc-core explorer page and call:
| Function | Expected Result |
|---|---|
get-reserve-balance |
> 0 sats (current reserve) |
get-fee-basis-points |
(ok u5) — 0.05% fee |
get-max-single-loan |
(ok u10000000) — 0.1 BTC |
get-admin |
(ok SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5) |
is-approved-receiver → SP20XD46...sbtc-test-receiver |
(ok true) |
calculate-fee u10000 |
(ok u1) — minimum 1 sat fee |
calculate-fee u1000000 |
(ok u50) — 0.05% of 1M sats |
get-stats |
tuple with total-loans, total-volume, total-fees-collected |
These should all fail cleanly with no fund loss:
| Test | How to trigger | Expected error |
|---|---|---|
| Loan exceeds reserve | flash-loan with amount > reserve |
(err u303) |
| Loan exceeds max | flash-loan with amount > u10000000 |
(err u304) |
| Unapproved receiver | flash-loan with an unwhitelisted contract |
(err u306) |
| Zero amount | flash-loan with u0 |
(err u301) |
All revert cleanly — only gas is spent.
Any Clarity contract that implements the sbtc-flash-receiver-trait can borrow sBTC from FlashStack.
The trait (one function required):
(define-trait sbtc-flash-receiver-trait
(
(execute-sbtc-flash (uint principal) (response bool uint))
)
)Minimal receiver template:
(impl-trait 'SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5.sbtc-flash-receiver-trait.sbtc-flash-receiver-trait)
(define-constant SBTC 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-token)
(define-constant CORE 'SP20XD46NGAX05ZQZDKFYCCX49A3852BQABNP0VG5.flashstack-sbtc-core)
(define-constant ERR-REPAY-FAILED (err u500))
;; Called by flashstack-sbtc-core after transferring sBTC to this contract.
;; sBTC is already in this contract when this function runs.
;; You MUST repay amount + fee before this function returns.
(define-public (execute-sbtc-flash (amount uint) (core principal))
(let (
;; Get current fee rate from core
(fee-bp (unwrap! (contract-call? CORE get-fee-basis-points) ERR-REPAY-FAILED))
(raw-fee (/ (* amount fee-bp) u10000))
(fee (if (> raw-fee u0) raw-fee u1)) ;; minimum 1 sat
(owed (+ amount fee))
)
;; ── YOUR STRATEGY GOES HERE ──────────────────────────────
;; e.g. swap sBTC for USDA on Velar, arbitrage, liquidate
;; The sBTC loan is already in this contract.
;; ─────────────────────────────────────────────────────────
;; Repay principal + fee to core
(unwrap!
(as-contract (contract-call? SBTC transfer owed tx-sender core none))
ERR-REPAY-FAILED)
(ok true)
)
)Key points:
- Use
as-contractwhen callingtransfer— the contract is the sender, not tx-sender - The fee is minimum 1 sat regardless of amount
- Pre-fund your receiver with enough sBTC to cover the fee if your strategy doesn't generate surplus
- Repayment must happen before
execute-sbtc-flashreturns — it's verified by the core
Deployment steps:
- Write your receiver contract
- Deploy to Stacks Mainnet (
clarinet deployor via Hiro Explorer) - Message Matt with your deployed contract address to get whitelisted
- Call
flash-loan(amount, your-receiver)onflashstack-sbtc-core
| Tx | Type | Explorer |
|---|---|---|
0x67f0c77d... |
sBTC flash loan — test receiver | View |
0xc9d8e86f... |
sBTC flash loan — production frontend | View |
- Message Matt directly on X @flashstackbtc or via GitHub
- For failed txs: share the txid from Hiro Explorer
- To request whitelist for your receiver contract: open an issue on GitHub
- Do not call admin functions (
deposit-reserve,add-approved-receiver,set-paused) — deployer wallet only