Bezalt
Menu

Protocol documentation

Private credit for tokenized assets, explained.

Bezalt lets holders of tokenized stocks and real-world assets draw a USD stablecoin against what they own without selling it, and without disclosing the portfolio behind the proof. This page covers the mechanics, the on-chain contracts, who controls what, and how to integrate.

Last updated 20 September 2026 · matches contracts v0.1.0

Overview

Bezalt is a private credit layer. A borrower proves that a tokenized asset is owned, valued, eligible and unencumbered; the protocol then opens a USDG credit line against it. The asset is held in the protocol's escrow for the life of the line and returned in full on close, the loan carries a fixed one-time fee rather than a running interest rate, and the proof that made the loan possible can be reused across draws and venues.

Keep ownership

Collateral is escrowed, never sold or lent out, and returned in full when the line closes. Upside stays with the holder.

Pay once

A single origination fee is set per collateral class when the line is drawn. Nothing accrues while the position is open.

Prove privately

Venues learn that a position is eligible. They never learn which assets, how many, or in which wallets.

How it works

A credit line moves through five states. Each transition is recorded on-chain as an attestation; the underlying holdings are never published.

  1. Verify

    The borrower connects a wallet or custodial account. Bezalt runs four private checks: ownership of the tokenized asset, its current valuation from an accepted oracle, its eligibility under protocol parameters, and any existing liabilities against it.

  2. Issue a passport

    Passing checks produce a collateral passport: a signed, time-bounded proof that a position is eligible for a stated borrowing capacity. The passport contains eligibility signals only.

  3. Draw

    The borrower opens a credit line and draws USDG up to the passport's capacity. The origination fee is deducted at draw time. No further charges accrue.

  4. Maintain

    Valuation and liability checks refresh on a schedule. If collateral falls below the maintenance threshold, the position becomes eligible for liquidation by the stability pool.

  5. Repay and release

    Repaying the drawn USDG releases the pledge. The passport remains valid until it expires and can back a new line without repeating full verification.

Eligible collateral

Bezalt accepts tokenized assets whose issuer, transfer rules and price feeds satisfy the protocol's eligibility parameters. Collateral classes are configured with a maximum loan-to-value ratio, a maintenance threshold and an origination fee.

Collateral classes and parameter roles. Figures are illustrative examples, not live protocol values.
ClassExamplesMax LTVMaintenanceFee
Tokenized equitiesListed stocks and ETFs issued on-chain by a regulated issuere.g. 60%e.g. 75%e.g. 1.5%
Tokenized treasuriesShort-duration government bond fundse.g. 85%e.g. 92%e.g. 0.6%
Private credit & fundsTokenized fund units with periodic NAVe.g. 45%e.g. 65%e.g. 2.0%
Other RWAsReal estate and receivables with accepted valuation attestationsPer listingPer listingPer listing

Loan-to-value is applied to the accepted value, which may be haircut from market value for thinly traded assets. Each class lists the oracles and attestation providers it accepts. To see these parameters applied to a position, open the terminal.

Fixed fees

Bezalt does not charge interest. A credit line costs a fixed origination fee, quoted as a percentage of the amount drawn and settled once, at draw time. Fees are set per collateral class and published before the borrower commits.

Worked example (illustrative)

Accepted collateral value
$100,000
Max LTV for class
60%
Amount drawn
$50,000 USDG
Origination fee (1.5%)
$750, deducted at draw
Charges while open
None
Amount to repay
$50,000 USDG

Because nothing accrues, the cost of a position does not depend on how long it stays open. Borrowers who need short-term liquidity pay the same fee as those who hold a line for a year. Fee revenue flows to the stability pool and to the protocol treasury according to the published split.

Fee routing and the BEZALT buyback

Every origination fee is split in the transaction that charges it. CreditLine.draw computes fee = amount × originationFeeBps / 10 000, retains fee × poolFeeShareBps / 10 000 in the stability pool as depositor income and transfers the remainder to treasury. There is no intermediate balance, no claim step and no discretion over the split at run time; both parameters are set by the risk admin through the 48 h timelock.

The treasury's share funds a programmatic buyback of BEZALT (ticker $BEZALT), the protocol token. The buyback is a separate, non-upgradeable contract, BuybackRouter, whose only inputs are the USDG it receives from the treasury and a set of bounded parameters:

