Only this pageAll pages
Powered by GitBook
Couldn't generate the PDF for 164 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

Threshold Docs

Loading...

THRESHOLD DASHBOARD

Loading...

Applications

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Governance

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Staking & Running a Node

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

App Development

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

tBTC Minting Walkthrough

Follow along with this guide and leverage your Bitcoin by minting tBTC.

Before you start

Here are some things you will need before you start the minting process:

✅ Bitcoin (BTC)

✅ Ethereum (ETH) for Ethereum transaction gas costs

✅ Bitcoin compatible wallet

✅ Ethereum compatible wallet

If you don't already have one, you'll need to set up a Bitcoin wallet. A solid option to use is Green Wallet. You can download it here:

  • Apple store download

  • Google store download

After you've created your wallet, select Bitcoin as your network.

Start minting tBTC

🎉 You're ready to mint! Go to the tBTC dapp here: https://dashboard.threshold.network/tBTC/mint

If you have questions about terminology, you can toggle over to the How it Works page in the dapp to learn more. You can also dive into documentation on wallet generation and bridge permissionlessness in the docs.

STEP 1: Generate a deposit address

  1. Connect your preferred Ethereum wallet to the tBTC dapp by selecting Connect Wallet.

  1. Navigate to the tBTC Bridge page. This is represented in the lefthand navigation by the tBTC icon.

  1. Enter a Bitcoin wallet address as the BTC Recovery Address. Click Generate Deposit Address to create your unique deposit address.

  1. Make sure to download the JSON file by clicking Download. The JSON file contains a wallet public key, a refund public key, and a refund lock time. You need to keep this JSON file until you receive your tBTC tokens.

STEP 2: Make BTC Deposit

  1. In your Bitcoin wallet, take a picture of the QR code of the generated Deposit Address in the tBTC dapp. It will look something like this:

  2. Send your BTC deposit to this Deposit Address from your Bitcoin wallet. The minimum deposit is 0.01 BTC.

  3. Return to the tBTC dapp. After your funds are sent, the screen will automatically advance to Step 3.

STEP 3: Initiate Minting

  1. To initiate minting, click on Confirm deposit & mint.

  1. Sign the transaction in your Ethereum wallet.

STEP 4: tBTC Minting

  1. Your mint is now in progress! You don't need to remain in the dapp to wait for your tBTC tokens to mint. You can see the status of your mint in the dapp. The first stage requires 1 confirmation on the Bitcoin Network before advancing to the next step.

  1. Now you will see the following while waiting for a Minter to assess the minting initialization:

  1. Next, you will see the following while waiting for a Guardian to examine the minting request:

  1. Success! You can see all transaction links on this screen.

  1. Make sure to add the tBTC token address to your Ethereum wallet by clicking token address.

Congrats — you minted!

🎉 You've successfully minted tBTC! 🎉

What is the Threshold Network?

Threshold Network powers tBTC, the Bitcoin standard for DeFi—a trust-minimized way to bring BTC liquidity into the crypto ecosystem while always maintaining a direct settlement path back to native Bitcoin.

Unlike other BTC derivatives or wrapped tokens, tBTC is designed to be as close to Bitcoin as possible while enabling seamless participation in DeFi. No complex staking mechanics, no dependency on a single entity—just pure BTC liquidity that always settles back to Bitcoin.

Threshold Network's powers tBTC and is the value accrual asset used to distribute profits from bridge fee via token buybacks.

Fundamentals: Dive a little deeper

Learn the fundamentals of the Threshold Network to get a deeper understanding of our main features:

Advanced Options

Advanced configuration options and tBTC v2 staking client options

Configuration options for logging

Alternative application authorization methods to using the Threshold Dashboard.

Application configuration can be stored in a file and passed to the application with the --config flag.

Client information exposed by the tBTC v2 staking client.

Sepolia Testnet

Here you'll find the version of the Threshold dapp.

DAO Contracts

To get familiarized with API of contracts related to the DAO, go .

Logging
Alternatives to Dashboard
Config File
CLI Options
Client Info
here
Sepolia testnet
Sepolia Testnet - Threshold Dashboard

Fees

tBTC fees are action-based (i.e. users incur a mint and redemption fee when using the bridge). Currently, the mint fee is 0% and the redemption fee is 0.2%. The bridge fees can be changed through governance.

thUSD on Build on Bitcoin (BOB)

Threshold USD (thUSD) is a stablecoin soft-pegged against USD, backed by ETH and tBTC as collateral. For more details, refer to the THUSD overview.

With thUSD, you can now:

  • Open collateral vaults seamlessly on BOB Network using bridged tBTC or ETH

  • Mint thUSD and utilize it across the BOB Network ecosystem

Start by connecting your preferred wallet (e.g., MetaMask) at app.gobob.xyz/wallet and bridging your tBTC at app.gobob.xyz/bridge.

Connecting to BOB Network

Connecting to BOB Network

  1. Access Wallet Setup

    • Navigate to app.gobob.xyz/wallet.

  2. Connect Your Wallet

    • Choose your preferred wallet (e.g., MetaMask) and follow the prompts to connect it to the BOB Network.

  3. Switch to BOB Network

    • Click on the option "Switch to BOB." This will automatically add the BOB Network to your wallet and connect your wallet to the BOB Network.

  4. Manual Setup

    • Alternatively, you can manually set up your wallet with the following settings:

      Chain ID: 60808

      Gas Token: ETH

      RPC URL: https://rpc.gobob.xyz/

      WS URL: wss://rpc.gobob.xyz

      Explorer: https://explorer.gobob.xyz/

Operator Account

Prepare your machine to install the tBTC v2 staking client

The client requires an Ethereum Key File of an Operator Account to connect to the Ethereum chain. This account is created in a subsequent step using Geth (GoEthereum).

The Ethereum Key File is expected to be encrypted with a password. The password has to be provided in a prompt after the client starts or configured as a KEEP_ETHEREUM_PASSWORD environment variable.

The Operator Account has to maintain a positive Ether balance at all times. We strongly advise you monitor the account and top-up when its balance gets below 0.5 Ether.

Please do NOT reuse an operator account that is being used for TACo or other applications.

Install Geth (GoEthereum)

To create a new Ethereum account, install Geth (GoEthereum) and create a new account using the command below. This account will subsequently be referred to as the Operator Account.

geth account new --keystore /home/$USER/.operator-key

When prompted, provide a password to protect the operator key file.

Use a password manager to generate a strong password and store it safely. It will be needed again during setup.

Avoid passwords that contain the following characters: ', ", `, $ These characters may be interpreted as part of the configuration which can lead to undesirable outcomes that may be extremely time intensive to correct.

Once the process completes, your public key will be displayed. Take note of your Operator Account public key.

DO NOT LOSE THE PASSWORD TO THE OPERATOR ACCOUNT.

Funding your Operator Account

Your Operator Account will need to maintain a positive ETH balance at all times to ensure proper operation and availability of your tBTC v2 node.

Installation

A tBTC v2 node can be set up using either a docker or binary installation.

Logging

Optional logging configuration for the tBTC v2 staking client.

Configuration

Logging can be configured with environment variables. Please see sample settings:

LOG_LEVEL=DEBUG
IPFS_LOGGING_FMT=nocolor
GOLOG_FILE=/var/log/keep/keep.log
GOLOG_TRACING_FILE=/var/log/keep/trace.json

LOG_LEVEL option DEBUG will generate extensive output. Consider INFO instead.

If you want to share your LibP2P address with others you can get it from the startup log. When sharing remember to substitute the /ipv4/ address with the public facing IP of your client if you’re running on a private machine, or replace the entire /ipv4/ segment with a DNS entry if you’re using a hostname.

BaseTokenholderGovernor

BaseTokenholderGovernor

VETO_POWER

bytes32 VETO_POWER

constructor

constructor(contract T _token, contract IVotesHistory _staking, contract TimelockController _timelock, address _vetoer, uint256 _quorumNumerator, uint256 _proposalThresholdNumerator, uint256 votingDelay, uint256 votingPeriod, uint64 votingExtension) public

cancel

function cancel(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) external returns (uint256)

propose

function propose(address[] targets, uint256[] values, bytes[] calldatas, string description) public returns (uint256)

quorum

function quorum(uint256 blockNumber) public view returns (uint256)

proposalThreshold

function proposalThreshold() public view returns (uint256)

getVotes

function getVotes(address account, uint256 blockNumber) public view returns (uint256)

state

function state(uint256 proposalId) public view returns (enum IGovernor.ProposalState)

supportsInterface

function supportsInterface(bytes4 interfaceId) public view returns (bool)

_execute

function _execute(uint256 proposalId, address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) internal

_cancel

function _cancel(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) internal returns (uint256)

_executor

function _executor() internal view returns (address)

proposalDeadline

function proposalDeadline(uint256 proposalId) public view virtual returns (uint256)

_castVote

function _castVote(uint256 proposalId, address account, uint8 support, string reason) internal virtual returns (uint256)

IVotesHistory

IVotesHistory

getPastVotes

function getPastVotes(address account, uint256 blockNumber) external view returns (uint96)

getPastTotalSupply

function getPastTotalSupply(uint256 blockNumber) external view returns (uint96)

PercentUtils

PercentUtils

percent

function percent(uint256 a, uint256 b) internal pure returns (uint256)

asPercentOf

function asPercentOf(uint256 a, uint256 b) internal pure returns (uint256)

SafeTUpgradeable

SafeTUpgradeable

A wrapper around OpenZeppelin's SafeERC20Upgradeable but specific to the T token. Use this library in upgradeable contracts. If your contract is non-upgradeable, then the traditional SafeERC20 works. The motivation is to prevent upgradeable contracts that use T from depending on the Address library, which can be problematic since it uses delegatecall, which is discouraged by OpenZeppelin for use in upgradeable contracts.

This implementation force-casts T to IERC20Upgradeable to make it work with SafeERC20Upgradeable.

safeTransfer

function safeTransfer(contract T token, address to, uint256 value) internal

safeTransferFrom

function safeTransferFrom(contract T token, address from, address to, uint256 value) internal

StakerGovernor

StakerGovernor

VETO_POWER

bytes32 VETO_POWER

manager

address manager

constructor

constructor(contract IVotesHistory _staking, contract TimelockController _timelock, contract TokenholderGovernor tokenholderGovernor, address vetoer) public

cancel

function cancel(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) external returns (uint256)

propose

function propose(address[] targets, uint256[] values, bytes[] calldatas, string description) public returns (uint256)

quorum

function quorum(uint256 blockNumber) public view returns (uint256)

proposalThreshold

function proposalThreshold() public view returns (uint256)

getVotes

function getVotes(address account, uint256 blockNumber) public view returns (uint256)

state

function state(uint256 proposalId) public view returns (enum IGovernor.ProposalState)

supportsInterface

function supportsInterface(bytes4 interfaceId) public view returns (bool)

_execute

function _execute(uint256 proposalId, address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) internal

_cancel

function _cancel(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) internal returns (uint256)

_executor

function _executor() internal view returns (address)

Returns the address of the entity that acts as governance for this contract.

By default, Governor assumes this is either the Governor contract itself, or a timelock if there's one configured. We override this here for the StakerGovernor contract so it's the Tokenholder DAO's Timelock, which we obtain at constructor time.

TokenholderGovernor

TokenholderGovernor

constructor

constructor(contract T _token, contract IVotesHistory _staking, contract TimelockController _timelock, address vetoer) public

VendingMachine

Random Beacon

To get familiarized with Random Bitcoin contracts API, go here.

Governable

Governable

This file documents a contract which is not yet deployed to Mainnet.

Governable contract.

A constructor is not defined, which makes the contract compatible with upgradable proxies. This requires calling explicitly _transferGovernance function in a child contract.

governance

address governance

GovernanceTransferred

event GovernanceTransferred(address oldGovernance, address newGovernance)

onlyGovernance

modifier onlyGovernance()

transferGovernance

function transferGovernance(address newGovernance) external virtual

Transfers governance of the contract to newGovernance.

_transferGovernance

function _transferGovernance(address newGovernance) internal virtual
T token
tBTC Bitcoin Bridge

tBTC Bitcoin Bridge

A Decentralized, Permissionless Bitcoin Bridge to DeFi

Existing solutions that bridge Bitcoin to Ethereum require users to send their Bitcoin to an intermediary, in exchange for an ERC-20 token that represents the original asset. This centralized model requires you to trust a third party and is susceptible to censorship, threatening the premise of Bitcoin as sovereign, secure, permissionless digital asset.

The second generation of tBTC is a truly decentralized (and scalable) bridge between Bitcoin and Ethereum. It provides Bitcoin holders secure and open access to the broader DeFi ecosystem. tBTC v2 allows you to unlock your Bitcoin’s value to borrow and lend, mint stablecoins, provide liquidity, and much more.

Honest Majority Assumption

Instead of centralized intermediaries, tBTC uses a randomly selected group of operators running nodes on the Threshold Network to secure deposited Bitcoin through threshold cryptography. That means tBTC requires a threshold majority agreement before operators perform any action with your Bitcoin. By rotating the selection of operators weekly, tBTC protects against any individual or group of operators colluding to fraudulently seize the underlying deposits. By relying on an honest-majority-assumption, we can calculate the likelihood any wallet comprised of a quorum of dishonest operators (for a deeper discussion of the probability calculations, see here).

Unlike other solutions on the market, users of tBTC trust math, not hardware or people.

Threshold DAO

Threshold is community-driven and governed by an eponymous DAO that includes the constituencies of both the NuCypher and Keep networks. The Threshold DAO has two primary bodies: the Tokenholder DAO and the Elected Council. The goal of this two-pronged structure is to enhance representation while ensuring accountability. Each of these governance bodies holds the other accountable, similar to the system of checks and balances found in most constitutional governments. They also hold separate responsibilities that are embedded in the governance structure.

From an implementation perspective, the Tokenholder DAO is based on the Governor Bravo governance model (in particular, using OpenZeppelin Governance). The Tokenholder DAO is on Ethereum Mainnet at 0xd101f2B25bCBF992BdF55dB67c104FE7646F5447. The Elected Council is implemented as a Gnosis Safe contract, with a 6-of-9 configuration, also deployed on Ethereum Mainnet, at 0x9F6e831c8F8939DC0C830C6e492e7cEf4f9C2F5f.

A technical architecture diagram is show below.

Threshold DAO Governance Architecture Diagram

Coverage Pool

A backstop for assets secured by the tBTC protocol

Summary

Threshold Coverage Pool has been sunset via the DAO ratified proposal.

The Threshold Coverage Pool served as a backstop for assets secured by the tBTC protocol. In the event that secured Bitcoin were lost from the protocol, assets from the coverage pool were to be withdrawn by the risk manager, converted to BTC, and put back into the protocol to achieve the supply peg as closely as possible. The risk manager for the coverage pool was the multisig.

Over time, it became clear that this backstop model was sub-optimal given the relationship between tBTC and T - in the case of a BTC collateral loss event, T's value would also decline, greatly reducing its effectiveness as an insurance backstop. Consequently, the explicit Threshold Coverage Pool has been sunset with the approval of TIP-064.

Under the newly implemented "implicit coverage pool" model, tBTC is backed by the full faith and credit of Threshold DAO - by its treasury assets, which currently include T, ETH, wBTC, tBTC, stablecoins, CVX, veCRV, and associated LP tokens, etc.

Join the discussion and ask questions about the coverage pool in the in the .

Threshold USD

A Bitcoin-backed Stablecoin

Threshold USD (thUSD) is a stablecoin soft-pegged against USD and backed by ETH and tBTC as collateral, with a minimum collateral ratio of 110%.

With either BTC or ETH as your collateral, you'll be able to borrow thUSD at very low cost.

Threshold USD is a modified fork of built to be self-sustained through a PCV ("Protocol Controlled Value"). There is no equivalent of LQTY token in Threshold USD. Instead all profits accrue into the PCV. Since there is no token, is completed through an .

The result of protocol owning its own liquidity ("PCV") is a more predictable trajectory and sustainability long-term. The stability pool is funded by the PCV instead of user deposits. Thanks to this, no funds are wasted on rewards to passive LQTY stakers (rented liquidity) and those funds can instead by re-injected into the stability pool. As the protocol grows and accrue fees, the stability pool will be consistently topped up.

Multi-collateral

Liquity is limited to ETH as the only type of collateral. Threshold USD expand to support BTC as collateral for loans through the decentralized BTC wrapper tBTC. In the future other types of collateral may be considered for on-boarding if they are sufficiently decentralized.

Each collateral will be deployed with its own contracts and each new collateral will require a DAO vote with a lengthy time delay before deployment. By having each collateral be its own contract any collateral can easily be deprecated by disallowing minting on that specific contract.

All contracts related to thUSD can be found .

Redemptions and thUSD Price Stability

Redemptions are a mechanism to return thUSD to a value of 1USD if it falls below it

How does thUSD closely follow the price of USD?

The ability to redeem thUSD for tBTC at face value (i.e. 1 thUSD for $1 of tBTC) and the minimum collateral ratio of 110% create a price floor and price ceiling (respectively) through arbitrage opportunities. We call these "hard peg mechanisms" since they are based on direct processes.

thUSD also benefits from less direct mechanisms for USD parity — called "soft peg mechanisms". One of these mechanisms is parity as a Schelling point. Since Threshold USD treats thUSD as being equal to USD, parity between the two is an implied equilibrium state of the protocol. Another of these mechanisms is the borrowing fee on new debts. As redemptions increase (implying thUSD is below $1), so too does the baseRate — making borrowing less attractive which keeps new thUSD from hitting the market and driving the price below $1.

Liquity (on which thUSD Protocol is based on) did a thorough analysis on the price stability of LUSD (). This analysis is equally relevant for thUSD.

What are redemptions?

A redemption is the process of exchanging thUSD for tBTC at face value, as if 1 thUSD is exactly worth $1. That is, for x thUSD you get x Dollars worth of tBTC in return.

Users can redeem their thUSD for tBTC at any time without limitations. However, a redemption fee might be charged on the redeemed amount.

For example, if the current redemption fee is 1%, the price of tBTC is $50,000 and you redeem 10,000 thUSD, you would get 0.198 tBTC (0.2 tBTC minus a redemption fee of 0.002 tBTC).

Note that the redeemed amount is taken into account for calculating the base rate and might have an impact on the redemption fee, especially if the amount is large.

To illustrate how Redemptions work, below you can see the mechanism in action for LUSD, when its value went below 1 USD. The Borrowing rate (red line) immediately spiked from 0.5% up to around 1.8% and redemptions (blue bars) started to happen, i.e. users would buy LUSD at less than 1 USD and redeem for ETH in Vaults as if it was valued at 1 USD, arbitraging the difference, reducing the supply of LUSD and increasing its price.

Is a redemption the same as paying back my debt?

No, redemptions are a completely separate mechanism. All one has to do to pay back their debt is adjust their Vault's debt and collateral.

How is the redemption fee calculated?

Under normal operation, the redemption fee is given by the formula (baseRate + 0.5%) * tBTCdrawn

How is the baseRate calculated?

Redemption fees are based on the baseRate state variable in Threshold USD, which is dynamically updated. The baseRate increases with each redemption, and decays according to time passed since the last fee event - i.e. the last redemption or issuance of thUSD.

Upon each redemption:

  • baseRate is decayed based on time passed since the last fee event

  • baseRate is incremented by an amount proportional to the fraction of the total thUSD supply that was redeemed

  • The redemption fee is given by (baseRate + 0.5%) * tBTCdrawn

As a borrower, do I lose money if I'm redeemed against?

If your Vault is redeemed against, you do not incur a net loss. However, you will lose some of your tBTC exposure. Your Vault's collateral ratio will also improve after a redemption.

How can I avoid being redeemed against?

The best way to avoid being redeemed against is by maintaining a high collateral ratio relative to the rest of the Vault's in the system. Remember: The riskiest Vault (i.e. lowest collateralized Vaults) are first in line when a redemption takes place.

Bootstrapping

A Bitcoin-backed Stablecoin

A certain network effect is required for a stablecoin to function well. Bootstrapping is the process of ensuring the protocol has gained enough traction to become self-sufficient.

In the past this was usually accomplished by being an early adopter of coins, such as buying Bitcoin early and benefiting from the lower price. DeFi revolutionized the bootstrapping concept by enabling the bootstrapping of capital through yield-farming (aka rented liquidity).

Liquity Protocol (creators of the LUSD stablecoin) bootstrapped by issuing LQTY tokens to depositors in the stability pool. This incentivized people to take out loans and deposit to the stability pool.

For normal, healthy operations of the protocol, it's vital that there is sufficient funds in the stability pool to cover all liquidations. However the LQTY incentive resulted in far more deposits than required, essentially creating wasted capital. Furthermore there are concerns on how sustainable Liquity will be after the initial 100 million LQTY has been issued and the pool is only sustained through liquidations rewards.

How does Threshold USD approach this?

In Threshold USD we take a different approach. A newer concept in DeFi known as Protocol Controlled Value (PCV) is the idea that the protocol itself can own liquidity and use that to improve the protocol. This is a new alternative to renting it elsewhere (aka yield-farming) and has the benefit of long-term sustainability.

But the PCV has to come from somewhere. In thUSD we resolve this by issuing an to the PCV which then deposit these funds directly to the stability pool. Read more about this here:

That lets us bootstrap the stability pool at zero cost to the protocol.

But not only that, by eliminating the LQTY token, all profits (from interest, redemption fee, etc) will go directly into the PCV. Compared to Liquity, where profits are distributed among LQTY holders, we have eliminated a massive drain on the system, at no cost.

Having the stability pool funded on its own is enough for the protocol to function, and we can expect some users that want to borrow against their tBTC to take part, but it's not going to create major adoption, and for thUSD to function well it needs to be stable at ~$1, that requires people to put up liquidity at decentralized exchanges.

In order to draw in more users and create liquidity for thUSD, we will issue rewards to pools on other platforms (such as a thUSD stablecoin pool on Curve). This will incentivize users to take debt and deposit into Curve, with the added benefit of improved liquidity and resiliency.

How fast we want to grow can be almost entirely adjusted by how much reward is sent to the pool.

This part is indeed rented liquidity with all its drawbacks, but remember that Threshold USD profits are sent to the PCV instead of LQTY holders: as the protocol grows in popularity, profits from the protocol itself can be used to fund these rewards, thus creating a self-sustaining feedback loop.

On top of that, Threshold DAO will store some of its reserves in stablecoins, so the Threshold DAO can initiate a PCV with deposits on Curve. That will result in more stability for thUSD which is important for adoption.

Opening Collateral Vaults and Minting thUSD

Steps to Open Collateral Vaults and Mint thUSD

  1. Access the thUSD App Interface:

    • Navigate to and connect your preferred wallet on BOB Network.

  2. Open a New Vault:

    • Go to the “Borrow” menu, choose tBTC or ETH as your collateral, and select "Open a Vault".

  3. Deposit tBTC or ETH:

    • Specify the amount of tBTC or ETH you wish to deposit as collateral. Ensure the deposited amount meets the minimum collateral requirement of $2,000 ($1,800 minimum debt + $200 liquidation reserve) + transaction fees.

  4. Set the collateral amount:

    • Set your desired collateral amount. Ensure it meets the minimum required ratio of 110% of your debt.

  5. Mint thUSD:

    • Enter the amount of thUSD you wish to mint based on your collateral and collateralization ratio.

    • Review the details of your vault, including the collateral amount, collateralization ratio, and minted thUSD.

    • In case of tBTC as collateral, approve tBTC expenditure.

    • Confirm, and authorize the transaction to mint thUSD.

  6. Manage Your Vault:

    • Monitor your vault on to ensure it remains adequately collateralized. You can adjust collateral and debt position to adjust the collateralization ratio or repay thUSD as needed.

Governance Process

Lifecycle of a successful proposal

  1. Forum discussion: A Threshold community member posts a potential proposal on the Threshold governance forums. Community members who post the initial proposal are encouraged to get active in Meta governance discussions within the community on forums and in Discord to earn support for the proposal. There is no minimum token threshold required to create a new proposal on the forums. Community mods reserve the right to moderate spam proposals during this stage by deleting the proposal from the forums.

  2. Temperature Check: Once a potential proposal has enough traction and discussion within the community, it can proceed for a temperature check via an off-chain . If a proposal receives majority support during the temperature check, it is eligible to move onto the next step.

  3. Proposal Creation: A community member holding enough vote weight (at least 0.25% of T total supply) submits the proposal on-chain. The proposal enters a 2-day delay period before voting officially begins.

  4. Vote Period: Proposal voting remains open for 10 days. If the proposal passes with enough quorum (at least 1.5% of T total supply), it moves onto the next step; if the proposal fails, it is canceled. Creators and supporters of the proposal may bring a modified proposal forward again but it must be sufficiently different and pass requirements outlined in previous steps.

  5. Timelock Period: Once a proposal is approved, the Governor Bravo smart contracts include an additional timelock delay of 2 days. Anyone can interact with the Governor Bravo smart contracts to queue an approved proposal into the Timelock contract.

  6. Execution: After the timelock delay, anyone can execute an approved proposal.

Deviations in the proposal lifecycle

  • Council vetos: During the on-chain phase, the Elected Council can veto any proposal. This is intended to be an extra security mechanism in the event that a dangerous proposal passes.

  • Late quorum prevention: The 10-days voting period is automatically extended when quorum is reached late, to prevent governance attacks that try to reach quorum at the last minute; in case a proposal reaches quorum less than 2 days before the deadline, the proposal deadline is extended for 2 more days from the moment the quorum was reached.

  • Off-chain proposals: Some proposals do not imply any on-chain action (e.g. Council and Guild elections). In these cases, there is a Snapshot vote that takes 5 days for regular proposals and 7 days for elections, with no associated on-chain proposal.

Vote Delegation

To participate in DAO governance, token holders must set a governance delegate address. Vote delegation is the process of granting a delegate the power to vote on your behalf, using your voting weight, on DAO governance issues. The delegate can be yourself (self-delegation) or a third party. A configured delegate can be changed or revoked at any time.

Delegation never involves the transfer of custody of assets but rather just the vote weight those assets represent in the Threshold DAO.

Third-Party Delegation

Third-party delegates are volunteers who actively participate in Threshold governance and wish to grow their influence over critical DAO decisions. Delegates provide ETH addresses to which other Threshold token holders and Threshold stakers can delegate their vote weight. Third-party delegates are subsequently responsible for voting on Threshold governance proposals with this vote weight that others have assigned to them.

Community members who are unsure or uninterested in governance and do not want to actively participate in proposals, discussions, and voting, can instead delegate to active DAO members who have demonstrated a commitment to Threshold. Delegating their vote allows token holders and stakers to have an indirect say in the DAO without being involved in day-to-day governance activities.

Community members interested in serving as delegates can self-nominate .

There is no compensation for third-party delegate contribution to governance.

The process for delegating is explained in the following subsection. It's not currently possible to delegate tokens deposited as LP in AMM pools (e.g. Curve), although this is an area for future improvement.

Liquid Token Delegation

How to delegate liquid T tokens

Liquid T holders can delegate their token weights to themselves or a third party for voting on governance proposals.

Delegation is accomplished via an on-chain transaction which costs ETH gas.

1. Go to .

2. Connect your wallet.

3. Click "Set Up Delegation".

4. Select your "Delegation Type" - either to yourself or a third party.

5. Enter "Delegate Address" and click on "Delegate Votes".

6. Sign the transaction.

Network Configuration

Required network configuration for the tBTC v2 staking client.

Required Ports

The node has to be accessible publicly to establish and maintain connections with bootstrap nodes and discovered peers. The node exposes metrics and diagnostics services for network health monitoring purposes. Update firewall rules as necessary, including application level firewalls for the following ports

Purpose
Config Property
Protocol
Default

The network port must be exposed publicly for peers to connect to your node.

The status port must be exposed publicly for rewards allocation.

Announced Addresses

An Announced Address is a layered addressing information (multiaddress/multiaddr) announced to the Threshold Network that is used by peers to connect with your node, e.g.: /dns4/bootstrap-0.test.keep.network/tcp/3919 or /ip4/104.154.61.116/tcp/3919.

If the machine you’re running your node is not exposing a public IP (e.g. it is behind NAT) you should set the network.AnnouncedAddresses (flag: --network.announcedAddresses) configuration property to addresses (ip4 or dns4) under which your node is reachable for the public.

To read more about multiaddress see the .

Data Storage

Setup persistent data directories for the client.

The client requires two persistent directories. These directories will store configuration files and data generated and used by the client. It is highly recommended to create frequent backups of these directories. Loss of these data may be catastrophic and may lead to slashing.

It is crucial to ensure the data directory is persisted and backed up on a regular basis.

Create folders for tBTC v2 client

The tBTC v2 client will create two subdirectories within the storage directory: keystore and work. You do not need to create these.

The keystore subdirectory contains sensitive key material data generated by the client. Loosing the keystore data is a serious protocol offense and leads to slashing and potentially losing funds.

It is the operator’s responsibility to ensure the keystore data are not lost under any circumstances.

The work directory contains data generated by the client that should persist the client restarts or relocations. If the work data are lost the client will be able to recreate them, but it is inconvenient due to the time needed for the operation to complete and may lead to losing rewards.

Copy Operator keystore file

Assuming for the root user with the command provided in the step, the operator-key file should be located in the /.operator-key directory.

Contained within the operator-key directory is the account key file (operator key file), its name will be similar to the following: UTC--2018-11-01T06-23-57.810787758Z--fa3da235947aab49d439f3bcb46effd1a7237e32

copy (not move!) this account key file to the config directory created above

Frequently Asked Questions

Find answers to some of the most commonly asked questions here.

Errors in Logs or Console

Certain errors may be reported by your node during the early stages of Chaosnet and the tBTC 2 launch. See a sample below:

This warning is normal and may happen on chaosnet. It will disappear and reappear periodically.

This is expected during this stage of the chaosnet. When the pool is locked, new operators cannot join but sortition (selecting operators from the pool) is possible. When the pool is unlocked, new operators can join but the sortition is not possible. Once the first set of beta operators is registered, the pool will get locked for some time to allow the sortition. After some time, when another set of beta operators are added, the pool will be unlocked again.

DAO Contracts API

You can learn about APIs of contracts related to the DAO under the following links:

KeepStake

IManagedGrant

grantee

KeepStake

T network staking contract supports existing KEEP stakes by allowing KEEP stakers to use their stakes in T network and weights them based on KEEP<>T token ratio. KEEP stake owner is cached in T staking contract and used to restrict access to all functions only owner or operator should call. To cache KEEP stake owner in T staking contract, T staking contract first needs to resolve the owner.

Resolving liquid KEEP stake owner is easy. Resolving token grant stake owner is complicated and not possible to do on-chain from a contract external to KEEP TokenStaking contract. Keep TokenStaking knows the grant ID but does not expose it externally.

KeepStake contract addresses this problem by exposing operator-owner mappings snapshotted off-chain based on events and information publicly available from KEEP TokenStaking contract and KEEP TokenGrant contract. Additionally, it gives the Governance ability to add new mappings in case they are ever needed; in practice, this will be needed only if someone decides to stake their KEEP token grant in KEEP network after 2021-11-11 when the snapshot was taken.

Operator-owner pairs were snapshotted 2021-11-11 in the following way:

  1. Fetch all TokenStaking events from KEEP staking contract.

  2. Filter out undelegated operators.

  3. Filter out canceled delegations.

  4. Fetch grant stake information from KEEP TokenGrant for that operator to determine if we are dealing with grant delegation.

  5. Fetch grantee address from KEEP TokenGrant contract.

  6. Check if we are dealing with ManagedGrant by looking for all created ManagedGrants and comparing their address against grantee address fetched from TokenGrant contract.

keepTokenStaking

operatorToManagedGrant

operatorToGrantee

constructor

setManagedGrant

Allows the Governance to set new operator-managed grant pair. This function should only be called for managed grants if the snapshot does include this pair.

setGrantee

Allows the Governance to set new operator-grantee pair. This function should only be called for non-managed grants if the snapshot does include this pair.

resolveOwner

Resolves KEEP stake owner for the provided operator address. Reverts if could not resolve the owner.

resolveSnapshottedManagedGrantees

resolveSnapshottedGrantees

StakerGovernorVotes

StakerGovernorVotes

Staker DAO voting power extraction from staked T positions,

staking

constructor

getVotes

Read the voting weight from the snapshot mechanism in the T staking contracts. Note that this also tracks legacy stakes (NU/KEEP).

See {IGovernor-getVotes}

Parameters

Name
Type
Description

_getPastTotalSupply

Compute the total voting power for the Staker DAO.

Parameters

Name
Type
Description

Callback

Callback

This file documents a contract which is not yet deployed to Mainnet.

Library for handling calls to random beacon consumer.

Data

CallbackFailed

setCallbackContract

Sets callback contract.

Parameters

Name
Type
Description

executeCallback

Executes consumer specified callback for the relay entry request.

Parameters

Name
Type
Description

IRandomBeacon

IRandomBeacon

This file documents a contract which is not yet deployed to Mainnet.

requestRelayEntry

Creates a request to generate a new relay entry. Requires a request fee denominated in T token.

Parameters

Name
Type
Description

ModUtils

ModUtils

This file documents a contract which is not yet deployed to Mainnet.

modExp

Wraps the modular exponent pre-compile introduced in Byzantium. Returns base^exponent mod p.

modSqrt

Calculates and returns the square root of a mod p if such a square root exists. The modulus p must be an odd prime. If a square root does not exist, function returns 0.

legendre

Calculates the Legendre symbol of the given a mod p.

Return Values

Name
Type
Description

IRandomBeaconConsumer

IRandomBeaconConsumer

This file documents a contract which is not yet deployed to Mainnet.

__beaconCallback

Receives relay entry produced by Keep Random Beacon. This function should be called only by Keep Random Beacon.

Parameters

Name
Type
Description

Initialize SDK

Before initializing the tBTC SDK instance in your project, you need to answer the following questions:

  • What Bitcoin network and which tBTC contracts do you plan to interact with?

  • Do you want to perform just read-only actions or send transactions as well?

Answering those questions will allow initializing the SDK in the right way. The SDK is prepared to handle common use cases but provides some flexibility as well. This guide explains that in detail.

Specific guides

These guides will show you how to initialize the SDK in mainnet, testnet, crosschain or custom mode.

tBTC

is a decentralized, permissionless Bitcoin bridge developed by the Threshold Network designed to bring Bitcoin (BTC) liquidity to Ethereum and other EVM-compatible networks. It allows Bitcoin holders to mint tBTC, an ERC-20 token fully backed 1:1 by BTC, enabling seamless participation in Ethereum's DeFi ecosystem without relying on centralized intermediaries.

By integrating tBTC, developers can bridge the gap between Bitcoin's liquidity and Ethereum's smart contract capabilities. tBTC provides a decentralized, secure and permissionless way to bring BTC into the Ethereum ecosystem enabling innovative DeFi applications and expanding possibilities for cross-chain interactions.

Utilizing the provided SDKs and developer resources you can seamlessly integrate tBTC into your projects and contribute to the growth of decentralized finance.

2022-10-10T18:30:42.571Z    WARN    keep-beacon    beacon/node.go:78    selecting group not possible: [cannot select group in the sortition pool: [got error [execution reverted: Sortition pool unlocked] while resolving original error [execution reverted: Sortition pool unlocked]]]    {"seed": "0x29c60250c292e108e6abf4dcc76cb161d8ae8e803c4d4419eaff6e97f514b080"}
function grantee() external view returns (address)
contract IKeepTokenStaking keepTokenStaking
mapping(address => address) operatorToManagedGrant
mapping(address => address) operatorToGrantee
constructor(contract IKeepTokenStaking _keepTokenStaking) public
function setManagedGrant(address operator, address managedGrant) external
function setGrantee(address operator, address grantee) external
function resolveOwner(address operator) external view returns (address)
function resolveSnapshottedManagedGrantees(address operator) internal view returns (address)
function resolveSnapshottedGrantees(address operator) internal pure returns (address)
contract IVotesHistory staking
constructor(contract IVotesHistory tStakingAddress) internal
function getVotes(address account, uint256 blockNumber) public view virtual returns (uint256)

account

address

Delegate account with T staking voting power

blockNumber

uint256

The block number to get the vote balance at

function _getPastTotalSupply(uint256 blockNumber) internal view virtual returns (uint256)

blockNumber

uint256

The block number to get the voting power at

struct Data {
  contract IRandomBeaconConsumer callbackContract;
}
event CallbackFailed(uint256 entry, uint256 entrySubmittedBlock)
function setCallbackContract(struct Callback.Data self, contract IRandomBeaconConsumer callbackContract) internal

self

struct Callback.Data

callbackContract

contract IRandomBeaconConsumer

Callback contract.

function executeCallback(struct Callback.Data self, uint256 entry, uint256 callbackGasLimit) internal

self

struct Callback.Data

entry

uint256

The generated random number.

callbackGasLimit

uint256

Callback gas limit.

function requestRelayEntry(contract IRandomBeaconConsumer callbackContract) external

callbackContract

contract IRandomBeaconConsumer

Beacon consumer callback contract.

function modExp(uint256 base, uint256 exponent, uint256 p) internal view returns (uint256 o)
function modSqrt(uint256 a, uint256 p) internal view returns (uint256)
function legendre(uint256 a, uint256 p) internal view returns (int256)

[0]

int256

Returns 1 if a is a quadratic residue mod p, -1 if it is a non-quadratic residue, and 0 if a is 0.

function __beaconCallback(uint256 relayEntry, uint256 blockNumber) external

relayEntry

uint256

Relay entry (random number) produced by Keep Random Beacon.

blockNumber

uint256

Block number at which the relay entry was submitted to the chain.

Sunsetting tBTC coverage pools
Threshold Treasury Guild
Treasury Guild Channel
Threshold Network Discord
Liquity Protocol
Bootstrapping
Initial Protocol Loan
here
app.thresholdusd.org
app.thresholdusd.org
here
liquid tokens

Network

network.port

TCP

3919

Status

clientInfo.port

TCP

9601

