Skip to content

[VPD-1430, 1431, 1432] feat(bstock): atomic backstop liquidator + off-chain scripts #683

Open
Debugger022 wants to merge 3 commits into
developfrom
feat/VPD-1430
Open

[VPD-1430, 1431, 1432] feat(bstock): atomic backstop liquidator + off-chain scripts #683
Debugger022 wants to merge 3 commits into
developfrom
feat/VPD-1430

Conversation

@Debugger022

@Debugger022 Debugger022 commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Description

Backstop liquidation for bStock (ERC-8056 tokenized stocks: TSLAB, NVDAB, SPCXB) collateral. bStock is RFQ-only (Native is the sole market maker, no AMM), so third-party liquidators may not show up. This adds Venus's own fallback liquidator plus the off-chain tooling to drive it. Liquidation stays permissionless; this is only the backstop so the protocol never carries bad debt on an RFQ-only asset.

Changes

VPD-1430: BStockLiquidator contract + tests

Upgradeable, owner/operator-gated atomic liquidator. In one tx it repays an underwater borrow, seizes + redeems the bStock vToken, and sells the bStock to USDT via a pre-signed Native firm-quote with a hard minOut (a bad or late quote reverts the whole tx, so the protocol never holds the RFQ-only asset across a price-drift window).

  • Two funding modes: inventory (own USDT) and flash (Venus executeFlashLoan, repaid + premium in the same tx).
  • Routes the repay through the pool-wide Venus Liquidator gate when set, else liquidates directly; seize measured by balance delta so the liquidator treasury cut is handled correctly.
  • Router-allowlisted and operator-gated (it custodies funds and forwards caller-supplied swap calldata to an external router).
  • Extends the shared IComptroller (InterfacesV8) with getAccountLiquidity and executeFlashLoan (additive).

VPD-1431: off-chain liquidation scripts

The on-chain contract only does the atomic settle; every liquidation must be paired with an off-chain Native firm-quote. These scripts are that off-chain half.

VPD-1432: Safe multisig fallback

A read-only generator for a Safe Transaction Builder batch, used to settle a liquidation manually when the Native RFQ path is unavailable.

Scripts (scripts/bstock/)

lib/native.ts — Native (nativefi) RFQ API client (library). Orderbook / indicative / firm-quote, parses the signed txRequest + TTL, holds the TSLAB/NVDAB/USDT constants. API key via NATIVE_API_KEY, never hardcoded. Not run directly.

native-quote.ts — live quote smoke test (read-only, no chain). Sanity-checks the Native integration and shows price / spread / TTL / book depth before anything goes on-chain (depth is thin, so quotes above book size fail).

NATIVE_API_KEY=... npx hardhat run scripts/bstock/native-quote.ts
# options (env): TOKEN=TSLAB|NVDAB (def TSLAB), AMOUNT=1, FROM=0x..

atomic-liquidate.ts — driver for the deployed BStockLiquidator (the normal path). Confirms shortfall, precomputes the seize (adjusted for the redeem treasuryPercent), fetches the MM-signed firm-quote with the contract as taker, checks the router is allowlisted, computes minOut, then calls liquidate (inventory) or flashLiquidate (flash loan).

NATIVE_API_KEY=... LIQUIDATOR=0x.. BORROWER=0x.. VBSTOCK=0x.. VDEBT=0x.. REPAY_AMOUNT=5000 npx hardhat run scripts/bstock/atomic-liquidate.ts --network bscmainnet
# MODE=inventory|flash (def inventory), SLIPPAGE=0.5, MIN_OUT_BUFFER=0.5, DRY_RUN=1 (callStatic only), MOCK_NATIVE=<router>:<calldata> (fork tests)

lib/safe.ts — Safe Transaction Builder JSON helper (library). Encodes calls and builds the batch JSON the Safe{Wallet} Transaction Builder imports. Not run directly.

safe-fallback.ts — manual multisig fallback (read-only, emits JSON). When the RFQ path is down (API outage, halt, weekend, thin depth), a Safe multisig settles manually. Emits a 4-tx batch (approve, liquidateBorrow, redeem, transfer raw bStock to a custody/Binance address), seize amount snapshotted from chain. Sends nothing.

RPC_URL=... BORROWER=0x.. VBSTOCK=0x.. VDEBT=0x.. REPAY_AMOUNT=5000 TARGET=0x.. npx ts-node scripts/bstock/safe-fallback.ts
# ALLOW_PLACEHOLDER=1 emits a draft with a zero TARGET; OUT=path overrides output

Testing

  • Unit (contract) tests/hardhat/BStockLiquidator.ts: 23 passing — both funding modes, gate + treasury-cut path, all guards and error paths.
  • Driver (mock) tests/hardhat/BStockAtomicLiquidate.ts: 4 passing — drives atomic-liquidate.ts end to end (inventory + flash, healthy-borrower and router-allowlist guards).
  • Fork (real Venus Core) tests/hardhat/Fork/BStockLiquidatorFork.ts: passing — real liquidateBorrow → seize → redeem through the live Venus Liquidator, with strict profit/borrow assertions (stand-in markets + mock router, since no bStock market is listed yet).
  • Native RFQ client validated against the live API (quote, TTL, router).

