solvBTC technical analysis
Summary
This is a technical analysis of all the smart contracts of the SolvBTC asset and its main dependencies.
No matter if the Aave on BOB network activation is done or not, the analysis is applicable for future listing appetite in other instances.
Disclosure: This is not an exhaustive security review of the asset like the ones done by the Solv 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
SolvBTC is a wrapped version of 1:1 Bitcoin across different EVM chains. Users can deposit BTC directly from the Bitcoin blockchain and receive SolvBTC on the BNB Chain, or mint using other wrapped Bitcoin assets (on BOB, only WBTC is acceptable). Users can also redeem through a two-step process by first requesting and then claiming.
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 important.
- Analysis of the access control (ownerships, admin roles) and 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
- Most system contracts can be upgraded with a 3-day Timelock, while two contracts have their upgradable admin set to an EOA.
- It uses the OZ Beacon proxy for the SolvBTC implementation and the Transparent proxy pattern for other proxies.
- For access control, it employs role-based and ownable standards.
- For the token, it uses the SFT standard.
- For the exchange rate, it uses different NAV values for deposit and redemptions.
Contracts
The following is a non-exhaustive overview of the main smart contracts involved with SolvBTC.
SolvBTC
It is an OZ BeaconProxy ERC20 contract with 2-step ownable and role-based access control. It represents 1:1 of BTC in the BOB ecosystem.
Permission Owner | functions | Criticality | Risk |
---|---|---|---|
UpgradableBeacon → SolvBTCFactory → 3-day Timelock | upgradeTo | CRITICAL | ![]() |
owner: Safe 3-of-5 | destroyBlackFunds, updateBlacklistManager, transferOwnership | HIGH | ![]() |
blacklist manager: Safe 3-of-5 | addBlacklist, addBlacklistBatch, removeBlacklist, removeBlacklistBatch | HIGH | ![]() |
DEFAULT_ADMIN_ROLE : 3-day Timelock |
grantRole, revokeRole | HIGH | ![]() |
SOLVBTC_MINTER_ROLE : SolvBTCMultiAssetPool, BurnMintTokenPool (CCIP), xSolvBTCPool, TunnelContract (Free Tunnel Bridge) |
mint, burn | HIGH | ![]() |
SOLVBTC_POOL_BURNER_ROLE : SolvBTCMultiAssetPool, xSolvBTCPool |
burn | HIGH | ![]() |
-
Access Control
- The owner can burn funds from a blacklisted address by calling the
destroyBlackFunds(address)
method. - The
DEFAULT_ADMIN_ROLE
can grant and revoke roles through thegrantRole(role, address)
andrevokeRole(role, address)
methods. - The
SOLVBTC_MINTER_ROLE
can mint new SolvBTC tokens and burn them to accounts not blacklisted via themint(to, amount)
andburn(from, amount)
functions. - The
SOLVBTC_POOL_BURNER_ROLE
can also burn SolvBTC tokens via theburn(from, amount)
function. - The blacklist manager can add and remove accounts from the blacklist by calling the
addBlacklist(address)
andremoveBlacklist(address)
methods. The manager can also add or remove multiple accounts in batches via theaddBlacklistBatch(address[])
removeBlacklistBatch(address[])
functions.
- The owner can burn funds from a blacklisted address by calling the
-
Bridges
- ChainLink CCIP: Chainlink CCIP transmits cross-chain messages between BOB and other blockchains through the Router contract. The BurnMintTokenPool is the facilitator that enables cross-chain transfers via the CCIP Router. It is configured with mint and burn capabilities on SolvBTC.
- Free Tunnel Bridge: The Free Tunnel bridge enables cross-chain transfers between BOB and other blockchains through the FreeTunnelHub contract. The Tunnel Contract is the facilitator with minting and burning capabilities on SolvBTC. It is an Atomic-Lock-Mint/Atomic-Burn-Mint system where, through the FreeTunnelHub contract, users initiate cross-chain transfers by proposing to burn their assets, and in the destination chain, the Tunnel admin proposes the mint. Then, on the origin chain, the tokens are burned, and in the destination chain, the tokens are minted.
SolvBTCRouter
The SolvBTC Router serves as the starting point for users to mint and redeem SolvBTC by interacting with the SolvBTCMultiAssetPool. It is an OZ Transparent Proxy with a two-step ownable access control.
Permission Owner | functions | Criticality | Risk |
---|---|---|---|
ProxyAdmin → 3-day Timelock | upgrade, upgradeAndCall | CRITICAL | ![]() |
Admin: Safe 3-of-5 | setOpenFundMarket, setSolvBTCMultiAssetPool, transferAdmin | HIGH | ![]() |
-
Access Control
- The admin can configure the OFM and the SolvBTCMultiAssetPool addresses via the
setOpenFundMarket(address)
andsetSolvBTCMultiAssetPool(address)
methods.
- The admin can configure the OFM and the SolvBTCMultiAssetPool addresses via the
-
Minting and Redemptions
- Users can mint SolvBTC on BOB by depositing WBTC through the
createSubscription(pool Id, amount)
method. Internally, it first validates thatpool Id
is the SolvBTC and creates a subscription in the OFM (Open Fund Market) contract by callingOFM.subscribe(pool Id, amount, open fund share, expire)
which calculates the amount of shares the user will receive through a navOracle contract (basically a contract to determine the denominator that controls how many shares per subscription the user gets*; for WBTC, the exchange rate is set at1:0.9975
).* and mints the amount as an SFT (Semi Fungible Token) to the router. Then, the router deposits the SFT amount in the SolvBTCMultiAssetPool contract via theSolvBTCMultiAssetPool.deposit(SFT, id, amount)
, which sends the SolvBTC amount to the user. - Users can redeem their SolvBTC using the
createRedemption(pool Id, amount)
function. Internally, after transferring the SolvBTC to the router, it withdraws the SFT from the SolvBTCMultiAssetPool and creates a request in the OFM contract by calling theOFM.requestRedeem(pool Id, amount)
, which mints a redemption SFT to the user. After 7 days, the user can claim their tokens from the Redemption SFT contract using theopenFundRedemption.claimTo(to, tokenId, token, amount)
which burns the SFT. It’s important to mention that no fee is applied for redemptions. Therefore, in the case of redemptions on BOB from SolvBTC to WBTC, the conversion ratio is1:1
. - It is also possible to cancel the request via the
cancelRedemption(pool Id, sft redemption Id)
, which will transfer the redemption SFT to the router and mint back the SolvBTC tokens to the user.
- Users can mint SolvBTC on BOB by depositing WBTC through the
OpenFundMarket
It is the contract that creates and tracks pools of SolvBTC and handles minting of SFT tokens for the router. It’s a Transparent Proxy contract with a two-step ownable access control.
Permission Owner | functions | Criticality | Risk |
---|---|---|---|
ProxyAdmin →EOA (0x55C0…013E) | upgrade, upgradeAndCall | CRITICAL | ![]() |
Admin: Safe 3-of-5 | setGovernorOnlyAdmin | HIGH | ![]() |
Governor: EOA (0x55C0…013E) | updatePoolInfoOnlyGovernor, setCurrencyOnlyGovernor, addSFTOnlyGovernor, removeSFTOnlyGovernor, setProtocolFeeOnlyGovernor, updateFundraisingEndTime | HIGH | ![]() |
Pool Managers: WBTC → EOA (0x2e51…2BB0) | setWhiteList, closeCurrentRedeemSlot, removePool | HIGH | ![]() |
subscribeNavManager: Safe 1-of-3 | setSubscribeNav | HIGH | ![]() |
redeemNavManager: Safe 1-of-3 | setRedeemNav, updateFundraisingEndTime | HIGH | ![]() |
-
Access Control
- The admin can configure the governor address through the
setGovernorOnlyAdmin(address)
method. - New SolvBTC Pools are created permissionlessly through the
createPool(Pool Info)
function; however, the Governor needs to enable the token that this pool will receive in exchange for SolvBTC via thesetCurrencyOnlyGovernor(address)
function and enable both theSFT OpenFundMarket
and theSFT OpenFundRedemption
addresses and their manager via theaddSFTOnlyGovernor(address sft, address manager)
method. - The Governor can change the pool configuration via the
updatePoolInfoOnlyGovernor()
, including max cap, min, and max values of subscription, and subscription and redemption NAV addresses. - The Governor can remove the SFT OpenFund Market and Redemption info by calling the
removeSFTOnlyGovernor(address sft)
. This will disable the pool. - Pool managers can whitelist addresses through the
setWhiteList(address)
function. By whitelisting an address, the pool becomes non-permissionless, and only those whitelisted addresses can subscribe for the SFT shares. - Pool managers can delete a pool by calling the
removePool(Pool Id)
function, which is only possible if no subscriptions have been made. - The subscribe Nav Manager can configure new pools and their
nav
value (used to calculate the exchange rate) in the navOracle via thesetSubscribeNav(Pool Id, nav)
function. While the redeem Nav Manager via thesetRedeemNav(Pool Id, nav)
function. - The redeem Nav Manager and the Governor can set a new period in which it is possible to fund the pool, via the
updateFundraisingEndTime(Pool Id, time)
.
- The admin can configure the governor address through the
-
Subscription and Redemption
- The router mainly, but users can also subscribe (mint SFT shares that can be deposited in exchange for SolvBTC tokens) by calling the
subscribe(poolId, amount, openFundShareId, expireTime)
function. It first validates whether thepoolId
exists, and if the cap has been reached by minting theamount
. After all validations, the OFM contract calls themintValueOnlyIssueMarket()
in the SFT token, which sends the SFT shares to themsg.sender
(router or users). It is important to mention that this function does not mint any SolvBTC; instead, it issues SFT shares, which are used to mint SolvBTC through the SolvBTCMultiAssetPool contract. - The router or users can request a redemption by calling the
requestRedeem(poolId, openFundShareId, openFundRedemptionId, amount)
method. After checking whether the request is enabled and if thepoolId
exists, it will call theSFT openFundRedemption
token and mint via themintOnlyIssueMarket()
a redemption NFT, which will be used to claim the underlying assets later (WBTC in this case). - Requests can also be canceled through the
revokeRedeem(poolId, openFundRedemptionId)
method. - The pool manager can complete a current redemption request and start a new round via the
closeCurrentRedeemSlot(Pool Id)
function. It will retrieve the latest redeem slot and verify if it is closed at least 24h after the previous request, and call the OpenFundRedemption to create a new slot with the data of the latest redemption using theOPR.createSlotOnlyIssueMarket(RedeemInfo)
to allow users to claim their underlying tokens later.
- The router mainly, but users can also subscribe (mint SFT shares that can be deposited in exchange for SolvBTC tokens) by calling the
OpenFundRedemption
The OpenFundRedemption is the contract for users to claim their requested redemptions. It is an OZ Beacon Proxy with a 2-step ownable for access control.
Permission Owner | functions | Criticality | Risk |
---|---|---|---|
Admin → BeaconFactory → EOA (0x55C0…013E) | upgradeTo, setConcreteOnlyAdmin | CRITICAL | ![]() |
OpenFundMarket | createSlotOnlyIssueMarket, setRedeemNavOnlyMarket | HIGH | ![]() |
-
Access Control
- The admin can set the
OpenFundRedemptionConcrete
contract calling thesetConcreteOnlyAdmin(address)
function. This OFR Concrete contract is used to store data related to slots, token IDs, and claimable amounts. - The OFM contract can create slots with data regarding the underlying token and its pool ID via the
createSlotOnlyIssueMarket(SlotInfo)
function. - The OFM contract can set the redemption
nav
for at aslot
via thesetRedeemNavOnlyDelegate(slot, nav)
function. The nav is the value of shares with the decimal places used during the redemption.
- The admin can set the
-
Claim redemption request
- Users can claim their request via the
claimTo(to, tokenId, currency, value)
function. Internally, it validates whether the user is the owner or has permissions for thetokenId
, whether thecurrency
is a valid underlying token, and whether thevalue
is claimable. Then, it calls the OFR Concrete to update the internal claimable slot storage of thetokenId
The system burns the users’ SFT shares and transfers the underlying token to the user.
- Users can claim their request via the
SolvBTCMultiAssetPool
The MultiAssetPool is the contract that interacts directly with the SolvBTC by minting or burning tokens. It can receive different SFT tokens minted with different underlying (on BOB only WBTC) and, in exchange, will mint/burn the new SolvBTC tokens. It’s an OZ Transparent proxy with 2-step ownable/admin access control.
Permission Owner | functions | Criticality | Risk |
---|---|---|---|
ProxyAdmin → 3-day Timelock | upgrade, upgradeAndCall | CRITICAL | ![]() |
Admin: Safe 3-of-5 | addSftSlotOnlyAdmin, changeSftSlotAllowedOnlyAdmin, transferAdmin | HIGH | ![]() |
-
Access Control
- The admin can add new SFT slots for specific ERC20 underlying tokens via the
addSftSlotOnlyAdmin(sft, slot, token, holdingValueSftId)
function. It first validates that the slot is new and wasn’t configured previously with another ERC20 token, then ifholdingValueSftId > 0
, it checks if the slot matches theslot
parameter, and if the MultiAssetPool contract is the owner of theSftId
of theholdingValueSftId
. After those validations, deposits and withdrawals of the SFT in that specific slot are enabled. - The admin can modify deposits and withdrawals from a specific SFT slot via the
changeSftSlotAllowedOnlyAdmin(sft, slot, depositAllowed, withdrawAllowed)
function.
- The admin can add new SFT slots for specific ERC20 underlying tokens via the
-
Deposits and Withdraws
- The router mainly, but users with SFT shares can mint SolvBTC via the
deposit(sft, sftId, value)
function. It validates that deposited SFT shares are allowed in theslot
of thesftId
and then mints thevalue
of SolvBTC tokens. - The router mainly, but users can withdraw SFT shares by calling the
withdraw(sft, slot, sftId, value)
. Internally, it checks if withdrawals are enabled in theslot
of thesftId
and then burns the SolvBTC tokens and sends the equivalentvalue
of SFT shares.
- The router mainly, but users with SFT shares can mint SolvBTC via the
Pricing strategy
See Conclusion.
Miscellaneous
- The system has undergone audits by OZ, Paladin, Salus, and Quantstamp that can be found here.
- In general, the system relies on Multisig and EOAs for upgradability and configuration, which could pose several risks to the asset if, for example, the EOA keys are compromised and the contracts are upgraded to malicious implementations. We suggested the team adjust the general configuration with multisig wallets and timelocks.
Conclusion
We think the complexity of solvBTC, in combination with the highly centralized access control of different critical parts of the system (especially the OpenFundMarket), doesn’t make the asset suitable for onboarding on Aave at the current moment.
Once the team does an overall review of the access control and improves it, we will be able to re-evaluate.
Its yield-bearing version (xSolvBTC) is dependent on the underlying being suitable for listing.