libp2p documentation
cd /home/$USER/
mkdir keep
cd keep
mkdir storage config
cd /home/$USER/.operator-key
ls -la
cp name_of_account_key_file /home/$USER/keep/config/name_of_account_key_file
Geth was installed
Operator Account creation
BaseTokenholderGovernor
Checkpoints
GovernorParameters
IApplication
ILegacyTokenStaking
IStaking
IVotesHistory
KeepStake
PercentUtils
ProxyAdminWithDeputy
SafeTUpgradeable
StakerGovernor
StakerGovernorVotes
T
TokenholderGovernor
TokenholderGovernorVotes
TokenStaking
VendingMachine
tBTC
here
https://www.liquity.org/blog/on-price-stability-of-liquity
Redemptions example
snapshot
The lifecycle of a successful proposal
https://boardroom.io/threshold
Set Up Delegation
Choose Delegation Type
Delegate Votes

Recovery Mode

This is a mechanism to reduce systemic risk to thUSD by ensuring a total collateral ratio across all Vaults remain above 150%

What is Recovery Mode?

Recovery Mode kicks in when the Total Collateral Ratio (TCR) of the system falls below 150%.

During Recovery Mode, Vaults with a collateral ratio below 150% can be liquidated.

Moreover, the system blocks borrower transactions that would further decrease the TCR. New thUSD may only be issued by adjusting existing Vaults in a way that improves their collateral ratio, or by opening a new Vault with a collateral ratio>=150%. In general, if an existing Vault's adjustment reduces its collateral ratio, the transaction is only executed if the resulting TCR is above 150%.

What is the Total Collateral Ratio?

The Total Collateral Ratio or TCR is the ratio of the Dollar value of the entire system collateral at the current ETH:USD price, to the entire system debt. In other words, it's the sum of the collateral of all Vaults expressed in USD, divided by the debt of all Vaults expressed in thUSD.

What is the purpose of Recovery Mode?

The goal of Recovery Mode is to incentivize borrowers to behave in ways that promptly raise the TCR back above 150%, and to incentivize thUSD holders to replenish the Stability Pool.

Economically, Recovery Mode is designed to encourage collateral top-ups and debt repayments, and also itself acts as a self-negating deterrent: the possibility of it occurring actually guides the system away from ever reaching it. Recovery Mode is not a desirable state for the system.

What are the fees during Recovery Mode

While Recovery Mode has no impact on the redemption fee, the borrowing fee is set to 0% to maximally encourage borrowing (within the limits described above).

How can I make my Vault safe in Recovery Mode?

By increasing your collateral ratio to 150% or greater, your Vault will be protected from liquidation. This can be done by adding collateral, repaying debt, or both.

Can I be liquidated if my collateral ratio is below 150% in Recovery Mode?

Yes, you can be liquidated below 150% if your Vault's collateral ratio is smaller than 150%. In order to avoid liquidation in Normal Mode and Recovery Mode, a user should keep their collateral ratio above 150%.

How much of a Vault’s collateral can be liquidated in Recovery Mode?

In Recovery Mode, liquidation loss is capped at 110% of a Vault's collateral. Any remainder, i.e. the collateral above 110% (and below the TCR), can be reclaimed by the liquidated borrower using the standard web interface.

This means that a borrower will face the same liquidation “penalty” (10%) in Recovery Mode as in Normal Mode if their Vault gets liquidated.

Bridging Collateral to BOB Network

Ensure your wallet has sufficient ETH for transaction fees. You can fund your wallet on BOB by transferring tBTC or ETH from the Ethereum Mainnet.

Note: The minimum amount to mint thUSD on BOB is $2,000 ($1,800 minimum debt + $200 liquidation reserve) worth of tBTC or ETH + transaction fees.

Preparation

  1. Set up Wallet:

    • Ensure you have a wallet connected to the Ethereum Mainnet with sufficient tBTC or ETH for bridging.

Bridging Process

  1. Access Bridge Interface:

    • Navigate to the BOB bridge interface at app.gobob.xyz/bridge.

  2. Connect Wallet:

    • Connect your wallet on the Ethereum Mainnet network to the bridge interface.

  3. Select tBTC or ETH:

    • Choose tBTC or ETH as the asset you want to bridge.

  4. Enter Amount:

    • Specify the amount of tBTC or ETH you want to bridge.

  5. Confirm Transaction:

    • Review the transaction details, including fees, and confirm the transaction.

  6. Authorize Transaction:

    • Approve the transaction in your wallet.

  7. Wait for Confirmation:

    • Wait for the transaction to be confirmed on the Ethereum blockchain.

  8. Verify Receipt:

    • Once confirmed, check your wallet on the BOB Network to verify the receipt of the bridged tBTC or ETH.

Note: When bridging assets back from the BOB Network, please know that the process can take up to 7 days.

Guilds

Additionally to the DAO governance bodies, there are three community-led guilds with different focus areas: the Marketing Guild, the Integrations Guild, and the Treasury Guild. Each guild is managed by an elected committee and holds regular, rotating elections.

Join a guild (via Discord's #dao-contribute channel) and work together with other Threshold DAO members based on your interests and expertise!

Treasury Guild

The Threshold Treasury Guild is responsible for effectively managing the Threshold DAO treasury. This includes growing Protocol Owned Liquidity (POL), researching / implementing best practice treasury management strategies, executing ecosystem liquidity incentives, diversifying the treasury, etc.

Integrations Guild

The core mission of the Threshold Integrations Guild is to build successful, synergistic and long-lasting relationships with other protocols, DAOs and external organizations.

Marketing Guild

The Threshold Marketing Guild is responsible for general Threshold marketing, growing our network of contributors, onboarding new members to the Threshold DAO, educating people about the Threshold’s value, services and use cases, and more.

Alternatives to Dashboard

Alternative application authorization methods to using the Threshold Dashboard.

An operator-registering transaction can be submitted with the Keep Client if the staking provider address key file is available.

Authorizing Random Beacon via the Client (Docker)

export KEEP_CLIENT_CONFIG_DIR=$(pwd)/config

export KEEP_CLIENT_ETHEREUM_WS_URL="<Ethereum API WS URL>"

export STAKING_PROVIDER_KEY_FILE_PASSWORD="<Staking Provider Account Key File Password>"
export STAKING_PROVIDER_KEY_FILE_NAME="<Staking Provider Account Key File Name>"
export OPERATOR_ADDRESS="<Operator Account Address>"

docker run \
    --volume $KEEP_CLIENT_CONFIG_DIR:/mnt/keep-client/config \
    --env KEEP_CLIENT_ETHEREUM_PASSWORD=$STAKING_PROVIDER_KEY_FILE_PASSWORD
    us-docker.pkg.dev/keep-test-f3e0/public/keep-client:latest \
    ethereum \
    --ethereum.url $KEEP_CLIENT_ETHEREUM_WS_URL \
    --ethereum.keyFile /mnt/keep-client/config/$STAKING_PROVIDER_KEY_FILE_NAME \
    beacon random-beacon register-operator --submit \
    $OPERATOR_ADDRESS

Authorizing TBTC application via the Client (Docker)

export KEEP_CLIENT_CONFIG_DIR=$(pwd)/config

export KEEP_CLIENT_ETHEREUM_WS_URL="<Ethereum API WS URL>"

export STAKING_PROVIDER_KEY_FILE_PASSWORD="<Staking Provider Account Key File Password>"
export STAKING_PROVIDER_KEY_FILE_NAME="<Staking Provider Account Key File Name>"
export OPERATOR_ADDRESS="<Operator Account Address>"

docker run \
    --volume $KEEP_CLIENT_CONFIG_DIR:/mnt/keep-client/config \
    --env KEEP_CLIENT_ETHEREUM_PASSWORD=$STAKING_PROVIDER_KEY_FILE_PASSWORD
    us-docker.pkg.dev/keep-test-f3e0/public/keep-client:latest \
    ethereum \
    --ethereum.url $KEEP_CLIENT_ETHEREUM_WS_URL \
    --ethereum.keyFile /mnt/keep-client/config/$STAKING_PROVIDER_KEY_FILE_NAME \
    ecdsa wallet-registry register-operator --submit
    $OPERATOR_ADDRESS

Via Web Browser

An operator-registering transactions can be submitted with Etherscan.

For each of the RandomBeacon and WalletRegistry contracts perform the following steps:

  1. Find the address of the contract and open it on Etherscan (see below).

  2. Go to Contract → Write Contract tab.

  3. Connect your wallet with Connect to Web3 button.

  4. Submit the registerOperator function with your Operator address as an argument.

IApplication

IApplication

Generic interface for an application. Application is an external smart contract or a set of smart contracts utilizing functionalities offered by Threshold Network. Applications authorized for the given staking provider are eligible to slash the stake delegated to that staking provider.

RewardsWithdrawn

event RewardsWithdrawn(address stakingProvider, uint96 amount)

Event emitted by withdrawRewards function.

withdrawRewards

function withdrawRewards(address stakingProvider) external

Withdraws application rewards for the given staking provider. Rewards are withdrawn to the staking provider's beneficiary address set in the staking contract.

Emits RewardsWithdrawn event.

authorizationIncreased

function authorizationIncreased(address stakingProvider, uint96 fromAmount, uint96 toAmount) external

Used by T staking contract to inform the application that the authorized amount for the given staking provider increased. The application may do any necessary housekeeping. The application must revert the transaction in case the authorization is below the minimum required.

authorizationDecreaseRequested

function authorizationDecreaseRequested(address stakingProvider, uint96 fromAmount, uint96 toAmount) external

Used by T staking contract to inform the application that the authorization decrease for the given staking provider has been requested. The application should mark the authorization as pending decrease and respond to the staking contract with approveAuthorizationDecrease at its discretion. It may happen right away but it also may happen several months later. If there is already a pending authorization decrease request for the application, and the application does not agree for overwriting it, the function should revert.

involuntaryAuthorizationDecrease

function involuntaryAuthorizationDecrease(address stakingProvider, uint96 fromAmount, uint96 toAmount) external

Used by T staking contract to inform the application the authorization has been decreased for the given staking provider involuntarily, as a result of slashing. Lets the application to do any housekeeping neccessary. Called with 250k gas limit and does not revert the transaction if involuntaryAuthorizationDecrease call failed.

availableRewards

function availableRewards(address stakingProvider) external view returns (uint96)

Returns the amount of application rewards available for withdrawal for the given staking provider.

minimumAuthorization

function minimumAuthorization() external view returns (uint96)

The minimum authorization amount required for the staking provider so that they can participate in the application.

TokenholderGovernorVotes

TokenholderGovernorVotes

Tokenholder DAO voting power extraction from both liquid and staked T token positions, including legacy stakes (NU/KEEP).

token

contract IVotesHistory token

staking

contract IVotesHistory staking

constructor

constructor(contract IVotesHistory tokenAddress, contract IVotesHistory tStakingAddress) internal

getVotes

function getVotes(address account, uint256 blockNumber) public view virtual returns (uint256)

Read the voting weight from the snapshot mechanism in the token and staking contracts. For Tokenholder DAO, there are currently two voting power sources:

  • Liquid T, tracked by the T token contract

  • Stakes in the T network, tracked by the T staking contract. Note that this also tracks legacy stakes (NU/KEEP); legacy stakes count for tokenholders' voting power, but not for the total voting power of the Tokenholder DAO (see {_getPastTotalSupply}).

See {IGovernor-getVotes}

Parameters

Name
Type
Description

account

address

Tokenholder account in the T network

blockNumber

uint256

The block number to get the vote balance at

_getPastTotalSupply

function _getPastTotalSupply(uint256 blockNumber) internal view virtual returns (uint256)

Compute the total voting power for Tokenholder DAO. Note how it only uses the token total supply as source, as native T tokens that are staked continue existing, but as deposits in the staking contract. However, legacy stakes can't contribute to the total voting power as they're already implicitly counted as part of Vending Machines' liquid balance; hence, we only need to read total voting power from the token.

Parameters

Name
Type
Description

blockNumber

uint256

The block number to get the vote power at

BytesLib

BytesLib

This file documents a contract which is not yet deployed to Mainnet.

concatStorage

function concatStorage(bytes _preBytes, bytes _postBytes) internal

equalStorage

function equalStorage(bytes _preBytes, bytes _postBytes) internal view returns (bool)

concat

function concat(bytes _preBytes, bytes _postBytes) internal pure returns (bytes)

slice

function slice(bytes _bytes, uint256 _start, uint256 _length) internal pure returns (bytes res)

toAddress

function toAddress(bytes _bytes, uint256 _start) internal pure returns (address)

toUint8

function toUint8(bytes _bytes, uint256 _start) internal pure returns (uint8)

toUint

function toUint(bytes _bytes, uint256 _start) internal pure returns (uint256)

equal

function equal(bytes _preBytes, bytes _postBytes) internal pure returns (bool)

toBytes32

function toBytes32(bytes _source) internal pure returns (bytes32 result)

keccak256Slice

function keccak256Slice(bytes _bytes, uint256 _start, uint256 _length) internal pure returns (bytes32 result)

ProxyAdminWithDeputy

ProxyAdminWithDeputy

Based on ProxyAdmin, an auxiliary contract in OpenZeppelin's upgradeability approach meant to act as the admin of a TransparentUpgradeableProxy. This variant allows an additional actor, the "deputy", to perform upgrades, which originally can only be performed by the ProxyAdmin's owner. See OpenZeppelin's documentation for TransparentUpgradeableProxy for more details on why a ProxyAdmin is recommended.

deputy

address deputy

DeputyUpdated

event DeputyUpdated(address previousDeputy, address newDeputy)

onlyOwnerOrDeputy

modifier onlyOwnerOrDeputy()

constructor

constructor(contract StakerGovernor dao, address _deputy) public

setDeputy

function setDeputy(address newDeputy) external

upgrade

function upgrade(contract TransparentUpgradeableProxy proxy, address implementation) public virtual

Upgrades proxy to implementation. This contract must be the admin of proxy, and the caller must be this contract's owner or the deputy.

upgradeAndCall

function upgradeAndCall(contract TransparentUpgradeableProxy proxy, address implementation, bytes data) public payable virtual

Upgrades proxy to implementation and calls a function on the new implementation. This contract must be the admin of proxy, and the caller must be this contract's owner or the deputy.

_setDeputy

function _setDeputy(address newDeputy) internal

_checkCallerIsOwnerOrDeputy

function _checkCallerIsOwnerOrDeputy() internal view

Guides

Initial Protocol Loan
Initial Protocol Loan
Ethereum and Bitcoin mainnet
Ethereum and Bitcoin testnet
Crosschain
Custom mode

Sweeping

What is Sweeping?

Sweeping is an accounting optimization for tracking bitcoin deposits in tBTC contracts. It involves periodically consolidating recent bitcoin deposits into a single Unspent Transaction Output (UTXO) for a more optimal commitment cadence to Ethereum. This process simplifies tracking associated public keys and refund time locks while also helping prevent supply peg disruption. Typically, Sweeping runs every 8 hours depending on the volume and size of deposits.

How it works

When a user makes a deposit, they're locking up the funds using our specific locking script. This creates an Unspent Transaction Output (UTXO).

Typically, the job of a bitcoin "wallet" (wallets don't exist in the same way that they do on ethereum) is to scan the blockchain for particular locking script patterns that it thinks it can unlock (like a P2PKH script associated to your public key) and sum up the relevant funds. When you want to send someone else bitcoin, the wallet software figures out which UTXOs to unlock to send to them so that you don't have to.

If we wanted to be able to redeem bitcoin deposits, we would need to track which deposits are associated to which public keys the same way that wallet software does; this is a headache especially from a bitcoin mining fee perspective.

Further, our locking script includes this idea of a time-locked refund. If the funds are not touched by a certain amount of time (9 months at the time of writing; <locktime> in the script), we allow the depositor to reclaim their deposit. We do this as an effective escape hatch for if something goes wrong with the protocol or deposit.

As a result of this, we give ourselves the same time limit for collecting the deposit. If we don't move the funds by that time, a user could deposit 5 BTC, receive 5 TBTC, and then 9 months later reclaim their original 5 BTC without redeeming the 5 BTC. This would break the TBTC supply peg.

Sweeping aims to solve both of these problems simultaneously as well as serves as a performance optimization. Every 8 hours, we take all of the recent deposits, unlock them, and then send the funds using a normal P2PKH script back to the wallet itself, consolidating the UTXOs into a single UTXO.

The result is that all of the scattered deposits are "swept" into a single "pile" represented by one UTXO that we can commit to on ethereum.

Example

Here's a deposit for 10.3 BTC. That deposit is swept in transaction 0db1929c2fa41649264b95b9162bc7a12f43cb455a91bae7177667a53290d7e8 (along with many other deposits). That was one of three sweeps performed by that wallet (as of writing).

That transaction created a UTXO worth 1.51614471 BTC. The next transaction

Used the old UTXO (the first input) as well as all of the recent deposits to create a new UTXO (the output on the right) worth 15.42475511 BTC. The final transaction

Follows the same pattern. It uses the old UTXO (the first input) as well as the recent deposits to create a new UTXO worth ‎59.52818486 BTC.

B. Protocol

B. Protocol is a third-party decentralized backstop liquidity protocol aiming to make lending platforms more stable.

The problem

In Liquity Protocol there's a concept called "Stability Pool". The purpose of the stability pool is to purchase liquidated collateral (tBTC) at a discount by using Threshold USD (thUSD) as payment.

Users deposits thUSD into the stability pool and their funds are pooled with other depositors. If a pool consist of 900,000 thUSD and a user deposits 100,000 thUSD, that user is entitled to 10% of the collateral seized.

The issue with stability pool is that as liquidations occurs, thUSD is traded to tBTC, but never back to thUSD. If left untouched, the pool will eventually only consist of tBTC and no further liquidations can take place against the pool. Furthermore, by acquiring tBTC collateral, the value for depositors in the pool becomes subject to the price of BTC.

Users are therefore incentivized to quickly sell of the tBTC for thUSD to avoid taking on price risk, but in Liquity, this process is manual. Users have to manually open the UI and withdraw tBTC, sell it for thUSD and then re-deposit the thUSD. This is both a time consuming and gas costly operation, best suited for whales and users with their own bots.

Even worse, a DAO cannot realistically operate in the current stability pool model because voting to move and sell funds becomes impractical, costly and slow.

In order to establish a non-human, algorithmic, fully automated solution to compound profits we look to B.Protocol.

B.Protocol to the rescue

Instead of depositing directly into the stability pool, we can use B.Protocol's wrapping smart contract interface, which deposits the thUSD to the stability pool on behalf of all users (and the PCV).

Once liquidation happens, the discounted tBTC is automatically offered for sale by B.Protocol’s Backstop AMM (B.AMM). This is done according to a deterministic formula, which takes into account the current tBTC and thUSD inventory, and the current BTC-USD market price (which is taken from Chainlink). Whenever the sale occurs, the smart contract deposits the returned thUSD back to the stability pool.

If there are no takers for the offer on the B.AMM, Gelato Keepers will arbitrage through popular DEX pairs to fulfill the order.

This way the PCV becomes self-sufficient and able to operate indefinitely without outside interference.

Integration details

B.Protocol Team will deploy a non-upgradeable pool that is Threshold USD compatible.

The pool will be using Chainlink BTC/USD oracle (same as Threshold USD).

The pool will not deploy idle funds in any yield farming applications.

Threshold USD PCV will whitelist the smart contract for the B.Protocol pool and the Threshold Network DAO (T DAO) will vote to initiate a deposit of thUSD to the B.Protocol pool. The deposit is moved from the PCV smart contract to B.Protocol and can be moved back to the PCV at any time, without limitation, as long as the T DAO votes to do so. If there is tBTC in the pool at the time of withdraw, the tBTC may also be transferred back to the PCV.

The B.AMM contract's "A" parameter ("A" is amplification factor​​​, higher value means less slippage) is operated through a co-ownership between T DAO and B.Protocol, where B.Protocol propose "A" value and T DAO can approve or reject.

The discount rate is set to 4% on deployment, but the max rate should be configurable up to 10% in a joint governance mechanism between T DAO & B.Protocol.

All profits from liquidations will auto-compound within the B. Protocol pool.

Keeper

As a backup mechanism, B. Protocol will deploy Gelato keepers that can route.

Example:​

tBTC -> WBTC -> USDC -> thUSD

In this example funds could be routed through Curve Finance pools (both stablecoin and wrapped bitcoin pools).

In addition, funds could be routed through uniswap (v2 or v3) WBTC or USDC paired pools.

Other routes could be:

tBTC -> WBTC -> DAI -> thUSD tBTC -> WBTC -> USDT -> thUSD

The keeper is funded by the DAO and any profit it makes is distributed back to the PCV.

​

Architecture

The following diagram presents the architecture of the tBTC SDK and its place in the ecosystem:

tBTC SDK Architecture

As you can see, the SDK consists of several major parts:

  • TBTC component

  • Feature services

  • Shared libraries

TBTC component

The TBTC component is the main entry point to the SDK. It provides different ways to initialize the SDK that are supposed to address the common use cases (see Initialize SDK guide to learn more). Moreover, the TBTC component gives access to the SDK core features through feature services as well as low-level direct access to the underlying tBTC smart contracts and Bitcoin network client.

Feature services

The role of the SDK feature services is to provide seamless access to the core features of the tBTC bridge. The most important feature services of the SDK are:

  • Deposits: deposit and mint flow for BTC depositors

  • Redemptions: unmint and redeem flow for TBTC redeemers

  • Maintenance: authorized maintenance actions for maintainers (e.g. optimistic minting guardians)

Shared libraries

Shared libraries are reusable modules that provide cross-cutting concerns leveraged by multiple feature services and the TBTC component. The main shared libraries of the SDK are:

  • Bitcoin: interfaces and utilities necessary to interact with the Bitcoin chain

  • Contracts: chain-agnostic interfaces allowing to interact with tBTC smart contracts

  • Electrum: Electrum-based implementation of the Bitcoin client

  • Ethereum: implementations of tBTC smart contract interfaces for Ethereum chain

  • Utils: general utility functions

tBTC SDK

tBTC SDK is a TypeScript library that provides effortless access to the fundamental features of the tBTC Bitcoin bridge. The SDK allows developers to integrate tBTC into their own applications and offer the power of trustless tokenized Bitcoin to their users.

The SDK documentation consists of the following parts:

  • Quickstart

  • Architecture

  • Guides

  • API Reference

For a high-level overview of the tBTC protocol, please see the tBTC Bitcoin Bridge section.

The Path to Permissionlessness

The system was designed from the outset to be fully permissionless - anyone with the minimum amount of T stake, could run a node and have a of being a custodian.

In order to effectively ship the product, we launched with a permissioned set of Signers, as well as permissioned sets of Guardians and Minters. This document will explain the ideas behind those changes and what we're doing about it going forward.

TL;DR

  • tBTC currently uses a permissioned set of signers (aka ) operating under an honest-majority assumption.

  • The permissioned honest-majority signer set will remain in place until custody is upgraded to a 1-of-N trust assumption using BitVM2 (or an equivalent design).

Permissionless Custodianship

To select the 100 signers for a wallet, tBTC selects from the permissioned list of Beta Stakers. This decision was made for several reasons:

  1. During the early days of the system; the testnet and phases, it is important to ensure that signers are available to help test changes and quickly respond to critical bugs. Having direct access to individuals known to the development team is critical in this context.

  2. The underlying signature algorithm, , cannot identify misbehaving signers.

The point about GG18 is crucial. Without being able to identify misbehaving signers, a small, sophisticated, malicious minority can make it difficult to sign Bitcoin transactions in a timely manner. If we were able to identify the misbehaving signers, we could exclude them directly from the signing process.

Although alternative algorithms exist for identifying misbehaving signers, they are not yet viable for production use for one or more of the following reasons:

  • They are not yet codebases.

  • They are not open source.

  • They exist only as whitepapers.

To address this issue, we have been developing a proof-of-concept for and exploring .

Recent advances in Bitcoin bridge design, specifically BitVM2, suggest the possibility of improving custody from an honest-majority assumption to a 1-of-N trust assumption. Threshold Network is closely monitoring the development of this design and various implementations, with the medium-term intent of upgrading tBTC's underlying custody mechanics once the technology is sufficiently mature.

Guardians and Minters

Guardians and Minters are a permissioned set of high-trust public operators with their reputations on the line.

Guardians are responsible for validating mint and redemption requests, and have the ability to veto malicious or fraudulent requests. If we were to make these lists permissionless, a malicious Minter could flood the system with fake minting requests for the Guardians to deal with, or a malicious Guardian could veto every proposed mint to halt growth.

If a Guardian or Minter misbehaves, the can vote to remove them.

Minters serve a convenience function by enabling "fast minting." Importantly, they do not have the ability to gatekeep mint requests; any deposit that all Minters refuse to approve or that a Guardian refuses to accept can still be minted every eight hours by the signers. This ensures that the system remains resilient and able to handle situations in which a malicious actor attempts to disrupt the minting process.

Stability Pool and Liquidations

Mechanisms to keep thUSD solvent under changing collateral price

What is the Stability Pool?

The Stability Pool is the first line of defense in maintaining system solvency. It achieves that by acting as the source of liquidity to repay debt from liquidated Vaults—ensuring that the total thUSD supply always remains backed.

When any Vault is liquidated, an amount of thUSD corresponding to the remaining debt of the Vault is burned from the Stability Pool’s balance to repay its debt. In exchange, the entire collateral from the Vault is transferred to the Stability Pool.

The Stability Pool is funded by users transferring thUSD into it (called Stability Providers). Over time Stability Providers lose a pro-rata share of their thUSD deposits, while gaining a pro-rata share of the liquidated collateral. However, because Vaults are likely to be liquidated at just below 110% collateral ratios, it is expected that Stability Providers will receive a greater dollar-value of collateral relative to the debt they pay off.

Why should I deposit thUSD to the Stability Pool?

Stability Pool Providers will make liquidation gains.

What are liquidations?

To ensure that the entire Stablecoin supply remains fully backed by collateral, Vaults that fall under the minimum collateral ratio of 110% will be closed (liquidated).

The debt of the Vault is canceled and absorbed by the Stability Pool and its collateral distributed among Stability Providers.

The owner of the Vault still keeps the full amount of thUSD borrowed but loses ~10% value overall hence it is critical to always keep the ratio above 110%, ideally above 150%.

Who can liquidate Vaults?

Anybody can liquidate a Vault as soon as it drops below the Minimum Collateral Ratio of 110%. The initiator receives a gas compensation (200 thUSD + 0.5% of the Vaults collateral) as reward for this service.

How am I compensated for liquidating a Vault?

The liquidation of Vaults is connected with certain gas costs which the initiator has to cover. The cost per Vault was reduced by implementing batch liquidations of up to 160 - 185 Vaults but with the aim of ensuring that liquidations remain profitable even in times of soaring gas prices the protocol offers a gas compensation given by the following formula:

gas compensation = 200 thUSD + 0.5% of Vault's collateral (ETH)

The 200 thUSD is funded by a Liquidation Reserve while the variable 0.5% part (in tBTC) comes from the liquidated collateral, slightly reducing the liquidation gain for Stability Providers.

How do I benefit as a Stability Provider from liquidations?

As liquidations happen just below a collateral ratio of 110%, you will most likely experience a net gain whenever a Vault is liquidated.

Let’s say there is a total of 1,000,000 thUSD in the Stability Pool and your deposit is 100,000 thUSD.

Now, a Vault with debt of 200,000 thUSD and collateral of 10.9 tBTCis liquidated at a tBTC price of $20,000, and thus at a collateral ratio of 109% (= 100% * (10.9 * 20,000) / 200,000). Given that your pool share is 10%, your deposit will go down by 10% of the liquidated debt (20,000 thUSD), i.e. from 100,000 to 80,000 thUSD. In return, you will gain 10% of the liquidated collateral, i.e. 1.09 tBTC, which is currently worth $21,800. Your net gain from the liquidation is $1,800.

Note that depositors can immediately withdraw the collateral received from liquidations and sell it to reduce their exposure to tBTC, if the USD value of tBTC is expected to decrease (for an exception see the next section).

Can I withdraw my deposit whenever I want?

As a general rule, you can withdraw the deposit made to the Stability Pool at any time. There is no minimum lockup duration. However, withdrawals are temporarily suspended whenever there are liquidatable Vault with a collateral ratio below 110% that have not been liquidated yet.

What Oracle are you using to determine the price of tBTC?

The protocol uses price feed, falling back to the tBTC:USD oracle under the following (extreme) conditions:

  • Chainlink price has not been updated for more than 4 hours

  • Chainlink response call reverts, returns an invalid price or an invalid timestamp

  • The price change between two consecutive Chainlink price updates is >50%.

Can I lose money by depositing funds to the Stability Pool?

While liquidations will occur at a collateral ratio well above 100% most of the time, it is theoretically possible that a Vault gets liquidated below 100% in a flash crash or due to an oracle failure. In such a case, you may experience a loss since the collateral gain will be smaller than the reduction of your deposit.

If thUSD is trading above $1, liquidations may become unprofitable for Stability Providers even at collateral ratios higher than 100%. However, this loss is hypothetical since thUSD is expected to return to the peg, so the “loss” only materializes if you had withdrawn your deposit and sold the thUSD at a price above $1.

Please note that although the system is diligently audited, a hack or a bug that results in losses for the users can never be fully excluded.

What happens if the Stability Pool is empty when liquidations occur?

If the Stability Pool is empty, the system uses a secondary liquidation mechanism called redistribution. In such a case, the system redistributes the debt and collateral from liquidated Vaults to all other existing Vaults. The redistribution of debt and collateral is done in proportion to the recipient Vault's collateral amount.

tBTC v2 Node Setup

This document explains the basic installation and configuration for the tBTC v2 staking client.

This document is intended for members of the community who would like to run their own tBTC v2 node.

Please be aware that running a node is not an easy task and requires technical skill and commitment to maintaining node uptime and availability.

Please review this document in its entirety prior to beginning setup of your node to familiarize yourself with the general setup process.

Important Considerations

The Threshold Network expects certain capabilities for each node running on the network. To help attain these capabilities consider the following criteria:

  • It is paramount that tBTC v2 nodes remain available to the Network. We strongly encourage a stable and redundant internet connection.

  • Equally important is machine uptime and reliability. A VPS is strongly recommended.

  • A connection to a production grade self-hosted or third party Ethereum node deployment.

  • Persistent and redundant storage that will survive a VM or container rotation, and a disk failure.

  • Each node running on the network requires a unique Ethereum Operator Account. The account has to maintain a positive Ether balance at all times.

  • Each node running on the network requires a unique IP address or a unique application port running under the same IP.

Recommended Machine Types

While it is possible to run the client on a local machine, this is not recommended.

Your operating environment will ultimately dictate what machine type to go with. This is particularly relevant if you’re running a containerized solution where multiple applications are sharing VM resources. The below types are sufficient for running one instance of the tBTC v2 Node.

The preferred OS is Ubuntu.

VPS Provider
VPS Type

Ethereum API

A Keep Node requires a connection to a WebSocket Ethereum API. You should obtain a WS API URL from a service provider (e.g. , , ) or run your own Ethereum node (e.g. ).

Bitcoin API

Starting from version v2.0.0-m3 , the Keep Node interacts with the Bitcoin network through an Electrum protocol server. You can use one of the publicly available servers or run your own. The URL of the chosen server should be passed under the bitcoin.electrum section of the configuration. If no Electrum server is set explicitly in the bitcoin.electrum configuration section, the Keep Node will randomly pick one server from its embedded server list, appropriate for the Bitcoin network the Keep Node is currently running against.

Configuration

The client expects configuration options to be passed as CLI flags or specified in a . If you specify an option by using a parameter on the command line, it will override the value read from the configuration file.

T

T

Threshold Network T token

By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it requires users to delegate to themselves to activate checkpoints and have their voting power tracked.

DELEGATION_TYPEHASH

The EIP-712 typehash for the delegation struct used by delegateBySig.

constructor

delegateBySig

Delegates votes from signatory to delegatee

Parameters

Name
Type
Description

delegate

Delegate votes from msg.sender to delegatee.

Parameters

Name
Type
Description

beforeTokenTransfer

Hook that is called before any transfer of tokens. This includes minting and burning.

Calling conditions:

  • when from and to are both non-zero, amount of from's tokens will be to transferred to to.

  • when from is zero, amount tokens will be minted for to.

  • when to is zero, amount of from's tokens will be burned.

  • from and to are never both zero.

delegate

Change delegation for delegator to delegatee.

Random Beacon API

You can learn about APIs of contracts related to the Random Beacon under the following links:

RandomBeaconChaosnet

RandomBeaconChaosnet

This file documents a contract which is not yet deployed to Mainnet.

A stub contract that will be used temporarily until the real-world random beacon client implementation is ready.

authorizedRequesters

Authorized addresses that can request a relay entry.

entry

Arbitrary relay entry. Initially set to the Euler's number. It's updated after each relay entry request.

RequesterAuthorizationUpdated

requestRelayEntry

Executes the callback with an arbitrary relay entry number.

The caller must be an authorized requester.

Parameters

Name
Type
Description

setRequesterAuthorization

Authorizes a requester of the relay entry.

Client Info

Client information exposed by the tBTC v2 staking client.

The client exposes metrics and diagnostics on a configurable port (default: 9601) under /metrics and /diagnostics resources.

The data can be consumed by Prometheus to monitor the state of a node.

Metrics

The client exposes the following metrics:

  • connected peers count,

  • connected bootstraps count,

  • Ethereum client connectivity status (if a simple read-only CALL can be executed).

Metrics are enabled once the client starts. It is possible to customize the port at which metrics endpoint is exposed as well as the frequency with which the metrics are collected.

Exposed metrics contain the value and timestamp at which they were collected.

Example metrics endpoint call result:

Diagnostics

The client exposes the following diagnostics:

  • list of connected peers along with their network id and Ethereum operator address,

  • information about the client’s network id and Ethereum operator address.

Diagnostics are enabled once the client starts. It is possible to customize the port at which diagnostics endpoint is exposed.

Example diagnostics endpoint call result:

Quickstart

Here you can find instructions explaining how to use the SDK in your own project.

Installation

To install the tBTC SDK in your project, run:

Please note that you will also need to install the library to initialize a signer or provider. To do so, invoke:

Usage

Here is a brief example demonstrating the use of the SDK in Ethereum:

Crosschain

Here is a brief example demonstrating the use of the SDK in some L2, e.g. Arbitrum:

Reimbursable

Reimbursable

This file documents a contract which is not yet deployed to Mainnet.

reimbursementPool

ReimbursementPoolUpdated

refundable

onlyReimbursableAdmin

updateReimbursementPool

bytes32 DELEGATION_TYPEHASH
constructor() public
function delegateBySig(address signatory, address delegatee, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external

signatory

address

delegatee

address

The address to delegate votes to

deadline

uint256

The time at which to expire the signature

v

uint8

The recovery byte of the signature

r

bytes32

Half of the ECDSA signature pair

s

bytes32

Half of the ECDSA signature pair

function delegate(address delegatee) public virtual

delegatee

address

The address to delegate votes to

function beforeTokenTransfer(address from, address to, uint256 amount) internal
function delegate(address delegator, address delegatee) internal virtual
mapping(address => bool) authorizedRequesters
uint256 entry
event RequesterAuthorizationUpdated(address requester, bool isAuthorized)
function requestRelayEntry(contract IRandomBeaconConsumer callbackContract) external

callbackContract

contract IRandomBeaconConsumer

Beacon consumer callback contract - Wallet Registry

function setRequesterAuthorization(address requester, bool isAuthorized) external
$ curl localhost:9601/metrics
# TYPE connected_peers_count gauge
connected_peers_count 108 1623235129569

# TYPE connected_bootstrap_count gauge
connected_bootstrap_count 10 1623235129569

# TYPE eth_connectivity gauge
eth_connectivity 1 1623235129789
$ curl localhost:9601/diagnostics
{
  "client_info" {
   "ethereum_address":"0xDcd4199e22d09248cA2583cBDD2759b2acD22381",
   "network_id":"16Uiu2HAkzYFHsqbwt64ZztWWK1hyeLntRNqWMYFiZjaKu1PZgikN"
  },
  "connected_peers": [
    {"ethereum_address":"0x3712C6fED51CECA83cA953f6FF3458f2339436b4","network_id":"16Uiu2HAkyYtzNoWuF3ULaA7RMfVAxvfQQ9YRvRT3TK4tXmuZtaWi"},
    {"ethereum_address":"0x4bFa10B1538E8E765E995688D8EEc39C717B6797","network_id":"16Uiu2HAm9d4MG4LNrwkFmugD2pX7frm6ZmA4vE3EFAEjk7yaoeLd"},
    {"ethereum_address":"0x650A9eD18Df873cad98C88dcaC8170531cAD2399","network_id":"16Uiu2HAkvjVWogUk2gq6VTNLQdFoSHXYpobJdZyuAYeoWD66e8BD"},
    ...
  ]
}
contract ReimbursementPool reimbursementPool
event ReimbursementPoolUpdated(address newReimbursementPool)
modifier refundable(address receiver)
modifier onlyReimbursableAdmin()
function updateReimbursementPool(contract ReimbursementPool _reimbursementPool) external
Chainlink’s tBTC:USD
Tellor

AWS

c5.large

Azure

F2s v2

Google Cloud

n2-highcpu-2

Self-hosted

2 vCPU / 2 GB RAM / 1 GiB Persistent Storage

Alchemy
Infura
Ankr
Geth
config file
AltBn128
BeaconAuthorization
BeaconDkg
BeaconDkgValidator
BeaconInactivity
BLS
BytesLib
Callback
Governable
Groups
IRandomBeacon
IRandomBeaconConsumer
ModUtils
RandomBeacon
RandomBeaconChaosnet
RandomBeaconGovernance
Reimbursable
ReimbursementPool
Relay
proportional chance
Beta Stakers
chaosnet
GG18
production-ready
CGGMP
FROST
Threshold DAO
Optimistic Minting for tBTC
Sweeping option for minting tBTC
The SDK depends on ethers v5. Proper support for newer ethers versions is 
not guaranteed right now.
// Import SDK entrypoint component.
import { TBTC } from "@keep-network/tbtc-v2.ts"

// Create an instance of ethers signer.
const signer = (...)

// Initialize the SDK for Ethereum only.
const sdk = await TBTC.initializeMainnet(signer)

// Access SDK features.
sdk.deposits.(...)
sdk.redemptions.(...)

// Access tBTC smart contracts directly.
sdk.tbtcContracts.(...)

// Access Bitcoin client directly.
sdk.bitcoinClient.(...)
// Import SDK entrypoint component.
import { TBTC } from "@keep-network/tbtc-v2.ts"

// Create an instance of ethers provider.
const ethProvider = (...)

// Create an instance of ethers signer.
const arbitrumSigner = (...)

// Initialize the SDK for Ethereum only.
const sdk = await TBTC.initializeMainnet(ethProvider, true)

// Initialize it for any L2 (E.g., Arbitrum)
await sdk.initializeCrossChain("Arbitrum", arbitrumSigner);

// Access SDK features.
sdk.deposits.(...)
sdk.redemptions.(...)

// Access tBTC smart contracts directly.
sdk.tbtcContracts.(...)

// Access Bitcoin client directly.
sdk.bitcoinClient.(...)
ethers v5
yarn add @keep-network/tbtc-v2.ts
npm i @keep-network/tbtc-v2.ts
yarn add ethers@legacy-v5
npm i ethers@legacy-v5

Wallet Signing

The primary wallet responsibility is taking care of Bitcoins deposited by tBTC users. Whenever a wallet needs to move some Bitcoins due to a protocol action (e.g. deposit sweep or redemption), it must assemble an appropriate Bitcoin transaction and sign each transaction's input using the tECDSA signing algorithm. During the tECDSA signing, randomly selected 51-of-100 wallet signers must collaborate to produce a proper signature.

Anatomy of a signing attempt

A single tECDSA signing attempt consists of several steps. Each step has a maximum duration expressed in the specific number of host chain (e.g. Ethereum) blocks:

  1. Signers readiness announcement phase which takes exactly 6 blocks

  2. Signature production & advertisement phase which takes up to 30 blocks

  3. Cooldown period that takes exactly 5 blocks

That means a single signing attempt can take up to 41 blocks (~8 minutes on Ethereum, assuming 12 seconds as the average block time). If the signature is not produced during that time, wallet signers give up the signing attempt and retry.

Signing retries

A wallet that is asked to sign the given message (e.g. Bitcoin transaction input) does not limit itself to a single tECDSA signing attempt. Due to the distributed nature of the signing algorithm, a single signing attempt may fail due to, for example, some unexpected network problems affecting inter-signer communication. If the first signing attempt fails, another set of randomly selected 51 wallet signers retry signing during the next attempt. Subsequent signing attempts are scheduled on fixed intervals, based on the block the first attempt started at, and the known maximum duration of a single attempt (41 blocks). For example, if the first attempt started at the block B, subsequent attempts will be scheduled as follows:

  • Attempt 2 -> B + 41

  • Attempt 3 -> B + 82

  • Attempt N -> B + (N-1) * 41

The wallet always executes 5 signing attempts at maximum. Such a count of maximum attempts is an acceptable trade-off between a reasonable signing timeout and a sufficient tolerance for misbehaving wallet signers. Executing 5 signing attempts allows trying 5 different 51-signer subsets. This is enough to produce a signature even for an unlikely scenario of 2 misbehaving wallet signers. In that case, 51 signers must be selected out of the honest 98 signers. That means the probability of selecting a successful subset is:

P = (98 choose 51) / (100 choose 51) = ~0.24

which means 5 selections are required in the worst case.

Combining the 5 signing attempts at maximum with up to 41 blocks per single attempt means the wallet will try to sign the given message for 5*41 = 205 blocks (~40 minutes on Ethereum, assuming 12 seconds as the average block time). If the signature is not produced during that time, wallet signers give up ultimately.

Serial signing

Wallets have the ability to sign multiple messages (e.g. multiple inputs of a single Bitcoin transaction) in a serial manner, one message after another. In such a case, the whole signing process is successful only if all messages in the batch obtained their signatures.

As mentioned in the previous section, the maximum allowed time for signing a single message is 205 blocks (~40 minutes on Ethereum). That means the maximum time to sign a batch of N messages is N*205 blocks. During that time, the wallet cannot perform any other work as signing is computationally heavy. This factor must be taken into account while building wallet actions that leverage signing.

Signing as the foundation of wallet actions

The tBTC protocol defines a finite set of wallet actions:

  • Heartbeat

  • Deposit sweep

  • Redemption

  • Moving funds

  • Moved funds sweep

Each action involves wallet signing under the hood. The time necessary to complete the given action depends on how many signatures must be produced during execution. Knowing the worst-case time necessary to produce a single signature (205 blocks) and the number of signatures that may be required by the given action, we can set the right time boundaries for specific actions. This information is crucial for proper wallet coordination.

Example: Deposit sweep

Let's use the Deposit sweep action to show how signing affects wallet actions. This action is initiated by a coordinator that submits a deposit sweep proposal through the WalletCoordinator chain contract. An expected outcome of that action is a Bitcoin transaction with multiple inputs (swept deposits) and one output, submitted to the Bitcoin chain. That means the action's execution time strongly depends on the number of deposits suggested as part of the given sweep proposal. The WalletCoordinator contract has some governable parameters that impact the Deposit sweep action:

  • depositSweepMaxSize that sets the maximum number of deposits within a single sweep proposal

  • depositSweepProposalValidity which determines the proposal's validity time. This is an exclusive time the wallet obtains to execute the given sweep and no other actions can be taken meanwhile

In other words, the wallet can sweep up to depositSweepMaxSize deposits and has depositSweepProposalValidity time to do so.

Signing is the most important and complex step of the Deposit sweep action and the reference client implementation sets the signing timeout to be depositSweepProposalValidity - 1 hour for all inputs of the sweep transaction. Producing a signature for a single deposit input may take up to 205 blocks (5 attempts underneath). This is ~40 minutes assuming Ethereum's average block time of 12 seconds. Having that in mind, the aforementioned WalletCoordinator governable parameters must be chosen with care as they have a strong impact on signing execution:

  • If depositSweepMaxSize = 5 and depositSweepProposalValidity = 4 hours, the wallet will try to sign 5 inputs in 3 hours which gives ~36 minutes per input. As 5 signing attempts take ~40 minutes, this configuration will allow for 4 attempts per input

  • If depositSweepMaxSize = 10 and depositSweepProposalValidity = 4 hours, the wallet will try to sign 10 inputs in 3 hours which gives ~18 minutes per input. As 5 signing attempts take ~40 minutes, this configuration will allow for 2 attempts per input

  • If depositSweepMaxSize = 20 and depositSweepProposalValidity = 4 hours, the wallet will try to sign 20 inputs in 3 hours which gives ~9 minutes per input. As 5 signing attempts take ~40 minutes, this configuration will allow for 1 attempt per input

Binary Installation

This page will guide you through Binary setup steps.

Choose either Docker installation OR Binary installation.

Download the Binary

See GitHub for the latest release: https://github.com/keep-network/keep-core/releases

Under Assets, copy the path to the compressed binary and use the command below to download the file. Make relevant adjustments to version as necessary:

cd /home/keep
wget https://github.com/keep-network/keep-core/releases/download/XX.X.X-XX/keep-client-mainnet-XX.X.X-XX-linux-amd64.tar.gz

After the download completes, use the command below to display the contents of the folder:

ls -la

Extract the compressed file:

tar xzvf downloaded-file-name-here.tar.gz

You can save some time and prevent misspelling a file name by typing the first few letter of the file name and pressing the Tab key. This will autocomplete the file name. Be sure to verify the file name prior to pressing the enter key.

Configure tBTC v2 Client

To launch the tBTC v2 client, several configuration flags and environmental values need to be set. For simplicity, a bash script can be used rather than typing or pasting all the flags into the console.

The following flags must be set at minimum:

--ethereum.url "wss://mainnet-ETH-enpoint-here"
--storage.dir "/home/keep/storage"
--ethereum.keyFile "/home/keep/config/UTC--Your-Operator-Key-Name"
# to configure your node to use your machine's public IP
--network.announcedAddresses "/ip4/your.ipv4.address.here/tcp/3919"
# to configure your node to use DNS, provide your DNS in the following
# format
# /dns4/bootstrap-1.test.keep.network/tcp/3919

For a complete list of client commands and flags, see CLI Options.

To launch the client, execute the following:

./keep-client start --ethereum.url "wss://mainnet-ETH-enpoint-here" --storage.dir "/home/keep/storage" --ethereum.keyFile "/home/keep/config/UTC--Your-Operator-Key-Name" --network.announcedAddresses "/ip4/your.ipv4.address.here/tcp/3919"

If everything is configured correctly, the client will request the password for the Operator Account. Supply the password and press Enter.

You should see the following shortly:


▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  ▓▓▓▓▓▓    ▓▓▓▓▓▓▓▀    ▐▓▓▓▓▓▓    ▐▓▓▓▓▓   ▓▓▓▓▓▓     ▓▓▓▓▓   ▐▓▓▓▓▓▌   ▐▓▓▓▓▓▓
  ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀      ▐▓▓▓▓▓▓▄▄▄▄         ▓▓▓▓▓▓▄▄▄▄         ▐▓▓▓▓▓▌   ▐▓▓▓▓▓▓
  ▓▓▓▓▓▓▓▓▓▓▓▓▓▀        ▐▓▓▓▓▓▓▓▓▓▓         ▓▓▓▓▓▓▓▓▓▓▌        ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄       ▐▓▓▓▓▓▓▀▀▀▀         ▓▓▓▓▓▓▀▀▀▀         ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
  ▓▓▓▓▓▓   ▀▓▓▓▓▓▓▄     ▐▓▓▓▓▓▓     ▓▓▓▓▓   ▓▓▓▓▓▓     ▓▓▓▓▓   ▐▓▓▓▓▓▌
▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓

Trust math, not hardware.
	
-----------------------------------------------------------------------------------
| Keep Client Node                                                                |
|                                                                                 |
| Version: Version: vX.X.X-XX (4d745f6d0)                                         |
|                                                                                 |
| Operator: 0x_your_operator_address                                              |
|                                                                                 |
| Port: 3919                                                                      |
| IPs : /ip4/111.222.333.444/tcp/3919/ipfs/redacted                               |
|                                                                                 |
| Contracts:                                                                      |
| RandomBeacon   : 0x5499f54b4A1CB4816eefCf78962040461be3D80b                     |
| WalletRegistry : 0x46d52E41C2F300BC82217Ce22b920c34995204eb                     |
| TokenStaking   : 0x01B67b1194C75264d06F808A921228a95C765dd7                     |
-----------------------------------------------------------------------------------

Congratulations, your node is up and running.

Updating tBTC v2 Node

Update procedure for the tBTC v2 client depends on installation method

Docker Installation

The following instructions assume your docker install followed these installation instructions.

To update a tBTC node:

Pull the new image

docker pull keepnetwork/keep-client:latest

Restart the tBTC service

sudo systemctl restart tbtcv2

To free system resources, run

sudo docker container prune
sudo docker image prune

Examine logs to ensure the node started correctly. Find Docker instance identification; it'll be a random combination of words, e.g. stinky_brownie:

sudo docker ps

Use specific identification and substitute accordingly; specify a path and file name for the log file:

sudo docker logs stinky_brownie >& /path/to/output/file

Display the log file

cat /path/to/output/file

Look for the following and take note of the version:


▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  ▓▓▓▓▓▓    ▓▓▓▓▓▓▓▀    ▐▓▓▓▓▓▓    ▐▓▓▓▓▓   ▓▓▓▓▓▓     ▓▓▓▓▓   ▐▓▓▓▓▓▌   ▐▓▓▓▓▓▓
  ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀      ▐▓▓▓▓▓▓▄▄▄▄         ▓▓▓▓▓▓▄▄▄▄         ▐▓▓▓▓▓▌   ▐▓▓▓▓▓▓
  ▓▓▓▓▓▓▓▓▓▓▓▓▓▀        ▐▓▓▓▓▓▓▓▓▓▓         ▓▓▓▓▓▓▓▓▓▓▌        ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄       ▐▓▓▓▓▓▓▀▀▀▀         ▓▓▓▓▓▓▀▀▀▀         ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
  ▓▓▓▓▓▓   ▀▓▓▓▓▓▓▄     ▐▓▓▓▓▓▓     ▓▓▓▓▓   ▓▓▓▓▓▓     ▓▓▓▓▓   ▐▓▓▓▓▓▌
▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓

Trust math, not hardware.
	
-----------------------------------------------------------------------------------
| Keep Client Node                                                                |
|                                                                                 |
| Version: vX.X.X-XX (4d745f6d0)                                         |
|                                                                                 |
| Operator: 0x_your_operator_address                                              |
|                                                                                 |
| Port: 3919                                                                      |
| IPs : /ip4/111.222.333.444/tcp/3919/ipfs/redacted                               |
|                                                                                 |
| Contracts:                                                                      |
| RandomBeacon   : 0x5499f54b4A1CB4816eefCf78962040461be3D80b                     |
| WalletRegistry : 0x46d52E41C2F300BC82217Ce22b920c34995204eb                     |
| TokenStaking   : 0x01B67b1194C75264d06F808A921228a95C765dd7                     |
-----------------------------------------------------------------------------------

Alternatively, verify your client updated by visiting your status page:

http://111.222.333.444:9601/metrics

Compare the displayed version number to the version number you are expecting, i.e.:

client_info{version="vX.X.X-XX"}

This install method requires downloading the latest version of the binary. Follow the installation steps provided here.

BeaconInactivity

BeaconInactivity

This file documents a contract which is not yet deployed to Mainnet.

Claim

struct Claim {
  uint64 groupId;
  uint256[] inactiveMembersIndices;
  bytes signatures;
  uint256[] signingMembersIndices;
}

groupThreshold

uint256 groupThreshold

The minimum number of group members needed to interact according to the protocol to produce a valid inactivity claim.

signatureByteSize

uint256 signatureByteSize

Size in bytes of a single signature produced by member supporting the inactivity claim.

verifyClaim

function verifyClaim(contract SortitionPool sortitionPool, struct BeaconInactivity.Claim claim, bytes groupPubKey, uint256 nonce, uint32[] groupMembers) external view returns (uint32[] inactiveMembers)

Verifies the inactivity claim according to the rules defined in Claim struct documentation. Reverts if verification fails.

Group members hash is validated upstream in RandomBeacon.notifyOperatorInactivity()

Parameters

Name
Type
Description

sortitionPool

contract SortitionPool

Sortition pool reference

claim

struct BeaconInactivity.Claim

Inactivity claim

groupPubKey

bytes

Public key of the group raising the claim

nonce

uint256

Current nonce for group used in the claim

groupMembers

uint32[]

Identifiers of group members

Return Values

Name
Type
Description

inactiveMembers

uint32[]

Identifiers of members who are inactive

validateMembersIndices

function validateMembersIndices(uint256[] indices, uint256 groupSize) internal pure

Validates members indices array. Array is considered valid if its size and each single index are in [1, groupSize] range, indexes are unique, and sorted in an ascending order. Reverts if validation fails.

Parameters

Name
Type
Description

indices

uint256[]

Array to validate

groupSize

uint256

Group size used as reference

CLI Options

Review available CLI Options below.

$ keep-client start --help
Starts the Keep Client in the foreground

Usage:
  keep-client start [flags]

Flags:
      --ethereum.url string                                 WS connection URL for Ethereum client.
      --ethereum.keyFile string                             The local filesystem path to Keep operator account keyfile.
      --ethereum.miningCheckInterval duration               The time interval in seconds in which transaction mining status is checked. If the transaction is not mined within this time, the gas price is increased and transaction is resubmitted. (default 1m0s)
      --ethereum.maxGasFeeCap wei                           The maximum gas fee the client is willing to pay for the transaction to be mined. If reached, no resubmission attempts are performed. (default 500 gwei)
      --ethereum.requestPerSecondLimit int                  Request per second limit for all types of Ethereum client requests. (default 150)
      --ethereum.concurrencyLimit int                       The maximum number of concurrent requests which can be executed against Ethereum client. (default 30)
      --ethereum.balanceAlertThreshold wei                  The minimum balance of operator account below which client starts reporting errors in logs. (default 500000000 gwei)
      --bitcoin.electrum.url scheme://hostname:port         URL to the Electrum server in format: scheme://hostname:port.
      --bitcoin.electrum.connectTimeout duration            Timeout for a single attempt of Electrum connection establishment. (default 10s)
      --bitcoin.electrum.connectRetryTimeout duration       Timeout for Electrum connection establishment retries. (default 1m0s)
      --bitcoin.electrum.requestTimeout duration            Timeout for a single attempt of Electrum protocol request. (default 30s)
      --bitcoin.electrum.requestRetryTimeout duration       Timeout for Electrum protocol request retries. (default 2m0s)
      --bitcoin.electrum.keepAliveInterval duration         Interval for connection keep alive requests. (default 5m0s)
      --network.bootstrap                                   Run the client in bootstrap mode.
      --network.peers strings                               Addresses of the network bootstrap nodes.
  -p, --network.port int                                    Keep client listening port. (default 3919)
      --network.announcedAddresses strings                  Overwrites the default Keep client address announced in the network. Should be used for NAT or when more advanced firewall rules are applied.
      --network.disseminationTime int                       Specifies courtesy message dissemination time in seconds for topics the node is not subscribed to. Should be used only on selected bootstrap nodes. (0 = none)
      --storage.dir string                                  Location to store the Keep client key shares and other sensitive data.
      --clientInfo.port int                                 Client Info HTTP server listening port. (default 9601)
      --clientInfo.networkMetricsTick duration              Client Info network metrics check tick in seconds. (default 1m0s)
      --clientInfo.ethereumMetricsTick duration             Client info Ethereum metrics check tick in seconds. (default 10m0s)
      --tbtc.preParamsPoolSize int                          tECDSA pre-parameters pool size. (default 1000)
      --tbtc.preParamsGenerationTimeout duration            tECDSA pre-parameters generation timeout. (default 2m0s)
      --tbtc.preParamsGenerationDelay duration              tECDSA pre-parameters generation delay. (default 10s)
      --tbtc.preParamsGenerationConcurrency int             tECDSA pre-parameters generation concurrency. (default 1)
      --tbtc.keyGenerationConcurrency int                   tECDSA key generation concurrency. (default number of cores)
      --developer.bridgeAddress string                      Address of the Bridge smart contract
      --developer.lightRelayAddress string                  Address of the LightRelay smart contract
      --developer.lightRelayMaintainerProxyAddress string   Address of the LightRelayMaintainerProxy smart contract
      --developer.randomBeaconAddress string                Address of the RandomBeacon smart contract
      --developer.tokenStakingAddress string                Address of the TokenStaking smart contract
      --developer.walletRegistryAddress string              Address of the WalletRegistry smart contract
      --developer.walletCoordinatorAddress string           Address of the WalletCoordinator smart contract

Global Flags:
  -c, --config string   Path to the configuration file. Supported formats: TOML, YAML, JSON.
      --developer       Developer network
      --testnet         Testnet network

Environment variables:
    KEEP_ETHEREUM_PASSWORD    Password for Keep operator account keyfile decryption.
    LOG_LEVEL                 Space-delimited set of log level directives; set to "help" for help.

Wallet Generation

Wallets are created periodically based on governance. changes the time between wallets and reads it. The time between new wallets is held in walletCreationPeriod in number of seconds, so the current value of 1209600 represents 14 days. In order for the wallet to move funds, it produces signatures using a , requiring 51-of-100 Signers to cooperate.

The 100 signers on each wallet are chosen with our , and the randomness is provided by the .

The probability that a is chosen to be a Signer is equal to their percentage of the total TBTC Stake. Each Signer is chosen independently. The same Staker can be a signer on the same wallet multiple times. The same Staker can be a Signer on multiple wallets simultaneously.

Examples

For simplicity, say there are only three Stakers: Alice, Bob, and Carol. Alice has 250M T, Bob has 400M T, and Carol has 350M T staked, so they own 25%, 40% and 35% of the stake respectively. That means, Alice has a 25% chance to be a Signer, Bob has a 40% chance, and Carol has a 35% chance. Each Signer is selected independently.

The Sortition Pool is more complex (because of heavy gas optimization), but reasoning about it could look like this: For each Signer, we're going to generate a random number between 1 and 100. Alice is the Signer if the number is in [1, 25]. Bob is the Signer if the number is in [26, 65]. Carol is the Signer if the number is in [66, 100]. For example, if our first is 33, our first Signer would be Bob. We can generate 100 random numbers: (75, 51, 13, 48, 36, 62, 46, 65, 97, 67...), and then use that to determine Signers: (Carol, Bob, Alice, Bob, Bob, Bob, Bob, Bob, Carol, Carol...). This example illustrates a few properties mentioned earlier:

  • Each Signer is selected independently. Whether or not Carol is the first signer has no influence on Carol being the second Signer.

  • The chance that you become a particular Signer is equal to your share of the Stake.

Statistics

If Carol has a 35% chance of being a particular Signer, what are chances that Carol has at least 51 of the 100 seats (and could control the wallet by herself)?

The number of Signers that Carol controls on a wallet is modeled by the . From wikipedia:

A Binomial distribution with parameters n and p is the of the number of successes in a sequence of n , each asking a , and each with its own -valued : success (with probability p) or failure (with probability 1−p).

This is exactly our situation! From Carol's perspective, there will be n=100 independent experiments, each asking a yes-no question (does this Signer belong to Carol), each with a 35% probability.

That means we can use a to answer our question:

It's the last figure: "Cumulative probability: P(X>51)" that's relevant. That says there is a ~0.074% chance that Carol would have a controlling Share of any particular wallet.

The next important question is "The probability that Carol controls a Wallet is low, but it only needs to happen once for things to be bad. What is the probability that she controls any wallet in the next 2 years?"

A wallet is generated every 14 days, so over the next 2 years, the system would generate ~52 wallets. Each wallet has a 1 - 0.00074 = .99926 or 99.926% chance of not being controlled by Carol. That means we can exponentiate:

Carol would have a ~3.8% chance of getting control of a wallet in 2 years. That's with her owning 35% of the total Stake! Alice has a much lower chance. She has a 0.0002131% chance of controlling a wallet, which means that over the course of 52 wallets, she has a 0.02131% chance of controlling any wallet in 2 years.

As of writing, the *largest* Staker controls .

Sybil Resistance

The only thing that a Staker accomplishes by splitting up their Stake into multiple identities is making the system appear to be more diverse.

For example, say that Alice split up her Stake equally into 5 accounts: Alice1, Alice2, Alice3, Alice4, and Alice5. Now, rather than Alice having a 25% chance to be a Signer, each account has a 5% chance which collectively add up to 25%.

To use the example from earlier: Alice has 250M T, Bob has 400M T, and Carol has 350M T staked. Alice has her T split into 50M for each identity. Alice1 would own 50M/1000M = 5%. Keeping with the simplification of assigning numbers from 1 to 100 to identities, Alice1 would get [1, 5], Alice2 gets [6, 10], Alice3 gets [11, 15], Alice4 gets [16, 20], Alice5 gets [21, 25], Bob gets [26, 65], Carol gets [66, 100]. If we use the same random numbers as before, (75, 51, 13, 48, 36, 62, 46, 65, 97, 67...), then we make the assignments as (Carol, Bob, Alice3, Bob, Bob, Bob, Bob, Bob, Carol, Carol...). Nothing has effectively changed!

Threshold DAO Rules

This is a space to consolidate all the DAO rules, guidelines and all the efforts we are leading to improve our ways of work

DAO Rules

Inaugural Rules for the DAO

DAO rules were created with the inception of the DAO, and are mostly reflected in our forum, we refer as early rules to TIP-002, TIP-007, TIP-013, TIP-020, TIP-026 and TIP-032.

TIP-002:

TIP-002, outlined by Matt Luongo, describes the creation of the DAO, the different bodies and the general ways we interact with each other.

TIP-007:

TIP-007, outlined by Arj, describes the requirement of a minimum stake size for the Threshold Network stakers, at the moment of the network genesis.

TIP-013:

TIP-013, outlined by Will, describes the creation of the DAO Guilds, and its inner workings.

TIP-020:

TIP-020, outlined by Arj, describes the incentives for Threshold Network stakers at the interim era.

TIP-026:

TIP-026, outlined by Will, outlines our governance processes, and creates a template to be followed when creating TIPs.

TIP-032:

TIP-032, outlined by Arj, supersedes TIP-020 and establishes the reward allocation for stakers between the DAO products.

Rule (TIP)
Description

Rules for Guilds and Proposals Management

In 2022, the DAO lead an effort to improve our governance processes, leading to the creation and vote of several DAO rules. This endeavour was lead by the , a group of 3 people volunteered from each of the guilds + the DAO PM. The outcome of this workgroup was the creation of TIP-031 and TIP-034.

TIP-031:

TIP-031, outlined by the rules committee, establishes the rules for the DAO Guilds elections and management.

TIP-034:

TIP-034, outlined by the rules committee, extends TIP-026 by providing a more defined view to the way proposals are created for the DAO, outlining best practices and guidelines.

GP-021:

GP-021, outlined by ZeroInFo, details the expansion of the Integrations Guild Committee, increasing 2 seats.

TIP-049:

TIP-049, outlined by John Packel, describes the restructuring of the Marketing Guild Committee.

Rule (TIP)
Description
Snapshot link

Additional Rules for Elections

TC-003:

TC-003, outlined by Luna5, outlines the alignment of the Council elections with the Guilds on the month of March, and removes the staggered elections for the Council.

GovernorParameters

GovernorParameters

Abstract contract to handle governance parameters

Based on GovernorVotesQuorumFraction, but without being opinionated on what's the source of voting power, and extended to handle proposal thresholds too. See OpenZeppelin's GovernorVotesQuorumFraction, GovernorVotes and GovernorSettings for reference.

FRACTION_DENOMINATOR

AVERAGE_BLOCK_TIME_IN_SECONDS

quorumNumerator

proposalThresholdNumerator

QuorumNumeratorUpdated

ProposalThresholdNumeratorUpdated

VotingDelaySet

VotingPeriodSet

constructor

updateQuorumNumerator

updateProposalThresholdNumerator

setVotingDelay

Update the voting delay. This operation can only be performed through a governance proposal. Emits a VotingDelaySet event.

setVotingPeriod

Update the voting period. This operation can only be performed through a governance proposal. Emits a VotingPeriodSet event.

quorum

Compute the required amount of voting power to reach quorum

Parameters

Name
Type
Description

proposalThreshold

Compute the required amount of voting power to create a proposal at the last block height

This function is implemented to comply with Governor API but we we will actually use proposalThreshold(uint256 blockNumber), as in our DAOs the threshold amount changes according to supply.

proposalThreshold

Compute the required amount of voting power to create a proposal

Parameters

Name
Type
Description

votingDelay

module:user-config

Delay, in number of block, between the proposal is created and the vote starts. This can be increassed to leave time for users to buy voting power, of delegate it, before the voting of a proposal starts.

votingPeriod

module:user-config

Delay, in number of blocks, between the vote start and vote ends.

NOTE: The {votingDelay} can delay the start of the vote. This must be considered when setting the voting duration compared to the voting delay.

_updateQuorumNumerator

_updateProposalThresholdNumerator

_setVotingDelay

_setVotingPeriod

_getPastTotalSupply

Compute the past total voting power at a particular block

Parameters

Name
Type
Description
uint256 FRACTION_DENOMINATOR
uint64 AVERAGE_BLOCK_TIME_IN_SECONDS
uint256 quorumNumerator
uint256 proposalThresholdNumerator
event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator)
event ProposalThresholdNumeratorUpdated(uint256 oldThresholdNumerator, uint256 newThresholdNumerator)
event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay)
event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod)
constructor(uint256 quorumNumeratorValue, uint256 proposalNumeratorValue, uint256 initialVotingDelay, uint256 initialVotingPeriod) internal
function updateQuorumNumerator(uint256 newQuorumNumerator) external virtual
function updateProposalThresholdNumerator(uint256 newNumerator) external virtual
function setVotingDelay(uint256 newVotingDelay) external virtual
function setVotingPeriod(uint256 newVotingPeriod) external virtual
function quorum(uint256 blockNumber) public view virtual returns (uint256)

blockNumber

uint256

The block number to get the quorum at

function proposalThreshold() public view virtual returns (uint256)
function proposalThreshold(uint256 blockNumber) public view returns (uint256)

blockNumber

uint256

The block number to get the proposal threshold at

function votingDelay() public view virtual returns (uint256)
function votingPeriod() public view virtual returns (uint256)
function _updateQuorumNumerator(uint256 newQuorumNumerator) internal virtual
function _updateProposalThresholdNumerator(uint256 proposalNumerator) internal virtual
function _setVotingDelay(uint256 newVotingDelay) internal virtual
function _setVotingPeriod(uint256 newVotingPeriod) internal virtual
function _getPastTotalSupply(uint256 blockNumber) internal view virtual returns (uint256)

blockNumber

uint256

The block number to get the vote power at

TIP-002

Threshold Network DAO Proposal — v2

TIP-007

Minimum Stake Size of 40,000 T at Network Genesis

TIP-013

Bootstrap Threshold DAO Guilds

TIP-020

Interim Era Incentive Schemes: (1) One-off Migration+Stake Bonus & (2) Ongoing Stable Yield

TIP-026

Threshold Improvement Proposals (TIPs): Flows and Template

TIP-032

Council decision: Reward allocation between tBTCv2 & PRE (overall target APY = 15%)

TIP-031

Rules for Guild Committee Elections and Management

TIP-031 Snapshot

TIP-034

Rules for New Proposals

TIP-034 Snapshot

GP-021

Increase the Number of Seats on the Integrations Guild from 5 to 7

GP-021 Snapshot

TIP-049

Restructure the Marketing Guild Committee

TIP-049 Snapshot

Threshold Network DAO Proposal — v2
Minimum Stake Size of 40,000 T at Network Genesis
Bootstrap Threshold DAO Guilds
Interim Era Incentive Schemes: (1) One-off Migration+Stake Bonus & (2) Ongoing Stable Yield
Threshold Improvement Proposals (TIPs): Flows and Template
Council decision: Reward allocation between tBTCv2 & PRE (overall target APY = 15%)
Rules Subcommittee
Rules for Guild Committee Elections and Management
Rules for New Proposals
Increase the Number of Seats on the Integrations Guild from 5 to 7
Restructure the Marketing Guild Committee
Council Elections Retrospective and Lessons Learned for the Third Epoch
0.9992652=.96220.99926^{52} = .96220.9992652=.9622
updateWalletParameters
walletParameters
Threshold Elliptic Curve Digital Signature Algorithm
Sortition Pool
Random Beacon
Staker
random number
Binomial Distribution
discrete probability distribution
independent
experiments
yes–no question
Boolean
outcome
Binomial Probability Calculator
~8.83% of the Stake

Borrowing

Key aspects and parameters of borrowing thUSD

Why would I use Threshold USD for borrowing?

Threshold USD protocol offers low interest loans and is more capital efficient than other borrowing systems (i.e. less collateral is needed for the same loan). Instead of selling tBTC to have liquid funds, you can use the protocol to lock up your tBTC, borrow against the collateral to withdraw thUSD, and then repay your loan at a future date.

For example: Borrowers speculating on future tBTC price increases can use the protocol to leverage their tBTC positions up to 11 times, increasing their exposure to price changes. This is possible because thUSD can be borrowed against tBTC, sold on the open market to purchase more tBTC — rinse and repeat.*

*Note: This is NOT a recommendation for how to use Threshold USD. Leverage can be very risky as you can lose all your funds and should be used only by those with experience and for speculative amounts.

What do you mean by collateral?

Collateral is any asset which a borrower must provide to take out a loan, acting as a security for the debt.

Is tBTC the only collateral accepted by Threshold USD?

Currently, Threshold USD supports tBTC and ETH as collateral but additional collaterals could be added as voted by governance, as the code allows for it. The reasoning behind having these two collaterals is that they represent the two most decentralized assets in the Ethereum Ecosystem.

How can I borrow with Threshold USD?

To borrow you must open a Vault and deposit a certain amount of collateral (tBTC) to it. Then you can draw thUSD up to a collateral ratio of 110%. A minimum debt of 2,000 thUSD is required. This minimum debt threshold prevents an attacker from creating a large number of Vaults that are then unprofitable to liquidate.

What is a Vaults?

A Vault is where you take out and maintain your loan. Each Vault is linked to an Ethereum address and each address can have just one Vault. If you are familiar with Troves or CDPs from other platforms, Vault are similar in concept.

Vaults maintain two balances: one is an asset (tBTC) acting as collateral and the other is a debt denominated in thUSD. You can change the amount of each by adding collateral or repaying debt. As you make these balance changes, your Vault’s collateral ratio changes accordingly.

You can close your Vault at any time by fully paying off your debt.

Do I have to pay fees as a borrower?

For the base situation, see the section “Does Threshold USD charge any fees?”

Every time you draw thUSD from your Vault, a one-off borrowing fee is charged on the drawn amount and added to your debt. Please note that the borrowing fee is variable (and determined algorithmically) and has a minimum value of 0.5% under normal operation. The fee is 0% during Recovery Mode.

A 200 thUSD Liquidation Reserve charge will be applied as well, but returned to you upon repayment of debt.

How is the borrowing fee calculated?

When a Loan is opened, the borrowing fee is added to the debt of the Vault and is given by a baseRate . The fee rate is confined to a range between 0.5% and 5% and is multiplied by the amount of liquidity drawn by the borrower.

For example: The borrowing fee stands at 0.5% and the borrower wants to receive 4,000 thUSD to their wallet. Being charged a borrowing fee of 20.00 thUSD, the borrower will incur a debt of 4,220 thUSD after the Liquidation Reserve and issuance fee are added.

When do I need to pay my loan back?

Loans issued by the protocol do not have a repayment schedule. You can leave your Vault open and repay your debt any time, as long as you maintain a collateral ratio of at least 110%.

What is the collateral ratio?

This is the ratio between the Dollar value of the collateral in your Vault and its debt in thUSD. The collateral ratio of your Vault will fluctuate over time as the price of tBTC changes. You can influence the ratio by adjusting your Vault’s collateral and/or debt — i.e. adding more tBTC collateral or paying off some of your debt.

For example: Let’s say the current price of tBTC is $30,000 and you decide to deposit 1 tBTC. If you borrow 10,000 thUSD, then the collateral ratio for your Vault would be 200%.

(1tBTC * $30,000/​10,000 thUSD)*100 = 300%

​If you instead took out 15,000 thUSD that would put your ratio at 200%.

What is the minimum collateral ratio (MCR) and the "recommended" collateral ratio?

The minimum collateral ratio (or MCR for short) is the lowest ratio of debt to collateral that will not trigger a liquidation under normal operations (aka Normal Mode). This is a protocol parameter that is set to 110%. So if your Vault has a debt of 10,000 thUSD, you would need at least $11,000 worth of tBTC posted as collateral to avoid being liquidated.

To avoid liquidation during Recovery Mode, it is recommended to keep the ratio comfortably above 150% (e.g. 200% or better 250%).

What happens if my Vault is liquidated?

You lose your collateral as your debt is paid off through liquidation, i.e. you will no longer be able to retrieve your collateral by repaying your debt. A liquidation thus results in a net loss of 9.09% (= 100% * 10 / 110) of your collateral’s Dollar value.

What is the Liquidation Reserve?

When you open a Vault and draw a loan, 200 thUSD is set aside as a way to compensate gas costs for the transaction sender in the event your Vault gets liquidated. The Liquidation Reserve is fully refundable if your Vault is not liquidated, and is given back to you when you close your Vault by repaying your debt. The Liquidation Reserve counts as debt and is taken into account for the calculation of a Vault's collateral ratio, slightly increasing the actual collateral requirements.

What happens if my Vault is redeemed against?

When thUSD is redeemed, the tBTC provided to the redeemer is allocated from the Vault(s) with the lowest collateral ratio (even if it is above 110%). If at the time of redemption you have the Vault with the lowest ratio, you will give up some of your collateral, but your debt will be reduced accordingly.

The USD value by which your tBTC collateral is reduced corresponds to the nominal thUSD amount by which your Vault’s debt is decreased. You can think of redemptions as if somebody else is repaying your debt and retrieving an equivalent amount of your collateral. As a positive side effect, redemptions improve the collateral ratio of the affected Vaults, making them less risky.

Redemptions that do not reduce your debt to 0 are called partial redemptions, while redemptions that fully pay off a Vault’s debt are called full redemptions. In such a case, your Vault is closed, and you can claim your collateral surplus and the Liquidation Reserve at any time.

Let’s say you own a Vault with 2 tBTC collateralized and a debt of 32,000 thUSD. The current price of tBTC is $20,000. This puts your collateral ratio (CR) at 125% (= 100% * (2 * 20,000) / 32,000). Let’s imagine this is the lowest CR in the Threshold USD system and look at two examples of a partial redemption and a full redemption:

Example of a partial redemption

Somebody redeems 12,000 thUSD for 0.6 tBTC and thus repays 12,000 thUSD of your debt, reducing it from 32,000 thUSD to 20,000 thUSD. In return, 0.6 tBTC, worth $12,000, is transferred from your Vault to the redeemer. Your collateral goes down from 2 to 1.4 tBTC, while your collateral ratio goes up from 125% to 140% (= 100% * (1.4 * 20,000) / 20,000).

Example of a full redemption

Somebody redeems 60,000 thUSD for 3 tBTC. Given that the redeemed amount is larger than your debt minus 200 thUSD (set aside as a Liquidation Reserve), your debt of 32,000 thUSD is entirely cleared and your collateral gets reduced by $30,000 of tBTC, leaving you with a collateral of 0.5 tBTC (= 2 - 30,000 / 20,000).

How can you offer a collateral ratio as low as 110%?

By making liquidation instantaneous and more efficient, Threshold USD needs less collateral to provide the same security level as similar protocols that rely on lengthy auction mechanisms to sell off collateral in liquidations.

How can I take advantage of leverage?

You can sell the borrowed thUSD on the market for tBTC and use the latter to top up the collateral of your Vault. That allows you to draw and sell more thUSD, and by repeating the process you can reach the desired leverage ratio.

Note: This is NOT a recommendation for how to use Threshold USD. Leverage can be very risky and should be used only by those with experience and still will most probably end up with a Liquidation, so this should be avoided.

Why did the collateral and debt of my Vault increase without my intervention?

If Vaults are liquidated and the Stability Pool is empty (or gets emptied due to the liquidation), every borrower will receive a portion of the liquidated collateral and debt as part of a redistribution process.

tBTC Beta Stakers Program

About tBTC Beta Stakers Program

As part of bootstrapping tBTC utility, Threshold Network allow-lists authorized stakers to participate in tBTC minting and redemptions to ensure system continuity during progressive decentralization. These allow-listed stakers are called Beta Stakers. The goal with the Beta Staker program is to ensure key operations run properly without interruptions during early growth.

Note that this is a form of 'progressive decentralization', but with a clear, simple and already deployed pathway for the decentralization depth to increase – i.e. by adding more independent stakers to the allow list.

As of the latest contract query, there are now 35 Beta Stakers, an increase from 20 as previously reported in November 2023. A few notable professional staking providers can be found requesting DAO approval here.

Visit contract page on Etherscan to see the current number of Beta Stakers.

Beta Staker nodes form a permissioned set that is responsible for creating tBTC wallets that custody BTC. Running a tBTC Beta Staker node is a significant commitment with specific requirements. This document aims to highlight such requirements, technical recommendations, and possible costs of operating a Beta Staker tBTC node.

To stay informed about current node activity and overall network health please visit tbtcscan.com/operators or check status.threshold.network. These resources provide up-to-date statistics on node counts, stake distribution and operational metrics.

General requirements

Running a Beta Staker node is a long-term commitment. Once the given node is added to the Beta Stakers set, it must remain operational until the end of the Beta Staker Program (~12 months).

Operators of Beta Staker nodes are expected to be extremely responsive, especially regarding upgrades requested by software contributors. Ideally, they should be able to upgrade their nodes within 24 hours of notification.

Operators of Beta Staker nodes must be technically capable. They are responsible for ensuring high availability (more than 96% uptime) and security of the node.

Technical requirements

A Beta Staker node performs more computationally expensive operations (DKG, threshold signing, etc) compared to a standard node. To ensure a high level of service, a Beta Staker node requires a machine with:

  • 4 CPUs

  • 4 GB of RAM

  • 1 GB of persistent disk space

  • 80 Mbps of network bandwidth

  • Linux OS

In addition to the above:

  • The underlying machine’s operating system and tools must be up to date, especially regarding recent security patches.

  • Backups of the persistent disk must be performed on an ongoing basis. This is crucial to ensure the safety of mission-critical data (e.g. key material). The usage of an additional encryption layer for backups is highly recommended.

  • The network layer must provide a unique public IPv4 address allowing the node to be reached from the external network. A proper firewall configuration must be in place and ensure a safe perimeter allowing inbound traffic on two ports (communication and diagnostics). The internet connection must be stable and failover scenarios must be taken into account.

  • A Beta Staker node requires access to an Ethereum node. It is highly recommended to set up a private Ethereum stack (e.g. Geth + Prysm) and use public providers as failover (e.g. Alchemy, Infura, etc). Such a setup is crucial for network diversity and decentralization as it helps avoid the single-point-of-failure risk associated with public providers. Beta Staker operators should use their own judgement for selecting stack components. Specific technical requirements for a private Ethereum stack depend on the software used. For example, a stack based on Geth + Prysm requires 4 CPU / 16 GB RAM / 2 TB SSD.

  • A Beta Staker node requires an Electrum protocol server to interact with Bitcoin. It is highly recommended to set up a private stack consisting of an Electrum server (e.g. Fulcrum, ElectrumX, Electrs, etc) paired with a Bitcoin node (e.g. Bitcoin Core) and use public servers as failover. The reasons are exactly the same as for a private Ethereum stack. Beta Staker operators should use their own judgement for selecting stack components. Specific technical requirements for a private Bitcoin stack depend on the software used. For example, a stack based on Fulcrum + Bitcoin Core requires 8 CPU / 16 GB RAM / 2 TB SSD.

  • It is highly recommended to set up a private monitoring stack to ensure observability and alerting. The monitoring system should supervise the node itself as well as Ethereum and Bitcoin stacks. Beta Staker operators should use their own judgement for selecting stack components. Specific technical requirements for a monitoring stack depend on the software used. For example, a stack based on Grafana + Prometheus requires 2 CPU / 4 GB RAM / 20 GB HDD.

Cost estimation

The actual monthly costs of running a Beta Staker node depends on the hosting model (VPS vs self-hosting) and infrastructure configuration. The following estimation aims to provide a ballpark figure. It also assumes that all aforementioned technical requirements and recommendations are taken into account.

Key system assumptions

  • Beta Staker node requires 4 CPUs / 4 GB RAM / 1 GB HDD

  • Ethereum stack requires 4 CPUs / 16 GB RAM / 2 TB SSD

  • Bitcoin stack requires 8 CPUs / 16 GB RAM / 2 TB SSD

  • Monitoring stack requires 2 CPUs / 4 GB RAM / 20 GB HDD

  • External storage for backups (node and auxiliary stacks) is 1 TB HDD

  • Network layer ensures 100 Mbps of bandwidth and includes all necessary equipment (firewall, public IPs, etc)

The above assumptions can be used to divide costs into 4 categories:

  1. Computing costs: This category includes the costs of running 18 CPUs and 40 GB RAM in total. There are a number of possible configurations to provide such computing capacity. Using the VPS-based model as an example, the cost of 3x 8CPU/16GB VMs on leading cloud service providers starts from ~$400/month.

  2. Storage costs: This category includes the costs of using 4 TB SSD and ~1 TB HDD of storage in total. For the VPS-based model, part of this cost is often included in the cost of VMs, but some providers charge for the storage separately with rates like ~$0.08/GB (e.g., Amazon EBS). In that case, it is safe to assume that storage costs start from ~$200/month.

  3. Network costs: This category includes the cost of 100 Mbps of bandwidth and all auxiliary equipment and features like a firewall or public IPs. For the VPS-based model, such bandwidth is often provided out of the box with the VMs, but a firewall and public IPs may be charged separately. Moreover, some providers may apply an additional charge on egress traffic. It is safe to assume that network costs start from ~$50/month.

  4. Other costs: This category includes all maintenance and other costs depending on the hosting model and infrastructure configuration. For the VPS-based model, such costs can be OS licenses, automatic backup, additional management tools, and so on. For the self-hosting model, these can be costs of internet connection, power failover (e.g., UPS), auxiliary network equipment, and similar. This category of costs must be factored into the total monthly cost.

Bottom Line

In summary, the cost for running a Beta Staker node using the VPS-based model starts at approximately $650/month. Assuming a safety margin for other costs, a rough cost estimate is $800/month for running a setup in accordance with the technical requirements and recommendations.

Estimating the general cost for the self-hosting model is non-trivial due to the variety of possible configurations. The total monthly cost can be similar to the VPS-based model but may also incur additional hidden costs such as hardware maintenance

Disclaimer

Above assumptions and figures should be used cautiously as it is only intended to provide a general sense of the infrastructure costs. Each prospective Beta Staker node operator should conduct their own estimates based on their individual circumstances.

Last but not least, the presented estimate refers only to the infrastructure-related costs of operating a tBTC node. This estimate DOES NOT include human operational costs. Each Beta Staker operator must include this overhead when making their own estimates.

tBTC intro video

Threshold Multisigs

Compilation of current multisigs for the Threshold Council and Guilds

Council Multisigs

Chain
Public Key
Safe Link

Ethereum

0x9F6e831c8F8939DC0C830C6e492e7cEf4f9C2F5f

Eth: Foundation

0xf642Bd6A9F76294d86E99c2071cFE2Aa3B61fBDa

Arbitrum

0x9F6e831c8F8939DC0C830C6e492e7cEf4f9C2F5f

Optimism

0x7fB50BBabeDEE52b8760Ba15c0c199aF33Fc2EfA

Polygon

0x9F6e831c8F8939DC0C830C6e492e7cEf4f9C2F5f

Base

0x518385dd31289F1000fE6382b0C65df4d1Cd3bfC

Solana

814TqVmhQGKB3srSLRuMcH6m8qWFHRSbNpRxC5Xnador

Starknet

0x0314A42C20b0364C6df5Af35E0914dd65F24a61F3563a8bc2A9D664938f37c4F

N/A (Argent's Multisig)

The Threshold Council multisigs have a signature policy threshold set to 6 of 9.

Treasury Guild Multisigs

Chain
Public Key
Safe Link

Ethereum

0x71E47a4429d35827e0312AA13162197C23287546

Arbitrum

0x6c2bd894bD166DCF053045a4883Db49EAE3FC01C

Optimism

0x126940ea97b328E57E9e2285D546a677b2f38300

Polygon

0xc3Bf49eBA094AF346830dF4dbB42a07dE378EeB6

Base

0x3b8f79458b6d303B47D56734B00CC6941443bd95

Solana

4bZiv1J33tJHiWSHPU96wABAZbPnbJ9M2zCmH9Hs12SW

The Treasury Guild multisigs have a signature policy threshold set to 5 of 9.

Integrations Guild Multisigs

Chain
Public Key
Safe Link

Ethereum

eth:0x2ff7aB212cD6FEaE21bAc5300465E149FB6b85a9

Polygon

matic:0x5bD70E385414C8dCC25305AeB7E542d8FC70e667

The Integrations Guild multisigs have a signature policy threshold set to 4 of 7.

Marketing Guild Multisigs

Chain
Public Key
Safe Link

Ethereum

eth:0xd55c4261145EA1752662faA0485AfBC8C431b0CA

Polygon

0xe663AD6295d47ce33F9b65950761Ecba6654E5E8

The Marketing Guild multisigs have a signature policy threshold set to 5 of 8.

https://app.safe.global/home?safe=eth:0x9F6e831c8F8939DC0C830C6e492e7cEf4f9C2F5f
https://app.safe.global/home?safe=eth:0xf642Bd6A9F76294d86E99c2071cFE2Aa3B61fBDa
https://app.safe.global/home?safe=arb1:0x9F6e831c8F8939DC0C830C6e492e7cEf4f9C2F5f
https://app.safe.global/home?safe=oeth:0x7fB50BBabeDEE52b8760Ba15c0c199aF33Fc2EfA
https://app.safe.global/home?safe=matic:0x9F6e831c8F8939DC0C830C6e492e7cEf4f9C2F5f
https://app.safe.global/home?safe=base:0x518385dd31289F1000fE6382b0C65df4d1Cd3bfC
https://v3.squads.so/owners/M1NVajFvdzNFUHI0VUNpOUJuMmpxNmhjMjdQRVRHWDVkTjZrY3puc1ZUWkQ=
https://app.safe.global/home?safe=eth:0x71E47a4429d35827e0312AA13162197C23287546
https://app.safe.global/home?safe=arb1:0x6c2bd894bD166DCF053045a4883Db49EAE3FC01C
https://app.safe.global/home?safe=oeth:0x126940ea97b328E57E9e2285D546a677b2f38300
https://app.safe.global/home?safe=matic:0xc3Bf49eBA094AF346830dF4dbB42a07dE378EeB6
https://app.safe.global/home?safe=base:0x3b8f79458b6d303B47D56734B00CC6941443bd95
https://app.realms.today/dao/4bZiv1J33tJHiWSHPU96wABAZbPnbJ9M2zCmH9Hs12SW
https://app.safe.global/home?safe=eth:0x2ff7aB212cD6FEaE21bAc5300465E149FB6b85a9
https://app.safe.global/home?safe=matic:0x5bD70E385414C8dCC25305AeB7E542d8FC70e667
https://app.safe.global/home?safe=eth:0xd55c4261145EA1752662faA0485AfBC8C431b0CA
https://app.safe.global/home?safe=matic:0xe663AD6295d47ce33F9b65950761Ecba6654E5E8

Config File

Use a config file to store client configuration.

Application configuration can be stored in a file and passed to the application with the --config flag.

Example:

./keep-client --config /path/to/your/config.toml start

Configuration files in formats TOML, YAML and JSON are supported.

Sample configuration file:

# This is a sample TOML configuration file for the Keep client.

[ethereum]
URL = "ws://127.0.0.1:8546"
KeyFile = "/Users/someuser/ethereum/data/keystore/UTC--2018-03-11T01-37-33.202765887Z--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8AAAAAAAAA"

# Uncomment to override the defaults for transaction status monitoring.

# MiningCheckInterval is the interval in which transaction
# mining status is checked. If the transaction is not mined within this
# time, the gas price is increased and transaction is resubmitted.
#
# MiningCheckInterval = 60  # 60 sec (default value)

# MaxGasFeeCap specifies the maximum gas fee cap the client is
# willing to pay for the transaction to be mined. The offered transaction
# gas cost can not be higher than the max gas fee cap value. If the maximum
# allowed gas fee cap is reached, no further resubmission attempts are
# performed. This property should be set only for Ethereum. In case of
# legacy non-EIP-1559 transactions, this field works in the same way as
# `MaxGasPrice` property.
#
# MaxGasFeeCap = "500 Gwei" # 500 Gwei (default value)

# Uncomment to enable Ethereum node rate limiting. Both properties can be
# used together or separately.
#
# RequestsPerSecondLimit sets the maximum average number of requests
# per second which can be executed against the Ethereum node.
# All types of Ethereum node requests are rate-limited,
# including view function calls.
#
# RequestsPerSecondLimit = 150

# ConcurrencyLimit sets the maximum number of concurrent requests which
# can be executed against the Ethereum node at the same time.
# This limit affects all types of Ethereum node requests,
# including view function calls.
#
# ConcurrencyLimit = 30

# BalanceAlertThreshold defines a minimum value of the operator's account
# balance below which the client will start reporting errors in logs.
# A value can be provided in `wei`, `Gwei` or `ether`, e.g. `7.5 ether`,
# `7500000000 Gwei`.
#
# BalanceAlertThreshold = "0.5 ether" # 0.5 ether (default value)

[bitcoin.electrum]
# URL to the Electrum server in format: `scheme://hostname:port`.
# Should be uncommented only when using a custom Electrum server. Otherwise,
# one of the default embedded servers is selected randomly at startup.
# URL = "tcp://127.0.0.1:50001"

# Timeout for a single attempt of Electrum connection establishment.
# ConnectTimeout = "10s"

# Timeout for Electrum connection establishment retries.
# ConnectRetryTimeout = "1m"

# Timeout for a single attempt of Electrum protocol request.
# RequestTimeout = "30s"

# Timeout for Electrum protocol request retries.
# RequestRetryTimeout = "2m"

# Interval for connection keep alive requests.
# KeepAliveInterval = "5m"

[network]
Bootstrap = false
Peers = [
	"/ip4/127.0.0.1/tcp/3919/ipfs/16Uiu2HAmFRJtCWfdXhZEZHWb4tUpH1QMMgzH1oiamCfUuK6NgqWX",
]
Port = 3920

# Uncomment to override the node's default addresses announced in the network
# AnnouncedAddresses = ["/dns4/example.com/tcp/3919", "/ip4/80.70.60.50/tcp/3919"]

# Uncomment to enable courtesy message dissemination for topics this node is
# not subscribed to. Messages will be forwarded to peers for the duration
# specified as a value in seconds.
# Message dissemination is disabled by default and should be enabled only
# on selected bootstrap nodes. It is not a good idea to enable dissemination
# on non-bootstrap node as it may clutter communication and eventually lead
# to blacklisting the node. The maximum allowed value is 90 seconds.
#
# DisseminationTime = 90

[storage]
Dir = "/my/secure/location"

# ClientInfo exposes metrics and diagnostics modules.
# 
# Metrics collects and exposes information useful for external monitoring tools usually
# operating on time series data.
# All values exposed by metrics module are quantifiable or countable.
#
# The following metrics are available:
# - connected peers count
# - connected bootstraps count
# - eth client connectivity status
# 
# Diagnostics module exposes the following information:
# - list of connected peers along with their network id and ethereum operator address
# - information about the client's network id and ethereum operator address
[clientInfo]
Port = 9601
# NetworkMetricsTick = 60
# EthereumMetricsTick = 600

# Uncomment to overwrite default values for TBTC config.
#
# [tbtc]
# PreParamsPoolSize = 3000
# PreParamsGenerationTimeout = "2m"
# PreParamsGenerationDelay = "10s"
# PreParamsGenerationConcurrency = 1
# KeyGenConcurrency = 1

# Developer options to work with locally deployed contracts
#
# [developer]
# TokenStakingAddress = "0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
# RandomBeaconAddress = "0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
# WalletRegistryAddress = "0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
# BridgeAddress = "0xBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"

BeaconAuthorization

BeaconAuthorization

This file documents a contract which is not yet deployed to Mainnet.

Library managing the state of stake authorizations for the operator contract and the presence of operators in the sortition pool based on the stake authorized for them.

Parameters

struct Parameters {
  uint96 minimumAuthorization;
  uint64 authorizationDecreaseDelay;
  uint64 authorizationDecreaseChangePeriod;
}

AuthorizationDecrease

struct AuthorizationDecrease {
  uint96 decreasingBy;
  uint64 decreasingAt;
}

Data

struct Data {
  struct BeaconAuthorization.Parameters parameters;
  mapping(address => address) stakingProviderToOperator;
  mapping(address => address) operatorToStakingProvider;
  mapping(address => struct BeaconAuthorization.AuthorizationDecrease) pendingDecreases;
}

OperatorRegistered

event OperatorRegistered(address stakingProvider, address operator)

AuthorizationIncreased

event AuthorizationIncreased(address stakingProvider, address operator, uint96 fromAmount, uint96 toAmount)

AuthorizationDecreaseRequested

event AuthorizationDecreaseRequested(address stakingProvider, address operator, uint96 fromAmount, uint96 toAmount, uint64 decreasingAt)

AuthorizationDecreaseApproved

event AuthorizationDecreaseApproved(address stakingProvider)

InvoluntaryAuthorizationDecreaseFailed

event InvoluntaryAuthorizationDecreaseFailed(address stakingProvider, address operator, uint96 fromAmount, uint96 toAmount)

OperatorJoinedSortitionPool

event OperatorJoinedSortitionPool(address stakingProvider, address operator)

OperatorStatusUpdated

event OperatorStatusUpdated(address stakingProvider, address operator)

setParameters

function setParameters(struct BeaconAuthorization.Data self, uint96 _minimumAuthorization, uint64 _authorizationDecreaseDelay, uint64 _authorizationDecreaseChangePeriod) external

Updates authorization-related parameters.

Parameters

Name
Type
Description

self

struct BeaconAuthorization.Data

_minimumAuthorization

uint96

New value of the minimum authorization for the beacon. Without at least the minimum authorization, staking provider is not eligible to join and operate in the network.

_authorizationDecreaseDelay

uint64

New value of the authorization decrease delay. It is the time in seconds that needs to pass between the time authorization decrease is requested and the time the authorization decrease can be approved, no matter the authorization decrease amount.

_authorizationDecreaseChangePeriod

uint64

New value of the authorization decrease change period. It is the time in seconds, before authorization decrease delay end, during which the pending authorization decrease request can be overwritten. If set to 0, pending authorization decrease request can not be overwritten until the entire authorizationDecreaseDelay ends. If set to value equal authorizationDecreaseDelay, request can always be overwritten.

registerOperator

function registerOperator(struct BeaconAuthorization.Data self, address operator) public

Used by staking provider to set operator address that will operate a node. The given staking provider can set operator address only one time. The operator address can not be changed and must be unique. Reverts if the operator is already set for the staking provider or if the operator address is already in use. Reverts if there is a pending authorization decrease for the staking provider.

authorizationIncreased

function authorizationIncreased(struct BeaconAuthorization.Data self, address stakingProvider, uint96 fromAmount, uint96 toAmount) external

Used by T staking contract to inform the beacon that the authorized stake amount for the given staking provider increased.

Reverts if the authorization amount is below the minimum.

The function is not updating the sortition pool. Sortition pool state needs to be updated by the operator with a call to joinSortitionPool or updateOperatorStatus.

Should only be callable by T staking contract.

authorizationDecreaseRequested

function authorizationDecreaseRequested(struct BeaconAuthorization.Data self, address stakingProvider, uint96 fromAmount, uint96 toAmount) public

Used by T staking contract to inform the beacon that the authorization decrease for the given staking provider has been requested.

Reverts if the amount after deauthorization would be non-zero and lower than the minimum authorization.

Reverts if another authorization decrease request is pending for the staking provider and not enough time passed since the original request (see authorizationDecreaseChangePeriod).

If the operator is not known (registerOperator was not called) it lets to approveAuthorizationDecrease immediately. If the operator is known (registerOperator was called), the operator needs to update state of the sortition pool with a call to joinSortitionPool or updateOperatorStatus. After the sortition pool state is in sync, authorization decrease delay starts.

After authorization decrease delay passes, authorization decrease request needs to be approved with a call to approveAuthorizationDecrease function.

If there is a pending authorization decrease request, it is overwritten, but only if enough time passed since the original request. Otherwise, the function reverts.

Should only be callable by T staking contract.

approveAuthorizationDecrease

function approveAuthorizationDecrease(struct BeaconAuthorization.Data self, contract IStaking tokenStaking, address stakingProvider) public

Approves the previously registered authorization decrease request. Reverts if authorization decrease delay have not passed yet or if the authorization decrease was not requested for the given staking provider.

involuntaryAuthorizationDecrease

function involuntaryAuthorizationDecrease(struct BeaconAuthorization.Data self, contract IStaking tokenStaking, contract SortitionPool sortitionPool, address stakingProvider, uint96 fromAmount, uint96 toAmount) external

Used by T staking contract to inform the beacon the authorization has been decreased for the given staking provider involuntarily, as a result of slashing.

If the operator is not known (registerOperator was not called) the function does nothing. The operator was never in a sortition pool so there is nothing to update.

If the operator is known, sortition pool is unlocked, and the operator is in the sortition pool, the sortition pool state is updated. If the sortition pool is locked, update needs to be postponed. Every other staker is incentivized to call updateOperatorStatus for the problematic operator to increase their own rewards in the pool.

Should only be callable by T staking contract.

joinSortitionPool

function joinSortitionPool(struct BeaconAuthorization.Data self, contract IStaking tokenStaking, contract SortitionPool sortitionPool) public

Lets the operator join the sortition pool. The operator address must be known - before calling this function, it has to be appointed by the staking provider by calling registerOperator. Also, the operator must have the minimum authorization required by the beacon. Function reverts if there is no minimum stake authorized or if the operator is not known. If there was an authorization decrease requested, it is activated by starting the authorization decrease delay.

updateOperatorStatus

function updateOperatorStatus(struct BeaconAuthorization.Data self, contract IStaking tokenStaking, contract SortitionPool sortitionPool, address operator) public

Updates status of the operator in the sortition pool. If there was an authorization decrease requested, it is activated by starting the authorization decrease delay. Function reverts if the operator is not known.

isOperatorUpToDate

function isOperatorUpToDate(struct BeaconAuthorization.Data self, contract IStaking tokenStaking, contract SortitionPool sortitionPool, address operator) external view returns (bool)

Checks if the operator's authorized stake is in sync with operator's weight in the sortition pool. If the operator is not in the sortition pool and their authorized stake is non-zero, function returns false.

eligibleStake

function eligibleStake(struct BeaconAuthorization.Data self, contract IStaking tokenStaking, address stakingProvider) external view returns (uint96)

Returns the current value of the staking provider's eligible stake. Eligible stake is defined as the currently authorized stake minus the pending authorization decrease. Eligible stake is what is used for operator's weight in the pool. If the authorized stake minus the pending authorization decrease is below the minimum authorization, eligible stake is 0.

This function can be exposed to the public in contrast to the second variant accepting decreasingBy as a parameter.

eligibleStake

function eligibleStake(struct BeaconAuthorization.Data self, contract IStaking tokenStaking, address stakingProvider, uint96 decreasingBy) public view returns (uint96)

Returns the current value of the staking provider's eligible stake. Eligible stake is defined as the currently authorized stake minus the pending authorization decrease. Eligible stake is what is used for operator's weight in the pool. If the authorized stake minus the pending authorization decrease is below the minimum authorization, eligible stake is 0.

This function is not intended to be exposes to the public. decreasingBy must be fetched from pendingDecreases mapping and it is passed as a parameter to optimize gas usage of functions that call eligibleStake and need to use AuthorizationDecrease fetched from pendingDecreases for some additional logic.

pendingAuthorizationDecrease

function pendingAuthorizationDecrease(struct BeaconAuthorization.Data self, address stakingProvider) public view returns (uint96)

Returns the amount of stake that is pending authorization decrease for the given staking provider. If no authorization decrease has been requested, returns zero.

remainingAuthorizationDecreaseDelay

function remainingAuthorizationDecreaseDelay(struct BeaconAuthorization.Data self, address stakingProvider) external view returns (uint64)

Returns the remaining time in seconds that needs to pass before the requested authorization decrease can be approved. If the sortition pool state was not updated yet by the operator after requesting the authorization decrease, returns type(uint64).max.

AltBn128

AltBn128

This file documents a contract which is not yet deployed to Mainnet.

Implementations of common elliptic curve operations on Ethereum's (poorly named) alt_bn128 curve. Whenever possible, use post-Byzantium pre-compiled contracts to offset gas costs. Note that these pre-compiles might not be available on all (eg private) chains.

G1Point

struct G1Point {
  uint256 x;
  uint256 y;
}

gfP2

struct gfP2 {
  uint256 x;
  uint256 y;
}

G2Point

struct G2Point {
  struct AltBn128.gfP2 x;
  struct AltBn128.gfP2 y;
}

p

uint256 p

g1x

uint256 g1x

Gets generator of G1 group. Taken from go-ethereum/crypto/bn256/cloudflare/curve.go

g1y

uint256 g1y

g2xx

uint256 g2xx

Gets generator of G2 group. Taken from go-ethereum/crypto/bn256/cloudflare/twist.go

g2xy

uint256 g2xy

g2yx

uint256 g2yx

g2yy

uint256 g2yy

twistBx

uint256 twistBx

Gets twist curve B constant. Taken from go-ethereum/crypto/bn256/cloudflare/twist.go

twistBy

uint256 twistBy

hexRootX

uint256 hexRootX

Gets root of the point where x and y are equal.

hexRootY

uint256 hexRootY

g1YFromX

function g1YFromX(uint256 x) internal view returns (uint256)

g1YFromX computes a Y value for a G1 point based on an X value. This computation is simply evaluating the curve equation for Y on a given X, and allows a point on the curve to be represented by just an X value + a sign bit.

g1HashToPoint

function g1HashToPoint(bytes m) internal view returns (struct AltBn128.G1Point)

Hash a byte array message, m, and map it deterministically to a point on G1. Note that this approach was chosen for its simplicity and lower gas cost on the EVM, rather than good distribution of points on G1.

g1Decompress

function g1Decompress(bytes32 m) internal view returns (struct AltBn128.G1Point)

Decompress a point on G1 from a single uint256.

g1Add

function g1Add(struct AltBn128.G1Point a, struct AltBn128.G1Point b) internal view returns (struct AltBn128.G1Point c)

Wraps the point addition pre-compile introduced in Byzantium. Returns the sum of two points on G1. Revert if the provided points are not on the curve.

isG1PointOnCurve

function isG1PointOnCurve(struct AltBn128.G1Point point) internal view returns (bool)

Returns true if G1 point is on the curve.

scalarMultiply

function scalarMultiply(struct AltBn128.G1Point p_1, uint256 scalar) internal view returns (struct AltBn128.G1Point p_2)

Wraps the scalar point multiplication pre-compile introduced in Byzantium. The result of a point from G1 multiplied by a scalar should match the point added to itself the same number of times. Revert if the provided point isn't on the curve.

pairing

function pairing(struct AltBn128.G1Point p1, struct AltBn128.G2Point p2, struct AltBn128.G1Point p3, struct AltBn128.G2Point p4) internal view returns (bool result)

Wraps the pairing check pre-compile introduced in Byzantium. Returns the result of a pairing check of 2 pairs (G1 p1, G2 p2) (G1 p3, G2 p4)

getP

function getP() internal pure returns (uint256)

g1

function g1() internal pure returns (struct AltBn128.G1Point)

g2

function g2() internal pure returns (struct AltBn128.G2Point)

g2YFromX

function g2YFromX(struct AltBn128.gfP2 _x) internal pure returns (struct AltBn128.gfP2 y)

g2YFromX computes a Y value for a G2 point based on an X value. This computation is simply evaluating the curve equation for Y on a given X, and allows a point on the curve to be represented by just an X value + a sign bit.

g1Compress

function g1Compress(struct AltBn128.G1Point point) internal pure returns (bytes32)

Compress a point on G1 to a single uint256 for serialization.

g2Compress

function g2Compress(struct AltBn128.G2Point point) internal pure returns (bytes)

Compress a point on G2 to a pair of uint256 for serialization.

g1Unmarshal

function g1Unmarshal(bytes m) internal pure returns (struct AltBn128.G1Point)

Unmarshals a point on G1 from bytes in an uncompressed form.

g1Marshal

function g1Marshal(struct AltBn128.G1Point point) internal pure returns (bytes)

Marshals a point on G1 to bytes form.

g2Unmarshal

function g2Unmarshal(bytes m) internal pure returns (struct AltBn128.G2Point)

Unmarshals a point on G2 from bytes in an uncompressed form.

g2Decompress

function g2Decompress(bytes m) internal pure returns (struct AltBn128.G2Point)

Decompress a point on G2 from a pair of uint256.

gfP2Add

function gfP2Add(struct AltBn128.gfP2 a, struct AltBn128.gfP2 b) internal pure returns (struct AltBn128.gfP2)

Returns the sum of two gfP2 field elements.

gfP2Multiply

function gfP2Multiply(struct AltBn128.gfP2 a, struct AltBn128.gfP2 b) internal pure returns (struct AltBn128.gfP2)

Returns multiplication of two gfP2 field elements.

gfP2Pow

function gfP2Pow(struct AltBn128.gfP2 _a, uint256 _exp) internal pure returns (struct AltBn128.gfP2 result)

Returns gfP2 element to the power of the provided exponent.

gfP2Square

function gfP2Square(struct AltBn128.gfP2 a) internal pure returns (struct AltBn128.gfP2)

gfP2Cube

function gfP2Cube(struct AltBn128.gfP2 a) internal pure returns (struct AltBn128.gfP2)

gfP2CubeAddTwistB

function gfP2CubeAddTwistB(struct AltBn128.gfP2 a) internal pure returns (struct AltBn128.gfP2)

g2X2y

function g2X2y(struct AltBn128.gfP2 x, struct AltBn128.gfP2 y) internal pure returns (bool)

Returns true if G2 point's y^2 equals x.

isG2PointOnCurve

function isG2PointOnCurve(struct AltBn128.G2Point point) internal pure returns (bool)

Returns true if G2 point is on the curve.

Initial Protocol Loan

The Initial Protocol Loan (IPL) is a debt issued against the protocol itself in order to fund the stability pool. Even though it's technically debt, it does not in itself make the protocol a fractional reserve because all funds are accounted for.

How it works

thUSD is minted by the PCV and deposited into the stability pool. A freeze on withdraw is initiated until debt is fully repaid. At any time, the Threshold DAO can initiate a withdraw, which will destruct the debt and withdraw exceeds. If the PCV for some reason has less than the debt available, withdraw is denied.

Profits from usage of the protocol (loan interest, redemption fee, etc) accrues into the PCV, so in an event where there are less funds than debt the protocol first has to earn back the missing funds.

Benefits of the hybrid PCV + IPL model:

  • Bootstrap stability pool for free

  • No need for a Protocol token = all profits accrue directly to the PCV

  • Immediate surplus on first loan drawn / liquidation

  • Predictability and (once debt is repaid) high resilience against Black Swans

  • No idle capital (it doesn't "really" exist)

Disadvantages

  • Higher risk of under-collateralization during the initial stages

  • Poor management of PCV could result in not enough funds in the stability pool

About the risk

Each liquidation that occurs will draw against the stability pool and result in ~10% profit to the stability pool. This also means that the price can drop up to 10% after a liquidation before the stability pool is at risk of losing money on the liquidation. Our integration with B.Protocol ensures that liquidated tBTC is automatically converted back to thUSD and re-deposited into the stability pool. Under normal circumstances and without other factors, it is expected that the balance of thUSD in the pool will grow over time.

But there can be Black Swan events, large drops in the price of BTC in a short period of time or liquidity issues that results in a loss, even at ~10% profit. This is a risk of the protocol becoming undercollateralized, but the same issue would apply even without debt (stability pool not being profitable). The protocol is created to be sustainable, so these types of rare events will be averaged out. In addition, all income streams from Threshold USD also ends up in the PCV which further decrease the risk of undercollaterization.

The undercollaterialization is mostly a concern during the initial bootstrapping phase. As interest accrue and repay the initial debt, the funds in the stability pool will be replaced by fully owned PVC, thus eliminating this disadvantage.

ILegacyTokenStaking

IKeepTokenStaking

Interface for Keep TokenStaking contract

seize

Seize provided token amount from every member in the misbehaved operators array. The tattletale is rewarded with 5% of the total seized amount scaled by the reward adjustment parameter and the rest 95% is burned.

Parameters

Name
Type
Description

getDelegationInfo

Gets stake delegation info for the given operator.

Parameters

Name
Type
Description

Return Values

Name
Type
Description

ownerOf

Gets the stake owner for the specified operator address.

Return Values

Name
Type
Description

beneficiaryOf

Gets the beneficiary for the specified operator address.

Return Values

Name
Type
Description

authorizerOf

Gets the authorizer for the specified operator address.

Return Values

Name
Type
Description

eligibleStake

Gets the eligible stake balance of the specified address. An eligible stake is a stake that passed the initialization period and is not currently undelegating. Also, the operator had to approve the specified operator contract.

Operator with a minimum required amount of eligible stake can join the network and participate in new work selection.

Parameters

Name
Type
Description

Return Values

Name
Type
Description

INuCypherStakingEscrow

Interface for NuCypher StakingEscrow contract

slashStaker

Slash the staker's stake and reward the investigator

Parameters

Name
Type
Description

requestMerge

Request merge between NuCypher staking contract and T staking contract. Returns amount of staked tokens

getAllTokens

Get all tokens belonging to the staker

BeaconDkgValidator

BeaconDkgValidator

This file documents a contract which is not yet deployed to Mainnet.

DKGValidator allows performing a full validation of DKG result, including checking the format of fields in the result, declared selected group members, and signatures of operators supporting the result. The operator submitting the result should perform the validation using a free contract call before submitting the result to ensure their result is valid and can not be challenged. All other network operators should perform validation of the submitted result using a free contract call and challenge the result if the validation fails.

groupSize

Size of a group in the threshold relay.

groupThreshold

The minimum number of group members needed to interact according to the protocol to produce a relay entry. The adversary can not learn anything about the key as long as it does not break into groupThreshold+1 of members.

activeThreshold

The minimum number of active and properly behaving group members during the DKG needed to accept the result. This number is higher than groupThreshold to keep a safety margin for members becoming inactive after DKG so that the group can still produce a relay entry.

signatureByteSize

Size in bytes of a single signature produced by operator supporting DKG result.

sortitionPool

constructor

validate

Performs a full validation of DKG result, including checking the format of fields in the result, declared selected group members, and signatures of operators supporting the result.

Parameters

Name
Type
Description

Return Values

Name
Type
Description

validateFields

Performs a static validation of DKG result fields: lengths, ranges, and order of arrays.

Return Values

Name
Type
Description

validateGroupMembers

Performs validation of group members as declared in DKG result against group members selected by the sortition pool.

Parameters

Name
Type
Description

Return Values

Name
Type
Description

validateSignatures

Performs validation of signatures supplied in DKG result. Note that this function does not check if addresses which supplied signatures supporting the result are the ones selected to the group by sortition pool. This function should be used together with validateGroupMembers.

Parameters

Name
Type
Description

Return Values

Name
Type
Description

validateMembersHash

Performs validation of hashed group members that actively took part in DKG.

Parameters

Name
Type
Description

Return Values

Name
Type
Description

ReimbursementPool

ReimbursementPool

This file documents a contract which is not yet deployed to Mainnet.

isAuthorized

Authorized contracts that can interact with the reimbursment pool. Authorization can be granted and removed by the owner.

staticGas

Static gas includes:

  • cost of the refund function

  • base transaction cost

maxGasPrice

Max gas price used to reimburse a transaction submitter. Protects against malicious operator-miners.

StaticGasUpdated

MaxGasPriceUpdated

SendingEtherFailed

AuthorizedContract

UnauthorizedContract

FundsWithdrawn

constructor

receive

Receive ETH

refund

Refunds ETH to a spender for executing specific transactions.

Ignoring the result of sending ETH to a receiver is made on purpose. For EOA receiving ETH should always work. If a receiver is a smart contract, then we do not want to fail a transaction, because in some cases the refund is done at the very end of multiple calls where all the previous calls were already paid off. It is a receiver's smart contract resposibility to make sure it can receive ETH. Only authorized contracts are allowed calling this function.

Parameters

Name
Type
Description

authorize

Authorize a contract that can interact with this reimbursment pool. Can be authorized by the owner only.

Parameters

Name
Type
Description

unauthorize

Unauthorize a contract that was previously authorized to interact with this reimbursment pool. Can be unauthorized by the owner only.

Parameters

Name
Type
Description

setStaticGas

Setting a static gas cost for executing a transaction. Can be set by the owner only.

Parameters

Name
Type
Description

setMaxGasPrice

Setting a max gas price for transactions. Can be set by the owner only.

Parameters

Name
Type
Description

withdrawAll

Withdraws all ETH from this pool which are sent to a given address. Can be set by the owner only.

Parameters

Name
Type
Description

withdraw

Withdraws ETH amount from this pool which are sent to a given address. Can be set by the owner only.

Parameters

Name
Type
Description

BLS

BLS

This file documents a contract which is not yet deployed to Mainnet.

Library for verification of 2-pairing-check BLS signatures, including basic, aggregated, or reconstructed threshold BLS signatures, generated using the AltBn128 curve.

sign

Creates a signature over message using the provided secret key.

verifyBytes

Wraps the functionality of BLS.verify, but hashes a message to a point on G1 and marshal to bytes first to allow raw bytes verification.

verify

Verify performs the pairing operation to check if the signature is correct for the provided message and the corresponding public key. Public key must be a valid point on G2 curve in an uncompressed format. Message must be a valid point on G1 curve in an uncompressed format. Signature must be a valid point on G1 curve in an uncompressed format.

_verify

function seize(uint256 amountToSeize, uint256 rewardMultiplier, address tattletale, address[] misbehavedOperators) external

amountToSeize

uint256

Token amount to seize from every misbehaved operator.

rewardMultiplier

uint256

Reward adjustment in percentage. Min 1% and 100% max.

tattletale

address

Address to receive the 5% reward.

misbehavedOperators

address[]

Array of addresses to seize the tokens from.

function getDelegationInfo(address operator) external view returns (uint256 amount, uint256 createdAt, uint256 undelegatedAt)

operator

address

Operator address.

amount

uint256

The amount of tokens the given operator delegated.

createdAt

uint256

The time when the stake has been delegated.

undelegatedAt

uint256

The time when undelegation has been requested. If undelegation has not been requested, 0 is returned.

function ownerOf(address operator) external view returns (address)

[0]

address

Stake owner address.

function beneficiaryOf(address operator) external view returns (address payable)

[0]

address payable

Beneficiary address.

function authorizerOf(address operator) external view returns (address)

[0]

address

Authorizer address.

function eligibleStake(address operator, address operatorContract) external view returns (uint256 balance)

operator

address

address of stake operator.

operatorContract

address

address of operator contract.

balance

uint256

an uint256 representing the eligible stake balance.

function slashStaker(address staker, uint256 penalty, address investigator, uint256 reward) external

staker

address

Staker's address

penalty

uint256

Penalty

investigator

address

Investigator

reward

uint256

Reward for the investigator

function requestMerge(address staker, address stakingProvider) external returns (uint256)
function getAllTokens(address staker) external view returns (uint256)
uint256 groupSize
uint256 groupThreshold
uint256 activeThreshold
uint256 signatureByteSize
contract SortitionPool sortitionPool
constructor(contract SortitionPool _sortitionPool) public
function validate(struct BeaconDkg.Result result, uint256 seed, uint256 startBlock) external view returns (bool isValid, string errorMsg)

result

struct BeaconDkg.Result

seed

uint256

seed used to start the DKG and select group members

startBlock

uint256

DKG start block

isValid

bool

true if the result is valid, false otherwise

errorMsg

string

validation error message; empty for a valid result

function validateFields(struct BeaconDkg.Result result) public pure returns (bool isValid, string errorMsg)

isValid

bool

true if the result is valid, false otherwise

errorMsg

string

validation error message; empty for a valid result

function validateGroupMembers(struct BeaconDkg.Result result, uint256 seed) public view returns (bool)

result

struct BeaconDkg.Result

seed

uint256

seed used to start the DKG and select group members

[0]

bool

true if group members matches; false otherwise

function validateSignatures(struct BeaconDkg.Result result, uint256 startBlock) public view returns (bool)

result

struct BeaconDkg.Result

startBlock

uint256

DKG start block

[0]

bool

true if group members matches; false otherwise

function validateMembersHash(struct BeaconDkg.Result result) public pure returns (bool)

result

struct BeaconDkg.Result

DKG result

[0]

bool

true if result's group members hash matches with the one that is challenged.

mapping(address => bool) isAuthorized
uint256 staticGas
uint256 maxGasPrice
event StaticGasUpdated(uint256 newStaticGas)
event MaxGasPriceUpdated(uint256 newMaxGasPrice)
event SendingEtherFailed(uint256 refundAmount, address receiver)
event AuthorizedContract(address thirdPartyContract)
event UnauthorizedContract(address thirdPartyContract)
event FundsWithdrawn(uint256 withdrawnAmount, address receiver)
constructor(uint256 _staticGas, uint256 _maxGasPrice) public
receive() external payable
function refund(uint256 gasSpent, address receiver) external

gasSpent

uint256

Gas spent on a transaction that needs to be reimbursed.

receiver

address

Address where the reimbursment is sent.

function authorize(address _contract) external

_contract

address

Authorized contract.

function unauthorize(address _contract) external

_contract

address

Authorized contract.

function setStaticGas(uint256 _staticGas) external

_staticGas

uint256

Static gas cost.

function setMaxGasPrice(uint256 _maxGasPrice) external

_maxGasPrice

uint256

Max gas price used to reimburse tx submitters.

function withdrawAll(address receiver) external

receiver

address

An address where ETH is sent.

function withdraw(uint256 amount, address receiver) public

amount

uint256

Amount to withdraw from the pool.

receiver

address

An address where ETH is sent.

function sign(bytes message, uint256 secretKey) external view returns (bytes)
function verifyBytes(bytes publicKey, bytes message, bytes signature) external view returns (bool)
function verify(bytes publicKey, bytes message, bytes signature) public view returns (bool)
function _verify(struct AltBn128.G2Point publicKey, struct AltBn128.G1Point message, struct AltBn128.G1Point signature) public view returns (bool)

Checkpoints

Checkpoints

Abstract contract to support checkpoints for Compound-like voting and delegation. This implementation supports token supply up to 2^96 - 1. This contract keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting power can be publicly queried through {getVotes} and {getPastVotes}. NOTE: Extracted from OpenZeppelin ERCVotes.sol. This contract is upgrade-safe.

Checkpoint

struct Checkpoint {
  uint32 fromBlock;
  uint96 votes;
}

_delegates

mapping(address => address) _delegates

_checkpoints

mapping(address => uint128[]) _checkpoints

_totalSupplyCheckpoints

uint128[] _totalSupplyCheckpoints

DelegateChanged

event DelegateChanged(address delegator, address fromDelegate, address toDelegate)

Emitted when an account changes their delegate.

DelegateVotesChanged

event DelegateVotesChanged(address delegate, uint256 previousBalance, uint256 newBalance)

Emitted when a balance or delegate change results in changes to an account's voting power.

checkpoints

function checkpoints(address account, uint32 pos) public view virtual returns (struct Checkpoints.Checkpoint checkpoint)

numCheckpoints

function numCheckpoints(address account) public view virtual returns (uint32)

Get number of checkpoints for account.

delegates

function delegates(address account) public view virtual returns (address)

Get the address account is currently delegating to.

getVotes

function getVotes(address account) public view returns (uint96)

Gets the current votes balance for account.

Parameters

Name
Type
Description

account

address

The address to get votes balance

Return Values

Name
Type
Description

[0]

uint96

The number of current votes for account

getPastVotes

function getPastVotes(address account, uint256 blockNumber) public view returns (uint96)

Determine the prior number of votes for an account as of a block number.

Block number must be a finalized block or else this function will revert to prevent misinformation.

Parameters

Name
Type
Description

account

address

The address of the account to check

blockNumber

uint256

The block number to get the vote balance at

Return Values

Name
Type
Description

[0]

uint96

The number of votes the account had as of the given block

getPastTotalSupply

function getPastTotalSupply(uint256 blockNumber) public view returns (uint96)

Retrieve the totalSupply at the end of blockNumber. Note, this value is the sum of all balances, but it is NOT the sum of all the delegated votes!

blockNumber must have been already mined

Parameters

Name
Type
Description

blockNumber

uint256

The block number to get the total supply at

delegate

function delegate(address delegator, address delegatee) internal virtual

Change delegation for delegator to delegatee.

moveVotingPower

function moveVotingPower(address src, address dst, uint256 amount) internal

Moves voting power from one delegate to another

Parameters

Name
Type
Description

src

address

Address of old delegate

dst

address

Address of new delegate

amount

uint256

Voting power amount to transfer between delegates

writeCheckpoint

function writeCheckpoint(uint128[] ckpts, function (uint256,uint256) view returns (uint256) op, uint256 delta) internal returns (uint256 oldWeight, uint256 newWeight)

Writes a new checkpoint based on operating last stored value with a delta. Usually, said operation is the add or subtract functions from this contract, but more complex functions can be passed as parameters.

Parameters

Name
Type
Description

ckpts

uint128[]

The checkpoints array to use

op

function (uint256,uint256) view returns (uint256)

The function to apply over the last value and the delta

delta

uint256

Variation with respect to last stored value to be used for new checkpoint

lookupCheckpoint

function lookupCheckpoint(uint128[] ckpts, uint256 blockNumber) internal view returns (uint96)

Lookup a value in a list of (sorted) checkpoints.

Parameters

Name
Type
Description

ckpts

uint128[]

The checkpoints array to use

blockNumber

uint256

Block number when we want to get the checkpoint at

maxSupply

function maxSupply() internal view virtual returns (uint96)

Maximum token supply. Defaults to type(uint96).max (2^96 - 1)

encodeCheckpoint

function encodeCheckpoint(uint32 blockNumber, uint96 value) internal pure returns (uint128)

Encodes a blockNumber and value into a single uint128 checkpoint.

blockNumber is stored in the first 32 bits, while value in the remaining 96 bits.

decodeBlockNumber

function decodeBlockNumber(uint128 checkpoint) internal pure returns (uint32)

Decodes a block number from a uint128 checkpoint.

decodeValue

function decodeValue(uint128 checkpoint) internal pure returns (uint96)

Decodes a voting value from a uint128 checkpoint.

decodeCheckpoint

function decodeCheckpoint(uint128 checkpoint) internal pure returns (uint32 blockNumber, uint96 value)

Decodes a block number and voting value from a uint128 checkpoint.

add

function add(uint256 a, uint256 b) internal pure returns (uint256)

subtract

function subtract(uint256 a, uint256 b) internal pure returns (uint256)
keep-core/client-start-help at main · keep-network/keep-coreGitHub

Application Authorization & Operator Registration

The following demonstrates how to authorize applications for tBTC v2 and how to register your operator address via the Threshold Dashboard.

One important step to get your node operating on the Threshold Network is proper application authorization as well as operator account registration. Applications need only be authorized once.

It is CRITICALLY important that both the tBTC application as well as the Random Beacon applications are authorized. A node cannot be deployed without both applications being properly authorized.

Please note: by authorizing these applications, an unbonding period of 45 days will go into effect on the T you stake for these applications. This cool-down period begins the day you submit an unstake request.

Application Authorization

To get started, visit the Threshold Dashboard and connect your wallet.

Threshold Dashboard

Click on "Configure Apps"

Authorizing Threshold Applications

Select BOTH tBTC and Random Beacon applications and enter your desired amount of T to stake per application. Note that the minimum is 40,000T.

Authorizing Threshold Applications

Operator Registration

The operator account is the Ethereum account created on your node. In order for the network to associate your T stake with your node, you must register your Operator Address.

An Operator for the Provider registration can be submitted just once. The Operator address assignment cannot be updated.

Registering Operator Address

After both applications have been authorized, click on "Start Mapping" to begin the Operator Registration process.

Registering Operator Address

Enter your Operator Address in the field provided and click on "Map Address."

Once the steps above have been successfully completed, you are ready to move on to the next step in the node deployment process.

Don't forget: a tBTC v2 node will not be able to be deployed without successfully authorizing both the tBTC and Random Beacon applications, and registering the node's operator address FIRST.

Groups

Groups

This file documents a contract which is not yet deployed to Mainnet.

This library is used as a registry of created groups.

This library should be used along with DKG library that ensures linear groups creation (only one group creation happens at a time). A candidate group has to be popped or activated before adding a new candidate group.

Group

Data

GroupRegistered

validatePublicKey

Performs preliminary validation of a new group public key. The group public key must be unique and have 128 bytes in length. If the validation fails, the function reverts. This function must be called first for a public key of a group added with addGroup function.

Parameters

Name
Type
Description

addGroup

Adds a new candidate group. The group is stored with group public key and group members, but is not yet activated.

The group members list is stored with all misbehaved members filtered out. The code calling this function should ensure that the number of candidate (not activated) groups is never more than one.

Parameters

Name
Type
Description

expireOldGroups

Goes through groups starting from the oldest one that is still active and checks if it hasn't expired. If so, updates the information about expired groups so that all expired groups are marked as such.

terminateGroup

Terminates group with the provided index. Reverts if the group is already terminated.

Parameters

Name
Type
Description

selectGroup

Returns an index of a randomly selected active group. Terminated and expired groups are not considered as active. Before new group is selected, information about expired groups is updated. At least one active group needs to be present for this function to succeed.

Parameters

Name
Type
Description

setGroupLifetime

Setter for group lifetime.

Parameters

Name
Type
Description

isGroupTerminated

Checks if group with the given index is terminated.

groupLifetimeOf

Gets the cutoff time until which the given group is considered to be active assuming it hasn't been terminated before.

isGroupActive

Checks if group with the given index is active and non-terminated.

getGroup

getGroup

numberOfActiveGroups

Gets the number of active groups. Expired and terminated groups are not counted as active.

shiftByExpiredGroups

Evaluates the shift of a selected group index based on the number of expired groups.

shiftByTerminatedGroups

Evaluates the shift of a selected group index based on the number of non-expired but terminated groups.

struct Group {
  bytes groupPubKey;
  uint256 registrationBlockNumber;
  bytes32 membersHash;
  bool terminated;
}
struct Data {
  mapping(bytes32 => struct Groups.Group) groupsData;
  bytes32[] groupsRegistry;
  uint64[] activeTerminatedGroups;
  uint64 expiredGroupOffset;
  uint256 groupLifetime;
}
event GroupRegistered(uint64 groupId, bytes groupPubKey)
function validatePublicKey(struct Groups.Data self, bytes groupPubKey) internal view

self

struct Groups.Data

groupPubKey

bytes

Candidate group public key

function addGroup(struct Groups.Data self, bytes groupPubKey, bytes32 membersHash) internal

self

struct Groups.Data

groupPubKey

bytes

Generated candidate group public key

membersHash

bytes32

Keccak256 hash of members that actively took part in DKG.

function expireOldGroups(struct Groups.Data self) internal
function terminateGroup(struct Groups.Data self, uint64 groupId) internal

self

struct Groups.Data

groupId

uint64

Index in the groupRegistry array.

function selectGroup(struct Groups.Data self, uint256 seed) internal returns (uint64)

self

struct Groups.Data

seed

uint256

Random number used as a group selection seed.

function setGroupLifetime(struct Groups.Data self, uint256 lifetime) internal

self

struct Groups.Data

lifetime

uint256

Lifetime of a group in blocks.

function isGroupTerminated(struct Groups.Data self, uint64 groupId) internal view returns (bool)
function groupLifetimeOf(struct Groups.Data self, bytes32 groupPubKeyHash) internal view returns (uint256)
function isGroupActive(struct Groups.Data self, uint64 groupId) internal view returns (bool)
function getGroup(struct Groups.Data self, uint64 groupId) internal view returns (struct Groups.Group)
function getGroup(struct Groups.Data self, bytes groupPubKey) internal view returns (struct Groups.Group)
function numberOfActiveGroups(struct Groups.Data self) internal view returns (uint64)
function shiftByExpiredGroups(struct Groups.Data self, uint64 selectedIndex) internal view returns (uint64)
function shiftByTerminatedGroups(struct Groups.Data self, uint64 selectedIndex) internal view returns (uint64)

IStaking

IStaking

The staking contract enables T owners to have their wallets offline and their stake managed by staking providers on their behalf. The staking contract does not define operator role. The operator responsible for running off-chain client software is appointed by the staking provider in the particular application utilizing the staking contract. All off-chain client software should be able to run without exposing operator's or staking provider’s private key and should not require any owner’s keys at all. The stake delegation optimizes the network throughput without compromising the security of the owners’ stake.

StakeType

enum StakeType {
  NU,
  KEEP,
  T
}

stake

function stake(address stakingProvider, address payable beneficiary, address authorizer, uint96 amount) external

Creates a delegation with msg.sender owner with the given staking provider, beneficiary, and authorizer. Transfers the given amount of T to the staking contract.

The owner of the delegation needs to have the amount approved to transfer to the staking contract.

stakeKeep

function stakeKeep(address stakingProvider) external

Copies delegation from the legacy KEEP staking contract to T staking contract. No tokens are transferred. Caches the active stake amount from KEEP staking contract. Can be called by anyone.

The staking provider in T staking contract is the legacy KEEP staking contract operator.

stakeNu

function stakeNu(address stakingProvider, address payable beneficiary, address authorizer) external

Copies delegation from the legacy NU staking contract to T staking contract, additionally appointing staking provider, beneficiary and authorizer roles. Caches the amount staked in NU staking contract. Can be called only by the original delegation owner.

setMinimumStakeAmount

function setMinimumStakeAmount(uint96 amount) external

Allows the Governance to set the minimum required stake amount. This amount is required to protect against griefing the staking contract and individual applications are allowed to require higher minimum stakes if necessary.

approveApplication

function approveApplication(address application) external

Allows the Governance to approve the particular application before individual stake authorizers are able to authorize it.

increaseAuthorization

function increaseAuthorization(address stakingProvider, address application, uint96 amount) external

Increases the authorization of the given staking provider for the given application by the given amount. Can only be called by the authorizer for that staking provider.

Calls authorizationIncreased(address stakingProvider, uint256 amount) on the given application to notify the application about authorization change. See IApplication.

requestAuthorizationDecrease

function requestAuthorizationDecrease(address stakingProvider, address application, uint96 amount) external

Requests decrease of the authorization for the given staking provider on the given application by the provided amount. It may not change the authorized amount immediatelly. When it happens depends on the application. Can only be called by the given staking provider’s authorizer. Overwrites pending authorization decrease for the given staking provider and application if the application agrees for that. If the application does not agree for overwriting, the function reverts.

Calls authorizationDecreaseRequested(address stakingProvider, uint256 amount) on the given application. See IApplication.

requestAuthorizationDecrease

function requestAuthorizationDecrease(address stakingProvider) external

Requests decrease of all authorizations for the given staking provider on all applications by all authorized amount. It may not change the authorized amount immediatelly. When it happens depends on the application. Can only be called by the given staking provider’s authorizer. Overwrites pending authorization decrease for the given staking provider and application.

Calls authorizationDecreaseRequested(address stakingProvider, uint256 amount) for each authorized application. See IApplication.

approveAuthorizationDecrease

function approveAuthorizationDecrease(address stakingProvider) external returns (uint96)

Called by the application at its discretion to approve the previously requested authorization decrease request. Can only be called by the application that was previously requested to decrease the authorization for that staking provider. Returns resulting authorized amount for the application.

forceDecreaseAuthorization

function forceDecreaseAuthorization(address stakingProvider, address application) external

Decreases the authorization for the given stakingProvider on the given disabled application, for all authorized amount. Can be called by anyone.

pauseApplication

function pauseApplication(address application) external

Pauses the given application’s eligibility to slash stakes. Besides that stakers can't change authorization to the application. Can be called only by the Panic Button of the particular application. The paused application can not slash stakes until it is approved again by the Governance using approveApplication function. Should be used only in case of an emergency.

disableApplication

function disableApplication(address application) external

Disables the given application. The disabled application can't slash stakers. Also stakers can't increase authorization to that application but can decrease without waiting by calling requestAuthorizationDecrease at any moment. Can be called only by the governance. The disabled application can't be approved again. Should be used only in case of an emergency.

setPanicButton

function setPanicButton(address application, address panicButton) external

Sets the Panic Button role for the given application to the provided address. Can only be called by the Governance. If the Panic Button for the given application should be disabled, the role address should be set to 0x0 address.

setAuthorizationCeiling

function setAuthorizationCeiling(uint256 ceiling) external

Sets the maximum number of applications one staking provider can have authorized. Used to protect against DoSing slashing queue. Can only be called by the Governance.

topUp

function topUp(address stakingProvider, uint96 amount) external

Increases the amount of the stake for the given staking provider.

The sender of this transaction needs to have the amount approved to transfer to the staking contract.

topUpKeep

function topUpKeep(address stakingProvider) external

Propagates information about stake top-up from the legacy KEEP staking contract to T staking contract. Can be called only by the owner or the staking provider.

topUpNu

function topUpNu(address stakingProvider) external

Propagates information about stake top-up from the legacy NU staking contract to T staking contract. Can be called only by the owner or the staking provider.

unstakeT

function unstakeT(address stakingProvider, uint96 amount) external

Reduces the liquid T stake amount by the provided amount and withdraws T to the owner. Reverts if there is at least one authorization higher than the sum of the legacy stake and remaining liquid T stake or if the unstake amount is higher than the liquid T stake amount. Can be called only by the delegation owner or the staking provider.

unstakeKeep

function unstakeKeep(address stakingProvider) external

Sets the legacy KEEP staking contract active stake amount cached in T staking contract to 0. Reverts if the amount of liquid T staked in T staking contract is lower than the highest application authorization. This function allows to unstake from KEEP staking contract and still being able to operate in T network and earning rewards based on the liquid T staked. Can be called only by the delegation owner or the staking provider.

unstakeNu

function unstakeNu(address stakingProvider, uint96 amount) external

Reduces cached legacy NU stake amount by the provided amount. Reverts if there is at least one authorization higher than the sum of remaining legacy NU stake and liquid T stake for that staking provider or if the untaked amount is higher than the cached legacy stake amount. If succeeded, the legacy NU stake can be partially or fully undelegated on the legacy staking contract. This function allows to unstake from NU staking contract and still being able to operate in T network and earning rewards based on the liquid T staked. Can be called only by the delegation owner or the staking provider.

unstakeAll

function unstakeAll(address stakingProvider) external

Sets cached legacy stake amount to 0, sets the liquid T stake amount to 0 and withdraws all liquid T from the stake to the owner. Reverts if there is at least one non-zero authorization. Can be called only by the delegation owner or the staking provider.

notifyKeepStakeDiscrepancy

function notifyKeepStakeDiscrepancy(address stakingProvider) external

Notifies about the discrepancy between legacy KEEP active stake and the amount cached in T staking contract. Slashes the staking provider in case the amount cached is higher than the actual active stake amount in KEEP staking contract. Needs to update authorizations of all affected applications and execute an involuntary allocation decrease on all affected applications. Can be called by anyone, notifier receives a reward.

notifyNuStakeDiscrepancy

function notifyNuStakeDiscrepancy(address stakingProvider) external

Notifies about the discrepancy between legacy NU active stake and the amount cached in T staking contract. Slashes the staking provider in case the amount cached is higher than the actual active stake amount in NU staking contract. Needs to update authorizations of all affected applications and execute an involuntary allocation decrease on all affected applications. Can be called by anyone, notifier receives a reward.

setStakeDiscrepancyPenalty

function setStakeDiscrepancyPenalty(uint96 penalty, uint256 rewardMultiplier) external

Sets the penalty amount for stake discrepancy and reward multiplier for reporting it. The penalty is seized from the delegated stake, and 5% of the penalty, scaled by the multiplier, is given to the notifier. The rest of the tokens are burned. Can only be called by the Governance. See seize function.

setNotificationReward

function setNotificationReward(uint96 reward) external

Sets reward in T tokens for notification of misbehaviour of one staking provider. Can only be called by the governance.

pushNotificationReward

function pushNotificationReward(uint96 reward) external

Transfer some amount of T tokens as reward for notifications of misbehaviour

withdrawNotificationReward

function withdrawNotificationReward(address recipient, uint96 amount) external

Withdraw some amount of T tokens from notifiers treasury. Can only be called by the governance.

slash

function slash(uint96 amount, address[] stakingProviders) external

Adds staking providers to the slashing queue along with the amount that should be slashed from each one of them. Can only be called by application authorized for all staking providers in the array.

seize

function seize(uint96 amount, uint256 rewardMultipier, address notifier, address[] stakingProviders) external

Adds staking providers to the slashing queue along with the amount. The notifier will receive reward per each staking provider from notifiers treasury. Can only be called by application authorized for all staking providers in the array.

processSlashing

function processSlashing(uint256 count) external

Takes the given number of queued slashing operations and processes them. Receives 5% of the slashed amount. Executes involuntaryAllocationDecrease function on each affected application.

authorizedStake

function authorizedStake(address stakingProvider, address application) external view returns (uint96)

Returns the authorized stake amount of the staking provider for the application.

stakes

function stakes(address stakingProvider) external view returns (uint96 tStake, uint96 keepInTStake, uint96 nuInTStake)

Returns staked amount of T, Keep and Nu for the specified staking provider.

All values are in T denomination

getStartStakingTimestamp

function getStartStakingTimestamp(address stakingProvider) external view returns (uint256)

Returns start staking timestamp.

This value is set at most once.

stakedNu

function stakedNu(address stakingProvider) external view returns (uint256)

Returns staked amount of NU for the specified staking provider.

rolesOf

function rolesOf(address stakingProvider) external view returns (address owner, address payable beneficiary, address authorizer)

Gets the stake owner, the beneficiary and the authorizer for the specified staking provider address.

Return Values

Name
Type
Description

owner

address

Stake owner address.

beneficiary

address payable

Beneficiary address.

authorizer

address

Authorizer address.

getApplicationsLength

function getApplicationsLength() external view returns (uint256)

Returns length of application array

getSlashingQueueLength

function getSlashingQueueLength() external view returns (uint256)

Returns length of slashing queue

getMinStaked

function getMinStaked(address stakingProvider, enum IStaking.StakeType stakeTypes) external view returns (uint96)

Returns minimum possible stake for T, KEEP or NU in T denomination.

For example, suppose the given staking provider has 10 T, 20 T worth of KEEP, and 30 T worth of NU all staked, and the maximum application authorization is 40 T, then getMinStaked for that staking provider returns:

  • 0 T if KEEP stake type specified i.e. min = 40 T max - (10 T + 30 T worth of NU) = 0 T

  • 10 T if NU stake type specified i.e. min = 40 T max - (10 T + 20 T worth of KEEP) = 10 T

  • 0 T if T stake type specified i.e. min = 40 T max - (20 T worth of KEEP + 30 T worth of NU) < 0 T In other words, the minimum stake amount for the specified stake type is the minimum amount of stake of the given type needed to satisfy the maximum application authorization given the staked amounts of the other stake types for that staking provider.

getAvailableToAuthorize

function getAvailableToAuthorize(address stakingProvider, address application) external view returns (uint96)

Returns available amount to authorize for the specified application

BeaconDkg

BeaconDkg

This file documents a contract which is not yet deployed to Mainnet.

Parameters

Data

Result

State

groupSize

Size of a group in the threshold relay.

offchainDkgTime

Time in blocks after which DKG result is complete and ready to be

DkgStarted

DkgResultSubmitted

DkgTimedOut

DkgResultApproved

DkgResultChallenged

DkgStateLocked

DkgSeedTimedOut

init

Initializes SortitionPool and DKGValidator addresses. Can be performed only once.

Parameters

Name
Type
Description

currentState

Determines the current state of group creation. It doesn't take timeouts into consideration. The timeouts should be tracked and notified separately.

lockState

Locks the sortition pool and starts awaiting for the group creation seed.

start

submitResult

Allows to submit a DKG result. The submitted result does not go through a validation and before it gets accepted, it needs to wait through the challenge period during which everyone has a chance to challenge the result as invalid one. Submitter of the result needs to be in the sortition pool and if the result gets challenged, the submitter will get slashed.

hasDkgTimedOut

Checks if DKG timed out. The DKG timeout period includes time required for off-chain protocol execution and time for the result publication. After this time a result cannot be submitted and DKG can be notified about the timeout. DKG period is adjusted by result submission offset that include blocks that were mined while invalid result has been registered until it got challenged.

Return Values

Name
Type
Description

notifyTimeout

Notifies about DKG timeout.

notifySeedTimedOut

Notifies about the seed was not delivered and restores the initial DKG state (IDLE).

approveResult

Approves DKG result. Can be called when the challenge period for the submitted result is finished. Considers the submitted result as valid. For the first submitterPrecedencePeriodLength blocks after the end of the challenge period can be called only by the DKG result submitter. After that time, can be called by anyone.

Can be called after a challenge period for the submitted result.

Parameters

Name
Type
Description

Return Values

Name
Type
Description

challengeResult

Challenges DKG result. If the submitted result is proved to be invalid it reverts the DKG back to the result submission phase.

Can be called during a challenge period for the submitted result.

Parameters

Name
Type
Description

Return Values

Name
Type
Description

requireChallengeExtraGas

Due to EIP150, 1/64 of the gas is not forwarded to the call, and will be kept to execute the remaining operations in the function after the call inside the try-catch.

To ensure there is no way for the caller to manipulate gas limit in such a way that the call inside try-catch fails with out-of-gas and the rest of the function is executed with the remaining 1/64 of gas, we require an extra gas amount to be left at the end of the call to the function challenging DKG result and wrapping the call to BeaconDkgValidator and TokenStaking contracts inside a try-catch.

setParameters

Updates DKG-related parameters

Parameters

Name
Type
Description

complete

Completes DKG by cleaning up state.

Should be called after DKG times out or a result is approved.

Relay

Relay

This file documents a contract which is not yet deployed to Mainnet.

Data

relaySeed

Seed used as the first relay entry value. It's a G1 point G * PI = G * 31415926535897932384626433832795028841971693993751058209749445923078164062862 Where G is the generator of G1 abstract cyclic group.

RelayEntryRequested

RelayEntrySubmitted

RelayEntryTimedOut

initSeedEntry

Initializes the very first previousEntry with an initial relaySeed value. Can be performed only once.

requestEntry

Creates a request to generate a new relay entry, which will include a random number (by signing the previous entry's random number).

Parameters

Name
Type
Description

submitEntryBeforeSoftTimeout

Creates a new relay entry. Gas-optimized version that can be called only before the soft timeout. This should be the majority of cases.

Parameters

Name
Type
Description

submitEntry

Creates a new relay entry. Can be called at any time. In case the soft timeout has not been exceeded, it is more gas-efficient to call the second variation of submitEntry.

Parameters

Name
Type
Description

Return Values

Name
Type
Description

_submitEntry

calculateSlashingAmount

Calculates the slashing amount for all group members.

Must be used when a soft timeout was hit.

Return Values

Name
Type
Description

setTimeouts

Updates relay-related parameters

Parameters

Name
Type
Description

setRelayEntrySubmissionFailureSlashingAmount

Set relayEntrySubmissionFailureSlashingAmount parameter.

Parameters

Name
Type
Description

retryOnEntryTimeout

Retries the current relay request in case a relay entry timeout was reported.

Parameters

Name
Type
Description

cleanupOnEntryTimeout

Cleans up the current relay request in case a relay entry timeout was reported.

isRequestInProgress

Returns whether a relay entry request is currently in progress.

Return Values

Name
Type
Description

hasRequestTimedOut

Returns whether the current relay request has timed out.

Return Values

Name
Type
Description

softTimeoutBlock

Calculates soft timeout block for the pending relay request.

Return Values

Name
Type
Description
struct Parameters {
  uint256 resultChallengePeriodLength;
  uint256 resultChallengeExtraGas;
  uint256 resultSubmissionTimeout;
  uint256 submitterPrecedencePeriodLength;
}
struct Data {
  contract SortitionPool sortitionPool;
  contract BeaconDkgValidator dkgValidator;
  struct BeaconDkg.Parameters parameters;
  uint256 startBlock;
  uint256 seed;
  uint256 resultSubmissionStartBlockOffset;
  bytes32 submittedResultHash;
  uint256 submittedResultBlock;
}
struct Result {
  uint256 submitterMemberIndex;
  bytes groupPubKey;
  uint8[] misbehavedMembersIndices;
  bytes signatures;
  uint256[] signingMembersIndices;
  uint32[] members;
  bytes32 membersHash;
}
enum State {
  IDLE,
  AWAITING_SEED,
  KEY_GENERATION,
  AWAITING_RESULT,
  CHALLENGE
}
uint256 groupSize
uint256 offchainDkgTime
event DkgStarted(uint256 seed)
event DkgResultSubmitted(bytes32 resultHash, uint256 seed, struct BeaconDkg.Result result)
event DkgTimedOut()
event DkgResultApproved(bytes32 resultHash, address approver)
event DkgResultChallenged(bytes32 resultHash, address challenger, string reason)
event DkgStateLocked()
event DkgSeedTimedOut()
function init(struct BeaconDkg.Data self, contract SortitionPool _sortitionPool, contract BeaconDkgValidator _dkgValidator) internal

self

struct BeaconDkg.Data

_sortitionPool

contract SortitionPool

Sortition Pool reference

_dkgValidator

contract BeaconDkgValidator

DKGValidator reference

function currentState(struct BeaconDkg.Data self) internal view returns (enum BeaconDkg.State state)
function lockState(struct BeaconDkg.Data self) internal
function start(struct BeaconDkg.Data self, uint256 seed) internal
function submitResult(struct BeaconDkg.Data self, struct BeaconDkg.Result result) external
function hasDkgTimedOut(struct BeaconDkg.Data self) internal view returns (bool)

[0]

bool

True if DKG timed out, false otherwise.

function notifyTimeout(struct BeaconDkg.Data self) internal
function notifySeedTimedOut(struct BeaconDkg.Data self) internal
function approveResult(struct BeaconDkg.Data self, struct BeaconDkg.Result result) external returns (uint32[] misbehavedMembers)

self

struct BeaconDkg.Data

result

struct BeaconDkg.Result

Result to approve. Must match the submitted result stored during submitResult.

misbehavedMembers

uint32[]

Identifiers of members who misbehaved during DKG.

function challengeResult(struct BeaconDkg.Data self, struct BeaconDkg.Result result) external returns (bytes32 maliciousResultHash, uint32 maliciousSubmitter)

self

struct BeaconDkg.Data

result

struct BeaconDkg.Result

Result to challenge. Must match the submitted result stored during submitResult.

maliciousResultHash

bytes32

Hash of the malicious result.

maliciousSubmitter

uint32

Identifier of the malicious submitter.

function requireChallengeExtraGas(struct BeaconDkg.Data self) internal view
function setParameters(struct BeaconDkg.Data self, uint256 _resultChallengePeriodLength, uint256 _resultChallengeExtraGas, uint256 _resultSubmissionTimeout, uint256 _submitterPrecedencePeriodLength) internal

self

struct BeaconDkg.Data

_resultChallengePeriodLength

uint256

New value of the result challenge period length. It is the number of blocks for which a DKG result can be challenged.

_resultChallengeExtraGas

uint256

New value of the result challenge extra gas. It is the extra gas required to be left at the end of the challenge DKG result transaction.

_resultSubmissionTimeout

uint256

New value of the result submission timeout in seconds. It is a timeout for a group to provide a DKG result.

_submitterPrecedencePeriodLength

uint256

New value of the submitter precedence period length in blocks. It is the time during which only the original DKG result submitter can approve it.

function complete(struct BeaconDkg.Data self) internal
struct Data {
  uint64 requestCount;
  uint64 currentRequestID;
  uint64 currentRequestGroupID;
  uint64 currentRequestStartBlock;
  struct AltBn128.G1Point previousEntry;
  uint32 relayEntrySoftTimeout;
  uint32 relayEntryHardTimeout;
  uint96 relayEntrySubmissionFailureSlashingAmount;
}
bytes relaySeed
event RelayEntryRequested(uint256 requestId, uint64 groupId, bytes previousEntry)
event RelayEntrySubmitted(uint256 requestId, address submitter, bytes entry)
event RelayEntryTimedOut(uint256 requestId, uint64 terminatedGroupId)
function initSeedEntry(struct Relay.Data self) internal
function requestEntry(struct Relay.Data self, uint64 groupId) internal

self

struct Relay.Data

groupId

uint64

Identifier of the group chosen to handle the request.

function submitEntryBeforeSoftTimeout(struct Relay.Data self, bytes entry, bytes groupPubKey) internal

self

struct Relay.Data

entry

bytes

Group BLS signature over the previous entry.

groupPubKey

bytes

Public key of the group which signed the relay entry.

function submitEntry(struct Relay.Data self, bytes entry, bytes groupPubKey) internal returns (uint96)

self

struct Relay.Data

entry

bytes

Group BLS signature over the previous entry.

groupPubKey

bytes

Public key of the group which signed the relay entry.

[0]

uint96

slashingAmount Amount by which group members should be slashed in case the relay entry was submitted after the soft timeout. The value is zero if entry was submitted on time.

function _submitEntry(struct Relay.Data self, bytes entry, bytes groupPubKey) internal
function calculateSlashingAmount(struct Relay.Data self) internal view returns (uint96)

[0]

uint96

Amount by which group members should be slashed in case the relay entry was submitted after the soft timeout.

function setTimeouts(struct Relay.Data self, uint256 _relayEntrySoftTimeout, uint256 _relayEntryHardTimeout) internal

self

struct Relay.Data

_relayEntrySoftTimeout

uint256

New relay entry soft timeout value. It is the time in blocks during which a result is expected to be submitted so that the group is not slashed.

_relayEntryHardTimeout

uint256

New relay entry hard timeout value. It is the time in blocks for a group to submit the relay entry before slashing for the full slashing amount happens.

function setRelayEntrySubmissionFailureSlashingAmount(struct Relay.Data self, uint96 newRelayEntrySubmissionFailureSlashingAmount) internal

self

struct Relay.Data

newRelayEntrySubmissionFailureSlashingAmount

uint96

New value of the parameter.

function retryOnEntryTimeout(struct Relay.Data self, uint64 newGroupId) internal

self

struct Relay.Data

newGroupId

uint64

ID of the group chosen to retry the current request.

function cleanupOnEntryTimeout(struct Relay.Data self) internal
function isRequestInProgress(struct Relay.Data self) internal view returns (bool)

[0]

bool

True if there is a request in progress. False otherwise.

function hasRequestTimedOut(struct Relay.Data self) internal view returns (bool)

[0]

bool

True if the request timed out. False otherwise.

function softTimeoutBlock(struct Relay.Data self) internal view returns (uint256)

[0]

uint256

The soft timeout block

Docker Installation

This page will guide you through Docker setup steps.

Install Docker

Install or update Docker to the latest version. Visit the Official Docker website for detailed instructions. Use the command below to find your installed version if needed:

docker --version

Docker and Security Best Practices

General best practices recommend against running the tBTC v2 client as the root user as a security precaution. One security-minded approach is to Run the Docker daemon as a non-root user (Rootless mode).

Next, choose ONE of the following options: Docker Compose makes managing the container simple, but requires an additional step to ensure the container starts after a reboot. The tBTC v2 Service option configures the client to run as a service in order to ensure that the client is restarted automatically, should your machine reboot. The Docker Launch Script is faster to setup, but won't start the client if your machine reboots.

Navigate to cd /home/$USER/keep/

Create a file named docker-compose.yaml

Copy the the template below into the file and replace the <Placeholders> with respective details.

version: '3'
services:
  keep-client:
    image: keepnetwork/keep-client:latest
    container_name: keep-client
    restart: on-failure
    ports:
      - "3919:3919"
      - "9601:9601"
    volumes:
      - /home/$USER/keep/config/:/mnt/keep/config
      - /home/$USER/keep/storage/:/mnt/keep/storage
    environment:
      - KEEP_ETHEREUM_PASSWORD=<Operator Account keyfile password>
      - LOG_LEVEL=info
    logging:
      options:
        max-size: "100m"
        max-file: "3"
    command: start --ethereum.url "<Ethereum API WS URL>"  --ethereum.keyFile "/mnt/keep/config/<Operator Account keyfile name>" --storage.dir "/mnt/keep/storage" --network.announcedAddresses "/ip4/<PUBLIC_IP_OF_MACHINE>/tcp/3919"

Save and close the file when finished.

To start the tBTC client sudo docker compose up

tBTC will start up with the console output on screen. Verify that the client is running correctly, then press CTRL + c to stop the client.

Restart the client with the 'detach' flag: sudo docker compose up -d

To stop the container at a later time, use sudo docker compose down

Starting Container Automatically After Reboot

To ensure the tBTC container starts after a server reboot, use the template below to create a service in /etc/systemd/system/ called docker-compose-app.service

# /etc/systemd/system/docker-compose-app.service

[Unit]
Description=Docker Compose Application Service
Requires=docker.service
After=docker.service
StartLimitIntervalSec=60

[Service]
WorkingDirectory=/home/$USER/keep/
ExecStart=/usr/local/bin/docker-compose up
ExecStop=/usr/local/bin/docker-compose down
TimeoutStartSec=0
Restart=on-failure
StartLimitBurst=3

[Install]
WantedBy=multi-user.target

Adjust the WorkingDirectory to the path of your tBTC folder. Save and close the file when finished.

use sudo systemctl enable docker-compose-app to enable the service.

Create the tbtcv2.service file:

cd /etc/systemd/system/
nano tbtcv2.service

Paste the following in the tbtcv2.service file:

[Unit]
Description=tBTC v2 client
After=network.target
Wants=network.target

[Service]
Environment="ETHEREUM_WS_URL=<Ethereum API WS URL>"
Environment="OPERATOR_KEY_FILE_NAME=<Operator Account keyfile name>"
Environment="OPERATOR_KEY_FILE_PASSWORD=<Operator Account keyfile password>"
Environment="PUBLIC_IP=/ip4/<PUBLIC_IP_OF_MACHINE>/tcp/3919"
Environment="CONFIG_DIR=/home/<user name>/keep/config"
Environment="STORAGE_DIR=/home/<user name>/keep/storage"

# These items only apply if you setup rootless-mode.
# Do not enable unless using rootless mode. Don't forget to adjust user UID.
#Environment="XDG_RUNTIME_DIR=/run/<user name>/<user UID>/"
#Environment="DOCKER_HOST=unix:///run/<user name>/<user UID>/docker.sock"

Type=simple
WorkingDirectory=/home/$USER

ExecStart=/usr/bin/docker run \
    --volume ${CONFIG_DIR}:/mnt/keep/config \
    --volume ${STORAGE_DIR}:/mnt/keep/storage \
    --env KEEP_ETHEREUM_PASSWORD=${OPERATOR_KEY_FILE_PASSWORD} \
    --env LOG_LEVEL=info \
    --log-opt max-size=100m \
    --log-opt max-file=3 \
    -p 3919:3919 \
    -p 9601:9601 \
    keepnetwork/keep-client:latest \
    start \
    --ethereum.url ${ETHEREUM_WS_URL} \
    --ethereum.keyFile /mnt/keep/config/${OPERATOR_KEY_FILE_NAME} \
    --storage.dir /mnt/keep/storage \
    --network.announcedAddresses $PUBLIC_IP

Restart=always
RestartSec=15s

[Install]
WantedBy=default.target

Replace the placeholders inside the <> brackets, remove the <> brackets but be sure to not edit anything further.

When done, save and close the file.

Next, test to make sure your configuration works:

sudo systemctl start tbtcv2

There will be no console output because it will be running in the background. Use systemctl to get the status of the service:

sudo systemctl status tbtcv2

If the service failed, go back and double check your configuration.

Another option is to see if the Docker container is running:

docker ps

If everything is running as intended, enable the service:

systemctl enable tbtcv2

Now, with the service running, you should make sure that your configuration will tolerate a reboot and start up again automatically.

reboot now

Log back in to your machine and check that the service is running. If it is, you're done. If not, go back and review your work.

Docker Launch Script

To launch the tBTC v2 client, several configuration flags and environmental values need to be set. For simplicity, a bash script can be used rather than typing or pasting all the flags into the console.

Create the launch script:

nano keep.sh

And paste the following:

# Keep tBTC v2 Client
#
# Ethereum endpoint WebSocket URL
# This can be a provider such as Infura, Alchemy, Ankr, etc or your own Geth Nodeq
# ETHEREUM_WS_URL="wss://mainnet.infura.io/ws/v3/redacted_credentials"
# note: only replace characters inside the " ". The Quotation marks must be retained
ETHEREUM_WS_URL="<Ethereum API WS URL>"

# copied to home/keep/config earlier
OPERATOR_KEY_FILE_NAME="<Operator Account keyfile name>"

# password set during Operator Account Address creation
OPERATOR_KEY_FILE_PASSWORD="<Operator Account keyfile password>"

# To configure your node with a Public IP, enter it below.
PUBLIC_IP="<PUBLIC_IP_OF_MACHINE>"
# Alternatively, you can use DNS.
# To configure DNS, modify the last line of the script
# and add your DNS in the following format:
# /dns4/bootstrap-1.test.keep.network/tcp/3919

# Setup configuration and storage directories
# THESE MUST BE PERSISTENT STORAGE
CONFIG_DIR="/home/$USER/keep/config"
STORAGE_DIR="/home/$USER/keep/storage"

docker run \
    --detach \
    --restart on-failure \
    --volume $CONFIG_DIR:/mnt/keep/config \
    --volume $STORAGE_DIR:/mnt/keep/storage \
    --env KEEP_ETHEREUM_PASSWORD=$OPERATOR_KEY_FILE_PASSWORD \
    --env LOG_LEVEL=info \
    --log-opt max-size=100m \
    --log-opt max-file=3 \
    -p 3919:3919 \
    -p 9601:9601 \
    keepnetwork/keep-client:latest \
    start \
    --ethereum.url $ETHEREUM_WS_URL \
    --ethereum.keyFile /mnt/keep/config/$OPERATOR_KEY_FILE_NAME \
    --storage.dir /mnt/keep/storage \
    --network.announcedAddresses /ip4/$PUBLIC_IP/tcp/3919

Save and close the file, and make it executable:

sudo chmod +x keep.sh

To launch the tBTC v2 client, execute:

sudo bash keep.sh

The --detach property will prevent the status messages from the client to be printed to the console. Review the Docker logs for detailed status information.

The path shown in the example configuration will differ from yours. Make sure it is configured correctly.

Client Startup and Logs

Unless the --detach flag was removed from the startup script, there will be no console output. In order to check your node, retrieve the Docker logs.

First, find your Docker instance identification, it'll be a random combination of words, e.g. stinky_brownie:

sudo docker ps

Use your specific identification and substitute:

sudo docker logs stinky_brownie >& /path/to/output/file

Scroll down about half a page, and you should see the following:


▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  ▓▓▓▓▓▓    ▓▓▓▓▓▓▓▀    ▐▓▓▓▓▓▓    ▐▓▓▓▓▓   ▓▓▓▓▓▓     ▓▓▓▓▓   ▐▓▓▓▓▓▌   ▐▓▓▓▓▓▓
  ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀      ▐▓▓▓▓▓▓▄▄▄▄         ▓▓▓▓▓▓▄▄▄▄         ▐▓▓▓▓▓▌   ▐▓▓▓▓▓▓
  ▓▓▓▓▓▓▓▓▓▓▓▓▓▀        ▐▓▓▓▓▓▓▓▓▓▓         ▓▓▓▓▓▓▓▓▓▓▌        ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄       ▐▓▓▓▓▓▓▀▀▀▀         ▓▓▓▓▓▓▀▀▀▀         ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
  ▓▓▓▓▓▓   ▀▓▓▓▓▓▓▄     ▐▓▓▓▓▓▓     ▓▓▓▓▓   ▓▓▓▓▓▓     ▓▓▓▓▓   ▐▓▓▓▓▓▌
▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓

Trust math, not hardware.
	
-----------------------------------------------------------------------------------
| Keep Client Node                                                                |
|                                                                                 |
| Version: Version: vX.X.X-XX (4d745f6d0)                                         |
|                                                                                 |
| Operator: 0x_your_operator_address                                              |
|                                                                                 |
| Port: 3919                                                                      |
| IPs : /ip4/111.222.333.444/tcp/3919/ipfs/redacted                               |
|                                                                                 |
| Contracts:                                                                      |
| RandomBeacon   : 0x5499f54b4A1CB4816eefCf78962040461be3D80b                     |
| WalletRegistry : 0x46d52E41C2F300BC82217Ce22b920c34995204eb                     |
| TokenStaking   : 0x01B67b1194C75264d06F808A921228a95C765dd7                     |
-----------------------------------------------------------------------------------

Congratulations, your node is up and running.

Logo

Overview of thUSD

Background of Threshold USD, Main Parameters and Key Characteristics

Introduction

launched in 2023 the second generation of , a decentralized and scalable bridge between Bitcoin and Ethereum. It provides Bitcoin holders secure and open access to the broader DeFi ecosystem.

An immediate question that comes up is providing utility for the bridged tBTC for DeFi applications. Threshold USD provides an alternative utility to tBTC by allowing to mint thUSD, which then can be used for multiple purposes in a wider ecosystem.

What is Threshold USD?

Threshold USD is a decentralized borrowing protocol that allows you to draw loans using tBTC and ETH as collateral. Loans are paid out in thUSD (a USD pegged stablecoin) and need to maintain a minimum collateral ratio of 110%.

In addition to the collateral, the loans are secured by a Stability Pool containing thUSD and by fellow borrowers collectively acting as guarantors of last resort. Learn more about these mechanisms under .

Threshold USD as a protocol is based on a fork of Liquity (), which has established itself as a successful protocol since its launch on May 2021, going through several severe stress tests successfully.

Liquity is totally governance free and immutable; although that is a tremendous strength for a DeFi Protocol as it makes it completely censorship resistant, it also exposes the protocol to existential oracle risk and limits its ability to scale and respond to market dynamics.

With that in mind some flexibilitiy is embeded into Threshold USD. It has a few parameters that can be changed through governance:

  • Collateral Type: in addition to tBTC other collateral types may be added (ETH will be an additional collateral to tBTC, as it is the most decentralized and abundant asset in Ethereum) and also removed by revoking mint and burn capabilities.

  • Price Feed Contract: Oracles are critical for the health of the protocol (defining liquidations) and may need to be updated due to poor performance of existing oracles

  • PCV Contract: Withdraw from BAMM to PCV, pay the debt and withdraw funds from PCV after repaying all the debt.

  • BAMM Contract: Dao has veto capability for change submission to the "A" parameter and B.Protocol fees.

This documentation is based on the Liquity documents () with the appropriate changes that are relevant to Threshold USD.

What’s the motivation behind Threshold USD?

Stable-value assets are an essential building block for Ethereum applications and have grown to represent tens of billions of dollars in value.

However, the vast majority of this value is in the form of fiat-collateralized stablecoins like USDT and USDC. Decentralized stablecoins like DAI and sUSD make up only a small portion of the total stablecoin supply, meaning the vast majority of stablecoins are centralized.

Threshold USD addresses this by creating a more capital efficient and user-friendly way to borrow stablecoins.

What are the key benefits of Threshold USD?

Threshold USD’s key benefits include:

  • Low cost for tBTC loan (0.5% issuance fee and no interest rate at launch).

  • Minimum collateral ratio of 110% — more efficient usage of deposited tBTC

  • Governance Minimized— most operations are algorithmic and fully automated, and most protocol parameters are set at time of contract deployment.

    • Note: See section above for parameters that can be changed through governance

  • Directly redeemable — thUSD can be redeemed at face value for the underlying collateral at any time

Can Threshold USD be upgraded or changed?

Yes. The Threshold USD token contracts mint function will be able to add additional contracts; this is how we keep the option to upgrade while leaving all the contracts immutable with the exception of the price feed contract.

How can I use Threshold USD?

You can use thUSD via a web interface (aka frontend) or directly via the smart contracts.

What are the main use cases of Threshold USD?

  • Borrow thUSD against tBTC by opening a Vault and use directly in DeFi applications or exchange for other coins

  • Secure Threshold USD by providing thUSD to the Stability Pool in exchange for Liquidation rewards

  • Providing liquidity to the thUSD Pool in Curve () to earn swapping fees and maybe incentive fees (these may be time dependent and could be turned on or off at will)

  • Redeem 1 thUSD for 1 USD worth of tBTC when the thUSD peg falls below $1 (i.e. arbitrage the system)

What is thUSD?

thUSD is the USD-pegged stablecoin used to pay out loans on the Threshold USD protocol. At any time it can be redeemed against the underlying collateral at face value.

What do I need in order to use Threshold USD?

To borrow thUSD, all you need is a wallet (e.g. MetaMask or ) and sufficient tBTC or ETH to open a Vault and pay the gas fees.

To become a Stability Pool depositor you need to have thUSD, which can be borrowed by opening a Vault. You can also use Curve or another (decentralized) exchange to buy the tokens on the open market.

Does Threshold USD charge any fees?

These are the fees for using Threshold USD Protocol:

  • A one-time fee whenever thUSD is borrowed, as a percentage of the drawn amount (in thUSD)

  • A fee when thUSD is redeemed (see Section “Redemptions and thUSD Price Stability” for details on the Redemption Mechanism)

    • For redeemers, there is a redemption fee on the amount paid to users by the system (in tBTC) when exchanging thUSD for tBTC. Note that redemption is separate from repaying your loan as a borrower, which is free of charge

  • Both fees depend on the redemption volumes, i.e. they increase upon every redemption in function of the redeemed amount, and decay over time as long as no redemptions take place. The intent is to throttle large redemptions with higher fees, and to throttle borrowing directly after large redemption volumes. The fee decay over time ensures that the fee for both borrowers and redeemers will “cool down”, while redemptions volumes are low.

  • The fees cannot become smaller than 0.5% (except in Recovery Mode), which protects the redemption facility from being misused by arbitrageurs front-running the price feed. The borrowing fee is capped at 5%, keeping the system (somewhat) attractive for borrowers even in phases where the monetary is contracting due to redemptions. Other than that, the two fees are identical.

  • Interest rate: at launch no ongoing interest rate is charged when using ETH and tBTC as collateral, only the initial borrowing fee.

    The decision to incorporate an ongoing interest rate that will accrue on the loan amount in thUSD, which is calculated based on a predetermined interest rate and the amount of time that has elapsed, will be made by the DAO in the near future for these and any other future collaterals.

How can I earn money using Threshold USD?

There are two main ways to generate revenue using Threshold USD:

  • Providing liquidity to the thUSD Pool in Curve () to earn swapping fees and maybe incentive fees (these may be time dependent and could be turned on or off at will)

  • Deposit thUSD to the Stability Pool and earn liquidation gains (in tBTC).

Other opportunities may arise in time in the DeFi Ecosystem to use thUSD and earn rewards or yield.

Can I lose my funds?

As a non-custodial system, all the tokens sent to the protocol will be held and managed algorithmically without the interference of any person or legal entity. That means your funds will only be subject to the rules set forth in the smart contract code.

You may lose all or part of your funds in a Liquidation scenario:

  • You are a borrower (Vault owner) and your collateral in tBTC is liquidated. You will still keep your borrowed thUSD, but your Vault will be closed and your collateral will be used to compensate Stability Pool depositors.

    • If the liquidation happens under Normal Mode (i.e. your Vault is at 110% Collateral Ratio), all your Collateral will be used the Liquidation and fees

    • If the liquidation happens under Recovery Mode (i.e. Total Protocol Collateral Ratio is less than 150%), you may recover a part of the Collateral if your Vault is between the 110% and 150% Collateral Ratio)

In addition, the following Scenarios may happen that may lead to loss of funds:

  • Bugs/Hacks: Please note that although the system is diligently audited, a hack or a bug that results in losses for the users can never be fully excluded. This includes potential bugs/hacks on tBTC as it is the collateral for thUSD.

  • Price Oracle Risk: Liquidations are triggered by the Collateral Ratio, which is a function of the Collateral Price. There is a risk of malfunction of manipulation with the Price Feed. We are minimizing the exposure to those risks, but they cannot be completely ruled out

  • If the Threshold USD is not accepted and all users want to leave, the last fraction of collateral cannot be recovered as there won’t be enough thUSD available (due to Loan fees accrued). This would only affect a very minor fraction of capital

  • For redemptions, in case of an acceleration of redemptions, the redemption fee increases and can lead to losses. If the Total Collateral Ratio is less than 110%, redemptions are blocked

Threshold Protocol
tBTC
Liquidation
https://www.liquity.org/
https://docs.liquity.org/
https://curve.fi/
TaHo
https://curve.fi/

Testnet tBTC v2 node Setup

This page will show you how to launch a tBTC v2 node on the testnet.

This is a TESTNET guide document. Following this document will not result in a node enabling you to earn mainnet rewards.

Recommended Machine Types

While it is possible to run the client on a local machine, this is not recommended.

Your operating environment will ultimately dictate what machine type to go with. This is particularly relevant if you’re running a containerized solution where multiple applications are sharing VM resources. The below types are sufficient for running one instance of the tBTC v2 Node.

The preferred OS is Ubuntu.

VPS Provider
VPS Type

AWS

c5.large

Azure

F2s v2

Google Cloud

n2-highcpu-2

Self-hosted

2 vCPU / 2 GB RAM / 1 GiB Persistent Storage

Ethereum API

A Keep Node requires a connection to a WebSocket Ethereum API. You should obtain a WS API URL from a service provider (e.g. Alchemy, Infura, Ankr) or run your own Ethereum node (e.g. Geth).

The client requires an Ethereum Key File of an Operator Account to connect to the Ethereum chain. This account is created in a subsequent step using Geth (GoEthereum).

The Ethereum Key File is expected to be encrypted with a password. The password has to be provided in a prompt after the client starts or configured as a KEEP_ETHEREUM_PASSWORD environment variable.

The Operator Account has to maintain a positive Ether balance at all times.

Please do NOT reuse an operator account that is being used for PRE or other applications.

Install Geth (GoEthereum)

To create a new Ethereum account, install Geth (GoEthereum) and create a new account using the command below. This account will subsequently be referred to as the Operator Account.

geth account new --keystore ./operator-key

When prompted, provide a password to protect the operator key file.

Use a password manager to generate a strong password and store it safely. It will be needed again during setup.

Avoid passwords that contain the following characters: ', ", `, $ These characters may be interpreted as part of the configuration which can lead to undesirable outcomes that may be extremely time intensive to correct.

Once the process completes, your public key will be displayed. Take note of your Operator Account public key.

DO NOT LOSE THE PASSWORD TO THE OPERATOR ACCOUNT.

Funding your Operator Account

Your Operator Account will need to be funded with sepolia ETH and maintain a positive balance at all times to ensure proper operation and availability of your tBTC v2 node.

Network Configuration

The node has to be accessible publicly to establish and maintain connections with bootstrap nodes and discovered peers.

Update firewall rules as necessary, including application level firewalls.

The node exposes metrics and diagnostics services for monitoring. A network port has to be exposed publicly, so the peers can connect to your node. A Diagnostics Port has to be exposed publicly, for the rewards allocation.

Purpose
Config Property
Protocol
Default

Network

network.port

TCP

3919

Status

clientInfo.port

TCP

9601

A Diagnostics Port has to be exposed publicly, for the rewards allocation.

Announced Addresses

An Announced Address is a layered addressing information (multiaddress/multiaddr) announced to the Threshold Network that is used by peers to connect with your node, e.g.: /dns4/bootstrap-0.test.keep.network/tcp/3919 or /ip4/104.154.61.116/tcp/3919.

If the machine you’re running your node is not exposing a public IP (e.g. it is behind NAT) you should set the network.AnnouncedAddresses (flag: --network.announcedAddresses) configuration property to an addresses (ip4 or dns4) under which your node is reachable for the public.

To read more about multiaddress see the libp2p docummentation.

One important step to get your node operating on the Threshold Network is proper application authorization as well as operator account registration. Applications need only be authorized once.

It is CRITICALLY important that both the tBTC application as well as the Random Beacon applications are authorized. A node cannot be deployed without both applications being properly authorized.

Please note: by authorizing these applications, an unbonding period of 45 days will go into effect on the T you stake for these applications. This cool-down period begins the day you submit an unstake request.

Application Authorization

To get started, visit the Threshold Dashboard and connect your wallet.

Threshold Dashboard

Click on "Configure Apps"

Authorizing Threshold Applications

Select BOTH tBTC and Random Beacon applications and enter your desired amount of T to stake per application. Note that the minimum is 40,000T.

Authorizing Threshold Applications

Operator Registration

The operator account is the Ethereum account created on your node. In order for the network to associate your T stake with your node, you must register your Operator Address.

An Operator for the Provider registration can be submitted just once. The Operator address assignment cannot be updated.

Registering Operator Address

After both applications have been authorized, click on "Start Mapping" to begin the Operator Registration process.

Registering Operator Address

Enter your Operator Address in the field provided and click on "Map Address."

Once the steps above have been successfully completed, you are ready to move on to the next step in the node deployment process.

Don't forget: a tBTC v2 node will not be able to be deployed without successfully authorizing both the tBTC and Random Beacon applications, and registering the node's operator address FIRST.

Create Folder Structure

The client requires two persistent directories. These directories will store configuration files and data generated and used by the client. It is highly recommended to create frequent backups of these directories. Loss of these data may be catastrophic and may lead to slashing.

It is crucial to ensure the data directory is persisted and backed up on a regular basis.

Create folders for tBTC v2 client

cd /home
mkdir keep
cd keep
mkdir storage config

The tBTC v2 client will create two subdirectories within the storage directory: keystore and work. You do not need to create these.

The keystore subdirectory contains sensitive key material data generated by the client. Loosing the keystore data is a serious protocol offense and leads to slashing and potentially losing funds.

It is the operator’s responsibility to ensure the keystore data are not lost under any circumstances.

The work directory contains data generated by the client that should persist the client restarts or relocations. If the work data are lost the client will be able to recreate them, but it is inconvenient due to the time needed for the operation to complete and may lead to losing rewards.

Copy Operator keystore file

Assuming Geth was installed for the root user with the command provided in the Operator Account creation step, the operator-key file should be located in the ~/operator-key directory.

cd ~/operator-key

Contained within the operator-key directory is the account key file (operator key file), its name will be similar to the following: UTC--2018-11-01T06-23-57.810787758Z--fa3da235947aab49d439f3bcb46effd1a7237e32

copy (not move!) this account key file to the config directory created above

ls -la
cp name_of_account_key_file /home/keep/config/name_of_account_key_file

Install Docker

Install or update Docker to the latest version. Visit the Official Docker website for detailed instructions. Use the command below to find your installed version if needed:

docker --version

Docker Launch Script

To launch the tBTC v2 client, several configuration flags and environmental values need to be set. For simplicity, a bash script can be used rather than typing or pasting all the flags into the console.

Create the launch script:

nano keep.sh

And paste the following:

# Keep Testnet tBTC v2 Client
#
# Ethereum endpoint WebSocket URL
# This can be a provider such as Infura, Alchemy, Ankr, etc or your own Geth Nodeq
# ETHEREUM_WS_URL="wss://sepolia.infura.io/ws/v3/redacted_credentials"
# note: only replace characters inside the " ". The Quotation marks must be retained
ETHEREUM_WS_URL="<Ethereum API WS URL>"

# copied to home/keep/config earlier
OPERATOR_KEY_FILE_NAME="<Operator Account keyfile name>"

# password set during Operator Account Address creation
OPERATOR_KEY_FILE_PASSWORD="<Operator Account keyfile password>"

# To configure your node with a Public IP, enter it below.
PUBLIC_IP="<PUBLIC_IP_OF_MACHINE>"
# Alternatively, you can use DNS.
# To configure DNS, modify the last line of the script
# and add your DNS in the following format:
# /dns4/bootstrap-1.test.keep.network/tcp/3919

# Setup configuration and storage directories
# THESE MUST BE PERSISTENT STORAGE
CONFIG_DIR="/home/keep/config"
STORAGE_DIR="/home/keep/storage"

docker run \
    --detach \
    --restart on-failure \
    --volume $CONFIG_DIR:/mnt/keep/config \
    --volume $STORAGE_DIR:/mnt/keep/storage \
    --env KEEP_ETHEREUM_PASSWORD=$OPERATOR_KEY_FILE_PASSWORD \
    --env LOG_LEVEL=info \
    --log-opt max-size=100m \
    --log-opt max-file=3 \
    -p 3919:3919 \
    -p 9601:9601 \
    us-docker.pkg.dev/keep-test-f3e0/public/keep-client \
    start \
    --testnet \
    --ethereum.url $ETHEREUM_WS_URL \
    --ethereum.keyFile /mnt/keep/config/$OPERATOR_KEY_FILE_NAME \
    --storage.dir /mnt/keep/storage \
    --network.announcedAddresses /ip4/$PUBLIC_IP/tcp/3919

Save and close the file, and make it executable:

sudo chmod +x keep.sh

To launch the tBTC v2 client, execute:

sudo bash keep.sh

The --detach property will prevent the status messages from the client to be printed to the console. Review the Docker logs for detailed status information.

The path shown in the example configuration will differ from yours. Make sure it is configured correctly.

Client Startup

Unless the --detach flag was removed from the startup script, there will be no console output. In order to check your node, retrieve the Docker logs.

First, find your Docker instance identification, it'll be a random combination of words, e.g. stinky_brownie:

sudo docker ps

Use your specific identification and substitute:

sudo docker logs stinky_brownie >& /path/to/output/file

Scroll down about half a page, and you should see the following:


▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  ▓▓▓▓▓▓    ▓▓▓▓▓▓▓▀    ▐▓▓▓▓▓▓    ▐▓▓▓▓▓   ▓▓▓▓▓▓     ▓▓▓▓▓   ▐▓▓▓▓▓▌   ▐▓▓▓▓▓▓
  ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀      ▐▓▓▓▓▓▓▄▄▄▄         ▓▓▓▓▓▓▄▄▄▄         ▐▓▓▓▓▓▌   ▐▓▓▓▓▓▓
  ▓▓▓▓▓▓▓▓▓▓▓▓▓▀        ▐▓▓▓▓▓▓▓▓▓▓         ▓▓▓▓▓▓▓▓▓▓▌        ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄       ▐▓▓▓▓▓▓▀▀▀▀         ▓▓▓▓▓▓▀▀▀▀         ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
  ▓▓▓▓▓▓   ▀▓▓▓▓▓▓▄     ▐▓▓▓▓▓▓     ▓▓▓▓▓   ▓▓▓▓▓▓     ▓▓▓▓▓   ▐▓▓▓▓▓▌
▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓

Trust math, not hardware.
	
-----------------------------------------------------------------------------------
| Keep Client Node                                                                |
|                                                                                 |
| Version: Version: vX.X.XX (4d745f6d0)                                         |
|                                                                                 |
| Operator: 0x_your_operator_address                                              |
|                                                                                 |
| Port: 3919                                                                      |
| IPs : /ip4/111.222.333.444/tcp/3919/ipfs/redacted                               |
|                                                                                 |
| Contracts:                                                                      |
| RandomBeacon   : 0x2bA82903B635a96154A515488d2952E86D6adc3A                     |
| WalletRegistry : 0x2363cc10b7680000C02E4a7067A68d1788ffc86F                     |
| TokenStaking   : 0x69f962a0fbA5635e84eC94131f9072108E2E4F24                     |
-----------------------------------------------------------------------------------

Congratulations, your node is up and running.

TokenStaking

TokenStaking

TokenStaking is the main staking contract of the Threshold Network. Apart from the basic usage of enabling T stakes, it also acts as a sort of "meta-staking" contract, accepting existing legacy NU/KEEP stakes. Additionally, it serves as application manager for the apps that run on the Threshold Network. Note that legacy NU/KEEP staking contracts see TokenStaking as an application (e.g., slashing is requested by TokenStaking and performed by the legacy contracts).

TokenStaking is upgradeable, using OpenZeppelin's Upgradeability framework. As such, it is required to satisfy OZ's guidelines, like restrictions on constructors, immutable variables, base contracts and libraries. See https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable

ApplicationStatus

enum ApplicationStatus {
  NOT_APPROVED,
  APPROVED,
  PAUSED,
  DISABLED
}

StakingProviderInfo

struct StakingProviderInfo {
  uint96 nuInTStake;
  address owner;
  uint96 keepInTStake;
  address payable beneficiary;
  uint96 tStake;
  address authorizer;
  mapping(address => struct TokenStaking.AppAuthorization) authorizations;
  address[] authorizedApplications;
  uint256 startStakingTimestamp;
}

AppAuthorization

struct AppAuthorization {
  uint96 authorized;
  uint96 deauthorizing;
}

ApplicationInfo

struct ApplicationInfo {
  enum TokenStaking.ApplicationStatus status;
  address panicButton;
}

SlashingEvent

struct SlashingEvent {
  address stakingProvider;
  uint96 amount;
}

SLASHING_REWARD_PERCENT

uint256 SLASHING_REWARD_PERCENT

MIN_STAKE_TIME

uint256 MIN_STAKE_TIME

GAS_LIMIT_AUTHORIZATION_DECREASE

uint256 GAS_LIMIT_AUTHORIZATION_DECREASE

CONVERSION_DIVISOR

uint256 CONVERSION_DIVISOR

token

contract T token

keepStakingContract

contract IKeepTokenStaking keepStakingContract

keepStake

contract KeepStake keepStake

nucypherStakingContract

contract INuCypherStakingEscrow nucypherStakingContract

keepRatio

uint256 keepRatio

nucypherRatio

uint256 nucypherRatio

governance

address governance

minTStakeAmount

uint96 minTStakeAmount

authorizationCeiling

uint256 authorizationCeiling

stakeDiscrepancyPenalty

uint96 stakeDiscrepancyPenalty

stakeDiscrepancyRewardMultiplier

uint256 stakeDiscrepancyRewardMultiplier

notifiersTreasury

uint256 notifiersTreasury

notificationReward

uint256 notificationReward

stakingProviders

mapping(address => struct TokenStaking.StakingProviderInfo) stakingProviders

applicationInfo

mapping(address => struct TokenStaking.ApplicationInfo) applicationInfo

applications

address[] applications

slashingQueue

struct TokenStaking.SlashingEvent[] slashingQueue

slashingQueueIndex

uint256 slashingQueueIndex

Staked

event Staked(enum IStaking.StakeType stakeType, address owner, address stakingProvider, address beneficiary, address authorizer, uint96 amount)

MinimumStakeAmountSet

event MinimumStakeAmountSet(uint96 amount)

ApplicationStatusChanged

event ApplicationStatusChanged(address application, enum TokenStaking.ApplicationStatus newStatus)

AuthorizationIncreased

event AuthorizationIncreased(address stakingProvider, address application, uint96 fromAmount, uint96 toAmount)

AuthorizationDecreaseRequested

event AuthorizationDecreaseRequested(address stakingProvider, address application, uint96 fromAmount, uint96 toAmount)

AuthorizationDecreaseApproved

event AuthorizationDecreaseApproved(address stakingProvider, address application, uint96 fromAmount, uint96 toAmount)

AuthorizationInvoluntaryDecreased

event AuthorizationInvoluntaryDecreased(address stakingProvider, address application, uint96 fromAmount, uint96 toAmount, bool successfulCall)

PanicButtonSet

event PanicButtonSet(address application, address panicButton)

AuthorizationCeilingSet

event AuthorizationCeilingSet(uint256 ceiling)

ToppedUp

event ToppedUp(address stakingProvider, uint96 amount)

Unstaked

event Unstaked(address stakingProvider, uint96 amount)

TokensSeized

event TokensSeized(address stakingProvider, uint96 amount, bool discrepancy)

StakeDiscrepancyPenaltySet

event StakeDiscrepancyPenaltySet(uint96 penalty, uint256 rewardMultiplier)

NotificationRewardSet

event NotificationRewardSet(uint96 reward)

NotificationRewardPushed

event NotificationRewardPushed(uint96 reward)

NotificationRewardWithdrawn

event NotificationRewardWithdrawn(address recipient, uint96 amount)

NotifierRewarded

event NotifierRewarded(address notifier, uint256 amount)

SlashingProcessed

event SlashingProcessed(address caller, uint256 count, uint256 tAmount)

OwnerRefreshed

event OwnerRefreshed(address stakingProvider, address oldOwner, address newOwner)

GovernanceTransferred

event GovernanceTransferred(address oldGovernance, address newGovernance)

onlyGovernance

modifier onlyGovernance()

onlyPanicButtonOf

modifier onlyPanicButtonOf(address application)

onlyAuthorizerOf

modifier onlyAuthorizerOf(address stakingProvider)

onlyOwnerOrStakingProvider

modifier onlyOwnerOrStakingProvider(address stakingProvider)

onlyOwnerOf

modifier onlyOwnerOf(address stakingProvider)

constructor

constructor(contract T _token, contract IKeepTokenStaking _keepStakingContract, contract INuCypherStakingEscrow _nucypherStakingContract, contract VendingMachine _keepVendingMachine, contract VendingMachine _nucypherVendingMachine, contract KeepStake _keepStake) public

Parameters

Name
Type
Description

_token

contract T

Address of T token contract

_keepStakingContract

contract IKeepTokenStaking

Address of Keep staking contract

_nucypherStakingContract

contract INuCypherStakingEscrow

Address of NuCypher staking contract

_keepVendingMachine

contract VendingMachine

Address of Keep vending machine

_nucypherVendingMachine

contract VendingMachine

Address of NuCypher vending machine

_keepStake

contract KeepStake

Address of Keep contract with grant owners

initialize

function initialize() external

stake

function stake(address stakingProvider, address payable beneficiary, address authorizer, uint96 amount) external

Creates a delegation with msg.sender owner with the given staking provider, beneficiary, and authorizer. Transfers the given amount of T to the staking contract.

The owner of the delegation needs to have the amount approved to transfer to the staking contract.

stakeKeep

function stakeKeep(address stakingProvider) external

Copies delegation from the legacy KEEP staking contract to T staking contract. No tokens are transferred. Caches the active stake amount from KEEP staking contract. Can be called by anyone.

The staking provider in T staking contract is the legacy KEEP staking contract operator.

stakeNu

function stakeNu(address stakingProvider, address payable beneficiary, address authorizer) external

Copies delegation from the legacy NU staking contract to T staking contract, additionally appointing beneficiary and authorizer roles. Caches the amount staked in NU staking contract. Can be called only by the original delegation owner.

setMinimumStakeAmount

function setMinimumStakeAmount(uint96 amount) external

Allows the Governance to set the minimum required stake amount. This amount is required to protect against griefing the staking contract and individual applications are allowed to require higher minimum stakes if necessary.

Staking providers are not required to maintain a minimum T stake all the time. 24 hours after the delegation, T stake can be reduced below the minimum stake. The minimum stake in the staking contract is just to protect against griefing stake operation. Please note that each application may have its own minimum authorization though and the authorization can not be higher than the stake.

approveApplication

function approveApplication(address application) external

Allows the Governance to approve the particular application before individual stake authorizers are able to authorize it.

increaseAuthorization

function increaseAuthorization(address stakingProvider, address application, uint96 amount) external

Increases the authorization of the given staking provider for the given application by the given amount. Can only be called by the given staking provider’s authorizer.

Calls authorizationIncreased callback on the given application to notify the application about authorization change. See IApplication.

requestAuthorizationDecrease

function requestAuthorizationDecrease(address stakingProvider) external

Requests decrease of all authorizations for the given staking provider on all applications by all authorized amount. It may not change the authorized amount immediatelly. When it happens depends on the application. Can only be called by the given staking provider’s authorizer. Overwrites pending authorization decrease for the given staking provider and application.

Calls authorizationDecreaseRequested callback for each authorized application. See IApplication.

approveAuthorizationDecrease

function approveAuthorizationDecrease(address stakingProvider) external returns (uint96)

Called by the application at its discretion to approve the previously requested authorization decrease request. Can only be called by the application that was previously requested to decrease the authorization for that staking provider. Returns resulting authorized amount for the application.

forceDecreaseAuthorization

function forceDecreaseAuthorization(address stakingProvider, address application) external

Decreases the authorization for the given stakingProvider on the given disabled application, for all authorized amount. Can be called by anyone.

pauseApplication

function pauseApplication(address application) external

Pauses the given application’s eligibility to slash stakes. Besides that stakers can't change authorization to the application. Can be called only by the Panic Button of the particular application. The paused application can not slash stakes until it is approved again by the Governance using approveApplication function. Should be used only in case of an emergency.

disableApplication

function disableApplication(address application) external

Disables the given application. The disabled application can't slash stakers. Also stakers can't increase authorization to that application but can decrease without waiting by calling forceDecreaseAuthorization at any moment. Can be called only by the governance. The disabled application can't be approved again. Should be used only in case of an emergency.

setPanicButton

function setPanicButton(address application, address panicButton) external

Sets the Panic Button role for the given application to the provided address. Can only be called by the Governance. If the Panic Button for the given application should be disabled, the role address should be set to 0x0 address.

setAuthorizationCeiling

function setAuthorizationCeiling(uint256 ceiling) external

Sets the maximum number of applications one staking provider can have authorized. Used to protect against DoSing slashing queue. Can only be called by the Governance.

topUp

function topUp(address stakingProvider, uint96 amount) external

Increases the amount of the stake for the given staking provider.

The sender of this transaction needs to have the amount approved to transfer to the staking contract.

topUpKeep

function topUpKeep(address stakingProvider) external

Propagates information about stake top-up from the legacy KEEP staking contract to T staking contract. Can be called only by the owner or the staking provider.

topUpNu

function topUpNu(address stakingProvider) external

Propagates information about stake top-up from the legacy NU staking contract to T staking contract. Can be called only by the owner or the staking provider.

unstakeT

function unstakeT(address stakingProvider, uint96 amount) external

Reduces the liquid T stake amount by the provided amount and withdraws T to the owner. Reverts if there is at least one authorization higher than the sum of the legacy stake and remaining liquid T stake or if the unstake amount is higher than the liquid T stake amount. Can be called only by the owner or the staking provider. Can only be called when 24h passed since the stake has been delegated.

unstakeKeep

function unstakeKeep(address stakingProvider) external

Sets the legacy KEEP staking contract active stake amount cached in T staking contract to 0. Reverts if the amount of liquid T staked in T staking contract is lower than the highest application authorization. This function allows to unstake from KEEP staking contract and still being able to operate in T network and earning rewards based on the liquid T staked. Can be called only by the delegation owner or the staking provider. Can only be called when 24h passed since the stake has been delegated.

This function (or unstakeAll) must be called before undelegate/undelegateAt in Keep staking contract. Otherwise provider can be slashed by notifyKeepStakeDiscrepancy method.

unstakeNu

function unstakeNu(address stakingProvider, uint96 amount) external

Reduces cached legacy NU stake amount by the provided amount. Reverts if there is at least one authorization higher than the sum of remaining legacy NU stake and liquid T stake for that staking provider or if the untaked amount is higher than the cached legacy stake amount. If succeeded, the legacy NU stake can be partially or fully undelegated on the legacy staking contract. This function allows to unstake from NU staking contract and still being able to operate in T network and earning rewards based on the liquid T staked. Can be called only by the delegation owner or the staking provider. Can only be called when 24h passed since the stake has been delegated.

This function (or unstakeAll) must be called before withdraw in NuCypher staking contract. Otherwise NU tokens can't be unlocked.

Parameters

Name
Type
Description

stakingProvider

address

Staking provider address

amount

uint96

Amount of NU to unstake in T denomination

unstakeAll

function unstakeAll(address stakingProvider) external

Sets cached legacy stake amount to 0, sets the liquid T stake amount to 0 and withdraws all liquid T from the stake to the owner. Reverts if there is at least one non-zero authorization. Can be called only by the delegation owner or the staking provider. Can only be called when 24h passed since the stake has been delegated.

notifyKeepStakeDiscrepancy

function notifyKeepStakeDiscrepancy(address stakingProvider) external

Notifies about the discrepancy between legacy KEEP active stake and the amount cached in T staking contract. Slashes the staking provider in case the amount cached is higher than the actual active stake amount in KEEP staking contract. Needs to update authorizations of all affected applications and execute an involuntary authorization decrease on all affected applications. Can be called by anyone, notifier receives a reward.

notifyNuStakeDiscrepancy

function notifyNuStakeDiscrepancy(address stakingProvider) external

Notifies about the discrepancy between legacy NU active stake and the amount cached in T staking contract. Slashes the staking provider in case the amount cached is higher than the actual active stake amount in NU staking contract. Needs to update authorizations of all affected applications and execute an involuntary authorization decrease on all affected applications. Can be called by anyone, notifier receives a reward.

Real discrepancy between T and Nu is impossible. This method is a safeguard in case of bugs in NuCypher staking contract

setStakeDiscrepancyPenalty

function setStakeDiscrepancyPenalty(uint96 penalty, uint256 rewardMultiplier) external

Sets the penalty amount for stake discrepancy and reward multiplier for reporting it. The penalty is seized from the delegated stake, and 5% of the penalty, scaled by the multiplier, is given to the notifier. The rest of the tokens are burned. Can only be called by the Governance. See seize function.

setNotificationReward

function setNotificationReward(uint96 reward) external

Sets reward in T tokens for notification of misbehaviour of one staking provider. Can only be called by the governance.

pushNotificationReward

function pushNotificationReward(uint96 reward) external

Transfer some amount of T tokens as reward for notifications of misbehaviour

withdrawNotificationReward

function withdrawNotificationReward(address recipient, uint96 amount) external

Withdraw some amount of T tokens from notifiers treasury. Can only be called by the governance.

slash

function slash(uint96 amount, address[] _stakingProviders) external

Adds staking providers to the slashing queue along with the amount that should be slashed from each one of them. Can only be called by application authorized for all staking providers in the array.

This method doesn't emit events for providers that are added to the queue. If necessary events can be added to the application level.

seize

function seize(uint96 amount, uint256 rewardMultiplier, address notifier, address[] _stakingProviders) external

Adds staking providers to the slashing queue along with the amount. The notifier will receive reward per each provider from notifiers treasury. Can only be called by application authorized for all staking providers in the array.

This method doesn't emit events for staking providers that are added to the queue. If necessary events can be added to the application level.

processSlashing

function processSlashing(uint256 count) external virtual

Takes the given number of queued slashing operations and processes them. Receives 5% of the slashed amount. Executes involuntaryAuthorizationDecrease function on each affected application.

delegateVoting

function delegateVoting(address stakingProvider, address delegatee) external

Delegate voting power from the stake associated to the stakingProvider to a delegatee address. Caller must be the owner of this stake.

transferGovernance

function transferGovernance(address newGuvnor) external virtual

Transfers ownership of the contract to newGuvnor.

authorizedStake

function authorizedStake(address stakingProvider, address application) external view returns (uint96)

Returns the authorized stake amount of the staking provider for the application.

stakes

function stakes(address stakingProvider) external view returns (uint96 tStake, uint96 keepInTStake, uint96 nuInTStake)

Returns staked amount of T, Keep and Nu for the specified staking provider.

All values are in T denomination

getStartStakingTimestamp

function getStartStakingTimestamp(address stakingProvider) external view returns (uint256)

Returns start staking timestamp.

This value is set at most once.

stakedNu

function stakedNu(address stakingProvider) external view returns (uint256 nuAmount)

Returns staked amount of NU for the specified staking provider.

rolesOf

function rolesOf(address stakingProvider) external view returns (address owner, address payable beneficiary, address authorizer)

Gets the stake owner, the beneficiary and the authorizer for the specified staking provider address.

Return Values

Name
Type
Description

owner

address

Stake owner address.

beneficiary

address payable

Beneficiary address.

authorizer

address

Authorizer address.

getApplicationsLength

function getApplicationsLength() external view returns (uint256)

Returns length of application array

getSlashingQueueLength

function getSlashingQueueLength() external view returns (uint256)

Returns length of slashing queue

requestAuthorizationDecrease

function requestAuthorizationDecrease(address stakingProvider, address application, uint96 amount) public

Requests decrease of the authorization for the given staking provider on the given application by the provided amount. It may not change the authorized amount immediatelly. When it happens depends on the application. Can only be called by the given staking provider’s authorizer. Overwrites pending authorization decrease for the given staking provider and application if the application agrees for that. If the application does not agree for overwriting, the function reverts.

Calls authorizationDecreaseRequested callback on the given application. See IApplication.

getMinStaked

function getMinStaked(address stakingProvider, enum IStaking.StakeType stakeTypes) public view returns (uint96)

Returns minimum possible stake for T, KEEP or NU in T denomination

For example, suppose the given staking provider has 10 T, 20 T worth of KEEP, and 30 T worth of NU all staked, and the maximum application authorization is 40 T, then getMinStaked for that staking provider returns:

  • 0 T if KEEP stake type specified i.e. min = 40 T max - (10 T + 30 T worth of NU) = 0 T

  • 10 T if NU stake type specified i.e. min = 40 T max - (10 T + 20 T worth of KEEP) = 10 T

  • 0 T if T stake type specified i.e. min = 40 T max - (20 T worth of KEEP + 30 T worth of NU) < 0 T In other words, the minimum stake amount for the specified stake type is the minimum amount of stake of the given type needed to satisfy the maximum application authorization given the staked amounts of the other stake types for that staking provider.

getAvailableToAuthorize

function getAvailableToAuthorize(address stakingProvider, address application) public view returns (uint96 availableTValue)

Returns available amount to authorize for the specified application.

delegate

function delegate(address stakingProvider, address delegatee) internal virtual

Delegate voting power from the stake associated to the stakingProvider to a delegatee address. Caller must be the owner of this stake.

Original abstract function defined in Checkpoints contract had two parameters, delegator and delegatee. Here we override it and comply with the same signature but the semantics of the first parameter changes to the stakingProvider address.

notify

function notify(uint96 amount, uint256 rewardMultiplier, address notifier, address[] _stakingProviders) internal

Adds staking providers to the slashing queue along with the amount. The notifier will receive reward per each staking provider from notifiers treasury. Can only be called by application authorized for all staking providers in the array.

processSlashing

function processSlashing(struct TokenStaking.SlashingEvent slashing) internal returns (uint96 tAmountToBurn)

Processes one specified slashing event. Executes involuntaryAuthorizationDecrease function on each affected application.

authorizationDecrease

function authorizationDecrease(address stakingProvider, struct TokenStaking.StakingProviderInfo stakingProviderStruct, uint96 slashedAmount) internal

Synchronize authorizations (if needed) after slashing stake

seizeKeep

function seizeKeep(struct TokenStaking.StakingProviderInfo stakingProviderStruct, address stakingProvider, uint96 tAmountToSlash, uint256 rewardMultiplier) internal returns (uint96)

Convert amount from T to Keep and call seize in Keep staking contract. Returns remainder of slashing amount in T

Note this internal function doesn't update stake checkpoints

seizeNu

function seizeNu(struct TokenStaking.StakingProviderInfo stakingProviderStruct, uint96 tAmountToSlash, uint256 rewardMultiplier) internal returns (uint96)

Convert amount from T to NU and call slashStaker in NuCypher staking contract. Returns remainder of slashing amount in T

Note this internal function doesn't update the stake checkpoints

cleanAuthorizedApplications

function cleanAuthorizedApplications(struct TokenStaking.StakingProviderInfo stakingProviderStruct, uint256 numberToDelete) internal

Removes application with zero authorization from authorized applications array

newStakeCheckpoint

function newStakeCheckpoint(address _delegator, uint96 _amount, bool increase) internal

Creates new checkpoints due to a change of stake amount

Parameters

Name
Type
Description

_delegator

address

Address of the staking provider acting as delegator

_amount

uint96

Amount of T to increment

increase

bool

True if the change is an increase, false if a decrease

increaseStakeCheckpoint

function increaseStakeCheckpoint(address _delegator, uint96 _amount) internal

Creates new checkpoints due to an increment of a stakers' stake

Parameters

Name
Type
Description

_delegator

address

Address of the staking provider acting as delegator

_amount

uint96

Amount of T to increment

decreaseStakeCheckpoint

function decreaseStakeCheckpoint(address _delegator, uint96 _amount) internal

Creates new checkpoints due to a decrease of a stakers' stake

Parameters

Name
Type
Description

_delegator

address

Address of the stake owner acting as delegator

_amount

uint96

Amount of T to decrease

getNuAmountInT

function getNuAmountInT(address owner, address stakingProvider) internal returns (uint96)

Returns amount of Nu stake in the NuCypher staking contract for the specified staking provider. Resulting value in T denomination

_transferGovernance

function _transferGovernance(address newGuvnor) internal virtual

getKeepAmountInT

function getKeepAmountInT(address stakingProvider) internal view returns (uint96)

Returns amount of Keep stake in the Keep staking contract for the specified staking provider. Resulting value in T denomination

convertToT

function convertToT(uint256 amount, uint256 ratio) internal pure returns (uint96 tAmount, uint256 remainder)

Returns the T token amount that's obtained from amount legacy tokens for the given ratio, and the remainder that can't be converted.

convertFromT

function convertFromT(uint96 tAmount, uint256 ratio) internal pure returns (uint256 amount, uint96 tRemainder)

Returns the amount of legacy tokens that's obtained from tAmount T tokens for the given ratio, and the T remainder that can't be converted.

RandomBeacon

RandomBeacon

This file documents a contract which is not yet deployed to Mainnet.

Keep Random Beacon contract. It lets to request a new relay entry and validates the new relay entry provided by the network. This contract is in charge of all other Random Beacon activities such as group lifecycle or slashing.

Should be owned by the governance contract controlling Random Beacon parameters.

genesisSeed

uint256 genesisSeed

Seed value used for the genesis group selection. https://www.wolframalpha.com/input/?i=pi+to+78+digits

_callbackGasLimit

uint256 _callbackGasLimit

Relay entry callback gas limit. This is the gas limit with which callback function provided in the relay request transaction is executed. The callback is executed with a new relay entry value in the same transaction the relay entry is submitted.

_groupCreationFrequency

uint256 _groupCreationFrequency

The frequency of new group creation. Groups are created with a fixed frequency of relay requests.

_maliciousDkgResultSlashingAmount

uint96 _maliciousDkgResultSlashingAmount

Slashing amount for submitting a malicious DKG result. Every DKG result submitted can be challenged for the time of dkg.ResultChallengePeriodLength. If the DKG result submitted is challenged and proven to be malicious, the operator who submitted the malicious result is slashed for _maliciousDkgResultSlashingAmount.

_unauthorizedSigningSlashingAmount

uint96 _unauthorizedSigningSlashingAmount

Slashing amount when an unauthorized signing has been proved, which means the private key leaked and all the group members should be punished.

_sortitionPoolRewardsBanDuration

uint256 _sortitionPoolRewardsBanDuration

Duration of the sortition pool rewards ban imposed on operators who misbehaved during DKG by being inactive or disqualified and for operators that were identified by the rest of group members as inactive via notifyOperatorInactivity.

_relayEntryTimeoutNotificationRewardMultiplier

uint256 _relayEntryTimeoutNotificationRewardMultiplier

Percentage of the staking contract malicious behavior notification reward which will be transferred to the notifier reporting about relay entry timeout. Notifiers are rewarded from a notifiers treasury pool. For example, if notification reward is 1000 and the value of the multiplier is 5, the notifier will receive: 5% of 1000 = 50 per each operator affected.

_unauthorizedSigningNotificationRewardMultiplier

uint256 _unauthorizedSigningNotificationRewardMultiplier

Percentage of the staking contract malicious behavior notification reward which will be transferred to the notifier reporting about unauthorized signing. Notifiers are rewarded from a notifiers treasury pool. For example, if a notification reward is 1000 and the value of the multiplier is 5, the notifier will receive: 5% of 1000 = 50 per each operator affected.

_dkgMaliciousResultNotificationRewardMultiplier

uint256 _dkgMaliciousResultNotificationRewardMultiplier

Percentage of the staking contract malicious behavior notification reward which will be transferred to the notifier reporting about a malicious DKG result. Notifiers are rewarded from a notifiers treasury pool. For example, if notification reward is 1000 and the value of the multiplier is 5, the notifier will receive: 5% of 1000 = 50 per each operator affected.

_dkgResultSubmissionGas

uint256 _dkgResultSubmissionGas

Calculated gas cost for submitting a DKG result. This will be refunded as part of the DKG approval process. It is in the submitter's interest to not skip his priority turn on the approval, otherwise the refund of the DKG submission will be refunded to another group member that will call the DKG approve function.

_dkgResultApprovalGasOffset

uint256 _dkgResultApprovalGasOffset

Gas that is meant to balance the DKG result approval's overall cost. Can be updated by the governance based on the current market conditions.

_notifyOperatorInactivityGasOffset

uint256 _notifyOperatorInactivityGasOffset

Gas that is meant to balance the operator inactivity notification cost. Can be updated by the governance based on the current market conditions.

_relayEntrySubmissionGasOffset

uint256 _relayEntrySubmissionGasOffset

Gas that is meant to balance the relay entry submission cost. Can be updated by the governance based on the current market conditions.

inactivityClaimNonce

mapping(uint64 => uint256) inactivityClaimNonce

Stores current operator inactivity claim nonce for given group. Each claim is made with an unique nonce which protects against claim replay.

authorizedRequesters

mapping(address => bool) authorizedRequesters

Authorized addresses that can request a relay entry.

sortitionPool

contract SortitionPool sortitionPool

tToken

contract IERC20 tToken

staking

contract IStaking staking

authorization

struct BeaconAuthorization.Data authorization

dkg

struct BeaconDkg.Data dkg

groups

struct Groups.Data groups

relay

struct Relay.Data relay

callback

struct Callback.Data callback

AuthorizationParametersUpdated

event AuthorizationParametersUpdated(uint96 minimumAuthorization, uint64 authorizationDecreaseDelay, uint64 authorizationDecreaseChangePeriod)

RelayEntryParametersUpdated

event RelayEntryParametersUpdated(uint256 relayEntrySoftTimeout, uint256 relayEntryHardTimeout, uint256 callbackGasLimit)

GroupCreationParametersUpdated

event GroupCreationParametersUpdated(uint256 groupCreationFrequency, uint256 groupLifetime, uint256 dkgResultChallengePeriodLength, uint256 dkgResultChallengeExtraGas, uint256 dkgResultSubmissionTimeout, uint256 dkgResultSubmitterPrecedencePeriodLength)

RewardParametersUpdated

event RewardParametersUpdated(uint256 sortitionPoolRewardsBanDuration, uint256 relayEntryTimeoutNotificationRewardMultiplier, uint256 unauthorizedSigningNotificationRewardMultiplier, uint256 dkgMaliciousResultNotificationRewardMultiplier)

SlashingParametersUpdated

event SlashingParametersUpdated(uint256 relayEntrySubmissionFailureSlashingAmount, uint256 maliciousDkgResultSlashingAmount, uint256 unauthorizedSigningSlashingAmount)

GasParametersUpdated

event GasParametersUpdated(uint256 dkgResultSubmissionGas, uint256 dkgResultApprovalGasOffset, uint256 notifyOperatorInactivityGasOffset, uint256 relayEntrySubmissionGasOffset)

RequesterAuthorizationUpdated

event RequesterAuthorizationUpdated(address requester, bool isAuthorized)

DkgStarted

event DkgStarted(uint256 seed)

DkgResultSubmitted

event DkgResultSubmitted(bytes32 resultHash, uint256 seed, struct BeaconDkg.Result result)

DkgTimedOut

event DkgTimedOut()

DkgResultApproved

event DkgResultApproved(bytes32 resultHash, address approver)

DkgResultChallenged

event DkgResultChallenged(bytes32 resultHash, address challenger, string reason)

DkgMaliciousResultSlashed

event DkgMaliciousResultSlashed(bytes32 resultHash, uint256 slashingAmount, address maliciousSubmitter)

DkgMaliciousResultSlashingFailed

event DkgMaliciousResultSlashingFailed(bytes32 resultHash, uint256 slashingAmount, address maliciousSubmitter)

DkgStateLocked

event DkgStateLocked()

DkgSeedTimedOut

event DkgSeedTimedOut()

GroupRegistered

event GroupRegistered(uint64 groupId, bytes groupPubKey)

RelayEntryRequested

event RelayEntryRequested(uint256 requestId, uint64 groupId, bytes previousEntry)

RelayEntrySubmitted

event RelayEntrySubmitted(uint256 requestId, address submitter, bytes entry)

RelayEntryTimedOut

event RelayEntryTimedOut(uint256 requestId, uint64 terminatedGroupId)

RelayEntryDelaySlashed

event RelayEntryDelaySlashed(uint256 requestId, uint256 slashingAmount, address[] groupMembers)

RelayEntryDelaySlashingFailed

event RelayEntryDelaySlashingFailed(uint256 requestId, uint256 slashingAmount, address[] groupMembers)

RelayEntryTimeoutSlashed

event RelayEntryTimeoutSlashed(uint256 requestId, uint256 slashingAmount, address[] groupMembers)

RelayEntryTimeoutSlashingFailed

event RelayEntryTimeoutSlashingFailed(uint256 requestId, uint256 slashingAmount, address[] groupMembers)

UnauthorizedSigningSlashed

event UnauthorizedSigningSlashed(uint64 groupId, uint256 unauthorizedSigningSlashingAmount, address[] groupMembers)

UnauthorizedSigningSlashingFailed

event UnauthorizedSigningSlashingFailed(uint64 groupId, uint256 unauthorizedSigningSlashingAmount, address[] groupMembers)

CallbackFailed

event CallbackFailed(uint256 entry, uint256 entrySubmittedBlock)

InactivityClaimed

event InactivityClaimed(uint64 groupId, uint256 nonce, address notifier)

OperatorRegistered

event OperatorRegistered(address stakingProvider, address operator)

AuthorizationIncreased

event AuthorizationIncreased(address stakingProvider, address operator, uint96 fromAmount, uint96 toAmount)

AuthorizationDecreaseRequested

event AuthorizationDecreaseRequested(address stakingProvider, address operator, uint96 fromAmount, uint96 toAmount, uint64 decreasingAt)

AuthorizationDecreaseApproved

event AuthorizationDecreaseApproved(address stakingProvider)

InvoluntaryAuthorizationDecreaseFailed

event InvoluntaryAuthorizationDecreaseFailed(address stakingProvider, address operator, uint96 fromAmount, uint96 toAmount)

OperatorJoinedSortitionPool

event OperatorJoinedSortitionPool(address stakingProvider, address operator)

OperatorStatusUpdated

event OperatorStatusUpdated(address stakingProvider, address operator)

constructor

constructor(contract SortitionPool _sortitionPool, contract IERC20 _tToken, contract IStaking _staking, contract BeaconDkgValidator _dkgValidator, contract ReimbursementPool _reimbursementPool) public

Assigns initial values to parameters to make the beacon work safely. These parameters are just proposed defaults and they might be updated with update* functions after the contract deployment and before transferring the ownership to the governance contract.

onlyStakingContract

modifier onlyStakingContract()

onlyReimbursableAdmin

modifier onlyReimbursableAdmin()

updateAuthorizationParameters

function updateAuthorizationParameters(uint96 _minimumAuthorization, uint64 _authorizationDecreaseDelay, uint64 _authorizationDecreaseChangePeriod) external

Updates the values of authorization parameters.

Can be called only by the contract guvnor, which should be the random beacon governance contract. The caller is responsible for validating parameters.

Parameters

Name
Type
Description

_minimumAuthorization

uint96

New minimum authorization amount

_authorizationDecreaseDelay

uint64

New authorization decrease delay in seconds

_authorizationDecreaseChangePeriod

uint64

New authorization decrease change period in seconds

updateRelayEntryParameters

function updateRelayEntryParameters(uint256 relayEntrySoftTimeout, uint256 relayEntryHardTimeout, uint256 callbackGasLimit) external

Updates the values of relay entry parameters.

Can be called only by the contract guvnor, which should be the random beacon governance contract. The caller is responsible for validating parameters.

Parameters

Name
Type
Description

relayEntrySoftTimeout

uint256

New relay entry submission soft timeout

relayEntryHardTimeout

uint256

New relay entry hard timeout

callbackGasLimit

uint256

New callback gas limit

updateGroupCreationParameters

function updateGroupCreationParameters(uint256 groupCreationFrequency, uint256 groupLifetime, uint256 dkgResultChallengePeriodLength, uint256 dkgResultChallengeExtraGas, uint256 dkgResultSubmissionTimeout, uint256 dkgSubmitterPrecedencePeriodLength) external

Updates the values of group creation parameters.

Can be called only by the contract guvnor, which should be the random beacon governance contract. The caller is responsible for validating parameters.

Parameters

Name
Type
Description

groupCreationFrequency

uint256

New group creation frequency

groupLifetime

uint256

New group lifetime in blocks

dkgResultChallengePeriodLength

uint256

New DKG result challenge period length

dkgResultChallengeExtraGas

uint256

New DKG result challenge extra gas

dkgResultSubmissionTimeout

uint256

New DKG result submission timeout

dkgSubmitterPrecedencePeriodLength

uint256

New DKG result submitter precedence period length

updateRewardParameters

function updateRewardParameters(uint256 sortitionPoolRewardsBanDuration, uint256 relayEntryTimeoutNotificationRewardMultiplier, uint256 unauthorizedSigningNotificationRewardMultiplier, uint256 dkgMaliciousResultNotificationRewardMultiplier) external

Updates the values of reward parameters.

Can be called only by the contract guvnor, which should be the random beacon governance contract. The caller is responsible for validating parameters.

Parameters

Name
Type
Description

sortitionPoolRewardsBanDuration

uint256

New sortition pool rewards ban duration in seconds.

relayEntryTimeoutNotificationRewardMultiplier

uint256

New value of the relay entry timeout notification reward multiplier

unauthorizedSigningNotificationRewardMultiplier

uint256

New value of the unauthorized signing notification reward multiplier

dkgMaliciousResultNotificationRewardMultiplier

uint256

New value of the DKG malicious result notification reward multiplier

updateSlashingParameters

function updateSlashingParameters(uint96 relayEntrySubmissionFailureSlashingAmount, uint96 maliciousDkgResultSlashingAmount, uint96 unauthorizedSigningSlashingAmount) external

Updates the values of slashing parameters.

Can be called only by the contract guvnor, which should be the random beacon governance contract. The caller is responsible for validating parameters.

Parameters

Name
Type
Description

relayEntrySubmissionFailureSlashingAmount

uint96

New relay entry submission failure amount

maliciousDkgResultSlashingAmount

uint96

New malicious DKG result slashing amount

unauthorizedSigningSlashingAmount

uint96

New unauthorized signing slashing amount

updateGasParameters

function updateGasParameters(uint256 dkgResultSubmissionGas, uint256 dkgResultApprovalGasOffset, uint256 notifyOperatorInactivityGasOffset, uint256 relayEntrySubmissionGasOffset) external

Updates the values of gas parameters.

Can be called only by the contract guvnor, which should be the random beacon governance contract. The caller is responsible for validating parameters.

Parameters

Name
Type
Description

dkgResultSubmissionGas

uint256

New DKG result submission gas

dkgResultApprovalGasOffset

uint256

New DKG result approval gas offset

notifyOperatorInactivityGasOffset

uint256

New operator inactivity notification gas offset

relayEntrySubmissionGasOffset

uint256

New relay entry submission gas offset

setRequesterAuthorization

function setRequesterAuthorization(address requester, bool isAuthorized) external

Set authorization for requesters that can request a relay entry.

Can be called only by the contract guvnor, which should be the random beacon governance contract.

Parameters

Name
Type
Description

requester

address

Requester, can be a contract or EOA

isAuthorized

bool

True or false

withdrawRewards

function withdrawRewards(address stakingProvider) external

Withdraws application rewards for the given staking provider. Rewards are withdrawn to the staking provider's beneficiary address set in the staking contract. Reverts if staking provider has not registered the operator address.

Emits RewardsWithdrawn event.

withdrawIneligibleRewards

function withdrawIneligibleRewards(address recipient) external

Withdraws rewards belonging to operators marked as ineligible for sortition pool rewards.

Can be called only by the contract guvnor, which should be the random beacon governance contract.

Parameters

Name
Type
Description

recipient

address

Recipient of withdrawn rewards.

registerOperator

function registerOperator(address operator) external

Used by staking provider to set operator address that will operate a node. The given staking provider can set operator address only one time. The operator address can not be changed and must be unique. Reverts if the operator is already set for the staking provider or if the operator address is already in use. Reverts if there is a pending authorization decrease for the staking provider.

joinSortitionPool

function joinSortitionPool() external

Lets the operator join the sortition pool. The operator address must be known - before calling this function, it has to be appointed by the staking provider by calling registerOperator. Also, the operator must have the minimum authorization required by the beacon. Function reverts if there is no minimum stake authorized or if the operator is not known. If there was an authorization decrease requested, it is activated by starting the authorization decrease delay.

updateOperatorStatus

function updateOperatorStatus(address operator) external

Updates status of the operator in the sortition pool. If there was an authorization decrease requested, it is activated by starting the authorization decrease delay. Function reverts if the operator is not known.

authorizationIncreased

function authorizationIncreased(address stakingProvider, uint96 fromAmount, uint96 toAmount) external

Used by T staking contract to inform the beacon that the authorized stake amount for the given staking provider increased.

Reverts if the authorization amount is below the minimum.

The function is not updating the sortition pool. Sortition pool state needs to be updated by the operator with a call to joinSortitionPool or updateOperatorStatus.

Can only be called by T staking contract.

authorizationDecreaseRequested

function authorizationDecreaseRequested(address stakingProvider, uint96 fromAmount, uint96 toAmount) external

Used by T staking contract to inform the beacon that the authorization decrease for the given staking provider has been requested.

Reverts if the amount after deauthorization would be non-zero and lower than the minimum authorization.

Reverts if another authorization decrease request is pending for the staking provider and not enough time passed since the original request (see authorizationDecreaseChangePeriod).

If the operator is not known (registerOperator was not called) it lets to approveAuthorizationDecrease immediately. If the operator is known (registerOperator was called), the operator needs to update state of the sortition pool with a call to joinSortitionPool or updateOperatorStatus. After the sortition pool state is in sync, authorization decrease delay starts.

After authorization decrease delay passes, authorization decrease request needs to be approved with a call to approveAuthorizationDecrease function.

If there is a pending authorization decrease request, it is overwritten, but only if enough time passed since the original request. Otherwise, the function reverts.

Can only be called by T staking contract.

approveAuthorizationDecrease

function approveAuthorizationDecrease(address stakingProvider) external

Approves the previously registered authorization decrease request. Reverts if authorization decrease delay has not passed yet or if the authorization decrease was not requested for the given staking provider.

involuntaryAuthorizationDecrease

function involuntaryAuthorizationDecrease(address stakingProvider, uint96 fromAmount, uint96 toAmount) external

Used by T staking contract to inform the beacon the authorization has been decreased for the given staking provider involuntarily, as a result of slashing.

If the operator is not known (registerOperator was not called) the function does nothing. The operator was never in a sortition pool so there is nothing to update.

If the operator is known, sortition pool is unlocked, and the operator is in the sortition pool, the sortition pool state is updated. If the sortition pool is locked, update needs to be postponed. Every other staker is incentivized to call updateOperatorStatus for the problematic operator to increase their own rewards in the pool.

genesis

function genesis() external

Triggers group selection if there are no active groups.

submitDkgResult

function submitDkgResult(struct BeaconDkg.Result dkgResult) external

\x19Ethereum signed message: before signing, so the message to sign is: \x19Ethereum signed message:\n${keccak256(chainID,groupPubKey,misbehaved,startBlock)}

Parameters

Name
Type
Description

dkgResult

struct BeaconDkg.Result

DKG result.

notifyDkgTimeout

function notifyDkgTimeout() external

Notifies about DKG timeout.

approveDkgResult

function approveDkgResult(struct BeaconDkg.Result dkgResult) external

Approves DKG result. Can be called when the challenge period for the submitted result is finished. Considers the submitted result as valid, bans misbehaved group members from the sortition pool rewards, and completes the group creation by activating the candidate group. For the first submitterPrecedencePeriodLength blocks after the end of the challenge period can be called only by the DKG result submitter. After that time, can be called by anyone.

Parameters

Name
Type
Description

dkgResult

struct BeaconDkg.Result

Result to approve. Must match the submitted result stored during submitDkgResult.

challengeDkgResult

function challengeDkgResult(struct BeaconDkg.Result dkgResult) external

Challenges DKG result. If the submitted result is proved to be invalid it reverts the DKG back to the result submission phase. It removes a candidate group that was previously registered with the DKG result submission.

Due to EIP-150 1/64 of the gas is not forwarded to the call, and will be kept to execute the remaining operations in the function after the call inside the try-catch. To eliminate a class of attacks related to the gas limit manipulation, this function requires an extra amount of gas to be left at the end of the execution.

Parameters

Name
Type
Description

dkgResult

struct BeaconDkg.Result

Result to challenge. Must match the submitted result stored during submitDkgResult.

getGroupCreationState

function getGroupCreationState() external view returns (enum BeaconDkg.State)

Check current group creation state.

hasDkgTimedOut

function hasDkgTimedOut() external view returns (bool)

Checks if DKG timed out. The DKG timeout period includes time required for off-chain protocol execution and time for the result publication for all group members. After this time result cannot be submitted and DKG can be notified about the timeout.

Return Values

Name
Type
Description

[0]

bool

True if DKG timed out, false otherwise.

getGroupsRegistry

function getGroupsRegistry() external view returns (bytes32[])

getGroup

function getGroup(uint64 groupId) external view returns (struct Groups.Group)

getGroup

function getGroup(bytes groupPubKey) external view returns (struct Groups.Group)

requestRelayEntry

function requestRelayEntry(contract IRandomBeaconConsumer callbackContract) external

Creates a request to generate a new relay entry, which will include a random number (by signing the previous entry's random number). Requester must be previously authorized by the governance.

Parameters

Name
Type
Description

callbackContract

contract IRandomBeaconConsumer

Beacon consumer callback contract.

submitRelayEntry

function submitRelayEntry(bytes entry) external

Creates a new relay entry. Gas-optimized version that can be called only before the soft timeout. This should be the majority of cases.

Parameters

Name
Type
Description

entry

bytes

Group BLS signature over the previous entry.

submitRelayEntry

function submitRelayEntry(bytes entry, uint32[] groupMembers) external

Creates a new relay entry.

Parameters

Name
Type
Description

entry

bytes

Group BLS signature over the previous entry.

groupMembers

uint32[]

Identifiers of group members.

reportRelayEntryTimeout

function reportRelayEntryTimeout(uint32[] groupMembers) external

Reports a relay entry timeout.

Parameters

Name
Type
Description

groupMembers

uint32[]

Identifiers of group members.

reportUnauthorizedSigning

function reportUnauthorizedSigning(bytes signedMsgSender, uint64 groupId, uint32[] groupMembers) external

Reports unauthorized groups signing. Must provide a valid signature of the sender's address as a message. Successful signature verification means the private key has been leaked and all group members should be punished by slashing their tokens. Group has to be active or expired. Unauthorized signing cannot be reported for a terminated group. In case of reporting unauthorized signing for a terminated group, or when the signature is invalid, function reverts.

Parameters

Name
Type
Description

signedMsgSender

bytes

Signature of the sender's address as a message.

groupId

uint64

Group that is being reported for leaking a private key.

groupMembers

uint32[]

Identifiers of group members.

notifyOperatorInactivity

function notifyOperatorInactivity(struct BeaconInactivity.Claim claim, uint256 nonce, uint32[] groupMembers) external

Notifies about operators who are inactive. Using this function, a majority of the group can decide about punishing specific group members who constantly fail doing their job. If the provided claim is proved to be valid and signed by sufficient number of group members, operators of members deemed as inactive are banned for sortition pool rewards for duration specified by _sortitionPoolRewardsBanDuration parameter. The sender of the claim must be one of the claim signers. This function can be called only for active and non-terminated groups.

Parameters

Name
Type
Description

claim

struct BeaconInactivity.Claim

Operator inactivity claim.

nonce

uint256

Current inactivity claim nonce for the given group. Must be the same as the stored one.

groupMembers

uint32[]

Identifiers of group members.

minimumAuthorization

function minimumAuthorization() external view returns (uint96)

The minimum authorization amount required so that operator can participate in the random beacon. This amount is required to execute slashing for providing a malicious DKG result or when a relay entry times out.

isRelayRequestInProgress

function isRelayRequestInProgress() external view returns (bool)

Return Values

Name
Type
Description

[0]

bool

Flag indicating whether a relay entry request is currently in progress.

eligibleStake

function eligibleStake(address stakingProvider) external view returns (uint96)

Returns the current value of the staking provider's eligible stake. Eligible stake is defined as the currently authorized stake minus the pending authorization decrease. Eligible stake is what is used for operator's weight in the sortition pool. If the authorized stake minus the pending authorization decrease is below the minimum authorization, eligible stake is 0.

availableRewards

function availableRewards(address stakingProvider) external view returns (uint96)

Returns the amount of rewards available for withdrawal for the given staking provider. Reverts if staking provider has not registered the operator address.

pendingAuthorizationDecrease

function pendingAuthorizationDecrease(address stakingProvider) external view returns (uint96)

Returns the amount of stake that is pending authorization decrease for the given staking provider. If no authorization decrease has been requested, returns zero.

remainingAuthorizationDecreaseDelay

function remainingAuthorizationDecreaseDelay(address stakingProvider) external view returns (uint64)

Returns the remaining time in seconds that needs to pass before the requested authorization decrease can be approved. If the sortition pool state was not updated yet by the operator after requesting the authorization decrease, returns type(uint64).max.

stakingProviderToOperator

function stakingProviderToOperator(address stakingProvider) public view returns (address)

Returns operator registered for the given staking provider.

operatorToStakingProvider

function operatorToStakingProvider(address operator) public view returns (address)

Returns staking provider of the given operator.

isOperatorUpToDate

function isOperatorUpToDate(address operator) external view returns (bool)

Checks if the operator's authorized stake is in sync with operator's weight in the sortition pool. If the operator is not in the sortition pool and their authorized stake is non-zero, function returns false.

isOperatorInPool

function isOperatorInPool(address operator) external view returns (bool)

Returns true if the given operator is in the sortition pool. Otherwise, returns false.

selectGroup

function selectGroup() external view returns (uint32[])

Selects a new group of operators. Can only be called when DKG is in progress and the pool is locked. At least one operator has to be registered in the pool, otherwise the function fails reverting the transaction.

Return Values

Name
Type
Description

[0]

uint32[]

IDs of selected group members.

authorizationParameters

function authorizationParameters() external view returns (uint96 minimumAuthorization, uint64 authorizationDecreaseDelay, uint64 authorizationDecreaseChangePeriod)

Returns authorization-related parameters of the beacon.

The minimum authorization is also returned by minimumAuthorization() function, as a requirement of IApplication interface.

Return Values

Name
Type
Description

minimumAuthorization

uint96

The minimum authorization amount required so that operator can participate in the random beacon. This amount is required to execute slashing for providing a malicious DKG result or when a relay entry times out.

authorizationDecreaseDelay

uint64

Delay in seconds that needs to pass between the time authorization decrease is requested and the time that request gets approved. Protects against free-riders earning rewards and not being active in the network.

authorizationDecreaseChangePeriod

uint64

Authorization decrease change period in seconds. It is the time, before authorization decrease delay end, during which the pending authorization decrease request can be overwritten. If set to 0, pending authorization decrease request can not be overwritten until the entire authorizationDecreaseDelay ends. If set to value equal authorizationDecreaseDelay, request can always be overwritten.

relayEntryParameters

function relayEntryParameters() external view returns (uint256 relayEntrySoftTimeout, uint256 relayEntryHardTimeout, uint256 callbackGasLimit)

Returns relay-entry-related parameters of the beacon.

Return Values

Name
Type
Description

relayEntrySoftTimeout

uint256

Soft timeout in blocks for a group to submit the relay entry. If the soft timeout is reached for submitting the relay entry, the slashing starts.

relayEntryHardTimeout

uint256

Hard timeout in blocks for a group to submit the relay entry. After the soft timeout passes without relay entry submitted, all group members start getting slashed. The slashing amount increases linearly until the group submits the relay entry or until relayEntryHardTimeout is reached. When the hard timeout is reached, each group member will get slashed for _relayEntrySubmissionFailureSlashingAmount.

callbackGasLimit

uint256

Relay entry callback gas limit. This is the gas limit with which callback function provided in the relay request transaction is executed. The callback is executed with a new relay entry value in the same transaction the relay entry is submitted.

groupCreationParameters

function groupCreationParameters() external view returns (uint256 groupCreationFrequency, uint256 groupLifetime, uint256 dkgResultChallengePeriodLength, uint256 dkgResultChallengeExtraGas, uint256 dkgResultSubmissionTimeout, uint256 dkgSubmitterPrecedencePeriodLength)

Returns group-creation-related parameters of the beacon.

Return Values

Name
Type
Description

groupCreationFrequency

uint256

The frequency of a new group creation. Groups are created with a fixed frequency of relay requests.

groupLifetime

uint256

Group lifetime in blocks. When a group reached its lifetime, it is no longer selected for new relay requests but may still be responsible for submitting relay entry if relay request assigned to that group is still pending.

dkgResultChallengePeriodLength

uint256

The number of blocks for which a DKG result can be challenged. Anyone can challenge DKG result for a certain number of blocks before the result is fully accepted and the group registered in the pool of active groups. If the challenge gets accepted, all operators who signed the malicious result get slashed for and the notifier gets rewarded.

dkgResultChallengeExtraGas

uint256

The extra gas required to be left at the end of the challenge DKG result transaction.

dkgResultSubmissionTimeout

uint256

Timeout in blocks for a group to submit the DKG result. All members are eligible to submit the DKG result. If dkgResultSubmissionTimeout passes without the DKG result submitted, DKG is considered as timed out and no DKG result for this group creation can be submitted anymore.

dkgSubmitterPrecedencePeriodLength

uint256

Time during the DKG result approval stage when the submitter of the DKG result takes the precedence to approve the DKG result. After this time passes anyone can approve the DKG result.

rewardParameters

function rewardParameters() external view returns (uint256 sortitionPoolRewardsBanDuration, uint256 relayEntryTimeoutNotificationRewardMultiplier, uint256 unauthorizedSigningNotificationRewardMultiplier, uint256 dkgMaliciousResultNotificationRewardMultiplier)

Returns reward-related parameters of the beacon.

Return Values

Name
Type
Description

sortitionPoolRewardsBanDuration

uint256

Duration of the sortition pool rewards ban imposed on operators who misbehaved during DKG by being inactive or disqualified and for operators that were identified by the rest of group members as inactive via notifyOperatorInactivity.

relayEntryTimeoutNotificationRewardMultiplier

uint256

Percentage of the staking contract malicious behavior notification reward which will be transferred to the notifier reporting about relay entry timeout. Notifiers are rewarded from a notifiers treasury pool. For example, if notification reward is 1000 and the value of the multiplier is 5, the notifier will receive: 5% of 1000 = 50 per each operator affected.

unauthorizedSigningNotificationRewardMultiplier

uint256

Percentage of the staking contract malicious behavior notification reward which will be transferred to the notifier reporting about unauthorized signing. Notifiers are rewarded from a notifiers treasury pool. For example, if a notification reward is 1000 and the value of the multiplier is 5, the notifier will receive: 5% of 1000 = 50 per each operator affected.

dkgMaliciousResultNotificationRewardMultiplier

uint256

Percentage of the staking contract malicious behavior notification reward which will be transferred to the notifier reporting about a malicious DKG result. Notifiers are rewarded from a notifiers treasury pool. For example, if notification reward is 1000 and the value of the multiplier is 5, the notifier will receive: 5% of 1000 = 50 per each operator affected.

slashingParameters

function slashingParameters() external view returns (uint96 relayEntrySubmissionFailureSlashingAmount, uint96 maliciousDkgResultSlashingAmount, uint96 unauthorizedSigningSlashingAmount)

Returns slashing-related parameters of the beacon.

Return Values

Name
Type
Description

relayEntrySubmissionFailureSlashingAmount

uint96

Slashing amount for not submitting relay entry. When relay entry hard timeout is reached without the relay entry submitted, each group member gets slashed for relayEntrySubmissionFailureSlashingAmount. If the relay entry gets submitted after the soft timeout, but before the hard timeout, each group member gets slashed proportionally to relayEntrySubmissionFailureSlashingAmount and the time passed since the soft deadline.

maliciousDkgResultSlashingAmount

uint96

Slashing amount for submitting a malicious DKG result. Every DKG result submitted can be challenged for the time of dkg.ResultChallengePeriodLength. If the DKG result submitted is challenged and proven to be malicious, the operator who submitted the malicious result is slashed for maliciousDkgResultSlashingAmount.

unauthorizedSigningSlashingAmount

uint96

Slashing amount when an unauthorized signing has been proved, which means the private key leaked and all the group members should be punished.

gasParameters

function gasParameters() external view returns (uint256 dkgResultSubmissionGas, uint256 dkgResultApprovalGasOffset, uint256 notifyOperatorInactivityGasOffset, uint256 relayEntrySubmissionGasOffset)

Returns gas-related parameters of the beacon.

Return Values

Name
Type
Description

dkgResultSubmissionGas

uint256

Calculated gas cost for submitting a DKG result. This will be refunded as part of the DKG approval process.

dkgResultApprovalGasOffset

uint256

Gas that is meant to balance the DKG result approval's overall cost.

notifyOperatorInactivityGasOffset

uint256

Gas that is meant to balance the operator inactivity notification cost.

relayEntrySubmissionGasOffset

uint256

Gas that is meant to balance the relay entry submission cost.

RandomBeaconGovernance

RandomBeaconGovernance

This file documents a contract which is not yet deployed to Mainnet.

Owns the RandomBeacon contract and is responsible for updating its governable parameters in respect to governance delay individual for each parameter.

newGovernanceDelay

uint256 newGovernanceDelay

governanceDelayChangeInitiated

uint256 governanceDelayChangeInitiated

newRandomBeaconGovernance

address newRandomBeaconGovernance

randomBeaconGovernanceTransferInitiated

uint256 randomBeaconGovernanceTransferInitiated

newRelayEntrySoftTimeout

uint256 newRelayEntrySoftTimeout

relayEntrySoftTimeoutChangeInitiated

uint256 relayEntrySoftTimeoutChangeInitiated

newRelayEntryHardTimeout

uint256 newRelayEntryHardTimeout

relayEntryHardTimeoutChangeInitiated

uint256 relayEntryHardTimeoutChangeInitiated

newCallbackGasLimit

uint256 newCallbackGasLimit

callbackGasLimitChangeInitiated

uint256 callbackGasLimitChangeInitiated

newGroupCreationFrequency

uint256 newGroupCreationFrequency

groupCreationFrequencyChangeInitiated

uint256 groupCreationFrequencyChangeInitiated

newGroupLifetime

uint256 newGroupLifetime

groupLifetimeChangeInitiated

uint256 groupLifetimeChangeInitiated

newDkgResultChallengePeriodLength

uint256 newDkgResultChallengePeriodLength

dkgResultChallengePeriodLengthChangeInitiated

uint256 dkgResultChallengePeriodLengthChangeInitiated

newDkgResultChallengeExtraGas

uint256 newDkgResultChallengeExtraGas

dkgResultChallengeExtraGasChangeInitiated

uint256 dkgResultChallengeExtraGasChangeInitiated

newDkgResultSubmissionTimeout

uint256 newDkgResultSubmissionTimeout

dkgResultSubmissionTimeoutChangeInitiated

uint256 dkgResultSubmissionTimeoutChangeInitiated

newDkgSubmitterPrecedencePeriodLength

uint256 newDkgSubmitterPrecedencePeriodLength

dkgSubmitterPrecedencePeriodLengthChangeInitiated

uint256 dkgSubmitterPrecedencePeriodLengthChangeInitiated

newRelayEntrySubmissionFailureSlashingAmount

uint96 newRelayEntrySubmissionFailureSlashingAmount

relayEntrySubmissionFailureSlashingAmountChangeInitiated

uint256 relayEntrySubmissionFailureSlashingAmountChangeInitiated

newMaliciousDkgResultSlashingAmount

uint96 newMaliciousDkgResultSlashingAmount

maliciousDkgResultSlashingAmountChangeInitiated

uint256 maliciousDkgResultSlashingAmountChangeInitiated

newUnauthorizedSigningSlashingAmount

uint96 newUnauthorizedSigningSlashingAmount

unauthorizedSigningSlashingAmountChangeInitiated

uint256 unauthorizedSigningSlashingAmountChangeInitiated

newSortitionPoolRewardsBanDuration

uint256 newSortitionPoolRewardsBanDuration

sortitionPoolRewardsBanDurationChangeInitiated

uint256 sortitionPoolRewardsBanDurationChangeInitiated

newRelayEntryTimeoutNotificationRewardMultiplier

uint256 newRelayEntryTimeoutNotificationRewardMultiplier

relayEntryTimeoutNotificationRewardMultiplierChangeInitiated

uint256 relayEntryTimeoutNotificationRewardMultiplierChangeInitiated

newUnauthorizedSigningNotificationRewardMultiplier

uint256 newUnauthorizedSigningNotificationRewardMultiplier

unauthorizedSigningNotificationRewardMultiplierChangeInitiated

uint256 unauthorizedSigningNotificationRewardMultiplierChangeInitiated

newMinimumAuthorization

uint96 newMinimumAuthorization

minimumAuthorizationChangeInitiated

uint256 minimumAuthorizationChangeInitiated

newAuthorizationDecreaseDelay

uint64 newAuthorizationDecreaseDelay

authorizationDecreaseDelayChangeInitiated

uint256 authorizationDecreaseDelayChangeInitiated

newAuthorizationDecreaseChangePeriod

uint64 newAuthorizationDecreaseChangePeriod

authorizationDecreaseChangePeriodChangeInitiated

uint256 authorizationDecreaseChangePeriodChangeInitiated

newDkgMaliciousResultNotificationRewardMultiplier

uint256 newDkgMaliciousResultNotificationRewardMultiplier

dkgMaliciousResultNotificationRewardMultiplierChangeInitiated

uint256 dkgMaliciousResultNotificationRewardMultiplierChangeInitiated

newDkgResultSubmissionGas

uint256 newDkgResultSubmissionGas

dkgResultSubmissionGasChangeInitiated

uint256 dkgResultSubmissionGasChangeInitiated

newDkgResultApprovalGasOffset

uint256 newDkgResultApprovalGasOffset

dkgResultApprovalGasOffsetChangeInitiated

uint256 dkgResultApprovalGasOffsetChangeInitiated

newNotifyOperatorInactivityGasOffset

uint256 newNotifyOperatorInactivityGasOffset

notifyOperatorInactivityGasOffsetChangeInitiated

uint256 notifyOperatorInactivityGasOffsetChangeInitiated

newRelayEntrySubmissionGasOffset

uint256 newRelayEntrySubmissionGasOffset

relayEntrySubmissionGasOffsetChangeInitiated

uint256 relayEntrySubmissionGasOffsetChangeInitiated

randomBeacon

contract RandomBeacon randomBeacon

governanceDelay

uint256 governanceDelay

GovernanceDelayUpdateStarted

event GovernanceDelayUpdateStarted(uint256 governanceDelay, uint256 timestamp)

GovernanceDelayUpdated

event GovernanceDelayUpdated(uint256 governanceDelay)

RandomBeaconGovernanceTransferStarted

event RandomBeaconGovernanceTransferStarted(address newRandomBeaconGovernance, uint256 timestamp)

RandomBeaconGovernanceTransferred

event RandomBeaconGovernanceTransferred(address newRandomBeaconGovernance)

RelayEntrySoftTimeoutUpdateStarted

event RelayEntrySoftTimeoutUpdateStarted(uint256 relayEntrySoftTimeout, uint256 timestamp)

RelayEntrySoftTimeoutUpdated

event RelayEntrySoftTimeoutUpdated(uint256 relayEntrySoftTimeout)

RelayEntryHardTimeoutUpdateStarted

event RelayEntryHardTimeoutUpdateStarted(uint256 relayEntryHardTimeout, uint256 timestamp)

RelayEntryHardTimeoutUpdated

event RelayEntryHardTimeoutUpdated(uint256 relayEntryHardTimeout)

CallbackGasLimitUpdateStarted

event CallbackGasLimitUpdateStarted(uint256 callbackGasLimit, uint256 timestamp)

CallbackGasLimitUpdated

event CallbackGasLimitUpdated(uint256 callbackGasLimit)

GroupCreationFrequencyUpdateStarted

event GroupCreationFrequencyUpdateStarted(uint256 groupCreationFrequency, uint256 timestamp)

GroupCreationFrequencyUpdated

event GroupCreationFrequencyUpdated(uint256 groupCreationFrequency)

GroupLifetimeUpdateStarted

event GroupLifetimeUpdateStarted(uint256 groupLifetime, uint256 timestamp)

GroupLifetimeUpdated

event GroupLifetimeUpdated(uint256 groupLifetime)

DkgResultChallengePeriodLengthUpdateStarted

event DkgResultChallengePeriodLengthUpdateStarted(uint256 dkgResultChallengePeriodLength, uint256 timestamp)

DkgResultChallengePeriodLengthUpdated

event DkgResultChallengePeriodLengthUpdated(uint256 dkgResultChallengePeriodLength)

DkgResultChallengeExtraGasUpdateStarted

event DkgResultChallengeExtraGasUpdateStarted(uint256 dkgResultChallengeExtraGas, uint256 timestamp)

DkgResultChallengeExtraGasUpdated

event DkgResultChallengeExtraGasUpdated(uint256 dkgResultChallengeExtraGas)

DkgResultSubmissionTimeoutUpdateStarted

event DkgResultSubmissionTimeoutUpdateStarted(uint256 dkgResultSubmissionTimeout, uint256 timestamp)

DkgResultSubmissionTimeoutUpdated

event DkgResultSubmissionTimeoutUpdated(uint256 dkgResultSubmissionTimeout)

DkgSubmitterPrecedencePeriodLengthUpdateStarted

event DkgSubmitterPrecedencePeriodLengthUpdateStarted(uint256 submitterPrecedencePeriodLength, uint256 timestamp)

DkgSubmitterPrecedencePeriodLengthUpdated

event DkgSubmitterPrecedencePeriodLengthUpdated(uint256 submitterPrecedencePeriodLength)

RelayEntrySubmissionFailureSlashingAmountUpdateStarted

event RelayEntrySubmissionFailureSlashingAmountUpdateStarted(uint96 relayEntrySubmissionFailureSlashingAmount, uint256 timestamp)

RelayEntrySubmissionFailureSlashingAmountUpdated

event RelayEntrySubmissionFailureSlashingAmountUpdated(uint96 relayEntrySubmissionFailureSlashingAmount)

MaliciousDkgResultSlashingAmountUpdateStarted

event MaliciousDkgResultSlashingAmountUpdateStarted(uint96 maliciousDkgResultSlashingAmount, uint256 timestamp)

MaliciousDkgResultSlashingAmountUpdated

event MaliciousDkgResultSlashingAmountUpdated(uint96 maliciousDkgResultSlashingAmount)

UnauthorizedSigningSlashingAmountUpdateStarted

event UnauthorizedSigningSlashingAmountUpdateStarted(uint96 unauthorizedSigningSlashingAmount, uint256 timestamp)

UnauthorizedSigningSlashingAmountUpdated

event UnauthorizedSigningSlashingAmountUpdated(uint96 unauthorizedSigningSlashingAmount)

SortitionPoolRewardsBanDurationUpdateStarted

event SortitionPoolRewardsBanDurationUpdateStarted(uint256 sortitionPoolRewardsBanDuration, uint256 timestamp)

SortitionPoolRewardsBanDurationUpdated

event SortitionPoolRewardsBanDurationUpdated(uint256 sortitionPoolRewardsBanDuration)

RelayEntryTimeoutNotificationRewardMultiplierUpdateStarted

event RelayEntryTimeoutNotificationRewardMultiplierUpdateStarted(uint256 relayEntryTimeoutNotificationRewardMultiplier, uint256 timestamp)

RelayEntryTimeoutNotificationRewardMultiplierUpdated

event RelayEntryTimeoutNotificationRewardMultiplierUpdated(uint256 relayEntryTimeoutNotificationRewardMultiplier)

UnauthorizedSigningNotificationRewardMultiplierUpdateStarted

event UnauthorizedSigningNotificationRewardMultiplierUpdateStarted(uint256 unauthorizedSigningTimeoutNotificationRewardMultiplier, uint256 timestamp)

UnauthorizedSigningNotificationRewardMultiplierUpdated

event UnauthorizedSigningNotificationRewardMultiplierUpdated(uint256 unauthorizedSigningTimeoutNotificationRewardMultiplier)

MinimumAuthorizationUpdateStarted

event MinimumAuthorizationUpdateStarted(uint96 minimumAuthorization, uint256 timestamp)

MinimumAuthorizationUpdated

event MinimumAuthorizationUpdated(uint96 minimumAuthorization)

AuthorizationDecreaseDelayUpdateStarted

event AuthorizationDecreaseDelayUpdateStarted(uint64 authorizationDecreaseDelay, uint256 timestamp)

AuthorizationDecreaseDelayUpdated

event AuthorizationDecreaseDelayUpdated(uint64 authorizationDecreaseDelay)

AuthorizationDecreaseChangePeriodUpdateStarted

event AuthorizationDecreaseChangePeriodUpdateStarted(uint64 authorizationDecreaseChangePeriod, uint256 timestamp)

AuthorizationDecreaseChangePeriodUpdated

event AuthorizationDecreaseChangePeriodUpdated(uint64 authorizationDecreaseChangePeriod)

DkgMaliciousResultNotificationRewardMultiplierUpdateStarted

event DkgMaliciousResultNotificationRewardMultiplierUpdateStarted(uint256 dkgMaliciousResultNotificationRewardMultiplier, uint256 timestamp)

DkgMaliciousResultNotificationRewardMultiplierUpdated

event DkgMaliciousResultNotificationRewardMultiplierUpdated(uint256 dkgMaliciousResultNotificationRewardMultiplier)

DkgResultSubmissionGasUpdateStarted

event DkgResultSubmissionGasUpdateStarted(uint256 dkgResultSubmissionGas, uint256 timestamp)

DkgResultSubmissionGasUpdated

event DkgResultSubmissionGasUpdated(uint256 dkgResultSubmissionGas)

DkgResultApprovalGasOffsetUpdateStarted

event DkgResultApprovalGasOffsetUpdateStarted(uint256 dkgResultApprovalGasOffset, uint256 timestamp)

DkgResultApprovalGasOffsetUpdated

event DkgResultApprovalGasOffsetUpdated(uint256 dkgResultApprovalGasOffset)

NotifyOperatorInactivityGasOffsetUpdateStarted

event NotifyOperatorInactivityGasOffsetUpdateStarted(uint256 notifyOperatorInactivityGasOffset, uint256 timestamp)

NotifyOperatorInactivityGasOffsetUpdated

event NotifyOperatorInactivityGasOffsetUpdated(uint256 notifyOperatorInactivityGasOffset)

RelayEntrySubmissionGasOffsetUpdateStarted

event RelayEntrySubmissionGasOffsetUpdateStarted(uint256 relayEntrySubmissionGasOffset, uint256 timestamp)

RelayEntrySubmissionGasOffsetUpdated

event RelayEntrySubmissionGasOffsetUpdated(uint256 relayEntrySubmissionGasOffset)

onlyAfterGovernanceDelay

modifier onlyAfterGovernanceDelay(uint256 changeInitiatedTimestamp)

Reverts if called before the governance delay elapses.

Parameters

Name
Type
Description

changeInitiatedTimestamp

uint256

Timestamp indicating the beginning of the change.

constructor

constructor(contract RandomBeacon _randomBeacon, uint256 _governanceDelay) public

beginGovernanceDelayUpdate

function beginGovernanceDelayUpdate(uint256 _newGovernanceDelay) external

Begins the governance delay update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newGovernanceDelay

uint256

New governance delay

finalizeGovernanceDelayUpdate

function finalizeGovernanceDelayUpdate() external

Finalizes the governance delay update process.

Can be called only by the contract owner, after the governance delay elapses.

beginRandomBeaconGovernanceTransfer

function beginRandomBeaconGovernanceTransfer(address _newRandomBeaconGovernance) external

Begins the random beacon governance transfer process.

Can be called only by the current contract governance.

finalizeRandomBeaconGovernanceTransfer

function finalizeRandomBeaconGovernanceTransfer() external

Finalizes the random beacon governance transfer process.

Can be called only by the current contract governance, after the governance delay elapses.

beginRelayEntrySoftTimeoutUpdate

function beginRelayEntrySoftTimeoutUpdate(uint256 _newRelayEntrySoftTimeout) external

Begins the relay entry soft timeout update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newRelayEntrySoftTimeout

uint256

New relay entry submission timeout in blocks

finalizeRelayEntrySoftTimeoutUpdate

function finalizeRelayEntrySoftTimeoutUpdate() external

Finalizes the relay entry soft timeout update process.

Can be called only by the contract owner, after the governance delay elapses.

beginRelayEntryHardTimeoutUpdate

function beginRelayEntryHardTimeoutUpdate(uint256 _newRelayEntryHardTimeout) external

Begins the relay entry hard timeout update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newRelayEntryHardTimeout

uint256

New relay entry hard timeout in blocks

finalizeRelayEntryHardTimeoutUpdate

function finalizeRelayEntryHardTimeoutUpdate() external

Finalizes the relay entry hard timeout update process.

Can be called only by the contract owner, after the governance delay elapses.

beginCallbackGasLimitUpdate

function beginCallbackGasLimitUpdate(uint256 _newCallbackGasLimit) external

Begins the callback gas limit update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newCallbackGasLimit

uint256

New callback gas limit

finalizeCallbackGasLimitUpdate

function finalizeCallbackGasLimitUpdate() external

Finalizes the callback gas limit update process.

Can be called only by the contract owner, after the governance delay elapses.

beginGroupCreationFrequencyUpdate

function beginGroupCreationFrequencyUpdate(uint256 _newGroupCreationFrequency) external

Begins the group creation frequency update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newGroupCreationFrequency

uint256

New group creation frequency

finalizeGroupCreationFrequencyUpdate

function finalizeGroupCreationFrequencyUpdate() external

Finalizes the group creation frequency update process.

Can be called only by the contract owner, after the governance delay elapses.

beginGroupLifetimeUpdate

function beginGroupLifetimeUpdate(uint256 _newGroupLifetime) external

Begins the group lifetime update process. Group lifetime needs to be shorter than the authorization decrease delay to ensure every active group is backed by enough stake. A new group lifetime value is in blocks and has to be calculated based on the average block time and authorization decrease delay which value is in seconds.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newGroupLifetime

uint256

New group lifetime in blocks

finalizeGroupLifetimeUpdate

function finalizeGroupLifetimeUpdate() external

Finalizes the group creation frequency update process.

Can be called only by the contract owner, after the governance delay elapses.

beginDkgResultChallengePeriodLengthUpdate

function beginDkgResultChallengePeriodLengthUpdate(uint256 _newDkgResultChallengePeriodLength) external

Begins the DKG result challenge period length update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newDkgResultChallengePeriodLength

uint256

New DKG result challenge period length in blocks

finalizeDkgResultChallengePeriodLengthUpdate

function finalizeDkgResultChallengePeriodLengthUpdate() external

Finalizes the DKG result challenge period length update process.

Can be called only by the contract owner, after the governance delay elapses.

beginDkgResultChallengeExtraGasUpdate

function beginDkgResultChallengeExtraGasUpdate(uint256 _newDkgResultChallengeExtraGas) external

Begins the DKG result challenge extra gas update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newDkgResultChallengeExtraGas

uint256

New DKG result challenge extra gas

finalizeDkgResultChallengeExtraGasUpdate

function finalizeDkgResultChallengeExtraGasUpdate() external

Finalizes the DKG result challenge extra gas update process.

Can be called only by the contract owner, after the governance delay elapses.

beginDkgResultSubmissionTimeoutUpdate

function beginDkgResultSubmissionTimeoutUpdate(uint256 _newDkgResultSubmissionTimeout) external

Begins the DKG result submission timeout update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newDkgResultSubmissionTimeout

uint256

New DKG result submission timeout in blocks

finalizeDkgResultSubmissionTimeoutUpdate

function finalizeDkgResultSubmissionTimeoutUpdate() external

Finalizes the DKG result submission timeout update process.

Can be called only by the contract owner, after the governance delay elapses.

beginDkgSubmitterPrecedencePeriodLengthUpdate

function beginDkgSubmitterPrecedencePeriodLengthUpdate(uint256 _newDkgSubmitterPrecedencePeriodLength) external

Begins the DKG submitter precedence period length.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newDkgSubmitterPrecedencePeriodLength

uint256

New DKG submitter precedence period length in blocks

finalizeDkgSubmitterPrecedencePeriodLengthUpdate

function finalizeDkgSubmitterPrecedencePeriodLengthUpdate() external

Finalizes the DKG submitter precedence period length.

Can be called only by the contract owner, after the governance delay elapses.

beginSortitionPoolRewardsBanDurationUpdate

function beginSortitionPoolRewardsBanDurationUpdate(uint256 _newSortitionPoolRewardsBanDuration) external

Begins the sortition pool rewards ban duration update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newSortitionPoolRewardsBanDuration

uint256

New sortition pool rewards ban duration.

finalizeSortitionPoolRewardsBanDurationUpdate

function finalizeSortitionPoolRewardsBanDurationUpdate() external

Finalizes the sortition pool rewards ban duration update process.

Can be called only by the contract owner, after the governance delay elapses.

beginRelayEntryTimeoutNotificationRewardMultiplierUpdate

function beginRelayEntryTimeoutNotificationRewardMultiplierUpdate(uint256 _newRelayEntryTimeoutNotificationRewardMultiplier) external

Begins the relay entry timeout notification reward multiplier update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newRelayEntryTimeoutNotificationRewardMultiplier

uint256

New relay entry timeout notification reward multiplier.

beginUnauthorizedSigningNotificationRewardMultiplierUpdate

function beginUnauthorizedSigningNotificationRewardMultiplierUpdate(uint256 _newUnauthorizedSigningNotificationRewardMultiplier) external

Begins the unauthorized signing notification reward multiplier update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newUnauthorizedSigningNotificationRewardMultiplier

uint256

New unauthorized signing notification reward multiplier.

finalizeUnauthorizedSigningNotificationRewardMultiplierUpdate

function finalizeUnauthorizedSigningNotificationRewardMultiplierUpdate() external

Finalizes the unauthorized signing notification reward multiplier update process.

Can be called only by the contract owner, after the governance delay elapses.

finalizeRelayEntryTimeoutNotificationRewardMultiplierUpdate

function finalizeRelayEntryTimeoutNotificationRewardMultiplierUpdate() external

Finalizes the relay entry timeout notification reward multiplier update process.

Can be called only by the contract owner, after the governance delay elapses.

beginDkgMaliciousResultNotificationRewardMultiplierUpdate

function beginDkgMaliciousResultNotificationRewardMultiplierUpdate(uint256 _newDkgMaliciousResultNotificationRewardMultiplier) external

Begins the DKG malicious result notification reward multiplier update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newDkgMaliciousResultNotificationRewardMultiplier

uint256

New DKG malicious result notification reward multiplier.

finalizeDkgMaliciousResultNotificationRewardMultiplierUpdate

function finalizeDkgMaliciousResultNotificationRewardMultiplierUpdate() external

Finalizes the DKG malicious result notification reward multiplier update process.

Can be called only by the contract owner, after the governance delay elapses.

beginRelayEntrySubmissionFailureSlashingAmountUpdate

function beginRelayEntrySubmissionFailureSlashingAmountUpdate(uint96 _newRelayEntrySubmissionFailureSlashingAmount) external

Begins the relay entry submission failure slashing amount update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newRelayEntrySubmissionFailureSlashingAmount

uint96

New relay entry submission failure slashing amount

finalizeRelayEntrySubmissionFailureSlashingAmountUpdate

function finalizeRelayEntrySubmissionFailureSlashingAmountUpdate() external

Finalizes the relay entry submission failure slashing amount update process.

Can be called only by the contract owner, after the governance delay elapses.

beginDkgResultSubmissionGasUpdate

function beginDkgResultSubmissionGasUpdate(uint256 _newDkgResultSubmissionGas) external

Begins the DKG result submission gas update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newDkgResultSubmissionGas

uint256

New relay entry submission gas offset

finalizeDkgResultSubmissionGasUpdate

function finalizeDkgResultSubmissionGasUpdate() external

Finalizes DKG result submission gas update process.

Can be called only by the contract owner, after the governance delay elapses.

beginDkgResultApprovalGasOffsetUpdate

function beginDkgResultApprovalGasOffsetUpdate(uint256 _newDkgResultApprovalGasOffset) external

Begins the DKG result approval gas offset update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newDkgResultApprovalGasOffset

uint256

New DKG approval gas offset

finalizeDkgResultApprovalGasOffsetUpdate

function finalizeDkgResultApprovalGasOffsetUpdate() external

Finalizes the DKG result approval gas offset update process.

Can be called only by the contract owner, after the governance delay elapses.

beginNotifyOperatorInactivityGasOffsetUpdate

function beginNotifyOperatorInactivityGasOffsetUpdate(uint256 _newNotifyOperatorInactivityGasOffset) external

Begins the notify operator inactivity gas offset update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newNotifyOperatorInactivityGasOffset

uint256

New operator inactivity notification gas offset

finalizeNotifyOperatorInactivityGasOffsetUpdate

function finalizeNotifyOperatorInactivityGasOffsetUpdate() external

Finalizes the notify operator inactivity gas offset update process.

Can be called only by the contract owner, after the governance delay elapses.

beginRelayEntrySubmissionGasOffsetUpdate

function beginRelayEntrySubmissionGasOffsetUpdate(uint256 _newRelayEntrySubmissionGasOffset) external

Begins the relay entry submission gas offset update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newRelayEntrySubmissionGasOffset

uint256

New relay entry submission gas offset

finalizeRelayEntrySubmissionGasOffsetUpdate

function finalizeRelayEntrySubmissionGasOffsetUpdate() external

Finalizes relay entry submission gas offset update process.

Can be called only by the contract owner, after the governance delay elapses.

beginMaliciousDkgResultSlashingAmountUpdate

function beginMaliciousDkgResultSlashingAmountUpdate(uint96 _newMaliciousDkgResultSlashingAmount) external

Begins the malicious DKG result slashing amount update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newMaliciousDkgResultSlashingAmount

uint96

New malicious DKG result slashing amount

finalizeMaliciousDkgResultSlashingAmountUpdate

function finalizeMaliciousDkgResultSlashingAmountUpdate() external

Finalizes the malicious DKG result slashing amount update process.

Can be called only by the contract owner, after the governance delay elapses.

beginUnauthorizedSigningSlashingAmountUpdate

function beginUnauthorizedSigningSlashingAmountUpdate(uint96 _newUnauthorizedSigningSlashingAmount) external

Begins the unauthorized signing slashing amount update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newUnauthorizedSigningSlashingAmount

uint96

New unauthorized signing slashing amount

finalizeUnauthorizedSigningSlashingAmountUpdate

function finalizeUnauthorizedSigningSlashingAmountUpdate() external

Finalizes the unauthorized signing slashing amount update process.

Can be called only by the contract owner, after the governance delay elapses.

beginMinimumAuthorizationUpdate

function beginMinimumAuthorizationUpdate(uint96 _newMinimumAuthorization) external

Begins the minimum authorization amount update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newMinimumAuthorization

uint96

New minimum authorization amount.

finalizeMinimumAuthorizationUpdate

function finalizeMinimumAuthorizationUpdate() external

Finalizes the minimum authorization amount update process.

Can be called only by the contract owner, after the governance delay elapses.

beginAuthorizationDecreaseDelayUpdate

function beginAuthorizationDecreaseDelayUpdate(uint64 _newAuthorizationDecreaseDelay) external

Begins the authorization decrease delay update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newAuthorizationDecreaseDelay

uint64

New authorization decrease delay

finalizeAuthorizationDecreaseDelayUpdate

function finalizeAuthorizationDecreaseDelayUpdate() external

Finalizes the authorization decrease delay update process.

Can be called only by the contract owner, after the governance delay elapses.

beginAuthorizationDecreaseChangePeriodUpdate

function beginAuthorizationDecreaseChangePeriodUpdate(uint64 _newAuthorizationDecreaseChangePeriod) external

Begins the authorization decrease change period update process.

Can be called only by the contract owner.

Parameters

Name
Type
Description

_newAuthorizationDecreaseChangePeriod

uint64

New authorization decrease change period

finalizeAuthorizationDecreaseChangePeriodUpdate

function finalizeAuthorizationDecreaseChangePeriodUpdate() external

Finalizes the authorization decrease change period update process.

Can be called only by the contract owner, after the governance delay elapses.

setRequesterAuthorization

function setRequesterAuthorization(address requester, bool isAuthorized) external

Set authorization for requesters that can request a relay entry. It can be done by the governance only.

Parameters

Name
Type
Description

requester

address

Requester, can be a contract or EOA

isAuthorized

bool

True or false

withdrawIneligibleRewards

function withdrawIneligibleRewards(address recipient) external

Withdraws rewards belonging to operators marked as ineligible for sortition pool rewards.

Can be called only by the contract owner.

Parameters

Name
Type
Description

recipient

address

Recipient of withdrawn rewards.

getRemainingGovernanceDelayUpdateTime

function getRemainingGovernanceDelayUpdateTime() external view returns (uint256)

Get the time remaining until the governance delay can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingRandomBeaconGovernanceTransferDelayTime

function getRemainingRandomBeaconGovernanceTransferDelayTime() external view returns (uint256)

Get the time remaining until the random beacon governance can be transferred.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingRelayEntrySoftTimeoutUpdateTime

function getRemainingRelayEntrySoftTimeoutUpdateTime() external view returns (uint256)

Get the time remaining until the relay entry submission soft timeout can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingRelayEntryHardTimeoutUpdateTime

function getRemainingRelayEntryHardTimeoutUpdateTime() external view returns (uint256)

Get the time remaining until the relay entry hard timeout can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingCallbackGasLimitUpdateTime

function getRemainingCallbackGasLimitUpdateTime() external view returns (uint256)

Get the time remaining until the callback gas limit can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingGroupCreationFrequencyUpdateTime

function getRemainingGroupCreationFrequencyUpdateTime() external view returns (uint256)

Get the time remaining until the group creation frequency can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingGroupLifetimeUpdateTime

function getRemainingGroupLifetimeUpdateTime() external view returns (uint256)

Get the time remaining until the group lifetime can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingDkgResultChallengePeriodLengthUpdateTime

function getRemainingDkgResultChallengePeriodLengthUpdateTime() external view returns (uint256)

Get the time remaining until the DKG result challenge period length can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingDkgResultChallengeExtraGasUpdateTime

function getRemainingDkgResultChallengeExtraGasUpdateTime() external view returns (uint256)

Get the time remaining until the DKG result challenge extra gas can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingDkgResultSubmissionTimeoutUpdateTime

function getRemainingDkgResultSubmissionTimeoutUpdateTime() external view returns (uint256)

Get the time remaining until the DKG result submission timeout can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingDkgSubmitterPrecedencePeriodLengthUpdateTime

function getRemainingDkgSubmitterPrecedencePeriodLengthUpdateTime() external view returns (uint256)

Get the time remaining until the wallet owner can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingRelayEntrySubmissionFailureSlashingAmountUpdateTime

function getRemainingRelayEntrySubmissionFailureSlashingAmountUpdateTime() external view returns (uint256)

Get the time remaining until the relay entry submission failure slashing amount can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingMaliciousDkgResultSlashingAmountUpdateTime

function getRemainingMaliciousDkgResultSlashingAmountUpdateTime() external view returns (uint256)

Get the time remaining until the malicious DKG result slashing amount can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingUnauthorizedSigningSlashingAmountUpdateTime

function getRemainingUnauthorizedSigningSlashingAmountUpdateTime() external view returns (uint256)

Get the time remaining until the unauthorized signing slashing amount can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingMimimumAuthorizationUpdateTime

function getRemainingMimimumAuthorizationUpdateTime() external view returns (uint256)

Get the time remaining until the minimum authorization amount can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingAuthorizationDecreaseDelayUpdateTime

function getRemainingAuthorizationDecreaseDelayUpdateTime() external view returns (uint256)

Get the time remaining until the authorization decrease delay can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingAuthorizationDecreaseChangePeriodUpdateTime

function getRemainingAuthorizationDecreaseChangePeriodUpdateTime() external view returns (uint256)

Get the time remaining until the authorization decrease change period can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingSortitionPoolRewardsBanDurationUpdateTime

function getRemainingSortitionPoolRewardsBanDurationUpdateTime() external view returns (uint256)

Get the time remaining until the sortition pool rewards ban duration can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingRelayEntryTimeoutNotificationRewardMultiplierUpdateTime

function getRemainingRelayEntryTimeoutNotificationRewardMultiplierUpdateTime() external view returns (uint256)

Get the time remaining until the relay entry timeout notification reward multiplier duration can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingUnauthorizedSigningNotificationRewardMultiplierUpdateTime

function getRemainingUnauthorizedSigningNotificationRewardMultiplierUpdateTime() external view returns (uint256)

Get the time remaining until the unauthorized signing notification reward multiplier duration can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingDkgMaliciousResultNotificationRewardMultiplierUpdateTime

function getRemainingDkgMaliciousResultNotificationRewardMultiplierUpdateTime() external view returns (uint256)

Get the time remaining until the DKG malicious result notification reward multiplier duration can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingDkgResultSubmissionGasUpdateTime

function getRemainingDkgResultSubmissionGasUpdateTime() external view returns (uint256)

Get the time remaining until the DKG result submission gas duration can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingDkgResultApprovalGasOffsetUpdateTime

function getRemainingDkgResultApprovalGasOffsetUpdateTime() external view returns (uint256)

Get the time remaining until the DKG approval gas offset duration can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingNotifyOperatorInactivityGasOffsetUpdateTime

function getRemainingNotifyOperatorInactivityGasOffsetUpdateTime() external view returns (uint256)

Get the time remaining until the operator inactivity notification gas offset duration can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingRelayEntrySubmissionGasOffsetUpdateTime

function getRemainingRelayEntrySubmissionGasOffsetUpdateTime() external view returns (uint256)

Get the time remaining until the relay entry submission gas offset duration can be updated.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.

getRemainingChangeTime

function getRemainingChangeTime(uint256 changeTimestamp) internal view returns (uint256)

Gets the time remaining until the governable parameter update can be committed.

Parameters

Name
Type
Description

changeTimestamp

uint256

Timestamp indicating the beginning of the change.

Return Values

Name
Type
Description

[0]

uint256

Remaining time in seconds.