ParameterBoundInitialPurpose
epochLength1 h – 7 d24 hAt most one execution per epoch.
maxSpendPerEpoch≤ 2 % of StabilityPool.totalAssets()2 %Caps market impact regardless of how much USDG has accumulated.
maxDeviationBps≤ 500150Execution price must sit within ±1.5 % of the venue's 30-minute TWAP; otherwise the call reverts and the USDG waits for the next epoch.
burnBps0 – 10 0005 000Share of purchased BEZALT sent to address(0); the rest goes to the staking reserve.
venueallow-listedBEZALT/USDG pool on Robinhood ChainThe single swap route the router may use. Changing it is a timelocked action.
keeperTipBps≤ 5010Paid to whoever calls execute(), so the buyback needs no privileged operator.
Bounds are enforced in the constructor and in every setter; a misconfiguration cannot make the router spend beyond its cap or accept an off-market price.
// BuybackRouter, v1.1 interface (specified; not part of the audited v1 set) function execute() external returns (uint256 usdgSpent, uint256 bezaltBought, uint256 burned); // requires block.timestamp ≥ lastExecution + epochLength // spend = min(balance, maxSpendPerEpoch(pool.totalAssets())) // requires |executionPrice − twap30m| ≤ maxDeviationBps // burns bought × burnBps / 10 000; rest → stakingReserve; tip → msg.sender function previewExecute() external view returns (uint256 spend, uint256 minOut, bool ready); event Buyback( uint64 indexed epoch, uint256 usdgIn, uint256 bezaltOut, uint256 burned, uint256 reserved, address keeper );

What the buyback does not do. It never touches the stability pool's share of the fee, it holds no BEZALT between executions, it cannot sell, and it has no admin path that moves USDG anywhere except the allow-listed venue. Pool depositors' income is unaffected by whether a buyback runs.

Status. Fee splitting (poolFeeShareBps → pool, remainder → treasury) is implemented and tested in the v1 CreditLine. BuybackRouter and the BEZALT token are specified for v1.1 and are not part of the current contract set; until they ship, the treasury share accumulates in the treasury multisig.

Stability pool

The stability pool is the protocol's source of liquidity and its liquidation backstop. Participants deposit USDG and receive a proportional claim on pool outcomes. Returns are outcome-based rather than time-based: the pool earns when something happens, not while time passes.

Liquidation gains

When an undercollateralised position is liquidated, the pool repays the debt and receives the collateral at a discount to accepted value.

Protocol rewards

A published share of origination fees is distributed to pool participants in proportion to their deposit.

Partner revenue

Integrated venues that route credit lines through Bezalt share a portion of their revenue with the pool.

Deposits can be withdrawn subject to the pool's liquidity schedule, which is designed so that a withdrawal never leaves an open liquidation unfunded. Participants take on the risk that liquidated collateral is worth less than the debt repaid; the discount and maintenance thresholds exist to keep that outcome rare.

Private verification

Bezalt separates what must be true from what must be seen. A lending venue needs to know that a position is eligible for a stated capacity. It does not need the position's contents. The verification layer produces the first without revealing the second.

What each party can see
FactBorrowerBezalt verifierLending venuePublic chain
Which assets are heldYesDuring the check onlyNoNo
Quantity and walletsYesDuring the check onlyNoNo
Eligibility resultYesYesYesYes (attestation)
Borrowing capacityYesYesYesYes (bounded)
Existing liabilitiesYesDuring the check onlyPass/fail onlyNo

Checks are performed against signed data from issuers, custodians and oracles, and the verifier discards inputs once the attestation is produced. The published attestation carries an expiry so that stale proofs cannot be reused indefinitely.

Collateral passport

A passport is the portable form of a verification result. It binds an eligibility outcome and a capacity bound to a borrower key, with an expiry and the identifiers of the checks that produced it. Any integrated venue can verify a passport without contacting the original verifier and without learning what was checked.

  • Reusable. One passport can back lines at several venues until it expires, as long as total draws stay within capacity.
  • Cross-chain. Attestations are chain-agnostic; a passport issued against assets on one network can be presented on another.
  • Revocable. A failed refresh check revokes the passport, and every venue that relies on it sees the revocation.
  • Minimal. The passport holds eligibility signals and a capacity bound. It never holds holdings.

Contracts

Six non-upgradeable contracts, Solidity 0.8.28 on OpenZeppelin 5.4. Source, tests and deployment scripts live in contracts/ of the repository.

Deployed addresses. Real deployments are recorded in contracts/deployments/<chainId>.json and published here once they exist.
ContractRobinhood Chain mainnet (4663)Testnet (46630)
TimelockControllernot yet deployednot yet deployed
CollateralRegistrynot yet deployednot yet deployed
PassportRegistrynot yet deployednot yet deployed
ChainlinkPriceOraclenot yet deployednot yet deployed
AttestedPriceOraclenot yet deployednot yet deployed
StabilityPoolnot yet deployednot yet deployed
CreditLinenot yet deployednot yet deployed
BuybackRouter v1.1 · specified--
Status. The contracts are complete and verified locally (tests, invariants, static analysis, a scripted dry run on a chain with id 4663) but have not been deployed to Robinhood Chain and have not been externally audited. Treat every address above as pending.