Notes

  • No bStock market is listed in Core yet, so a full real liquidation and live RFQ settlement can only be exercised on mainnet once a market exists. The contract is collateral-agnostic and retargets to vTSLAB/vUSDT via env once listed.
  • Flash-loan authorization for the liquidator (whitelist + per-vToken setFlashLoanEnabled) is handled by a separate VIP (VPD-1433).

- bStock is RFQ-only (no AMM), so third-party liquidators may not
  show. Add a self-liquidation backstop that, in one tx, repays the
  borrow, seizes and redeems the bStock vToken, and sells it to USDT
  via a pre-signed Native RFQ quote with a hard minOut, so the
  protocol never holds the RFQ-only asset across a drift window.
- Two funding modes: own USDT inventory, or a Venus flash loan
  repaid (+ premium) in the same tx.
- Routes the repay through the pool-wide Venus Liquidator when that
  gate is set, else liquidates directly; the seized amount is read
  as a balance delta so the liquidator treasury cut is handled.
- Operator-gated and router-allowlisted: it custodies funds and
  forwards caller-supplied swap calldata to an external router.
- Extend the shared IComptroller with getAccountLiquidity and
  executeFlashLoan (additive); split storage-light interface into
  IBStockLiquidator.
- Add mocks, a unit suite (both modes, gate + treasury-cut, all
  guards and error paths), and a bscmainnet fork test on real Core.
- Native RFQ client (orderbook / firm-quote) plus a read-only quote
  smoke test for eyeballing price, depth and TTL
- atomic liquidation driver: precompute the seize, fetch the signed
  firm-quote with the contract as taker, then call liquidate or
  flashLiquidate; warn early if inventory cannot cover the repay
- mock-based test driving the script end to end: inventory and flash
  paths, plus the healthy-borrower and router-allowlist guards
- add scripts/ to the eslint tsconfig include (it was uncovered) and
  add an exchangeRateStored getter to the collateral mock that the
  driver reads
- Safe Transaction Builder JSON helper (encode calls, build batch)
- read-only script emitting a 4-tx batch (approve, liquidateBorrow,
  redeem, transfer raw bStock) for a multisig to settle a liquidation
  when the RFQ path is unavailable; seize amount snapshotted from
  chain, sends nothing
@Debugger022 Debugger022 changed the title [VPD-1430] feat(bstock): add atomic backstop liquidator [VPD-1430, 1431, 1432] feat(bstock): atomic backstop liquidator + off-chain scripts Jun 22, 2026
@Debugger022 Debugger022 self-assigned this Jun 22, 2026
@github-actions

Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Health
contracts 100% 100%
contracts.Admin 88% 41%
contracts.BStock 100% 93%
contracts.Comptroller 100% 90%
contracts.Comptroller.Diamond 95% 64%
contracts.Comptroller.Diamond.facets 87% 73%
contracts.Comptroller.Diamond.interfaces 100% 100%
contracts.Comptroller.Types 100% 100%
contracts.Comptroller.legacy 100% 100%
contracts.Comptroller.legacy.Diamond 0% 0%
contracts.Comptroller.legacy.Diamond.facets 0% 0%
contracts.Comptroller.legacy.Diamond.interfaces 100% 100%
contracts.DelegateBorrowers 100% 89%
contracts.FlashLoan.interfaces 100% 100%
contracts.Governance 68% 45%
contracts.InterestRateModels 74% 59%
contracts.Lens 43% 46%
contracts.Liquidator 83% 60%
contracts.Oracle 100% 100%
contracts.PegStability 88% 84%
contracts.Swap 87% 58%
contracts.Swap.interfaces 100% 100%
contracts.Swap.lib 81% 55%
contracts.Tokens 100% 100%
contracts.Tokens.Prime 97% 73%
contracts.Tokens.Prime.Interfaces 100% 100%
contracts.Tokens.Prime.libs 90% 76%
contracts.Tokens.VAI 82% 52%
contracts.Tokens.VRT 20% 9%
contracts.Tokens.VTokens 70% 52%
contracts.Tokens.VTokens.legacy 0% 0%
contracts.Tokens.VTokens.legacy.Utils 0% 0%
contracts.Tokens.XVS 19% 8%
contracts.Tokens.test 100% 100%
contracts.Utils 52% 31%
contracts.VAIVault 50% 45%
contracts.VRTVault 49% 36%
contracts.XVSVault 63% 50%
contracts.external 100% 100%
contracts.lib 89% 71%
Summary 58% (3806 / 6612) 44% (1451 / 3318)

@Debugger022 Debugger022 marked this pull request as ready for review June 25, 2026 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant