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.
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.
-
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.
-
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.
-
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.
-
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.
-
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.
| Class | Examples | Max LTV | Maintenance | Fee |
|---|---|---|---|---|
| Tokenized equities | Listed stocks and ETFs issued on-chain by a regulated issuer | e.g. 60% | e.g. 75% | e.g. 1.5% |
| Tokenized treasuries | Short-duration government bond funds | e.g. 85% | e.g. 92% | e.g. 0.6% |
| Private credit & funds | Tokenized fund units with periodic NAV | e.g. 45% | e.g. 65% | e.g. 2.0% |
| Other RWAs | Real estate and receivables with accepted valuation attestations | Per listing | Per listing | Per 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:
| Parameter | Bound | Initial | Purpose |
|---|---|---|---|
epochLength | 1 h – 7 d | 24 h | At most one execution per epoch. |
maxSpendPerEpoch | ≤ 2 % of StabilityPool.totalAssets() | 2 % | Caps market impact regardless of how much USDG has accumulated. |
maxDeviationBps | ≤ 500 | 150 | Execution 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. |
burnBps | 0 – 10 000 | 5 000 | Share of purchased BEZALT sent to address(0); the rest goes to the staking reserve. |
venue | allow-listed | BEZALT/USDG pool on Robinhood Chain | The single swap route the router may use. Changing it is a timelocked action. |
keeperTipBps | ≤ 50 | 10 | Paid to whoever calls execute(), so the buyback needs no privileged operator. |
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.
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.
| Fact | Borrower | Bezalt verifier | Lending venue | Public chain |
|---|---|---|---|---|
| Which assets are held | Yes | During the check only | No | No |
| Quantity and wallets | Yes | During the check only | No | No |
| Eligibility result | Yes | Yes | Yes | Yes (attestation) |
| Borrowing capacity | Yes | Yes | Yes | Yes (bounded) |
| Existing liabilities | Yes | During the check only | Pass/fail only | No |
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.
| Contract | Robinhood Chain mainnet (4663) | Testnet (46630) |
|---|---|---|
| TimelockController | not yet deployed | not yet deployed |
| CollateralRegistry | not yet deployed | not yet deployed |
| PassportRegistry | not yet deployed | not yet deployed |
| ChainlinkPriceOracle | not yet deployed | not yet deployed |
| AttestedPriceOracle | not yet deployed | not yet deployed |
| StabilityPool | not yet deployed | not yet deployed |
| CreditLine | not yet deployed | not yet deployed |
| BuybackRouter | - | - |
CreditLine
| Function | Who | What it does |
|---|---|---|
| open(asset, collateral, draw, passport, sig) | anyone | Escrows collateral, optionally draws. Returns the position id. |
| draw(id, amount, passport, sig) | owner | Draws more USDG under a valid passport; enforces max LTV, capacity and minDebt; charges the fee. |
| repay(id, amount) | anyone | Repays principal to the pool. type(uint256).max repays in full. Never paused. |
| addCollateral(id, amount) | anyone | Tops up escrow. Never paused. |
| withdrawCollateral(id, amount) | owner | Withdraws while staying at or below max LTV. |
| close(id) | owner | Returns all collateral of a debt-free position. |
| liquidate(id) | anyone | Liquidates at or above maintenance LTV; pays the keeper fee to the caller. |
| getPosition(id) · ltvBps(id) · isLiquidatable(id) | view | Position data and health. |
| maxAdditionalDraw(id) · quoteFee(asset, amount) · previewLiquidation(id) | view | Planning 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)
| Function | Who | What it does |
|---|---|---|
| deposit / mint / withdraw / redeem | anyone | Standard ERC-4626. Withdrawals are capped by idle USDG; use maxWithdraw / maxRedeem. |
| totalAssets() · idle() · loansOutstanding() · seizedValue() | view | Share-value composition. |
| availableLiquidity() | view | Idle minus the reserve: what the credit line may lend now. |
| buyCollateral(asset, amount, maxCost) | anyone | Buys seized collateral at oracle value minus the sale discount; requires a fresh price. |
| donate(amount) | anyone | Adds USDG to the pool for all depositors (partner revenue). |
| fund · collect · absorb | CreditLine only | Principal accounting hooks. |
| seizedAssets() · unpricedSeizedAssets() | view | Inventory and stale-price diagnostics. |
Registries and oracles
| Function | Contract | What it does |
|---|---|---|
| verify(passport, signature) → id | PassportRegistry | Reverts unless signed by an attester, in window, above minIssuedAt and not revoked. |
| hashPassport(passport) · isRevoked(id) · domainSeparator() | PassportRegistry | EIP-712 helpers for off-chain verification. |
| revoke(id) · setMinIssuedAt(ts) · setMaxValidity(s) | PassportRegistry | Revocation and issuance policy. |
| getParams(asset) · valueOf(asset, amount) · priceOf(asset) | CollateralRegistry | Parameters and fresh valuation (reverts when stale). |
| listAsset · setParams · setEnabled | CollateralRegistry | Risk-admin controls, validated against bounds. |
| getPrice(asset) → (price1e18, updatedAt) | both oracles | Common price interface. |
| setFeed · setSequencerUptimeFeed | ChainlinkPriceOracle | Feed wiring and L2 sequencer check. |
| post(valuation, sig) · forcePost · setDeviationBand | AttestedPriceOracle | Signed 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.
| Role | Recommended holder | Powers |
|---|---|---|
| DEFAULT_ADMIN | Timelock (48 h) | Grant/revoke roles, unpause, set treasury, mass-invalidate passports, sweep untracked tokens |
| RISK_ADMIN | Governor multisig | List assets; set LTV, maintenance, fee, bonus, keeper fee, price age, supply cap; feeds; reserve and sale discount; fee share; dust floor |
| GUARDIAN | Guardian multisig / hardware key | Pause new exposure; pause liquidations |
| ATTESTER | Verification service key | Sign and revoke passports |
| VALUATION | Valuation service key | Sign valuations for the attested oracle |
| CREDIT_LINE | CreditLine contract | Move principal in and out of the pool |
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.
Open a line from a front-end
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.
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.