CreditLine

FunctionWhoWhat it does
open(asset, collateral, draw, passport, sig)anyoneEscrows collateral, optionally draws. Returns the position id.
draw(id, amount, passport, sig)ownerDraws more USDG under a valid passport; enforces max LTV, capacity and minDebt; charges the fee.
repay(id, amount)anyoneRepays principal to the pool. type(uint256).max repays in full. Never paused.
addCollateral(id, amount)anyoneTops up escrow. Never paused.
withdrawCollateral(id, amount)ownerWithdraws while staying at or below max LTV.
close(id)ownerReturns all collateral of a debt-free position.
liquidate(id)anyoneLiquidates at or above maintenance LTV; pays the keeper fee to the caller.
getPosition(id) · ltvBps(id) · isLiquidatable(id)viewPosition data and health.
maxAdditionalDraw(id) · quoteFee(asset, amount) · previewLiquidation(id)viewPlanning helpers for front-ends and keepers.

Events: PositionOpened, CollateralAdded, CollateralWithdrawn, Drawn(id, amount, fee, passportId), Repaid, PositionClosed, Liquidated(id, liquidator, debt, seized, toPool, toKeeper, returned).

StabilityPool (ERC-4626)

FunctionWhoWhat it does
deposit / mint / withdraw / redeemanyoneStandard ERC-4626. Withdrawals are capped by idle USDG; use maxWithdraw / maxRedeem.
totalAssets() · idle() · loansOutstanding() · seizedValue()viewShare-value composition.
availableLiquidity()viewIdle minus the reserve: what the credit line may lend now.
buyCollateral(asset, amount, maxCost)anyoneBuys seized collateral at oracle value minus the sale discount; requires a fresh price.
donate(amount)anyoneAdds USDG to the pool for all depositors (partner revenue).
fund · collect · absorbCreditLine onlyPrincipal accounting hooks.
seizedAssets() · unpricedSeizedAssets()viewInventory and stale-price diagnostics.

Registries and oracles

FunctionContractWhat it does
verify(passport, signature) → idPassportRegistryReverts unless signed by an attester, in window, above minIssuedAt and not revoked.
hashPassport(passport) · isRevoked(id) · domainSeparator()PassportRegistryEIP-712 helpers for off-chain verification.
revoke(id) · setMinIssuedAt(ts) · setMaxValidity(s)PassportRegistryRevocation and issuance policy.
getParams(asset) · valueOf(asset, amount) · priceOf(asset)CollateralRegistryParameters and fresh valuation (reverts when stale).
listAsset · setParams · setEnabledCollateralRegistryRisk-admin controls, validated against bounds.
getPrice(asset) → (price1e18, updatedAt)both oraclesCommon price interface.
setFeed · setSequencerUptimeFeedChainlinkPriceOracleFeed wiring and L2 sequencer check.
post(valuation, sig) · forcePost · setDeviationBandAttestedPriceOracleSigned valuations with monotonic timestamps, nonces and an optional band.

Roles & governance

Administration is split into narrow roles held by different keys. The default admin of every contract is a TimelockController (48-hour minimum delay enforced by the deployment script on mainnet); the governor multisig proposes through it and directly holds the risk-parameter role. The deployer renounces its admin role at the end of deployment, and the dry run asserts it on-chain.

RoleRecommended holderPowers
DEFAULT_ADMINTimelock (48 h)Grant/revoke roles, unpause, set treasury, mass-invalidate passports, sweep untracked tokens
RISK_ADMINGovernor multisigList assets; set LTV, maintenance, fee, bonus, keeper fee, price age, supply cap; feeds; reserve and sale discount; fee share; dust floor
GUARDIANGuardian multisig / hardware keyPause new exposure; pause liquidations
ATTESTERVerification service keySign and revoke passports
VALUATIONValuation service keySign valuations for the attested oracle
CREDIT_LINECreditLine contractMove principal in and out of the pool
What a pause can never stop: repay, add collateral, close a debt-free position, pool withdrawals and redemptions, buying seized collateral. Liquidations have their own switch so a guardian can halt them during an oracle incident without freezing borrowers.

Integration guide

Verify a passport off-chain

A passport is EIP-712 typed data under the domain { name: "Bezalt Passport", version: "1", chainId, verifyingContract: PassportRegistry }. Any library that implements signTypedData / verifyTypedData can check one; the on-chain verify adds the role, window and revocation checks.

