tBTC (Threshold Network) technical analysis
Summary
This is a technical analysis of all the smart contracts of the tBTC asset and main dependencies.
Disclosure: This is not an exhaustive security review of the asset like the ones done by the team supporting the Threshold Network, 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
tBTC is a trustless and decentralized Ethereum <> Bitcoin bridge that allows users to tokenize their Bitcoin on the Ethereum ecosystem, allowing them to participate in Ethereum’s DeFi applications.
High-level overview of tBTC
Users send their Bitcoin to a randomly chosen group of operators who run nodes on the Threshold Network. These nodes are secured through threshold cryptography that requires a minimum number of participants to perform operations. After necessary confirmations, users can reveal the BTC they have deposited through the Bridge on the Ethereum blockchain and mint their tBTC tokens.
For the context of this analysis, our focus has been on the following aspects, critical for the correct and secure integration with Aave:
- Access control (ownerships, admin roles) and nature of the entities involved.
- Any miscellaneous aspect of the code we can consider of importance.
- A recommendation of pricing strategy to be used in the integration asset <> Aave.
General points
- The only upgradeable contract within the system is the Bridge, and its upgradeability admin is the governance council’s Gnosis Safe 6-of-9.
- It uses an OZ transparent proxy pattern for proxies. The other non-upgradable contracts use OZ Ownable for access control, the OZ Governance for general governance, and libraries from Keep Network for cryptography validations.
- The integrity of Bitcoin funds is secured by Threshold Network stakers, who periodically create 51-of-100 threshold-ECDSA wallets to hold them and maintain account balances.
- tBTC uses a randomly selected group of operators running Threshold Network nodes. Before operators can move funds, they need to reach a majority agreement. These selected operators change every week, which helps protect against any bad actors trying to take control of BTC held by the system. The system can slash their stake if any operator is identified as a bad actor.
- If the secured Bitcoin gets lost from the system, tBTC is to be backstopped by Threshold DAO through its treasury assets, where the Treasury Guild is responsible for moving from liquidity management to covering any loss.
Contracts
The following is a non-exhaustive overview of the main smart contracts involved with tBTC.
It’s a non-upgradeable ERC20 token with EIP2612 permit functionality that represents 1:1 of Bitcoin in the Ethereum ecosystem.
- It uses the Ownable OZ pattern, in which the owner is the Vault contract.
- New tBTC tokens can be created by calling the
mint()
function, which is restricted to be called only by the Vault contract.
- Users can burn their tBTC tokens via the
burn()
function.
- The contract includes functionality to recover tokens sent by mistake to its address. The Vault contract can rescue ERC20 and ERC721 tokens by calling
recoverERC20()
and recoverERC721()
respectively.
The Bridge is the system’s core smart contract. It manages Bitcoin deposits and redemptions within the tBTC system, facilitating the minting and redemption of tBTC tokens on the Ethereum blockchain. It binds those services to governance, giving it more precise control of the network. The bridge also includes mechanisms to handle fraud, such as detecting unexpected transactions of wallet operators in the Bitcoin blockchain.
In implementation terms, it is an OZ Transparent Proxy with upgradeability by the general governance multi-sig council.
Deposits and Redemptions
- Users deposit BTC into off-chain ECDSA wallets associated with the Bridge, using specific Bitcoin transaction types containing information about the depositor’s Ethereum address. Once the deposit is made, the depositor reveals their Ethereum address and other required information via the
revealDeposit()
or revealDepositWithExtraData()
functions. The off-chain ECDSA wallet listens for these deposit events, verifies them on the Bitcoin Network, and, if valid, sweeps the funds via the submitDepositSweepProof()
function, which will update the depositor balance in the Bank contract.
- The minimum deposit amount is 0.01 BTC and can be tracked via the
depositDustThreshold
variable. During the sweep, the Bridge contract first computing the fee for the sweep. Currently, the fee deposit (depositTreasuryFeeDivisor
storage variable) is zero.
- Users with a tBTC balance can request redemption via the
requestRedemption()
function by providing the BTC wallet and redeeming amount. The Bridge transfers their balance in the Bank contract and coordinates with the off-chain wallet for redemption. As soon the redemption request is sent, the operators listen to it, and within a few hours to one day, the BTC is sent to the user. Once the BTC is sent to the users’ BTC address, the maintainers confirm it via the submitRedemptionProof()
and burn the tokens in the bank contract.
- The maximum redemption amount is equivalent to the amount held by the largest wallet, and the minimum amount is 0.009 BTC; it can be accessed via the
redemptionDustThreshold
variable. During the redemption, the Bridge computes the redemption fee of 0.2% set in the redemptionTreasuryFeeDivisor
variable.
- The maximum redemption amount is equivalent to the amount held by the largest wallet, and the minimum amount is 0.009 BTC; it can be accessed via the
redemptionDustThreshold
variable. During the redemption, the Bridge computes the redemption fee of 0.2% set in the redemptionTreasuryFeeDivisor
variable.
Access Control
- The critical functions of the Bridge are controlled by the Bridge Governance contract, which is protected by the modifier
onlyGovernance
. The Bridge Governance contract is responsible for updating its governable parameters regarding the governance delay individual for each parameter.
- The contract’s parameters, such as deposit/redemption fees and thresholds, are governable. They are updated via the
updateDepositParameters()
and updateRedemptionParameters()
functions.
- Governance can add/remove maintainers and vaults by calling the
setSpvMaintainerStatus()
and setVaultStatus()
functions.
- The governance can also update the management of funds across the wallets and the wallet’s lifecycle by calling the
updateMovingFundsParameters()
and updateWalletParameters()
, respectively.
- The fraud parameters can be updated via the
updateFraudParameters()
function.
- The system’s treasury address can be updated by calling the
updateTreasury()
function.
Wallet Lifecycle
Creation
- A new wallet is created when both the conditions defined by
walletCreationPeriod
(14 days) and walletCreationMinBtcBalance
(0 BTC) storage variables are met. The maximum amount a wallet can hold in the creation is 100 BTC, stored in the walletCreationMaxBtcBalance
variable.
- It started by calling the
requestNewWallet()
function, formalising it as the creation process is asynchronous. This process involves selecting a group of 100 operators from the pool of available operators using a sortition process, where the probability of selection is based on the operator’s stake in the system.
- When the wallet is created, the ECDSA Wallet Registry calls the callback
__ecdsaWalletCreatedCallback()
function to store the wallet’s public key in the Bridge contract.
Misc management
- If a wallet is older than the
walletMaxAge
(182 days) or its liveness drops below a certain threshold as time passes and some operators may leave the system, the system will initiate a transfer of funds to a new wallet.
Migration of wallets (closing)
- The closing of a wallet initiates by calling the
notifyWalletCloseable()
function, and the funds are moved to a new wallet.
- Operators submit the proof of the transfer of funds by calling
submitMovingFundsProof()
function.
Fraud prevention
- The bridge can handle suspicious transactions of funds being moved that do not comply with protocol rules. Anyone who detects these transactions can accuse wallets of fraud by calling the
submitFraudChallenge()
and passing the wallet and an ether amount to be held by the bridge. Anyone can defeat the pending fraud challenge against a wallet by calling the defeatFraudChallenge()
. In the case where the fraud is defeated successfully, the amount of ether deposited by the challenger is sent to the treasury. If the fraud challenge is not defeated in time, the challenger can call the notifyFraudChallengeDefeatTimeout()
function, which will slash the stake of each operator, and the ether deposited by the challenger is returned + rewards.
The MaintainerProxy smart contract defines functions that off-chain clients call to interact with the network. The DAO must approve maintainers before they can call these functions. Maintainers execute important tasks within the system, such as submitting proofs for Bitcoin transactions and managing the lifecycle of wallets. The Maintainer Proxy ensures that these maintainers are reimbursed for their gas costs after completing their calls.
It is a non-upgradeable contract using the Ownable OZ pattern.
- The governance multi-sig council owns the contract and is assigned to add/remove trusted maintainers, change the gas refund parameters, and update the bridge contract if needed.
- SPV Maintainers sent proofs of deposit and redemptions via the
submitDepositSweepProof()
and submitRedemptionProof()
functions, which forward the call to them Bridge contract. The governance can add/remove SPV Maintainers by calling the authorizeSpvMaintainer()
and unauthorizeSpvMaintainer()
functions.
- The Wallet Maintainers sent proof of funds being moved by calling
submitMovingFundsProof()
and submitMovedFundsSweepProof()
functions, which forward them to the Bridge contract. They can also call maintenance and cleanup functions like requestNewWallet()
, notifyMovingFundsBelowDust()
, notifyWalletCloseable()
, or notifyWalletClosingPeriodElapsed()
. The governance can add/remove Wallet Maintainers by calling the authorizeWalletMaintainer()
and unauthorizeWalletMaintainer()
functions.
The Bank contract is responsible for tracking Bitcoin balances in the tBTC Bridge system. It manages minting and burn operations through the tBTC contract, facilitates the transfer and approval of balances between different accounts, and integrates with the Bridge contract to update balances whenever Bitcoin is deposited or withdrawn from the system.
The Bank is a central, non-upgradeable contract keeping track of Bitcoin balances. It utilizes the Ownable OZ pattern.
- The system’s mint functions are restricted to the Bridge contract via the
onlyBridge
modifier. The bridge can increase balances in batch by calling the increaseBalances()
, increase for one account via the increaseBalance()
function, or increase the Vault contract balance and tokenize tBTC for an account by calling the increaseBalanceAndCall()
function.
- The
balanceOf(account)
mapping tracks the Bitcoin balance of each account within the system. Users can transfer their Bitcoin balance by calling the transferBalance()
function, and an allowed spender can transfer the users’ balance via the transferBalanceFrom()
function.
- Users can set an allowance for a spender over the caller’s balance via the
approveBalance()
function. They can also increase and decrease the allowance by calling increaseBalanceAllowance()
and decreaseBalanceAllowance()
functions. It also includes the EIP-2612 permit mechanism for gas-efficient approvals via the permit()
function.
- The governance can upgrade the Bridge contract by calling the
updateBridge()
function. It’s protected by the onlyOwner
modifier.
The Vault is a custody contract that is responsible for managing the minting and burning of tBTC tokens based on the Bitcoin balances held in the Bank contract. This contract ensures that the supply of tBTC is directly tied to the amount of Bitcoin held, maintaining a 1:1 peg.
A user with a Bank balance gives the Vault an allowance. Then, it uses that allowance to transfer the balance itself, holding on behalf of the user in exchange for minting the user the equivalent amount of tBTC tokens. Later, anyone with tBTC tokens can return them to the Vault, and the vault will grant them Bank balance.
Access Control
- The Vault contract is controlled by the governance multi-sig council, which can upgrade it to another Vault contract and recover any ERC20 or ERC721 sent by mistake to the tBTC token contract. These functions are restricted via the
onlyOwner
modifier.
- The Vault upgrade happens in two steps with a 24-hour governance delay. The owner of the Vault contract can initiate the upgrade by calling the
initiateUpgrade()
function and finalizing the upgrade to another vault via the finalizeUpgrade()
function. The new vault receives ownership of the tBTC token as well as the entire Vault balance in the Bank contract.
- The owner can rescue ERC20 and ERC721 tokens by calling
recoverERC20()
and recoverERC721()
respectively.
Mint and Burn tBTC
- Users with a Bank balance can mint tBTC tokens by calling the mint() function. They must first give allowance to the Vault so it can transfer the user’s Bank balance.
- The bank can mint tBTC tokens directly to a user’s account via the
receiveBalanceApproval()
function. This function is restricted with the onlyBank
modifier.
- Users with tBTC tokens can burn them in exchange for Bank balance via the
unmint()
function. If the user wants to receive the BTC balance in the Bitcoin Network, it can directly call the unmintAndRedeem()
function, which internally routes the redemption via the Bank contract, calling the approveBalanceAndCall()
function.
- A helper function called
amountToSatoshis(amount)
converts a given amount of tBTC to satoshis and handles rounding as tBTC has 18 decimals and BTC has 8 decimals. The function checks if the amount
is not divisible by 1e10
, and if it isn’t, the remainder is left on the caller’s account.
Miscellaneous
- The system has three technical audit reviews by CertiK, LeastAuthority, and ChainSecurity.
- Overall, the TBTC system appears to be a modular and secure implementation designed to bridge Bitcoin to Ethereum in a decentralized manner. The critical control points, such as bridge parameters and the upgradeability of the contracts, are designed to be kept under the governance’s control, which makes sense to keep the system more robust and trustless.
Asset pricing
Previously, there has been extensive discussion on this forum regarding the pricing mechanism of WBTC, and using BTC/USD (trusting the custody security of WBTC) versus purely monitoring secondary market venues of WBTC, with a WBTC/USD (or WBTC/BTC + BTC/USD) feed.
On Aave, WBTC was initially using the first approach, but changed after community decision to the second. There was extensive arguments for that, but a very important one was WBTC having very deep secondary market liquidity, so not being prone to temporary (malicious) market manipulations.
Now with tBTC, the situation is akin to early days of WBTC on Aave, with important differences:
- tBTC has considerably lower market cap and trading volume than WBTC.
- Trading venues are mainly decentralised exchanges, and an important part of the pairs are with WBTC.
- Redeeming tBTC to BTC is pretty fast, taking hours order of magnitude.
Holistically considering this security analysis and the previous market dynamics, we see two options for pricing that we will discuss with risk providers before AIP:
- If risk parameters (LTV/LT) and caps are quite conservative, it is an option to use a secondary market tBTC/USD (or tBTC/BTC + BTC/USD) feed with CAPO over BTC/USD. However, it is very difficult to evaluate how realistic are DEX manipulations with current volumes.
- Another option is to simply use BTC/USD, trusting the security of the asset and its BTC reserves, but not having any type of risk of market manipulation on DEXes. Currently this is the option we believe is the most reasonable.
We will be working on further integration of Chainlink Proof-of-Reserve for assets like this, so we will also discuss with the Threshold team about the need to actively try to support the system, to improve assurances. In parallel, risk providers should evaluate how secondary market liquidity (including listings on CEXes) improves, as the best approach would be to align the strategy with others’ like WBTC, pricing based on tBTC/USD.
Conclusion
We think tBTC doesn’t have any problem in terms of integration with Aave, and there is no major technical blocker for listing.
The main pending item is to coordinate with the risk service provider on choosing the most appropriate pricing mechanism, from the options we outlined in this report.