LBTC (Lombard) technical analysis
Summary
This is a technical analysis of all the smart contracts of the LBTC asset and main dependencies.
Disclosure: This is not an exhaustive security review of the asset like the ones done by the Lombard team, but an analysis from an Aave technical service provider on different aspects we consider critical to review before a new type of listing. Consequently, like with any security review, this is not an absolute statement that the asset is flawless, only that, in our opinion, we don’t see significant problems with its integration with Aave, apart from different trust points.
Analysis
LBTC is a wrapped version of BTC on EVM chains backed by 1:1 BTC reserves, staked in Babylon: users stake their BTC through Babylon and receive 1:1 LBTC that can seamlessly integrate into DeFi applications.
LBTC is also a yield-bearing token, which continues to accrue staking rewards from its staked BTC. Users can also burn LBTC and receive the BTC on Bitcoin.
For the context of this analysis, our focus has been on the following aspects, critical for the correct and secure integration with Aave:
- A recommendation of pricing strategy to be used in the integration asset <> Aave.
- Any miscellaneous aspect of the code we can consider of importance.
- Analysis of the access control (ownerships, admin roles), the nature of the entities involved in the system. Regarding the table permissions’ holders and their criticality/risk, it is done following these guidelines:
Criticality | Description |
---|---|
CRITICAL | Usually super-admin functionality: it can compromise the system by completely changing its fundamentals, leading to loss of funds if misused or exploited. E.g. proxy admin, default admin |
HIGH | It can control several parts of the system with some risk of losing funds. E.g., general owners or admin roles involved in the flow of funds |
MEDIUM | It can cause malfunction and/or minor financial losses if misused or exploited. E.g., fee setter, fee recipient addresses |
LOW | It can cause system malfunctions, but on non-critical parts without meaningful/direct financial losses. E.g., updating descriptions or certain non-critical parameters. |
Risk | Description |
---|---|
![]() |
The role is controlled via a mechanism we consider safe, such as on-chain governance, a timelock contract, or setups involving multi-sigs under certain circumstances. |
![]() |
The role is controlled in a way that could expose the system and users to some risk depending on the actions it can control. |
![]() |
The role is controlled via a clearly non-secure method, representing risks for the system and users. |
General points
- The upgradeability admin of the LBTC system is a Timelock (Lombard Timelock) with a 24-hour delay period.
- It uses an OZ transparent proxy pattern for proxies. Non-upgradable contracts, like the Bascule Drawbridge (technically external to LBTC), use OZ contract extensions, such as Access Control with role-based, Pausable, and the Math library.
- LBTC also includes of off-chain modules divided into:
- A consortium system for management across the BTC transactions, where the participants (consortium nodes) perform validations and sign transactions for stake/unstake from Babylon, or mint/burn LBTC across the EVM chains. The consortium generates BTC wallets and verifies deposits before signing any data that confirms the transaction.
- The so-called CubeSigner hardware manages the keys of the BTC wallets. It protects the keys to be extracted, restricts which transactions can be signed by whom, requests a 3-of-5 approval (MPA) from the consortium members to be validated, and timelocks the keys to safeguard multiple transactions to be signed.
- A general backend that tracks addresses with staked BTC listens to user transactions and communicates with the consortium by requesting the deposit/withdrawal data be signed, depending on the user’s action.
Contracts
The following is a non-exhaustive overview of the main smart contracts involved with LBTC.
LBTC
The so-called Liquidity BTC is an upgradeable ERC20 contract in which users can mint and burn LBTC, representing 1:1 of their staked BTC, and bridge it across supported EVM chains.
Its access control is managed through role-based permissions using OZ Ownable2StepUpgradeable
and a custom pauser role mechanism.
Permission Owner | functions | Criticality | Risk |
---|---|---|---|
proxy Admin → LombardTimeLock | upgradeAndCall() | CRITICAL | ![]() |
general owner: LombardTimelock | transferOwnership(), renounceOwnership(), toggleWithdrawals(), changeConsortium(), changeBascule(), transferPauserRole(), | CRITICAL | ![]() |
general owner: LombardTimelock | addDestination(), removeDestination() | HIGH | ![]() |
pauser: Safe 2-of-8 | pause(), unpause() | HIGH | ![]() |
general owner: LombardTimelock | changeDepositAbsoluteCommission(), changeDepositRelativeCommission(), changeTreasuryAddress(), changeBurnCommission(), changeDustFeeRate(), changeNameAndSymbol() | MEDIUM | ![]() |
Access Control
- The owner (Lombard Timelock) can add and remove supported chains via the
addDestination(chain)
andremoveDestination(chain)
functions. - The owner can change the contract’s ERC20 name and symbol via the
changeNameAndSymbol(name, symbol)
function. - The owner can enable/disable withdrawals via the
toggleWithdrawals()
function. - The owner can update the fees on LBTC operations (burn and bridge) by calling the
changeBurnCommission(fee)
,changeDustFeeRate(fee)
,changeDepositAbsoluteCommission(fee)
, andchangeDepositRelativeCommission(fee)
functions. - The owner can update the consortium, bascule drawbridge, treasury, and pauser addresses via the
changeConsortium(address)
,changeBascule(address)
,changeTreasuryAddress(address)
, andtransferPauserRole(address)
functions. - The pauser role can pause and unpause the contract via the
pause()
andunpause()
functions.
Mint and Burn
- Minting LBTC starts with the user sending their BTC to a staking address managed by the consortium. Once the deposit is confirmed by the consortium and reported in the bascule drawbridge, the user receives encoded
data
withuint256 chainId
,address to
,uint64 amount
,bytes32 txId
,uint32 index
, and thekeccak256
hash of the data signed by the Consortium threshold key.
Right after, With this data in hand, the user can finally initiate the mint via themint(data, signature)
function. Internally, it verifies if the signature is signed by the consortium address in the_checkAndUseProof()
function, stores it to avoid replay attacks, and confirms in the bascule bridge. - The user initiates the unstake of their BTC by calling the
redeem(BTCAddress, amount)
function, which checks internally if theBTCAddress
is valid and if theamount
is not below the dust amount set by the owner.
Then, the backend listens to the burn event and informs the consortium about the unstake. The consortium validates the request and builds the BTC transaction so the CubeSigner can sign the transaction with the consortium members to transfer the BTC to the user’s address.
The unstaking period lasts 9 days on Babylon and Lombard.
Bridging
- Users can bridge their LBTC to supported chains (currently mainnet, Binance Smart Chain, Base, and Corn) via the
depositToBridge(chain, address, amount)
function. Internally, it checks if the chain is supported and discounts the bridge fee before burning the tokens. Then, the backend listens to the bridge event and communicates to the consortium so it can validate, report to the bascule drawbridge, and providedata
withuint256 chainId, address to, uint64 amount, bytes32 txId, uint32 index
, and thekeccak256
hash of the data signed by the Consortium threshold key.
On the destination chain, the user, with this data in their hands, finalizes the bridge by claiming their tokens via thewithdrawFromBridge(data, signature)
function. This function internally checks if the data is correct and the signature is from the consortium address in the_checkAndUseProof()
function.
Finally, it is double-confirmed in the bascule bridge, and the tokens are sent to the user.
In the case of the Corn blockchain, the tokens are locked on the source chain and minted on the destination chain.
LombardConsortium
It’s an upgradeable ERC1271 contract that verifies signatures for a given hash.
Responsible for checking whether the hash is signed by a threshold address stored in the contract. It is upgradeable by the Lombard Timelock.
Permission owner | functions | Criticality | Risk |
---|---|---|---|
proxy admin → LombardTimelock | upgradeAndCall() | CRITICAL | ![]() |
general owner: LombardTimelock | transferOwnership(), renounceOwnership(), changeThresholdAddr(), | HIGH | ![]() |
- The contract owner (LombardTimelock) can set a different threshold address by calling the
changeThresholdAddr(address)
function. - This contract mainly validates signatures via the
isValidSignature(hash, signature)
function. It validates via ECDSA recovery if thehash
signed by thesignature
is equivalent to the threshold address.
Bascule Drawbridge
The Bascule is a non-upgradeable contract responsible for registering deposits with unique deposit IDs and validating them when users request to mint LBTC or redeem BTC. It uses an OZ roles-based access control.
Important to highlight is designed to be a third-party extra security layer (on top of the consortium), and it is controlled by a separate team and infrascture: Cubist.
Permission owner | functions | Criticality | Risk |
---|---|---|---|
DEFAULT_ADMIN_ROLE : EOA 0xC8bd…0889 (MPC by Cubist) |
grantRole(), revokeRole(), setMaxDeposits(), updateValidateThreshold() | MEDIUM | ![]() |
DEPOSIT_REPORTER_ROLE : EOA 0xfa70…B3fe |
reportDeposits() | MEDIUM | ![]() |
WITHDRAWAL_VALIDATOR_ROLE : LBTC contract |
validateWithdrawal() | MEDIUM | ![]() |
VALIDATION_GUARDIAN_ROLE : not assigned |
updateValidateThreshold() | MEDIUM | ![]() |
Access Control
- Permissions in the contract are divided into
DEFAULT_ADMIN_ROLE
,PAUSER_ROLE
,DEPOSIT_REPORTER_ROLE
,WITHDRAWAL_VALIDATOR_ROLE
, andVALIDATION_GUARDIAN_ROLE
, with theDEFAULT_ADMIN_ROLE
as responsible for setting the other roles. - The
DEFAULT_ADMIN_ROLE
(EOA 0xC8bd…0889) can set the maximum number of deposits that can be reported at once by calling thesetMaxDeposits(value)
function. - The
PAUSER_ROLE
(EOA 0x1E07…32aC) can pause or unpause the contract via thepause()
andunpause()
functions. - The
VALIDATION_GUARDIAN_ROLE
(not assigned) can update the withdrawal validation threshold via theupdateValidateThreshold(value)
function. However, the validation guardian role can only increase the threshold (validate fewer deposits), and its role is renounced right after execution, while the default admin can lower the threshold.
Validation Process
- The
DEPOSIT_REPORTER_ROLE
(EOA 0xfa70…B3fe) is the main responsible for reporting all BTC deposits into the stake addresses via thereportDeposits(reportId, depositIDs[])
. Internally, it verifies whether the number of reported deposits exceeds the maximum set and whether eachdepositID
wasn’t previously reported. - The
WITHDRAWAL_VALIDATOR_ROLE
(LBTC contract) verifies the user’s deposit transaction status via thevalidateWithdrawal(depositID, amount)
function. It checks whether thedepositID
has already been withdrawn and whether theamount
is above the threshold.
Miscellaneous
- The system has multiple security reviews: one on its V1 release by Halborn and Veridise and another on its V2 release by OpenZeppelin and Veridise. The reports can be found here.
- A Chainlink PoR (Proof of Reserve) is on the works (HERE). Additionally, the system has a RedStone’s Proof of Reserves oracle on mainnet, Base, and BSC, updated every 24 hours or if it deviates more than 1%. The PoRs can be visualized on their Dune reserves page.
- Because BTC is built on top of Babylon, it’s important to mention that slashing events can occur if validators take bad actions, such as double-signing. Slashing is not currently live but will be implemented in the future Babylon phase roadmap.
Asset pricing
Even if more an aspect to analyse on the side of the risk providers, secondary market pricing is not really recommended from our side, given that the asset has very thin liquidity and consequently, risk of manipulation.
Therefore, as we think the asset security is robust enough, pricing based on BTC/USD seems reasonable.
Complementary, we think the following aspects are very important to consider on the final listing risk parameters:
- At the moment, the asset doesn’t accrue rewards, but it will in the future (depending on the activation of the mechanism on Babylon). It is very important to monitor this, as the usage of a BTC/USD price feed will misprice the asset when rewards are enabled. Worth to mention that if only used as collateral, using BTC/USD with rewards enabled would only undervalue the asset, technically only hurting users borrowing against LBTC and not suppliers.
- The redeeming time from LBTC to BTC is relatively long, so liquidation bonus (in any e-mode) should be high enough to always keep liquidations profitable. Especially in e-mode/default combinations with no correlation, like LBTC collateral and stablecoins borrowings.
Conclusion
We think LBTC doesn’t have any problem in terms of integration with Aave, and there is no major technical blocker for listing.