// viem / ethers-style typed data const types = { Passport: [ { name: "borrower", type: "address" }, { name: "asset", type: "address" }, { name: "capacityUsd18", type: "uint256" }, { name: "issuedAt", type: "uint64" }, { name: "expiry", type: "uint64" }, { name: "checksHash", type: "bytes32" }, { name: "nonce", type: "uint256" }, ], }; const signer = await recoverTypedDataAddress({ domain, types, primaryType: "Passport", message: passport, signature, }); // then: registry.hasRole(ATTESTER_ROLE, signer) && !registry.isRevoked(id) // && issuedAt >= minIssuedAt && now < expiry

Open a line from a front-end

// 1. quote const fee = await creditLine.quoteFee(asset, drawAmount); // USDG units // 2. approve the collateral token for CreditLine await stockToken.approve(creditLine, collateralAmount); // 3. open (draw may be 0) const id = await creditLine.open(asset, collateralAmount, drawAmount, passport, signature); // 4. monitor const ltv = await creditLine.ltvBps(id); // reverts on a stale price const room = await creditLine.maxAdditionalDraw(id);

Run a keeper

Index PositionOpened / Drawn / Repaid to maintain the set of open positions; poll isLiquidatable(id) (or compute from ltvBps and the class maintenance); call previewLiquidation(id) to size the keeper fee against gas; send liquidate(id). Liquidations revert when the oracle is stale or when the guardian has paused them; check liquidationsPaused() first.

Units

  • Prices and values: USD with 18 decimals (1e18 = $1).
  • Debt and pool amounts: the debt asset's decimals (USDG: 6).
  • Collateral: the token's own decimals (read at listing).
  • Ratios: basis points out of 10 000.

Security

Tests

87 tests: unit, fuzz (4,096 runs per property in CI profile) and 9 system invariants over 24,576 randomised calls. 100 % line coverage of protocol sources.

Static analysis

Slither 0.11.6: no high-severity findings. Medium findings triaged in contracts/audit/slither-triage.md.

Dry run

Scripted deploy → timelock hand-over → asset listing → borrow → repay → liquidate → sell on a local chain with id 4663.

  • Reentrancy guards on every state-changing entry point; events emitted before external calls.
  • All arithmetic via Math.mulDiv; LTV checks cross-multiplied; rounding favours the protocol.
  • Parameter bounds enforced on-chain, including (1 + bonus)(1 − keeper) ≥ 1.
  • Fee-on-transfer tokens rejected; no delegatecall, no proxies, no selfdestruct.
  • ERC-4626 virtual-share offset (6 decimals) against inflation attacks.
  • Signature malleability rejected; EIP-712 domains separated per contract.
Not yet audited. An independent audit is required before any mainnet deployment holding user funds. Findings and the audit report will be linked here.

Ecosystem

Bezalt is built to sit underneath other financial surfaces. The same verification and credit primitives are exposed to three kinds of integrator.

Wallets

Surface a borrower's passport status and available credit inside the wallet, and let users draw or repay without leaving it.

Lending markets

Accept Bezalt passports as a collateral eligibility signal and share revenue with the stability pool on lines routed through the protocol.

RWA platforms

Give holders of tokenized assets a liquidity path that does not require redeeming or transferring the asset off-platform.

The logos shown on the home page illustrate the categories of venue Bezalt is designed to work with. They do not indicate partnerships or endorsements. For common questions from each audience see the FAQ.

Glossary

Accepted value
Oracle value of collateral after any class haircut; the base for LTV. Reported in USD with 18 decimals.
Attester
The key (role) that signs collateral passports after private verification.
Capacity
The maximum outstanding debt a passport authorises, cumulative across draws and positions.
Collateral passport
EIP-712 attestation of eligibility and capacity for a borrower–asset pair, with an expiry and a commitment to the checks performed.
Keeper
Any account that triggers a liquidation; paid a share of the seized collateral.
LTV
Loan-to-value: debt divided by accepted collateral value, in basis points.
Maintenance
The LTV at or above which a position may be liquidated.
Origination fee
The one-time fee charged on each draw; the only cost of a credit line.
Reserve
The fraction of pool assets kept unlent so withdrawals can be served.
Seized collateral
Collateral the pool received in a liquidation, marked at oracle value and sold at a discount.
Stability pool
The ERC-4626 vault that lends principal and absorbs liquidations.
Timelock
The contract that holds admin rights and delays every governance action by at least 48 hours on mainnet.

Disclosures

This documentation describes protocol mechanics for informational purposes. Numeric parameters shown on this page are examples used to explain the mechanism; live values are published per collateral class by the protocol. Nothing here is financial, legal or tax advice. Borrowing against volatile collateral carries the risk of liquidation, and stability-pool deposits carry the risk of receiving collateral worth less than the debt it repays. The contracts have not been audited by a third party. Third-party names and logos identify categories of compatible venue and do not imply partnership or endorsement.