@keep-network/tbtc-v2 0.1.1-dev.100 → 0.1.1-dev.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifacts/Bank.json +6 -6
- package/artifacts/Bridge.json +11 -11
- package/artifacts/BridgeGovernance.json +2931 -0
- package/artifacts/BridgeGovernanceParameters.json +1445 -0
- package/artifacts/Deposit.json +7 -7
- package/artifacts/DepositSweep.json +9 -9
- package/artifacts/EcdsaDkgValidator.json +3 -3
- package/artifacts/EcdsaInactivity.json +3 -3
- package/artifacts/EcdsaSortitionPool.json +5 -5
- package/artifacts/Fraud.json +7 -7
- package/artifacts/KeepRegistry.json +3 -3
- package/artifacts/KeepStake.json +5 -5
- package/artifacts/KeepToken.json +5 -5
- package/artifacts/KeepTokenStaking.json +3 -3
- package/artifacts/MaintainerProxy.json +1522 -0
- package/artifacts/MovingFunds.json +7 -7
- package/artifacts/NuCypherStakingEscrow.json +3 -3
- package/artifacts/NuCypherToken.json +5 -5
- package/artifacts/RandomBeaconStub.json +3 -3
- package/artifacts/Redemption.json +9 -9
- package/artifacts/ReimbursementPool.json +5 -5
- package/artifacts/Relay.json +9 -9
- package/artifacts/T.json +5 -5
- package/artifacts/TBTC.json +6 -6
- package/artifacts/TBTCToken.json +6 -6
- package/artifacts/TBTCVault.json +12 -12
- package/artifacts/TokenStaking.json +3 -3
- package/artifacts/TokenholderGovernor.json +19 -19
- package/artifacts/TokenholderTimelock.json +17 -17
- package/artifacts/VendingMachine.json +13 -13
- package/artifacts/VendingMachineKeep.json +3 -3
- package/artifacts/VendingMachineNuCypher.json +3 -3
- package/artifacts/WalletRegistry.json +11 -11
- package/artifacts/WalletRegistryGovernance.json +5 -5
- package/artifacts/Wallets.json +7 -7
- package/artifacts/solcInputs/{8b65103759482b36742c6820fa66b63b.json → 4d9b6f53ac50e0b0457be71c45eba893.json} +31 -22
- package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
- package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
- package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.dbg.json +1 -1
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +2 -2
- package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
- package/build/contracts/bridge/Bridge.sol/Bridge.json +2 -2
- package/build/contracts/bridge/BridgeGovernance.sol/BridgeGovernance.dbg.json +4 -0
- package/build/contracts/bridge/BridgeGovernance.sol/BridgeGovernance.json +2246 -0
- package/build/contracts/bridge/BridgeGovernanceParameters.sol/BridgeGovernanceParameters.dbg.json +4 -0
- package/build/contracts/bridge/BridgeGovernanceParameters.sol/BridgeGovernanceParameters.json +971 -0
- package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +1 -1
- package/build/contracts/bridge/BridgeState.sol/BridgeState.json +2 -2
- package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +1 -1
- package/build/contracts/bridge/Deposit.sol/Deposit.json +2 -2
- package/build/contracts/bridge/DepositSweep.sol/DepositSweep.dbg.json +1 -1
- package/build/contracts/bridge/DepositSweep.sol/DepositSweep.json +2 -2
- package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
- package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +1 -1
- package/build/contracts/bridge/Fraud.sol/Fraud.json +2 -2
- package/build/contracts/bridge/Heartbeat.sol/Heartbeat.dbg.json +1 -1
- package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +1 -1
- package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +1 -1
- package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +2 -2
- package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +1 -1
- package/build/contracts/bridge/Redemption.sol/OutboundTx.json +2 -2
- package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +1 -1
- package/build/contracts/bridge/Redemption.sol/Redemption.json +2 -2
- package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
- package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
- package/build/contracts/bridge/Wallets.sol/Wallets.json +2 -2
- package/build/contracts/maintainer/MaintainerProxy.sol/MaintainerProxy.dbg.json +4 -0
- package/build/contracts/maintainer/MaintainerProxy.sol/MaintainerProxy.json +1111 -0
- package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
- package/build/contracts/vault/DonationVault.sol/DonationVault.dbg.json +1 -1
- package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
- package/contracts/bridge/Bridge.sol +2 -2
- package/contracts/bridge/BridgeGovernance.sol +1533 -0
- package/contracts/bridge/BridgeGovernanceParameters.sol +1695 -0
- package/contracts/bridge/Redemption.sol +3 -3
- package/contracts/maintainer/MaintainerProxy.sol +512 -0
- package/deploy/07_deploy_bridge_governance.ts +40 -0
- package/deploy/08_deploy_maintainer_proxy.ts +30 -0
- package/deploy/{07_bank_update_bridge.ts → 09_bank_update_bridge.ts} +0 -0
- package/deploy/{08_transfer_bank_ownership.ts → 10_transfer_bank_ownership.ts} +0 -0
- package/deploy/{10_transfer_bridge_governance.ts → 11_transfer_bridge_governance.ts} +4 -2
- package/deploy/12_transfer_bridge_governance_ownership.ts +19 -0
- package/deploy/{09_transfer_tbtc_vault_ownership.ts → 13_transfer_tbtc_vault_ownership.ts} +0 -0
- package/deploy/14_transfer_maintainer_proxy_ownership.ts +19 -0
- package/deploy/{11_initialize_wallet_owner.ts → 15_initialize_wallet_owner.ts} +0 -0
- package/deploy/{11_transfer_proxy_admin_ownership.ts → 16_transfer_proxy_admin_ownership.ts} +0 -0
- package/deploy/17_authorize_maintainer_proxy.ts +22 -0
- package/deploy/18_transfer_reimbursement_pool_ownership.ts +19 -0
- package/deploy/{12_deploy_proxy_admin_with_deputy.ts → 19_deploy_proxy_admin_with_deputy.ts} +0 -0
- package/export.json +3812 -0
- package/package.json +1 -1
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)\n\npragma solidity ^0.8.1;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary AddressUpgradeable {\n /**\n * @dev Returns true if `account` is a contract.\n *\n * [IMPORTANT]\n * ====\n * It is unsafe to assume that an address for which this function returns\n * false is an externally-owned account (EOA) and not a contract.\n *\n * Among others, `isContract` will return false for the following\n * types of addresses:\n *\n * - an externally-owned account\n * - a contract in construction\n * - an address where a contract will be created\n * - an address where a contract lived, but was destroyed\n * ====\n *\n * [IMPORTANT]\n * ====\n * You shouldn't rely on `isContract` to protect against flash loan attacks!\n *\n * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets\n * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract\n * constructor.\n * ====\n */\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize/address.code.length, which returns 0\n // for contracts in construction, since the code is only stored at the end\n // of the constructor execution.\n\n return account.code.length > 0;\n }\n\n /**\n * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n * `recipient`, forwarding all available gas and reverting on errors.\n *\n * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n * of certain opcodes, possibly making contracts go over the 2300 gas limit\n * imposed by `transfer`, making them unable to receive funds via\n * `transfer`. {sendValue} removes this limitation.\n *\n * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n *\n * IMPORTANT: because control is transferred to `recipient`, care must be\n * taken to not create reentrancy vulnerabilities. Consider using\n * {ReentrancyGuard} or the\n * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n */\n function sendValue(address payable recipient, uint256 amount) internal {\n require(address(this).balance >= amount, \"Address: insufficient balance\");\n\n (bool success, ) = recipient.call{value: amount}(\"\");\n require(success, \"Address: unable to send value, recipient may have reverted\");\n }\n\n /**\n * @dev Performs a Solidity function call using a low level `call`. A\n * plain `call` is an unsafe replacement for a function call: use this\n * function instead.\n *\n * If `target` reverts with a revert reason, it is bubbled up by this\n * function (like regular Solidity function calls).\n *\n * Returns the raw returned data. To convert to the expected return value,\n * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n *\n * Requirements:\n *\n * - `target` must be a contract.\n * - calling `target` with `data` must not revert.\n *\n * _Available since v3.1._\n */\n function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n return functionCall(target, data, \"Address: low-level call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with\n * `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, 0, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but also transferring `value` wei to `target`.\n *\n * Requirements:\n *\n * - the calling contract must have an ETH balance of at least `value`.\n * - the called Solidity function must be `payable`.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value\n ) internal returns (bytes memory) {\n return functionCallWithValue(target, data, value, \"Address: low-level call with value failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but\n * with `errorMessage` as a fallback revert reason when `target` reverts.\n *\n * _Available since v3.1._\n */\n function functionCallWithValue(\n address target,\n bytes memory data,\n uint256 value,\n string memory errorMessage\n ) internal returns (bytes memory) {\n require(address(this).balance >= value, \"Address: insufficient balance for call\");\n require(isContract(target), \"Address: call to non-contract\");\n\n (bool success, bytes memory returndata) = target.call{value: value}(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n return functionStaticCall(target, data, \"Address: low-level static call failed\");\n }\n\n /**\n * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],\n * but performing a static call.\n *\n * _Available since v3.3._\n */\n function functionStaticCall(\n address target,\n bytes memory data,\n string memory errorMessage\n ) internal view returns (bytes memory) {\n require(isContract(target), \"Address: static call to non-contract\");\n\n (bool success, bytes memory returndata) = target.staticcall(data);\n return verifyCallResult(success, returndata, errorMessage);\n }\n\n /**\n * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the\n * revert reason using the provided one.\n *\n * _Available since v4.3._\n */\n function verifyCallResult(\n bool success,\n bytes memory returndata,\n string memory errorMessage\n ) internal pure returns (bytes memory) {\n if (success) {\n return returndata;\n } else {\n // Look for revert reason and bubble it up if present\n if (returndata.length > 0) {\n // The easiest way to bubble the revert reason is using memory via assembly\n\n assembly {\n let returndata_size := mload(returndata)\n revert(add(32, returndata), returndata_size)\n }\n } else {\n revert(errorMessage);\n }\n }\n }\n}\n"
|
|
129
129
|
},
|
|
130
130
|
"contracts/bridge/Bridge.sol": {
|
|
131
|
-
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\nimport \"@keep-network/random-beacon/contracts/Governable.sol\";\nimport {IWalletOwner as EcdsaWalletOwner} from \"@keep-network/ecdsa/contracts/api/IWalletOwner.sol\";\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol\";\n\nimport \"./IRelay.sol\";\nimport \"./BridgeState.sol\";\nimport \"./Deposit.sol\";\nimport \"./DepositSweep.sol\";\nimport \"./Redemption.sol\";\nimport \"./BitcoinTx.sol\";\nimport \"./EcdsaLib.sol\";\nimport \"./Wallets.sol\";\nimport \"./Fraud.sol\";\nimport \"./MovingFunds.sol\";\n\nimport \"../bank/IReceiveBalanceApproval.sol\";\nimport \"../bank/Bank.sol\";\n\n/// @title Bitcoin Bridge\n/// @notice Bridge manages BTC deposit and redemption flow and is increasing and\n/// decreasing balances in the Bank as a result of BTC deposit and\n/// redemption operations performed by depositors and redeemers.\n///\n/// Depositors send BTC funds to the most recently created off-chain\n/// ECDSA wallet of the bridge using pay-to-script-hash (P2SH) or\n/// pay-to-witness-script-hash (P2WSH) containing hashed information\n/// about the depositor’s Ethereum address. Then, the depositor reveals\n/// their Ethereum address along with their deposit blinding factor,\n/// refund public key hash and refund locktime to the Bridge on Ethereum\n/// chain. The off-chain ECDSA wallet listens for these sorts of\n/// messages and when it gets one, it checks the Bitcoin network to make\n/// sure the deposit lines up. If it does, the off-chain ECDSA wallet\n/// may decide to pick the deposit transaction for sweeping, and when\n/// the sweep operation is confirmed on the Bitcoin network, the ECDSA\n/// wallet informs the Bridge about the sweep increasing appropriate\n/// balances in the Bank.\n/// @dev Bridge is an upgradeable component of the Bank. The order of\n/// functionalities in this contract is: deposit, sweep, redemption,\n/// moving funds, wallet lifecycle, frauds, parameters.\ncontract Bridge is\n Governable,\n EcdsaWalletOwner,\n Initializable,\n IReceiveBalanceApproval\n{\n using BridgeState for BridgeState.Storage;\n using Deposit for BridgeState.Storage;\n using DepositSweep for BridgeState.Storage;\n using Redemption for BridgeState.Storage;\n using MovingFunds for BridgeState.Storage;\n using Wallets for BridgeState.Storage;\n using Fraud for BridgeState.Storage;\n\n BridgeState.Storage internal self;\n\n event DepositRevealed(\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex,\n address indexed depositor,\n uint64 amount,\n bytes8 blindingFactor,\n bytes20 indexed walletPubKeyHash,\n bytes20 refundPubKeyHash,\n bytes4 refundLocktime,\n address vault\n );\n\n event DepositsSwept(bytes20 walletPubKeyHash, bytes32 sweepTxHash);\n\n event RedemptionRequested(\n bytes20 indexed walletPubKeyHash,\n bytes redeemerOutputScript,\n address indexed redeemer,\n uint64 requestedAmount,\n uint64 treasuryFee,\n uint64 txMaxFee\n );\n\n event RedemptionsCompleted(\n bytes20 indexed walletPubKeyHash,\n bytes32 redemptionTxHash\n );\n\n event RedemptionTimedOut(\n bytes20 indexed walletPubKeyHash,\n bytes redeemerOutputScript\n );\n\n event WalletMovingFunds(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event MovingFundsCommitmentSubmitted(\n bytes20 indexed walletPubKeyHash,\n bytes20[] targetWallets,\n address submitter\n );\n\n event MovingFundsTimeoutReset(bytes20 indexed walletPubKeyHash);\n\n event MovingFundsCompleted(\n bytes20 indexed walletPubKeyHash,\n bytes32 movingFundsTxHash\n );\n\n event MovingFundsTimedOut(bytes20 indexed walletPubKeyHash);\n\n event MovingFundsBelowDustReported(bytes20 indexed walletPubKeyHash);\n\n event MovedFundsSwept(\n bytes20 indexed walletPubKeyHash,\n bytes32 sweepTxHash\n );\n\n event MovedFundsSweepTimedOut(\n bytes20 indexed walletPubKeyHash,\n bytes32 movingFundsTxHash,\n uint32 movingFundsTxOutputIndex\n );\n\n event NewWalletRequested();\n\n event NewWalletRegistered(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event WalletClosing(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event WalletClosed(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event WalletTerminated(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event FraudChallengeSubmitted(\n bytes20 indexed walletPubKeyHash,\n bytes32 sighash,\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n event FraudChallengeDefeated(\n bytes20 indexed walletPubKeyHash,\n bytes32 sighash\n );\n\n event FraudChallengeDefeatTimedOut(\n bytes20 indexed walletPubKeyHash,\n bytes32 sighash\n );\n\n event VaultStatusUpdated(address indexed vault, bool isTrusted);\n\n event DepositParametersUpdated(\n uint64 depositDustThreshold,\n uint64 depositTreasuryFeeDivisor,\n uint64 depositTxMaxFee\n );\n\n event RedemptionParametersUpdated(\n uint64 redemptionDustThreshold,\n uint64 redemptionTreasuryFeeDivisor,\n uint64 redemptionTxMaxFee,\n uint32 redemptionTimeout,\n uint96 redemptionTimeoutSlashingAmount,\n uint32 redemptionTimeoutNotifierRewardMultiplier\n );\n\n event MovingFundsParametersUpdated(\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n\n event WalletParametersUpdated(\n uint32 walletCreationPeriod,\n uint64 walletCreationMinBtcBalance,\n uint64 walletCreationMaxBtcBalance,\n uint64 walletClosureMinBtcBalance,\n uint32 walletMaxAge,\n uint64 walletMaxBtcTransfer,\n uint32 walletClosingPeriod\n );\n\n event FraudParametersUpdated(\n uint96 fraudChallengeDepositAmount,\n uint32 fraudChallengeDefeatTimeout,\n uint96 fraudSlashingAmount,\n uint32 fraudNotifierRewardMultiplier\n );\n\n /// @dev Initializes upgradable contract on deployment.\n /// @param _bank Address of the Bank the Bridge belongs to.\n /// @param _relay Address of the Bitcoin relay providing the current Bitcoin\n /// network difficulty.\n /// @param _treasury Address where the deposit and redemption treasury fees\n /// will be sent to.\n /// @param _ecdsaWalletRegistry Address of the ECDSA Wallet Registry contract.\n /// @param _txProofDifficultyFactor The number of confirmations on the Bitcoin\n /// chain required to successfully evaluate an SPV proof.\n function initialize(\n address _bank,\n address _relay,\n address _treasury,\n address _ecdsaWalletRegistry,\n uint96 _txProofDifficultyFactor\n ) external initializer {\n require(_bank != address(0), \"Bank address cannot be zero\");\n self.bank = Bank(_bank);\n\n require(_relay != address(0), \"Relay address cannot be zero\");\n self.relay = IRelay(_relay);\n\n require(\n _ecdsaWalletRegistry != address(0),\n \"ECDSA Wallet Registry address cannot be zero\"\n );\n self.ecdsaWalletRegistry = EcdsaWalletRegistry(_ecdsaWalletRegistry);\n\n require(_treasury != address(0), \"Treasury address cannot be zero\");\n self.treasury = _treasury;\n\n self.txProofDifficultyFactor = _txProofDifficultyFactor;\n\n //\n // All parameters set in the constructor are initial ones, used at the\n // moment contracts were deployed for the first time. Parameters are\n // governable and values assigned in the constructor do not need to\n // reflect the current ones. Keep in mind the initial parameters are\n // pretty forgiving and valid only for the early stage of the network.\n //\n\n self.depositDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC\n self.depositTxMaxFee = 100000; // 100000 satoshi = 0.001 BTC\n self.depositTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005\n self.redemptionDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC\n self.redemptionTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005\n self.redemptionTxMaxFee = 100000; // 100000 satoshi = 0.001 BTC\n self.redemptionTimeout = 5 days;\n self.redemptionTimeoutSlashingAmount = 100 * 1e18; // 100 T\n self.redemptionTimeoutNotifierRewardMultiplier = 100; // 100%\n self.movingFundsTxMaxTotalFee = 100000; // 100000 satoshi = 0.001 BTC\n self.movingFundsDustThreshold = 200000; // 200000 satoshi = 0.002 BTC\n self.movingFundsTimeoutResetDelay = 6 days;\n self.movingFundsTimeout = 7 days;\n self.movingFundsTimeoutSlashingAmount = 100 * 1e18; // 100 T\n self.movingFundsTimeoutNotifierRewardMultiplier = 100; //100%\n self.movedFundsSweepTxMaxTotalFee = 100000; // 100000 satoshi = 0.001 BTC\n self.movedFundsSweepTimeout = 7 days;\n self.movedFundsSweepTimeoutSlashingAmount = 100 * 1e18; // 100 T\n self.movedFundsSweepTimeoutNotifierRewardMultiplier = 100; //100%\n self.fraudChallengeDepositAmount = 5 ether;\n self.fraudChallengeDefeatTimeout = 7 days;\n self.fraudSlashingAmount = 100 * 1e18; // 100 T\n self.fraudNotifierRewardMultiplier = 100; // 100%\n self.walletCreationPeriod = 1 weeks;\n self.walletCreationMinBtcBalance = 1e8; // 1 BTC\n self.walletCreationMaxBtcBalance = 100e8; // 100 BTC\n self.walletClosureMinBtcBalance = 5 * 1e7; // 0.5 BTC\n self.walletMaxAge = 26 weeks; // ~6 months\n self.walletMaxBtcTransfer = 10e8; // 10 BTC\n self.walletClosingPeriod = 40 days;\n\n _transferGovernance(msg.sender);\n }\n\n /// @notice Used by the depositor to reveal information about their P2(W)SH\n /// Bitcoin deposit to the Bridge on Ethereum chain. The off-chain\n /// wallet listens for revealed deposit events and may decide to\n /// include the revealed deposit in the next executed sweep.\n /// Information about the Bitcoin deposit can be revealed before or\n /// after the Bitcoin transaction with P2(W)SH deposit is mined on\n /// the Bitcoin chain. Worth noting, the gas cost of this function\n /// scales with the number of P2(W)SH transaction inputs and\n /// outputs. The deposit may be routed to one of the trusted vaults.\n /// When a deposit is routed to a vault, vault gets notified when\n /// the deposit gets swept and it may execute the appropriate action.\n /// @param fundingTx Bitcoin funding transaction data, see `BitcoinTx.Info`.\n /// @param reveal Deposit reveal data, see `RevealInfo struct.\n /// @dev Requirements:\n /// - `reveal.walletPubKeyHash` must identify a `Live` wallet,\n /// - `reveal.vault` must be 0x0 or point to a trusted vault,\n /// - `reveal.fundingOutputIndex` must point to the actual P2(W)SH\n /// output of the BTC deposit transaction,\n /// - `reveal.depositor` must be the Ethereum address used in the\n /// P2(W)SH BTC deposit transaction,\n /// - `reveal.blindingFactor` must be the blinding factor used in the\n /// P2(W)SH BTC deposit transaction,\n /// - `reveal.walletPubKeyHash` must be the wallet pub key hash used in\n /// the P2(W)SH BTC deposit transaction,\n /// - `reveal.refundPubKeyHash` must be the refund pub key hash used in\n /// the P2(W)SH BTC deposit transaction,\n /// - `reveal.refundLocktime` must be the refund locktime used in the\n /// P2(W)SH BTC deposit transaction,\n /// - BTC deposit for the given `fundingTxHash`, `fundingOutputIndex`\n /// can be revealed only one time.\n ///\n /// If any of these requirements is not met, the wallet _must_ refuse\n /// to sweep the deposit and the depositor has to wait until the\n /// deposit script unlocks to receive their BTC back.\n function revealDeposit(\n BitcoinTx.Info calldata fundingTx,\n Deposit.DepositRevealInfo calldata reveal\n ) external {\n self.revealDeposit(fundingTx, reveal);\n }\n\n /// @notice Used by the wallet to prove the BTC deposit sweep transaction\n /// and to update Bank balances accordingly. Sweep is only accepted\n /// if it satisfies SPV proof.\n ///\n /// The function is performing Bank balance updates by first\n /// computing the Bitcoin fee for the sweep transaction. The fee is\n /// divided evenly between all swept deposits. Each depositor\n /// receives a balance in the bank equal to the amount inferred\n /// during the reveal transaction, minus their fee share.\n ///\n /// It is possible to prove the given sweep only one time.\n /// @param sweepTx Bitcoin sweep transaction data.\n /// @param sweepProof Bitcoin sweep proof data.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain. If no main UTXO exists for the given wallet,\n /// this parameter is ignored.\n /// @param vault Optional address of the vault where all swept deposits\n /// should be routed to. All deposits swept as part of the transaction\n /// must have their `vault` parameters set to the same address.\n /// If this parameter is set to an address of a trusted vault, swept\n /// deposits are routed to that vault.\n /// If this parameter is set to the zero address or to an address\n /// of a non-trusted vault, swept deposits are not routed to a\n /// vault but depositors' balances are increased in the Bank\n /// individually.\n /// @dev Requirements:\n /// - `sweepTx` components must match the expected structure. See\n /// `BitcoinTx.Info` docs for reference. Their values must exactly\n /// correspond to appropriate Bitcoin transaction fields to produce\n /// a provable transaction hash,\n /// - The `sweepTx` should represent a Bitcoin transaction with 1..n\n /// inputs. If the wallet has no main UTXO, all n inputs should\n /// correspond to P2(W)SH revealed deposits UTXOs. If the wallet has\n /// an existing main UTXO, one of the n inputs must point to that\n /// main UTXO and remaining n-1 inputs should correspond to P2(W)SH\n /// revealed deposits UTXOs. That transaction must have only\n /// one P2(W)PKH output locking funds on the 20-byte wallet public\n /// key hash,\n /// - All revealed deposits that are swept by `sweepTx` must have\n /// their `vault` parameters set to the same address as the address\n /// passed in the `vault` function parameter,\n /// - `sweepProof` components must match the expected structure. See\n /// `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`\n /// field must contain a valid number of block headers, not less\n /// than the `txProofDifficultyFactor` contract constant,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// If there is no main UTXO, this parameter is ignored.\n function submitDepositSweepProof(\n BitcoinTx.Info calldata sweepTx,\n BitcoinTx.Proof calldata sweepProof,\n BitcoinTx.UTXO calldata mainUtxo,\n address vault\n ) external {\n self.submitDepositSweepProof(sweepTx, sweepProof, mainUtxo, vault);\n }\n\n /// @notice Requests redemption of the given amount from the specified\n /// wallet to the redeemer Bitcoin output script. Handles the\n /// simplest case in which the redeemer's balance is decreased in\n /// the Bank.\n /// @param walletPubKeyHash The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key).\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC.\n /// @param amount Requested amount in satoshi. This is also the Bank balance\n /// that is taken from the `balanceOwner` upon request.\n /// Once the request is handled, the actual amount of BTC locked\n /// on the redeemer output script will be always lower than this value\n /// since the treasury and Bitcoin transaction fees must be incurred.\n /// The minimal amount satisfying the request can be computed as:\n /// `amount - (amount / redemptionTreasuryFeeDivisor) - redemptionTxMaxFee`.\n /// Fees values are taken at the moment of request creation.\n /// @dev Requirements:\n /// - Wallet behind `walletPubKeyHash` must be live,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain,\n /// - `redeemerOutputScript` must be a proper Bitcoin script,\n /// - `redeemerOutputScript` cannot have wallet PKH as payload,\n /// - `amount` must be above or equal the `redemptionDustThreshold`,\n /// - Given `walletPubKeyHash` and `redeemerOutputScript` pair can be\n /// used for only one pending request at the same time,\n /// - Wallet must have enough Bitcoin balance to process the request,\n /// - Redeemer must make an allowance in the Bank that the Bridge\n /// contract can spend the given `amount`.\n function requestRedemption(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata mainUtxo,\n bytes calldata redeemerOutputScript,\n uint64 amount\n ) external {\n self.requestRedemption(\n walletPubKeyHash,\n mainUtxo,\n msg.sender,\n redeemerOutputScript,\n amount\n );\n }\n\n /// @notice Requests redemption of the given amount from the specified\n /// wallet to the redeemer Bitcoin output script. Used by\n /// `Bank.approveBalanceAndCall`. Can handle more complex cases\n /// where balance owner may be someone else than the redeemer.\n /// For example, vault redeeming its balance for some depositor.\n /// @param balanceOwner The address of the Bank balance owner whose balance\n /// is getting redeemed.\n /// @param amount Requested amount in satoshi. This is also the Bank balance\n /// that is taken from the `balanceOwner` upon request.\n /// Once the request is handled, the actual amount of BTC locked\n /// on the redeemer output script will be always lower than this value\n /// since the treasury and Bitcoin transaction fees must be incurred.\n /// The minimal amount satisfying the request can be computed as:\n /// `amount - (amount / redemptionTreasuryFeeDivisor) - redemptionTxMaxFee`.\n /// Fees values are taken at the moment of request creation.\n /// @param redemptionData ABI-encoded redemption data:\n /// [\n /// address redeemer,\n /// bytes20 walletPubKeyHash,\n /// bytes32 mainUtxoTxHash,\n /// uint32 mainUtxoTxOutputIndex,\n /// uint64 mainUtxoTxOutputValue,\n /// bytes redeemerOutputScript\n /// ]\n ///\n /// - redeemer: The Ethereum address of the redeemer who will be able\n /// to claim Bank balance if anything goes wrong during the redemption.\n /// In the most basic case, when someone redeems their balance\n /// from the Bank, `balanceOwner` is the same as `redemeer`.\n /// However, when a Vault is redeeming part of its balance for some\n /// redeemer address (for example, someone who has earlier deposited\n /// into that Vault), `balanceOwner` is the Vault, and `redemeer` is\n /// the address for which the vault is redeeming its balance to,\n /// - walletPubKeyHash: The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key),\n /// - mainUtxoTxHash: Data of the wallet's main UTXO TX hash, as\n /// currently known on the Ethereum chain,\n /// - mainUtxoTxOutputIndex: Data of the wallet's main UTXO output\n /// index, as currently known on Ethereum chain,\n /// - mainUtxoTxOutputValue: Data of the wallet's main UTXO output\n /// value, as currently known on Ethereum chain,\n /// - redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC.\n /// @dev Requirements:\n /// - The caller must be the Bank,\n /// - Wallet behind `walletPubKeyHash` must be live,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain,\n /// - `redeemerOutputScript` must be a proper Bitcoin script,\n /// - `redeemerOutputScript` cannot have wallet PKH as payload,\n /// - `amount` must be above or equal the `redemptionDustThreshold`,\n /// - Given `walletPubKeyHash` and `redeemerOutputScript` pair can be\n /// used for only one pending request at the same time,\n /// - Wallet must have enough Bitcoin balance to process the request.\n ///\n /// Note on upgradeability:\n /// Bridge is an upgradeable contract deployed behind\n /// a TransparentUpgradeableProxy. Accepting redemption data as bytes\n /// provides great flexibility. The Bridge is just like any other\n /// contract with a balance approved in the Bank and can be upgraded\n /// to another version without being bound to a particular interface\n /// forever. This flexibility comes with the cost - developers\n /// integrating their vaults and dApps with `Bridge` using\n /// `approveBalanceAndCall` need to pay extra attention to\n /// `redemptionData` and adjust the code in case the expected structure\n /// of `redemptionData` changes.\n function receiveBalanceApproval(\n address balanceOwner,\n uint256 amount,\n bytes calldata redemptionData\n ) external override {\n require(msg.sender == address(self.bank), \"Caller is not the bank\");\n\n self.requestRedemption(\n balanceOwner,\n SafeCastUpgradeable.toUint64(amount),\n redemptionData\n );\n }\n\n /// @notice Used by the wallet to prove the BTC redemption transaction\n /// and to make the necessary bookkeeping. Redemption is only\n /// accepted if it satisfies SPV proof.\n ///\n /// The function is performing Bank balance updates by burning\n /// the total redeemed Bitcoin amount from Bridge balance and\n /// transferring the treasury fee sum to the treasury address.\n ///\n /// It is possible to prove the given redemption only one time.\n /// @param redemptionTx Bitcoin redemption transaction data.\n /// @param redemptionProof Bitcoin redemption proof data.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin\n /// HASH160 over the compressed ECDSA public key) of the wallet which\n /// performed the redemption transaction.\n /// @dev Requirements:\n /// - `redemptionTx` components must match the expected structure. See\n /// `BitcoinTx.Info` docs for reference. Their values must exactly\n /// correspond to appropriate Bitcoin transaction fields to produce\n /// a provable transaction hash,\n /// - The `redemptionTx` should represent a Bitcoin transaction with\n /// exactly 1 input that refers to the wallet's main UTXO. That\n /// transaction should have 1..n outputs handling existing pending\n /// redemption requests or pointing to reported timed out requests.\n /// There can be also 1 optional output representing the\n /// change and pointing back to the 20-byte wallet public key hash.\n /// The change should be always present if the redeemed value sum\n /// is lower than the total wallet's BTC balance,\n /// - `redemptionProof` components must match the expected structure.\n /// See `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`\n /// field must contain a valid number of block headers, not less\n /// than the `txProofDifficultyFactor` contract constant,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// Additionally, the recent main UTXO on Ethereum must be set,\n /// - `walletPubKeyHash` must be connected with the main UTXO used\n /// as transaction single input.\n /// Other remarks:\n /// - Putting the change output as the first transaction output can\n /// save some gas because the output processing loop begins each\n /// iteration by checking whether the given output is the change\n /// thus uses some gas for making the comparison. Once the change\n /// is identified, that check is omitted in further iterations.\n function submitRedemptionProof(\n BitcoinTx.Info calldata redemptionTx,\n BitcoinTx.Proof calldata redemptionProof,\n BitcoinTx.UTXO calldata mainUtxo,\n bytes20 walletPubKeyHash\n ) external {\n self.submitRedemptionProof(\n redemptionTx,\n redemptionProof,\n mainUtxo,\n walletPubKeyHash\n );\n }\n\n /// @notice Notifies that there is a pending redemption request associated\n /// with the given wallet, that has timed out. The redemption\n /// request is identified by the key built as\n /// `keccak256(keccak256(redeemerOutputScript) | walletPubKeyHash)`.\n /// The results of calling this function:\n /// - The pending redemptions value for the wallet will be decreased\n /// by the requested amount (minus treasury fee),\n /// - The tokens taken from the redeemer on redemption request will\n /// be returned to the redeemer,\n /// - The request will be moved from pending redemptions to\n /// timed-out redemptions,\n /// - If the state of the wallet is `Live` or `MovingFunds`, the\n /// wallet operators will be slashed and the notifier will be\n /// rewarded,\n /// - If the state of wallet is `Live`, the wallet will be closed or\n /// marked as `MovingFunds` (depending on the presence or absence\n /// of the wallet's main UTXO) and the wallet will no longer be\n /// marked as the active wallet (if it was marked as such).\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @param redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH).\n /// @dev Requirements:\n /// - The wallet must be in the Live or MovingFunds or Terminated state,\n /// - The redemption request identified by `walletPubKeyHash` and\n /// `redeemerOutputScript` must exist,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given `walletID`. Those IDs are not directly stored\n /// in the contract for gas efficiency purposes but they can be\n /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`\n /// events of the `WalletRegistry` contract,\n /// - The amount of time defined by `redemptionTimeout` must have\n /// passed since the redemption was requested (the request must be\n /// timed-out).\n function notifyRedemptionTimeout(\n bytes20 walletPubKeyHash,\n uint32[] calldata walletMembersIDs,\n bytes calldata redeemerOutputScript\n ) external {\n self.notifyRedemptionTimeout(\n walletPubKeyHash,\n walletMembersIDs,\n redeemerOutputScript\n );\n }\n\n /// @notice Submits the moving funds target wallets commitment.\n /// Once all requirements are met, that function registers the\n /// target wallets commitment and opens the way for moving funds\n /// proof submission.\n /// @param walletPubKeyHash 20-byte public key hash of the source wallet.\n /// @param walletMainUtxo Data of the source wallet's main UTXO, as\n /// currently known on the Ethereum chain.\n /// @param walletMembersIDs Identifiers of the source wallet signing group\n /// members.\n /// @param walletMemberIndex Position of the caller in the source wallet\n /// signing group members list.\n /// @param targetWallets List of 20-byte public key hashes of the target\n /// wallets that the source wallet commits to move the funds to.\n /// @dev Requirements:\n /// - The source wallet must be in the MovingFunds state,\n /// - The source wallet must not have pending redemption requests,\n /// - The source wallet must not have pending moved funds sweep requests,\n /// - The source wallet must not have submitted its commitment already,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given source wallet in the ECDSA registry. Those IDs are\n /// not directly stored in the contract for gas efficiency purposes\n /// but they can be read from appropriate `DkgResultSubmitted`\n /// and `DkgResultApproved` events,\n /// - The `walletMemberIndex` must be in range [1, walletMembersIDs.length],\n /// - The caller must be the member of the source wallet signing group\n /// at the position indicated by `walletMemberIndex` parameter,\n /// - The `walletMainUtxo` components must point to the recent main\n /// UTXO of the source wallet, as currently known on the Ethereum\n /// chain,\n /// - Source wallet BTC balance must be greater than zero,\n /// - At least one Live wallet must exist in the system,\n /// - Submitted target wallets count must match the expected count\n /// `N = min(liveWalletsCount, ceil(walletBtcBalance / walletMaxBtcTransfer))`\n /// where `N > 0`,\n /// - Each target wallet must be not equal to the source wallet,\n /// - Each target wallet must follow the expected order i.e. all\n /// target wallets 20-byte public key hashes represented as numbers\n /// must form a strictly increasing sequence without duplicates,\n /// - Each target wallet must be in Live state.\n function submitMovingFundsCommitment(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata walletMainUtxo,\n uint32[] calldata walletMembersIDs,\n uint256 walletMemberIndex,\n bytes20[] calldata targetWallets\n ) external {\n self.submitMovingFundsCommitment(\n walletPubKeyHash,\n walletMainUtxo,\n walletMembersIDs,\n walletMemberIndex,\n targetWallets\n );\n }\n\n /// @notice Resets the moving funds timeout for the given wallet if the\n /// target wallet commitment cannot be submitted due to a lack\n /// of live wallets in the system.\n /// @param walletPubKeyHash 20-byte public key hash of the moving funds wallet.\n /// @dev Requirements:\n /// - The wallet must be in the MovingFunds state,\n /// - The target wallets commitment must not be already submitted for\n /// the given moving funds wallet,\n /// - Live wallets count must be zero,\n /// - The moving funds timeout reset delay must be elapsed.\n function resetMovingFundsTimeout(bytes20 walletPubKeyHash) external {\n self.resetMovingFundsTimeout(walletPubKeyHash);\n }\n\n /// @notice Used by the wallet to prove the BTC moving funds transaction\n /// and to make the necessary state changes. Moving funds is only\n /// accepted if it satisfies SPV proof.\n ///\n /// The function validates the moving funds transaction structure\n /// by checking if it actually spends the main UTXO of the declared\n /// wallet and locks the value on the pre-committed target wallets\n /// using a reasonable transaction fee. If all preconditions are\n /// met, this functions closes the source wallet.\n ///\n /// It is possible to prove the given moving funds transaction only\n /// one time.\n /// @param movingFundsTx Bitcoin moving funds transaction data.\n /// @param movingFundsProof Bitcoin moving funds proof data.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin\n /// HASH160 over the compressed ECDSA public key) of the wallet\n /// which performed the moving funds transaction.\n /// @dev Requirements:\n /// - `movingFundsTx` components must match the expected structure. See\n /// `BitcoinTx.Info` docs for reference. Their values must exactly\n /// correspond to appropriate Bitcoin transaction fields to produce\n /// a provable transaction hash,\n /// - The `movingFundsTx` should represent a Bitcoin transaction with\n /// exactly 1 input that refers to the wallet's main UTXO. That\n /// transaction should have 1..n outputs corresponding to the\n /// pre-committed target wallets. Outputs must be ordered in the\n /// same way as their corresponding target wallets are ordered\n /// within the target wallets commitment,\n /// - `movingFundsProof` components must match the expected structure.\n /// See `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`\n /// field must contain a valid number of block headers, not less\n /// than the `txProofDifficultyFactor` contract constant,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// Additionally, the recent main UTXO on Ethereum must be set,\n /// - `walletPubKeyHash` must be connected with the main UTXO used\n /// as transaction single input,\n /// - The wallet that `walletPubKeyHash` points to must be in the\n /// MovingFunds state,\n /// - The target wallets commitment must be submitted by the wallet\n /// that `walletPubKeyHash` points to,\n /// - The total Bitcoin transaction fee must be lesser or equal\n /// to `movingFundsTxMaxTotalFee` governable parameter.\n function submitMovingFundsProof(\n BitcoinTx.Info calldata movingFundsTx,\n BitcoinTx.Proof calldata movingFundsProof,\n BitcoinTx.UTXO calldata mainUtxo,\n bytes20 walletPubKeyHash\n ) external {\n self.submitMovingFundsProof(\n movingFundsTx,\n movingFundsProof,\n mainUtxo,\n walletPubKeyHash\n );\n }\n\n /// @notice Notifies about a timed out moving funds process. Terminates\n /// the wallet and slashes signing group members as a result.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @dev Requirements:\n /// - The wallet must be in the MovingFunds state,\n /// - The moving funds timeout must be actually exceeded,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given `walletID`. Those IDs are not directly stored\n /// in the contract for gas efficiency purposes but they can be\n /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`\n /// events of the `WalletRegistry` contract.\n function notifyMovingFundsTimeout(\n bytes20 walletPubKeyHash,\n uint32[] calldata walletMembersIDs\n ) external {\n self.notifyMovingFundsTimeout(walletPubKeyHash, walletMembersIDs);\n }\n\n /// @notice Notifies about a moving funds wallet whose BTC balance is\n /// below the moving funds dust threshold. Ends the moving funds\n /// process and begins wallet closing immediately.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known\n /// on the Ethereum chain.\n /// @dev Requirements:\n /// - The wallet must be in the MovingFunds state,\n /// - The `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// If the wallet has no main UTXO, this parameter can be empty as it\n /// is ignored,\n /// - The wallet BTC balance must be below the moving funds threshold.\n function notifyMovingFundsBelowDust(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata mainUtxo\n ) external {\n self.notifyMovingFundsBelowDust(walletPubKeyHash, mainUtxo);\n }\n\n /// @notice Used by the wallet to prove the BTC moved funds sweep\n /// transaction and to make the necessary state changes. Moved\n /// funds sweep is only accepted if it satisfies SPV proof.\n ///\n /// The function validates the sweep transaction structure by\n /// checking if it actually spends the moved funds UTXO and the\n /// sweeping wallet's main UTXO (optionally), and if it locks the\n /// value on the sweeping wallet's 20-byte public key hash using a\n /// reasonable transaction fee. If all preconditions are\n /// met, this function updates the sweeping wallet main UTXO, thus\n /// their BTC balance.\n ///\n /// It is possible to prove the given sweep transaction only\n /// one time.\n /// @param sweepTx Bitcoin sweep funds transaction data.\n /// @param sweepProof Bitcoin sweep funds proof data.\n /// @param mainUtxo Data of the sweeping wallet's main UTXO, as currently\n /// known on the Ethereum chain.\n /// @dev Requirements:\n /// - `sweepTx` components must match the expected structure. See\n /// `BitcoinTx.Info` docs for reference. Their values must exactly\n /// correspond to appropriate Bitcoin transaction fields to produce\n /// a provable transaction hash,\n /// - The `sweepTx` should represent a Bitcoin transaction with\n /// the first input pointing to a moved funds sweep request targeted\n /// to the wallet, and optionally, the second input pointing to the\n /// wallet's main UTXO, if the sweeping wallet has a main UTXO set.\n /// There should be only one output locking funds on the sweeping\n /// wallet 20-byte public key hash,\n /// - `sweepProof` components must match the expected structure.\n /// See `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`\n /// field must contain a valid number of block headers, not less\n /// than the `txProofDifficultyFactor` contract constant,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the sweeping wallet, as currently known on the Ethereum chain.\n /// If there is no main UTXO, this parameter is ignored,\n /// - The sweeping wallet must be in the Live or MovingFunds state,\n /// - The total Bitcoin transaction fee must be lesser or equal\n /// to `movedFundsSweepTxMaxTotalFee` governable parameter.\n function submitMovedFundsSweepProof(\n BitcoinTx.Info calldata sweepTx,\n BitcoinTx.Proof calldata sweepProof,\n BitcoinTx.UTXO calldata mainUtxo\n ) external {\n self.submitMovedFundsSweepProof(sweepTx, sweepProof, mainUtxo);\n }\n\n /// @notice Notifies about a timed out moved funds sweep process. If the\n /// wallet is not terminated yet, that function terminates\n /// the wallet and slashes signing group members as a result.\n /// Marks the given sweep request as TimedOut.\n /// @param movingFundsTxHash 32-byte hash of the moving funds transaction\n /// that caused the sweep request to be created.\n /// @param movingFundsTxOutputIndex Index of the moving funds transaction\n /// output that is subject of the sweep request.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @dev Requirements:\n /// - The moved funds sweep request must be in the Pending state,\n /// - The moved funds sweep timeout must be actually exceeded,\n /// - The wallet must be either in the Live or MovingFunds or\n /// Terminated state,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given `walletID`. Those IDs are not directly stored\n /// in the contract for gas efficiency purposes but they can be\n /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`\n /// events of the `WalletRegistry` contract.\n function notifyMovedFundsSweepTimeout(\n bytes32 movingFundsTxHash,\n uint32 movingFundsTxOutputIndex,\n uint32[] calldata walletMembersIDs\n ) external {\n self.notifyMovedFundsSweepTimeout(\n movingFundsTxHash,\n movingFundsTxOutputIndex,\n walletMembersIDs\n );\n }\n\n /// @notice Requests creation of a new wallet. This function just\n /// forms a request and the creation process is performed\n /// asynchronously. Once a wallet is created, the ECDSA Wallet\n /// Registry will notify this contract by calling the\n /// `__ecdsaWalletCreatedCallback` function.\n /// @param activeWalletMainUtxo Data of the active wallet's main UTXO, as\n /// currently known on the Ethereum chain.\n /// @dev Requirements:\n /// - `activeWalletMainUtxo` components must point to the recent main\n /// UTXO of the given active wallet, as currently known on the\n /// Ethereum chain. If there is no active wallet at the moment, or\n /// the active wallet has no main UTXO, this parameter can be\n /// empty as it is ignored,\n /// - Wallet creation must not be in progress,\n /// - If the active wallet is set, one of the following\n /// conditions must be true:\n /// - The active wallet BTC balance is above the minimum threshold\n /// and the active wallet is old enough, i.e. the creation period\n /// was elapsed since its creation time,\n /// - The active wallet BTC balance is above the maximum threshold.\n function requestNewWallet(BitcoinTx.UTXO calldata activeWalletMainUtxo)\n external\n {\n self.requestNewWallet(activeWalletMainUtxo);\n }\n\n /// @notice A callback function that is called by the ECDSA Wallet Registry\n /// once a new ECDSA wallet is created.\n /// @param ecdsaWalletID Wallet's unique identifier.\n /// @param publicKeyX Wallet's public key's X coordinate.\n /// @param publicKeyY Wallet's public key's Y coordinate.\n /// @dev Requirements:\n /// - The only caller authorized to call this function is `registry`,\n /// - Given wallet data must not belong to an already registered wallet.\n function __ecdsaWalletCreatedCallback(\n bytes32 ecdsaWalletID,\n bytes32 publicKeyX,\n bytes32 publicKeyY\n ) external override {\n self.registerNewWallet(ecdsaWalletID, publicKeyX, publicKeyY);\n }\n\n /// @notice A callback function that is called by the ECDSA Wallet Registry\n /// once a wallet heartbeat failure is detected.\n /// @param publicKeyX Wallet's public key's X coordinate.\n /// @param publicKeyY Wallet's public key's Y coordinate.\n /// @dev Requirements:\n /// - The only caller authorized to call this function is `registry`,\n /// - Wallet must be in Live state.\n function __ecdsaWalletHeartbeatFailedCallback(\n bytes32,\n bytes32 publicKeyX,\n bytes32 publicKeyY\n ) external override {\n self.notifyWalletHeartbeatFailed(publicKeyX, publicKeyY);\n }\n\n /// @notice Notifies that the wallet is either old enough or has too few\n /// satoshi left and qualifies to be closed.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMainUtxo Data of the wallet's main UTXO, as currently\n /// known on the Ethereum chain.\n /// @dev Requirements:\n /// - Wallet must not be set as the current active wallet,\n /// - Wallet must exceed the wallet maximum age OR the wallet BTC\n /// balance must be lesser than the minimum threshold. If the latter\n /// case is true, the `walletMainUtxo` components must point to the\n /// recent main UTXO of the given wallet, as currently known on the\n /// Ethereum chain. If the wallet has no main UTXO, this parameter\n /// can be empty as it is ignored since the wallet balance is\n /// assumed to be zero,\n /// - Wallet must be in Live state.\n function notifyWalletCloseable(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata walletMainUtxo\n ) external {\n self.notifyWalletCloseable(walletPubKeyHash, walletMainUtxo);\n }\n\n /// @notice Notifies about the end of the closing period for the given wallet.\n /// Closes the wallet ultimately and notifies the ECDSA registry\n /// about this fact.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @dev Requirements:\n /// - The wallet must be in the Closing state,\n /// - The wallet closing period must have elapsed.\n function notifyWalletClosingPeriodElapsed(bytes20 walletPubKeyHash)\n external\n {\n self.notifyWalletClosingPeriodElapsed(walletPubKeyHash);\n }\n\n /// @notice Submits a fraud challenge indicating that a UTXO being under\n /// wallet control was unlocked by the wallet but was not used\n /// according to the protocol rules. That means the wallet signed\n /// a transaction input pointing to that UTXO and there is a unique\n /// sighash and signature pair associated with that input. This\n /// function uses those parameters to create a fraud accusation that\n /// proves a given transaction input unlocking the given UTXO was\n /// actually signed by the wallet. This function cannot determine\n /// whether the transaction was actually broadcast and the input was\n /// consumed in a fraudulent way so it just opens a challenge period\n /// during which the wallet can defeat the challenge by submitting\n /// proof of a transaction that consumes the given input according\n /// to protocol rules. To prevent spurious allegations, the caller\n /// must deposit ETH that is returned back upon justified fraud\n /// challenge or confiscated otherwise.\n /// @param walletPublicKey The public key of the wallet in the uncompressed\n /// and unprefixed format (64 bytes).\n /// @param preimageSha256 The hash that was generated by applying SHA-256\n /// one time over the preimage used during input signing. The preimage\n /// is a serialized subset of the transaction and its structure\n /// depends on the transaction input (see BIP-143 for reference).\n /// Notice that applying SHA-256 over the `preimageSha256` results\n /// in `sighash`. The path from `preimage` to `sighash` looks like\n /// this:\n /// preimage -> (SHA-256) -> preimageSha256 -> (SHA-256) -> sighash.\n /// @param signature Bitcoin signature in the R/S/V format.\n /// @dev Requirements:\n /// - Wallet behind `walletPublicKey` must be in Live or MovingFunds\n /// or Closing state,\n /// - The challenger must send appropriate amount of ETH used as\n /// fraud challenge deposit,\n /// - The signature (represented by r, s and v) must be generated by\n /// the wallet behind `walletPubKey` during signing of `sighash`\n /// which was calculated from `preimageSha256`,\n /// - Wallet can be challenged for the given signature only once.\n function submitFraudChallenge(\n bytes calldata walletPublicKey,\n bytes memory preimageSha256,\n BitcoinTx.RSVSignature calldata signature\n ) external payable {\n self.submitFraudChallenge(walletPublicKey, preimageSha256, signature);\n }\n\n /// @notice Allows to defeat a pending fraud challenge against a wallet if\n /// the transaction that spends the UTXO follows the protocol rules.\n /// In order to defeat the challenge the same `walletPublicKey` and\n /// signature (represented by `r`, `s` and `v`) must be provided as\n /// were used to calculate the sighash during input signing.\n /// The fraud challenge defeat attempt will only succeed if the\n /// inputs in the preimage are considered honestly spent by the\n /// wallet. Therefore the transaction spending the UTXO must be\n /// proven in the Bridge before a challenge defeat is called.\n /// If successfully defeated, the fraud challenge is marked as\n /// resolved and the amount of ether deposited by the challenger is\n /// sent to the treasury.\n /// @param walletPublicKey The public key of the wallet in the uncompressed\n /// and unprefixed format (64 bytes).\n /// @param preimage The preimage which produces sighash used to generate the\n /// ECDSA signature that is the subject of the fraud claim. It is a\n /// serialized subset of the transaction. The exact subset used as\n /// the preimage depends on the transaction input the signature is\n /// produced for. See BIP-143 for reference.\n /// @param witness Flag indicating whether the preimage was produced for a\n /// witness input. True for witness, false for non-witness input.\n /// @dev Requirements:\n /// - `walletPublicKey` and `sighash` calculated as `hash256(preimage)`\n /// must identify an open fraud challenge,\n /// - the preimage must be a valid preimage of a transaction generated\n /// according to the protocol rules and already proved in the Bridge,\n /// - before a defeat attempt is made the transaction that spends the\n /// given UTXO must be proven in the Bridge.\n function defeatFraudChallenge(\n bytes calldata walletPublicKey,\n bytes calldata preimage,\n bool witness\n ) external {\n self.defeatFraudChallenge(walletPublicKey, preimage, witness);\n }\n\n /// @notice Allows to defeat a pending fraud challenge against a wallet by\n /// proving the sighash and signature were produced for an off-chain\n /// wallet heartbeat message following a strict format.\n /// In order to defeat the challenge the same `walletPublicKey` and\n /// signature (represented by `r`, `s` and `v`) must be provided as\n /// were used to calculate the sighash during heartbeat message\n /// signing. The fraud challenge defeat attempt will only succeed if\n /// the signed message follows a strict format required for\n /// heartbeat messages. If successfully defeated, the fraud\n /// challenge is marked as resolved and the amount of ether\n /// deposited by the challenger is sent to the treasury.\n /// @param walletPublicKey The public key of the wallet in the uncompressed\n /// and unprefixed format (64 bytes).\n /// @param heartbeatMessage Off-chain heartbeat message meeting the heartbeat\n /// message format requirements which produces sighash used to\n /// generate the ECDSA signature that is the subject of the fraud\n /// claim.\n /// @dev Requirements:\n /// - `walletPublicKey` and `sighash` calculated as\n /// `hash256(heartbeatMessage)` must identify an open fraud challenge,\n /// - `heartbeatMessage` must follow a strict format of heartbeat\n /// messages.\n function defeatFraudChallengeWithHeartbeat(\n bytes calldata walletPublicKey,\n bytes calldata heartbeatMessage\n ) external {\n self.defeatFraudChallengeWithHeartbeat(\n walletPublicKey,\n heartbeatMessage\n );\n }\n\n /// @notice Notifies about defeat timeout for the given fraud challenge.\n /// Can be called only if there was a fraud challenge identified by\n /// the provided `walletPublicKey` and `sighash` and it was not\n /// defeated on time. The amount of time that needs to pass after\n /// a fraud challenge is reported is indicated by the\n /// `challengeDefeatTimeout`. After a successful fraud challenge\n /// defeat timeout notification the fraud challenge is marked as\n /// resolved, the stake of each operator is slashed, the ether\n /// deposited is returned to the challenger and the challenger is\n /// rewarded.\n /// @param walletPublicKey The public key of the wallet in the uncompressed\n /// and unprefixed format (64 bytes).\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @param preimageSha256 The hash that was generated by applying SHA-256\n /// one time over the preimage used during input signing. The preimage\n /// is a serialized subset of the transaction and its structure\n /// depends on the transaction input (see BIP-143 for reference).\n /// Notice that applying SHA-256 over the `preimageSha256` results\n /// in `sighash`. The path from `preimage` to `sighash` looks like\n /// this:\n /// preimage -> (SHA-256) -> preimageSha256 -> (SHA-256) -> sighash.\n /// @dev Requirements:\n /// - The wallet must be in the Live or MovingFunds or Closing or\n /// Terminated state,\n /// - The `walletPublicKey` and `sighash` calculated from\n /// `preimageSha256` must identify an open fraud challenge,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given `walletID`. Those IDs are not directly stored\n /// in the contract for gas efficiency purposes but they can be\n /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`\n /// events of the `WalletRegistry` contract,\n /// - The amount of time indicated by `challengeDefeatTimeout` must pass\n /// after the challenge was reported.\n function notifyFraudChallengeDefeatTimeout(\n bytes calldata walletPublicKey,\n uint32[] calldata walletMembersIDs,\n bytes memory preimageSha256\n ) external {\n self.notifyFraudChallengeDefeatTimeout(\n walletPublicKey,\n walletMembersIDs,\n preimageSha256\n );\n }\n\n /// @notice Allows the Governance to mark the given vault address as trusted\n /// or no longer trusted. Vaults are not trusted by default.\n /// Trusted vault must meet the following criteria:\n /// - `IVault.receiveBalanceIncrease` must have a known, low gas\n /// cost,\n /// - `IVault.receiveBalanceIncrease` must never revert.\n /// @dev Without restricting reveal only to trusted vaults, malicious\n /// vaults not meeting the criteria would be able to nuke sweep proof\n /// transactions executed by ECDSA wallet with deposits routed to\n /// them.\n /// @param vault The address of the vault.\n /// @param isTrusted flag indicating whether the vault is trusted or not.\n /// @dev Can only be called by the Governance.\n function setVaultStatus(address vault, bool isTrusted)\n external\n onlyGovernance\n {\n self.isVaultTrusted[vault] = isTrusted;\n emit VaultStatusUpdated(vault, isTrusted);\n }\n\n /// @notice Updates parameters of deposits.\n /// @param depositDustThreshold New value of the deposit dust threshold in\n /// satoshis. It is the minimal amount that can be requested to\n //// deposit. Value of this parameter must take into account the value\n /// of `depositTreasuryFeeDivisor` and `depositTxMaxFee` parameters\n /// in order to make requests that can incur the treasury and\n /// transaction fee and still satisfy the depositor.\n /// @param depositTreasuryFeeDivisor New value of the treasury fee divisor.\n /// It is the divisor used to compute the treasury fee taken from\n /// each deposit and transferred to the treasury upon sweep proof\n /// submission. That fee is computed as follows:\n /// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`\n /// For example, if the treasury fee needs to be 2% of each deposit,\n /// the `depositTreasuryFeeDivisor` should be set to `50`\n /// because `1/50 = 0.02 = 2%`.\n /// @param depositTxMaxFee New value of the deposit tx max fee in satoshis.\n /// It is the maximum amount of BTC transaction fee that can\n /// be incurred by each swept deposit being part of the given sweep\n /// transaction. If the maximum BTC transaction fee is exceeded,\n /// such transaction is considered a fraud.\n /// @dev Requirements:\n /// - Deposit dust threshold must be greater than zero,\n /// - Deposit treasury fee divisor must be greater than zero,\n /// - Deposit transaction max fee must be greater than zero.\n function updateDepositParameters(\n uint64 depositDustThreshold,\n uint64 depositTreasuryFeeDivisor,\n uint64 depositTxMaxFee\n ) external onlyGovernance {\n self.updateDepositParameters(\n depositDustThreshold,\n depositTreasuryFeeDivisor,\n depositTxMaxFee\n );\n }\n\n /// @notice Updates parameters of redemptions.\n /// @param redemptionDustThreshold New value of the redemption dust\n /// threshold in satoshis. It is the minimal amount that can be\n /// requested for redemption. Value of this parameter must take into\n /// account the value of `redemptionTreasuryFeeDivisor` and\n /// `redemptionTxMaxFee` parameters in order to make requests that\n /// can incur the treasury and transaction fee and still satisfy the\n /// redeemer.\n /// @param redemptionTreasuryFeeDivisor New value of the redemption\n /// treasury fee divisor. It is the divisor used to compute the\n /// treasury fee taken from each redemption request and transferred\n /// to the treasury upon successful request finalization. That fee is\n /// computed as follows:\n /// `treasuryFee = requestedAmount / redemptionTreasuryFeeDivisor`\n /// For example, if the treasury fee needs to be 2% of each\n /// redemption request, the `redemptionTreasuryFeeDivisor` should\n /// be set to `50` because `1/50 = 0.02 = 2%`.\n /// @param redemptionTxMaxFee New value of the redemption transaction max\n /// fee in satoshis. It is the maximum amount of BTC transaction fee\n /// that can be incurred by each redemption request being part of the\n /// given redemption transaction. If the maximum BTC transaction fee\n /// is exceeded, such transaction is considered a fraud.\n /// This is a per-redemption output max fee for the redemption\n /// transaction.\n /// @param redemptionTimeout New value of the redemption timeout in seconds.\n /// It is the time after which the redemption request can be reported\n /// as timed out. It is counted from the moment when the redemption\n /// request was created via `requestRedemption` call. Reported timed\n /// out requests are cancelled and locked balance is returned to the\n /// redeemer in full amount.\n /// @param redemptionTimeoutSlashingAmount New value of the redemption\n /// timeout slashing amount in T, it is the amount slashed from each\n /// wallet member for redemption timeout.\n /// @param redemptionTimeoutNotifierRewardMultiplier New value of the\n /// redemption timeout notifier reward multiplier as percentage,\n /// it determines the percentage of the notifier reward from the\n /// staking contact the notifier of a redemption timeout receives.\n /// The value must be in the range [0, 100].\n /// @dev Requirements:\n /// - Redemption dust threshold must be greater than moving funds dust\n /// threshold,\n /// - Redemption treasury fee divisor must be greater than zero,\n /// - Redemption transaction max fee must be greater than zero,\n /// - Redemption timeout must be greater than zero,\n /// - Redemption timeout notifier reward multiplier must be in the\n /// range [0, 100].\n function updateRedemptionParameters(\n uint64 redemptionDustThreshold,\n uint64 redemptionTreasuryFeeDivisor,\n uint64 redemptionTxMaxFee,\n uint32 redemptionTimeout,\n uint96 redemptionTimeoutSlashingAmount,\n uint32 redemptionTimeoutNotifierRewardMultiplier\n ) external onlyGovernance {\n self.updateRedemptionParameters(\n redemptionDustThreshold,\n redemptionTreasuryFeeDivisor,\n redemptionTxMaxFee,\n redemptionTimeout,\n redemptionTimeoutSlashingAmount,\n redemptionTimeoutNotifierRewardMultiplier\n );\n }\n\n /// @notice Updates parameters of moving funds.\n /// @param movingFundsTxMaxTotalFee New value of the moving funds transaction\n /// max total fee in satoshis. It is the maximum amount of the total\n /// BTC transaction fee that is acceptable in a single moving funds\n /// transaction. This is a _total_ max fee for the entire moving\n /// funds transaction.\n /// @param movingFundsDustThreshold New value of the moving funds dust\n /// threshold. It is the minimal satoshi amount that makes sense to\n /// be transferred during the moving funds process. Moving funds\n /// wallets having their BTC balance below that value can begin\n /// closing immediately as transferring such a low value may not be\n /// possible due to BTC network fees.\n /// @param movingFundsTimeoutResetDelay New value of the moving funds\n /// timeout reset delay in seconds. It is the time after which the\n /// moving funds timeout can be reset in case the target wallet\n /// commitment cannot be submitted due to a lack of live wallets\n /// in the system. It is counted from the moment when the wallet\n /// was requested to move their funds and switched to the MovingFunds\n /// state or from the moment the timeout was reset the last time.\n /// @param movingFundsTimeout New value of the moving funds timeout in\n /// seconds. It is the time after which the moving funds process can\n /// be reported as timed out. It is counted from the moment when the\n /// wallet was requested to move their funds and switched to the\n /// MovingFunds state.\n /// @param movingFundsTimeoutSlashingAmount New value of the moving funds\n /// timeout slashing amount in T, it is the amount slashed from each\n /// wallet member for moving funds timeout.\n /// @param movingFundsTimeoutNotifierRewardMultiplier New value of the\n /// moving funds timeout notifier reward multiplier as percentage,\n /// it determines the percentage of the notifier reward from the\n /// staking contact the notifier of a moving funds timeout receives.\n /// The value must be in the range [0, 100].\n /// @param movedFundsSweepTxMaxTotalFee New value of the moved funds sweep\n /// transaction max total fee in satoshis. It is the maximum amount\n /// of the total BTC transaction fee that is acceptable in a single\n /// moved funds sweep transaction. This is a _total_ max fee for the\n /// entire moved funds sweep transaction.\n /// @param movedFundsSweepTimeout New value of the moved funds sweep\n /// timeout in seconds. It is the time after which the moved funds\n /// sweep process can be reported as timed out. It is counted from\n /// the moment when the wallet was requested to sweep the received\n /// funds.\n /// @param movedFundsSweepTimeoutSlashingAmount New value of the moved\n /// funds sweep timeout slashing amount in T, it is the amount\n /// slashed from each wallet member for moved funds sweep timeout.\n /// @param movedFundsSweepTimeoutNotifierRewardMultiplier New value of\n /// the moved funds sweep timeout notifier reward multiplier as\n /// percentage, it determines the percentage of the notifier reward\n /// from the staking contact the notifier of a moved funds sweep\n /// timeout receives. The value must be in the range [0, 100].\n /// @dev Requirements:\n /// - Moving funds transaction max total fee must be greater than zero,\n /// - Moving funds dust threshold must be greater than zero and lower\n /// than the redemption dust threshold,\n /// - Moving funds timeout reset delay must be greater than zero,\n /// - Moving funds timeout must be greater than the moving funds\n /// timeout reset delay,\n /// - Moving funds timeout notifier reward multiplier must be in the\n /// range [0, 100],\n /// - Moved funds sweep transaction max total fee must be greater than zero,\n /// - Moved funds sweep timeout must be greater than zero,\n /// - Moved funds sweep timeout notifier reward multiplier must be in the\n /// range [0, 100].\n function updateMovingFundsParameters(\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n ) external onlyGovernance {\n self.updateMovingFundsParameters(\n movingFundsTxMaxTotalFee,\n movingFundsDustThreshold,\n movingFundsTimeoutResetDelay,\n movingFundsTimeout,\n movingFundsTimeoutSlashingAmount,\n movingFundsTimeoutNotifierRewardMultiplier,\n movedFundsSweepTxMaxTotalFee,\n movedFundsSweepTimeout,\n movedFundsSweepTimeoutSlashingAmount,\n movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n }\n\n /// @notice Updates parameters of wallets.\n /// @param walletCreationPeriod New value of the wallet creation period in\n /// seconds, determines how frequently a new wallet creation can be\n /// requested.\n /// @param walletCreationMinBtcBalance New value of the wallet minimum BTC\n /// balance in satoshi, used to decide about wallet creation.\n /// @param walletCreationMaxBtcBalance New value of the wallet maximum BTC\n /// balance in satoshi, used to decide about wallet creation.\n /// @param walletClosureMinBtcBalance New value of the wallet minimum BTC\n /// balance in satoshi, used to decide about wallet closure.\n /// @param walletMaxAge New value of the wallet maximum age in seconds,\n /// indicates the maximum age of a wallet in seconds, after which\n /// the wallet moving funds process can be requested.\n /// @param walletMaxBtcTransfer New value of the wallet maximum BTC transfer\n /// in satoshi, determines the maximum amount that can be transferred\n // to a single target wallet during the moving funds process.\n /// @param walletClosingPeriod New value of the wallet closing period in\n /// seconds, determines the length of the wallet closing period,\n // i.e. the period when the wallet remains in the Closing state\n // and can be subject of deposit fraud challenges.\n /// @dev Requirements:\n /// - Wallet minimum BTC balance must be greater than zero,\n /// - Wallet maximum BTC balance must be greater than the wallet\n /// minimum BTC balance,\n /// - Wallet maximum BTC transfer must be greater than zero,\n /// - Wallet closing period must be greater than zero.\n function updateWalletParameters(\n uint32 walletCreationPeriod,\n uint64 walletCreationMinBtcBalance,\n uint64 walletCreationMaxBtcBalance,\n uint64 walletClosureMinBtcBalance,\n uint32 walletMaxAge,\n uint64 walletMaxBtcTransfer,\n uint32 walletClosingPeriod\n ) external onlyGovernance {\n self.updateWalletParameters(\n walletCreationPeriod,\n walletCreationMinBtcBalance,\n walletCreationMaxBtcBalance,\n walletClosureMinBtcBalance,\n walletMaxAge,\n walletMaxBtcTransfer,\n walletClosingPeriod\n );\n }\n\n /// @notice Updates parameters related to frauds.\n /// @param fraudChallengeDepositAmount New value of the fraud challenge\n /// deposit amount in wei, it is the amount of ETH the party\n /// challenging the wallet for fraud needs to deposit.\n /// @param fraudChallengeDefeatTimeout New value of the challenge defeat\n /// timeout in seconds, it is the amount of time the wallet has to\n /// defeat a fraud challenge. The value must be greater than zero.\n /// @param fraudSlashingAmount New value of the fraud slashing amount in T,\n /// it is the amount slashed from each wallet member for committing\n /// a fraud.\n /// @param fraudNotifierRewardMultiplier New value of the fraud notifier\n /// reward multiplier as percentage, it determines the percentage of\n /// the notifier reward from the staking contact the notifier of\n /// a fraud receives. The value must be in the range [0, 100].\n /// @dev Requirements:\n /// - Fraud challenge defeat timeout must be greater than 0,\n /// - Fraud notifier reward multiplier must be in the range [0, 100].\n function updateFraudParameters(\n uint96 fraudChallengeDepositAmount,\n uint32 fraudChallengeDefeatTimeout,\n uint96 fraudSlashingAmount,\n uint32 fraudNotifierRewardMultiplier\n ) external onlyGovernance {\n self.updateFraudParameters(\n fraudChallengeDepositAmount,\n fraudChallengeDefeatTimeout,\n fraudSlashingAmount,\n fraudNotifierRewardMultiplier\n );\n }\n\n /// @notice Collection of all revealed deposits indexed by\n /// keccak256(fundingTxHash | fundingOutputIndex).\n /// The fundingTxHash is bytes32 (ordered as in Bitcoin internally)\n /// and fundingOutputIndex an uint32. This mapping may contain valid\n /// and invalid deposits and the wallet is responsible for\n /// validating them before attempting to execute a sweep.\n function deposits(uint256 depositKey)\n external\n view\n returns (Deposit.DepositRequest memory)\n {\n return self.deposits[depositKey];\n }\n\n /// @notice Collection of all pending redemption requests indexed by\n /// redemption key built as\n /// `keccak256(keccak256(redeemerOutputScript) | walletPubKeyHash)`.\n /// The walletPubKeyHash is the 20-byte wallet's public key hash\n /// (computed using Bitcoin HASH160 over the compressed ECDSA\n /// public key) and `redeemerOutputScript` is a Bitcoin script\n /// (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC as requested by the redeemer. Requests are added\n /// to this mapping by the `requestRedemption` method (duplicates\n /// not allowed) and are removed by one of the following methods:\n /// - `submitRedemptionProof` in case the request was handled\n /// successfully,\n /// - `notifyRedemptionTimeout` in case the request was reported\n /// to be timed out.\n function pendingRedemptions(uint256 redemptionKey)\n external\n view\n returns (Redemption.RedemptionRequest memory)\n {\n return self.pendingRedemptions[redemptionKey];\n }\n\n /// @notice Collection of all timed out redemptions requests indexed by\n /// redemption key built as\n /// `keccak256(keccak256(redeemerOutputScript) | walletPubKeyHash)`.\n /// The walletPubKeyHash is the 20-byte wallet's public key hash\n /// (computed using Bitcoin HASH160 over the compressed ECDSA\n /// public key) and `redeemerOutputScript` is the Bitcoin script\n /// (P2PKH, P2WPKH, P2SH or P2WSH) that is involved in the timed\n /// out request. Timed out requests are stored in this mapping to\n /// avoid slashing the wallets multiple times for the same timeout.\n /// Only one method can add to this mapping:\n /// - `notifyRedemptionTimeout` which puts the redemption key\n /// to this mapping basing on a timed out request stored\n /// previously in `pendingRedemptions` mapping.\n function timedOutRedemptions(uint256 redemptionKey)\n external\n view\n returns (Redemption.RedemptionRequest memory)\n {\n return self.timedOutRedemptions[redemptionKey];\n }\n\n /// @notice Collection of main UTXOs that are honestly spent indexed by\n /// keccak256(fundingTxHash | fundingOutputIndex). The fundingTxHash\n /// is bytes32 (ordered as in Bitcoin internally) and\n /// fundingOutputIndex an uint32. A main UTXO is considered honestly\n /// spent if it was used as an input of a transaction that have been\n /// proven in the Bridge.\n function spentMainUTXOs(uint256 utxoKey) external view returns (bool) {\n return self.spentMainUTXOs[utxoKey];\n }\n\n /// @notice Gets details about a registered wallet.\n /// @param walletPubKeyHash The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key).\n /// @return Wallet details.\n function wallets(bytes20 walletPubKeyHash)\n external\n view\n returns (Wallets.Wallet memory)\n {\n return self.registeredWallets[walletPubKeyHash];\n }\n\n /// @notice Gets the public key hash of the active wallet.\n /// @return The 20-byte public key hash (computed using Bitcoin HASH160\n /// over the compressed ECDSA public key) of the active wallet.\n /// Returns bytes20(0) if there is no active wallet at the moment.\n function activeWalletPubKeyHash() external view returns (bytes20) {\n return self.activeWalletPubKeyHash;\n }\n\n /// @notice Gets the live wallets count.\n /// @return The current count of wallets being in the Live state.\n function liveWalletsCount() external view returns (uint32) {\n return self.liveWalletsCount;\n }\n\n /// @notice Returns the fraud challenge identified by the given key built\n /// as keccak256(walletPublicKey|sighash).\n function fraudChallenges(uint256 challengeKey)\n external\n view\n returns (Fraud.FraudChallenge memory)\n {\n return self.fraudChallenges[challengeKey];\n }\n\n /// @notice Collection of all moved funds sweep requests indexed by\n /// `keccak256(movingFundsTxHash | movingFundsOutputIndex)`.\n /// The `movingFundsTxHash` is `bytes32` (ordered as in Bitcoin\n /// internally) and `movingFundsOutputIndex` an `uint32`. Each entry\n /// is actually an UTXO representing the moved funds and is supposed\n /// to be swept with the current main UTXO of the recipient wallet.\n /// @param requestKey Request key built as\n /// `keccak256(movingFundsTxHash | movingFundsOutputIndex)`.\n /// @return Details of the moved funds sweep request.\n function movedFundsSweepRequests(uint256 requestKey)\n external\n view\n returns (MovingFunds.MovedFundsSweepRequest memory)\n {\n return self.movedFundsSweepRequests[requestKey];\n }\n\n /// @notice Indicates if the vault with the given address is trusted or not.\n /// Depositors can route their revealed deposits only to trusted\n /// vaults and have trusted vaults notified about new deposits as\n /// soon as these deposits get swept. Vaults not trusted by the\n /// Bridge can still be used by Bank balance owners on their own\n /// responsibility - anyone can approve their Bank balance to any\n /// address.\n function isVaultTrusted(address vault) external view returns (bool) {\n return self.isVaultTrusted[vault];\n }\n\n /// @notice Returns the current values of Bridge deposit parameters.\n /// @return depositDustThreshold The minimal amount that can be requested\n /// to deposit. Value of this parameter must take into account the\n /// value of `depositTreasuryFeeDivisor` and `depositTxMaxFee`\n /// parameters in order to make requests that can incur the\n /// treasury and transaction fee and still satisfy the depositor.\n /// @return depositTreasuryFeeDivisor Divisor used to compute the treasury\n /// fee taken from each deposit and transferred to the treasury upon\n /// sweep proof submission. That fee is computed as follows:\n /// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`\n /// For example, if the treasury fee needs to be 2% of each deposit,\n /// the `depositTreasuryFeeDivisor` should be set to `50`\n /// because `1/50 = 0.02 = 2%`.\n /// @return depositTxMaxFee Maximum amount of BTC transaction fee that can\n /// be incurred by each swept deposit being part of the given sweep\n /// transaction. If the maximum BTC transaction fee is exceeded,\n /// such transaction is considered a fraud.\n function depositParameters()\n external\n view\n returns (\n uint64 depositDustThreshold,\n uint64 depositTreasuryFeeDivisor,\n uint64 depositTxMaxFee\n )\n {\n depositDustThreshold = self.depositDustThreshold;\n depositTreasuryFeeDivisor = self.depositTreasuryFeeDivisor;\n depositTxMaxFee = self.depositTxMaxFee;\n }\n\n /// @notice Returns the current values of Bridge redemption parameters.\n /// @return redemptionDustThreshold The minimal amount that can be requested\n /// for redemption. Value of this parameter must take into account\n /// the value of `redemptionTreasuryFeeDivisor` and `redemptionTxMaxFee`\n /// parameters in order to make requests that can incur the\n /// treasury and transaction fee and still satisfy the redeemer.\n /// @return redemptionTreasuryFeeDivisor Divisor used to compute the treasury\n /// fee taken from each redemption request and transferred to the\n /// treasury upon successful request finalization. That fee is\n /// computed as follows:\n /// `treasuryFee = requestedAmount / redemptionTreasuryFeeDivisor`\n /// For example, if the treasury fee needs to be 2% of each\n /// redemption request, the `redemptionTreasuryFeeDivisor` should\n /// be set to `50` because `1/50 = 0.02 = 2%`.\n /// @return redemptionTxMaxFee Maximum amount of BTC transaction fee that\n /// can be incurred by each redemption request being part of the\n /// given redemption transaction. If the maximum BTC transaction\n /// fee is exceeded, such transaction is considered a fraud.\n /// This is a per-redemption output max fee for the redemption\n /// transaction.\n /// @return redemptionTimeout Time after which the redemption request can be\n /// reported as timed out. It is counted from the moment when the\n /// redemption request was created via `requestRedemption` call.\n /// Reported timed out requests are cancelled and locked balance is\n /// returned to the redeemer in full amount.\n /// @return redemptionTimeoutSlashingAmount The amount of stake slashed\n /// from each member of a wallet for a redemption timeout.\n /// @return redemptionTimeoutNotifierRewardMultiplier The percentage of the\n /// notifier reward from the staking contract the notifier of a\n /// redemption timeout receives. The value is in the range [0, 100].\n function redemptionParameters()\n external\n view\n returns (\n uint64 redemptionDustThreshold,\n uint64 redemptionTreasuryFeeDivisor,\n uint64 redemptionTxMaxFee,\n uint32 redemptionTimeout,\n uint96 redemptionTimeoutSlashingAmount,\n uint32 redemptionTimeoutNotifierRewardMultiplier\n )\n {\n redemptionDustThreshold = self.redemptionDustThreshold;\n redemptionTreasuryFeeDivisor = self.redemptionTreasuryFeeDivisor;\n redemptionTxMaxFee = self.redemptionTxMaxFee;\n redemptionTimeout = self.redemptionTimeout;\n redemptionTimeoutSlashingAmount = self.redemptionTimeoutSlashingAmount;\n redemptionTimeoutNotifierRewardMultiplier = self\n .redemptionTimeoutNotifierRewardMultiplier;\n }\n\n /// @notice Returns the current values of Bridge moving funds between\n /// wallets parameters.\n /// @return movingFundsTxMaxTotalFee Maximum amount of the total BTC\n /// transaction fee that is acceptable in a single moving funds\n /// transaction. This is a _total_ max fee for the entire moving\n /// funds transaction.\n /// @return movingFundsDustThreshold The minimal satoshi amount that makes\n /// sense to be transferred during the moving funds process. Moving\n /// funds wallets having their BTC balance below that value can\n /// begin closing immediately as transferring such a low value may\n /// not be possible due to BTC network fees.\n /// @return movingFundsTimeoutResetDelay Time after which the moving funds\n /// timeout can be reset in case the target wallet commitment\n /// cannot be submitted due to a lack of live wallets in the system.\n /// It is counted from the moment when the wallet was requested to\n /// move their funds and switched to the MovingFunds state or from\n /// the moment the timeout was reset the last time. Value in seconds\n /// This value should be lower than the value of the\n /// `movingFundsTimeout`.\n /// @return movingFundsTimeout Time after which the moving funds process\n /// can be reported as timed out. It is counted from the moment\n /// when the wallet was requested to move their funds and switched\n /// to the MovingFunds state. Value in seconds.\n /// @return movingFundsTimeoutSlashingAmount The amount of stake slashed\n /// from each member of a wallet for a moving funds timeout.\n /// @return movingFundsTimeoutNotifierRewardMultiplier The percentage of the\n /// notifier reward from the staking contract the notifier of a\n /// moving funds timeout receives. The value is in the range [0, 100].\n /// @return movedFundsSweepTxMaxTotalFee Maximum amount of the total BTC\n /// transaction fee that is acceptable in a single moved funds\n /// sweep transaction. This is a _total_ max fee for the entire\n /// moved funds sweep transaction.\n /// @return movedFundsSweepTimeout Time after which the moved funds sweep\n /// process can be reported as timed out. It is counted from the\n /// moment when the wallet was requested to sweep the received funds.\n /// Value in seconds.\n /// @return movedFundsSweepTimeoutSlashingAmount The amount of stake slashed\n /// from each member of a wallet for a moved funds sweep timeout.\n /// @return movedFundsSweepTimeoutNotifierRewardMultiplier The percentage\n /// of the notifier reward from the staking contract the notifier\n /// of a moved funds sweep timeout receives. The value is in the\n /// range [0, 100].\n function movingFundsParameters()\n external\n view\n returns (\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n )\n {\n movingFundsTxMaxTotalFee = self.movingFundsTxMaxTotalFee;\n movingFundsDustThreshold = self.movingFundsDustThreshold;\n movingFundsTimeoutResetDelay = self.movingFundsTimeoutResetDelay;\n movingFundsTimeout = self.movingFundsTimeout;\n movingFundsTimeoutSlashingAmount = self\n .movingFundsTimeoutSlashingAmount;\n movingFundsTimeoutNotifierRewardMultiplier = self\n .movingFundsTimeoutNotifierRewardMultiplier;\n movedFundsSweepTxMaxTotalFee = self.movedFundsSweepTxMaxTotalFee;\n movedFundsSweepTimeout = self.movedFundsSweepTimeout;\n movedFundsSweepTimeoutSlashingAmount = self\n .movedFundsSweepTimeoutSlashingAmount;\n movedFundsSweepTimeoutNotifierRewardMultiplier = self\n .movedFundsSweepTimeoutNotifierRewardMultiplier;\n }\n\n /// @return walletCreationPeriod Determines how frequently a new wallet\n /// creation can be requested. Value in seconds.\n /// @return walletCreationMinBtcBalance The minimum BTC threshold in satoshi\n /// that is used to decide about wallet creation.\n /// @return walletCreationMaxBtcBalance The maximum BTC threshold in satoshi\n /// that is used to decide about wallet creation.\n /// @return walletClosureMinBtcBalance The minimum BTC threshold in satoshi\n /// that is used to decide about wallet closure.\n /// @return walletMaxAge The maximum age of a wallet in seconds, after which\n /// the wallet moving funds process can be requested.\n /// @return walletMaxBtcTransfer The maximum BTC amount in satoshi than\n /// can be transferred to a single target wallet during the moving\n /// funds process.\n /// @return walletClosingPeriod Determines the length of the wallet closing\n /// period, i.e. the period when the wallet remains in the Closing\n /// state and can be subject of deposit fraud challenges. Value\n /// in seconds.\n function walletParameters()\n external\n view\n returns (\n uint32 walletCreationPeriod,\n uint64 walletCreationMinBtcBalance,\n uint64 walletCreationMaxBtcBalance,\n uint64 walletClosureMinBtcBalance,\n uint32 walletMaxAge,\n uint64 walletMaxBtcTransfer,\n uint32 walletClosingPeriod\n )\n {\n walletCreationPeriod = self.walletCreationPeriod;\n walletCreationMinBtcBalance = self.walletCreationMinBtcBalance;\n walletCreationMaxBtcBalance = self.walletCreationMaxBtcBalance;\n walletClosureMinBtcBalance = self.walletClosureMinBtcBalance;\n walletMaxAge = self.walletMaxAge;\n walletMaxBtcTransfer = self.walletMaxBtcTransfer;\n walletClosingPeriod = self.walletClosingPeriod;\n }\n\n /// @notice Returns the current values of Bridge fraud parameters.\n /// @return fraudChallengeDepositAmount The amount of ETH in wei the party\n /// challenging the wallet for fraud needs to deposit.\n /// @return fraudChallengeDefeatTimeout The amount of time the wallet has to\n /// defeat a fraud challenge.\n /// @return fraudSlashingAmount The amount slashed from each wallet member\n /// for committing a fraud.\n /// @return fraudNotifierRewardMultiplier The percentage of the notifier\n /// reward from the staking contract the notifier of a fraud\n /// receives. The value is in the range [0, 100].\n function fraudParameters()\n external\n view\n returns (\n uint96 fraudChallengeDepositAmount,\n uint32 fraudChallengeDefeatTimeout,\n uint96 fraudSlashingAmount,\n uint32 fraudNotifierRewardMultiplier\n )\n {\n fraudChallengeDepositAmount = self.fraudChallengeDepositAmount;\n fraudChallengeDefeatTimeout = self.fraudChallengeDefeatTimeout;\n fraudSlashingAmount = self.fraudSlashingAmount;\n fraudNotifierRewardMultiplier = self.fraudNotifierRewardMultiplier;\n }\n\n /// @notice Returns the addresses of contracts Bridge is interacting with.\n /// @return bank Address of the Bank the Bridge belongs to.\n /// @return relay Address of the Bitcoin relay providing the current Bitcoin\n /// network difficulty.\n /// @return ecdsaWalletRegistry Address of the ECDSA Wallet Registry.\n function contractReferences()\n external\n view\n returns (\n Bank bank,\n IRelay relay,\n EcdsaWalletRegistry ecdsaWalletRegistry\n )\n {\n bank = self.bank;\n relay = self.relay;\n ecdsaWalletRegistry = self.ecdsaWalletRegistry;\n }\n\n /// @notice Address where the deposit treasury fees will be sent to.\n /// Treasury takes part in the operators rewarding process.\n function treasury() external view returns (address) {\n return self.treasury;\n }\n\n /// @notice The number of confirmations on the Bitcoin chain required to\n /// successfully evaluate an SPV proof.\n function txProofDifficultyFactor() external view returns (uint256) {\n return self.txProofDifficultyFactor;\n }\n}\n"
|
|
131
|
+
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\nimport \"@keep-network/random-beacon/contracts/Governable.sol\";\nimport {IWalletOwner as EcdsaWalletOwner} from \"@keep-network/ecdsa/contracts/api/IWalletOwner.sol\";\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol\";\n\nimport \"./IRelay.sol\";\nimport \"./BridgeState.sol\";\nimport \"./Deposit.sol\";\nimport \"./DepositSweep.sol\";\nimport \"./Redemption.sol\";\nimport \"./BitcoinTx.sol\";\nimport \"./EcdsaLib.sol\";\nimport \"./Wallets.sol\";\nimport \"./Fraud.sol\";\nimport \"./MovingFunds.sol\";\n\nimport \"../bank/IReceiveBalanceApproval.sol\";\nimport \"../bank/Bank.sol\";\n\n/// @title Bitcoin Bridge\n/// @notice Bridge manages BTC deposit and redemption flow and is increasing and\n/// decreasing balances in the Bank as a result of BTC deposit and\n/// redemption operations performed by depositors and redeemers.\n///\n/// Depositors send BTC funds to the most recently created off-chain\n/// ECDSA wallet of the bridge using pay-to-script-hash (P2SH) or\n/// pay-to-witness-script-hash (P2WSH) containing hashed information\n/// about the depositor’s Ethereum address. Then, the depositor reveals\n/// their Ethereum address along with their deposit blinding factor,\n/// refund public key hash and refund locktime to the Bridge on Ethereum\n/// chain. The off-chain ECDSA wallet listens for these sorts of\n/// messages and when it gets one, it checks the Bitcoin network to make\n/// sure the deposit lines up. If it does, the off-chain ECDSA wallet\n/// may decide to pick the deposit transaction for sweeping, and when\n/// the sweep operation is confirmed on the Bitcoin network, the ECDSA\n/// wallet informs the Bridge about the sweep increasing appropriate\n/// balances in the Bank.\n/// @dev Bridge is an upgradeable component of the Bank. The order of\n/// functionalities in this contract is: deposit, sweep, redemption,\n/// moving funds, wallet lifecycle, frauds, parameters.\ncontract Bridge is\n Governable,\n EcdsaWalletOwner,\n Initializable,\n IReceiveBalanceApproval\n{\n using BridgeState for BridgeState.Storage;\n using Deposit for BridgeState.Storage;\n using DepositSweep for BridgeState.Storage;\n using Redemption for BridgeState.Storage;\n using MovingFunds for BridgeState.Storage;\n using Wallets for BridgeState.Storage;\n using Fraud for BridgeState.Storage;\n\n BridgeState.Storage internal self;\n\n event DepositRevealed(\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex,\n address indexed depositor,\n uint64 amount,\n bytes8 blindingFactor,\n bytes20 indexed walletPubKeyHash,\n bytes20 refundPubKeyHash,\n bytes4 refundLocktime,\n address vault\n );\n\n event DepositsSwept(bytes20 walletPubKeyHash, bytes32 sweepTxHash);\n\n event RedemptionRequested(\n bytes20 indexed walletPubKeyHash,\n bytes redeemerOutputScript,\n address indexed redeemer,\n uint64 requestedAmount,\n uint64 treasuryFee,\n uint64 txMaxFee\n );\n\n event RedemptionsCompleted(\n bytes20 indexed walletPubKeyHash,\n bytes32 redemptionTxHash\n );\n\n event RedemptionTimedOut(\n bytes20 indexed walletPubKeyHash,\n bytes redeemerOutputScript\n );\n\n event WalletMovingFunds(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event MovingFundsCommitmentSubmitted(\n bytes20 indexed walletPubKeyHash,\n bytes20[] targetWallets,\n address submitter\n );\n\n event MovingFundsTimeoutReset(bytes20 indexed walletPubKeyHash);\n\n event MovingFundsCompleted(\n bytes20 indexed walletPubKeyHash,\n bytes32 movingFundsTxHash\n );\n\n event MovingFundsTimedOut(bytes20 indexed walletPubKeyHash);\n\n event MovingFundsBelowDustReported(bytes20 indexed walletPubKeyHash);\n\n event MovedFundsSwept(\n bytes20 indexed walletPubKeyHash,\n bytes32 sweepTxHash\n );\n\n event MovedFundsSweepTimedOut(\n bytes20 indexed walletPubKeyHash,\n bytes32 movingFundsTxHash,\n uint32 movingFundsTxOutputIndex\n );\n\n event NewWalletRequested();\n\n event NewWalletRegistered(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event WalletClosing(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event WalletClosed(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event WalletTerminated(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event FraudChallengeSubmitted(\n bytes20 indexed walletPubKeyHash,\n bytes32 sighash,\n uint8 v,\n bytes32 r,\n bytes32 s\n );\n\n event FraudChallengeDefeated(\n bytes20 indexed walletPubKeyHash,\n bytes32 sighash\n );\n\n event FraudChallengeDefeatTimedOut(\n bytes20 indexed walletPubKeyHash,\n bytes32 sighash\n );\n\n event VaultStatusUpdated(address indexed vault, bool isTrusted);\n\n event DepositParametersUpdated(\n uint64 depositDustThreshold,\n uint64 depositTreasuryFeeDivisor,\n uint64 depositTxMaxFee\n );\n\n event RedemptionParametersUpdated(\n uint64 redemptionDustThreshold,\n uint64 redemptionTreasuryFeeDivisor,\n uint64 redemptionTxMaxFee,\n uint32 redemptionTimeout,\n uint96 redemptionTimeoutSlashingAmount,\n uint32 redemptionTimeoutNotifierRewardMultiplier\n );\n\n event MovingFundsParametersUpdated(\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n\n event WalletParametersUpdated(\n uint32 walletCreationPeriod,\n uint64 walletCreationMinBtcBalance,\n uint64 walletCreationMaxBtcBalance,\n uint64 walletClosureMinBtcBalance,\n uint32 walletMaxAge,\n uint64 walletMaxBtcTransfer,\n uint32 walletClosingPeriod\n );\n\n event FraudParametersUpdated(\n uint96 fraudChallengeDepositAmount,\n uint32 fraudChallengeDefeatTimeout,\n uint96 fraudSlashingAmount,\n uint32 fraudNotifierRewardMultiplier\n );\n\n /// @dev Initializes upgradable contract on deployment.\n /// @param _bank Address of the Bank the Bridge belongs to.\n /// @param _relay Address of the Bitcoin relay providing the current Bitcoin\n /// network difficulty.\n /// @param _treasury Address where the deposit and redemption treasury fees\n /// will be sent to.\n /// @param _ecdsaWalletRegistry Address of the ECDSA Wallet Registry contract.\n /// @param _txProofDifficultyFactor The number of confirmations on the Bitcoin\n /// chain required to successfully evaluate an SPV proof.\n function initialize(\n address _bank,\n address _relay,\n address _treasury,\n address _ecdsaWalletRegistry,\n uint96 _txProofDifficultyFactor\n ) external initializer {\n require(_bank != address(0), \"Bank address cannot be zero\");\n self.bank = Bank(_bank);\n\n require(_relay != address(0), \"Relay address cannot be zero\");\n self.relay = IRelay(_relay);\n\n require(\n _ecdsaWalletRegistry != address(0),\n \"ECDSA Wallet Registry address cannot be zero\"\n );\n self.ecdsaWalletRegistry = EcdsaWalletRegistry(_ecdsaWalletRegistry);\n\n require(_treasury != address(0), \"Treasury address cannot be zero\");\n self.treasury = _treasury;\n\n self.txProofDifficultyFactor = _txProofDifficultyFactor;\n\n //\n // All parameters set in the constructor are initial ones, used at the\n // moment contracts were deployed for the first time. Parameters are\n // governable and values assigned in the constructor do not need to\n // reflect the current ones. Keep in mind the initial parameters are\n // pretty forgiving and valid only for the early stage of the network.\n //\n\n self.depositDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC\n self.depositTxMaxFee = 100000; // 100000 satoshi = 0.001 BTC\n self.depositTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005\n self.redemptionDustThreshold = 1000000; // 1000000 satoshi = 0.01 BTC\n self.redemptionTreasuryFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005\n self.redemptionTxMaxFee = 100000; // 100000 satoshi = 0.001 BTC\n self.redemptionTimeout = 5 days;\n self.redemptionTimeoutSlashingAmount = 100 * 1e18; // 100 T\n self.redemptionTimeoutNotifierRewardMultiplier = 100; // 100%\n self.movingFundsTxMaxTotalFee = 100000; // 100000 satoshi = 0.001 BTC\n self.movingFundsDustThreshold = 200000; // 200000 satoshi = 0.002 BTC\n self.movingFundsTimeoutResetDelay = 6 days;\n self.movingFundsTimeout = 7 days;\n self.movingFundsTimeoutSlashingAmount = 100 * 1e18; // 100 T\n self.movingFundsTimeoutNotifierRewardMultiplier = 100; //100%\n self.movedFundsSweepTxMaxTotalFee = 100000; // 100000 satoshi = 0.001 BTC\n self.movedFundsSweepTimeout = 7 days;\n self.movedFundsSweepTimeoutSlashingAmount = 100 * 1e18; // 100 T\n self.movedFundsSweepTimeoutNotifierRewardMultiplier = 100; //100%\n self.fraudChallengeDepositAmount = 5 ether;\n self.fraudChallengeDefeatTimeout = 7 days;\n self.fraudSlashingAmount = 100 * 1e18; // 100 T\n self.fraudNotifierRewardMultiplier = 100; // 100%\n self.walletCreationPeriod = 1 weeks;\n self.walletCreationMinBtcBalance = 1e8; // 1 BTC\n self.walletCreationMaxBtcBalance = 100e8; // 100 BTC\n self.walletClosureMinBtcBalance = 5 * 1e7; // 0.5 BTC\n self.walletMaxAge = 26 weeks; // ~6 months\n self.walletMaxBtcTransfer = 10e8; // 10 BTC\n self.walletClosingPeriod = 40 days;\n\n _transferGovernance(msg.sender);\n }\n\n /// @notice Used by the depositor to reveal information about their P2(W)SH\n /// Bitcoin deposit to the Bridge on Ethereum chain. The off-chain\n /// wallet listens for revealed deposit events and may decide to\n /// include the revealed deposit in the next executed sweep.\n /// Information about the Bitcoin deposit can be revealed before or\n /// after the Bitcoin transaction with P2(W)SH deposit is mined on\n /// the Bitcoin chain. Worth noting, the gas cost of this function\n /// scales with the number of P2(W)SH transaction inputs and\n /// outputs. The deposit may be routed to one of the trusted vaults.\n /// When a deposit is routed to a vault, vault gets notified when\n /// the deposit gets swept and it may execute the appropriate action.\n /// @param fundingTx Bitcoin funding transaction data, see `BitcoinTx.Info`.\n /// @param reveal Deposit reveal data, see `RevealInfo struct.\n /// @dev Requirements:\n /// - `reveal.walletPubKeyHash` must identify a `Live` wallet,\n /// - `reveal.vault` must be 0x0 or point to a trusted vault,\n /// - `reveal.fundingOutputIndex` must point to the actual P2(W)SH\n /// output of the BTC deposit transaction,\n /// - `reveal.depositor` must be the Ethereum address used in the\n /// P2(W)SH BTC deposit transaction,\n /// - `reveal.blindingFactor` must be the blinding factor used in the\n /// P2(W)SH BTC deposit transaction,\n /// - `reveal.walletPubKeyHash` must be the wallet pub key hash used in\n /// the P2(W)SH BTC deposit transaction,\n /// - `reveal.refundPubKeyHash` must be the refund pub key hash used in\n /// the P2(W)SH BTC deposit transaction,\n /// - `reveal.refundLocktime` must be the refund locktime used in the\n /// P2(W)SH BTC deposit transaction,\n /// - BTC deposit for the given `fundingTxHash`, `fundingOutputIndex`\n /// can be revealed only one time.\n ///\n /// If any of these requirements is not met, the wallet _must_ refuse\n /// to sweep the deposit and the depositor has to wait until the\n /// deposit script unlocks to receive their BTC back.\n function revealDeposit(\n BitcoinTx.Info calldata fundingTx,\n Deposit.DepositRevealInfo calldata reveal\n ) external {\n self.revealDeposit(fundingTx, reveal);\n }\n\n /// @notice Used by the wallet to prove the BTC deposit sweep transaction\n /// and to update Bank balances accordingly. Sweep is only accepted\n /// if it satisfies SPV proof.\n ///\n /// The function is performing Bank balance updates by first\n /// computing the Bitcoin fee for the sweep transaction. The fee is\n /// divided evenly between all swept deposits. Each depositor\n /// receives a balance in the bank equal to the amount inferred\n /// during the reveal transaction, minus their fee share.\n ///\n /// It is possible to prove the given sweep only one time.\n /// @param sweepTx Bitcoin sweep transaction data.\n /// @param sweepProof Bitcoin sweep proof data.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain. If no main UTXO exists for the given wallet,\n /// this parameter is ignored.\n /// @param vault Optional address of the vault where all swept deposits\n /// should be routed to. All deposits swept as part of the transaction\n /// must have their `vault` parameters set to the same address.\n /// If this parameter is set to an address of a trusted vault, swept\n /// deposits are routed to that vault.\n /// If this parameter is set to the zero address or to an address\n /// of a non-trusted vault, swept deposits are not routed to a\n /// vault but depositors' balances are increased in the Bank\n /// individually.\n /// @dev Requirements:\n /// - `sweepTx` components must match the expected structure. See\n /// `BitcoinTx.Info` docs for reference. Their values must exactly\n /// correspond to appropriate Bitcoin transaction fields to produce\n /// a provable transaction hash,\n /// - The `sweepTx` should represent a Bitcoin transaction with 1..n\n /// inputs. If the wallet has no main UTXO, all n inputs should\n /// correspond to P2(W)SH revealed deposits UTXOs. If the wallet has\n /// an existing main UTXO, one of the n inputs must point to that\n /// main UTXO and remaining n-1 inputs should correspond to P2(W)SH\n /// revealed deposits UTXOs. That transaction must have only\n /// one P2(W)PKH output locking funds on the 20-byte wallet public\n /// key hash,\n /// - All revealed deposits that are swept by `sweepTx` must have\n /// their `vault` parameters set to the same address as the address\n /// passed in the `vault` function parameter,\n /// - `sweepProof` components must match the expected structure. See\n /// `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`\n /// field must contain a valid number of block headers, not less\n /// than the `txProofDifficultyFactor` contract constant,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// If there is no main UTXO, this parameter is ignored.\n function submitDepositSweepProof(\n BitcoinTx.Info calldata sweepTx,\n BitcoinTx.Proof calldata sweepProof,\n BitcoinTx.UTXO calldata mainUtxo,\n address vault\n ) external {\n self.submitDepositSweepProof(sweepTx, sweepProof, mainUtxo, vault);\n }\n\n /// @notice Requests redemption of the given amount from the specified\n /// wallet to the redeemer Bitcoin output script. Handles the\n /// simplest case in which the redeemer's balance is decreased in\n /// the Bank.\n /// @param walletPubKeyHash The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key).\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC.\n /// @param amount Requested amount in satoshi. This is also the Bank balance\n /// that is taken from the `balanceOwner` upon request.\n /// Once the request is handled, the actual amount of BTC locked\n /// on the redeemer output script will be always lower than this value\n /// since the treasury and Bitcoin transaction fees must be incurred.\n /// The minimal amount satisfying the request can be computed as:\n /// `amount - (amount / redemptionTreasuryFeeDivisor) - redemptionTxMaxFee`.\n /// Fees values are taken at the moment of request creation.\n /// @dev Requirements:\n /// - Wallet behind `walletPubKeyHash` must be live,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain,\n /// - `redeemerOutputScript` must be a proper Bitcoin script,\n /// - `redeemerOutputScript` cannot have wallet PKH as payload,\n /// - `amount` must be above or equal the `redemptionDustThreshold`,\n /// - Given `walletPubKeyHash` and `redeemerOutputScript` pair can be\n /// used for only one pending request at the same time,\n /// - Wallet must have enough Bitcoin balance to process the request,\n /// - Redeemer must make an allowance in the Bank that the Bridge\n /// contract can spend the given `amount`.\n function requestRedemption(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata mainUtxo,\n bytes calldata redeemerOutputScript,\n uint64 amount\n ) external {\n self.requestRedemption(\n walletPubKeyHash,\n mainUtxo,\n msg.sender,\n redeemerOutputScript,\n amount\n );\n }\n\n /// @notice Requests redemption of the given amount from the specified\n /// wallet to the redeemer Bitcoin output script. Used by\n /// `Bank.approveBalanceAndCall`. Can handle more complex cases\n /// where balance owner may be someone else than the redeemer.\n /// For example, vault redeeming its balance for some depositor.\n /// @param balanceOwner The address of the Bank balance owner whose balance\n /// is getting redeemed.\n /// @param amount Requested amount in satoshi. This is also the Bank balance\n /// that is taken from the `balanceOwner` upon request.\n /// Once the request is handled, the actual amount of BTC locked\n /// on the redeemer output script will be always lower than this value\n /// since the treasury and Bitcoin transaction fees must be incurred.\n /// The minimal amount satisfying the request can be computed as:\n /// `amount - (amount / redemptionTreasuryFeeDivisor) - redemptionTxMaxFee`.\n /// Fees values are taken at the moment of request creation.\n /// @param redemptionData ABI-encoded redemption data:\n /// [\n /// address redeemer,\n /// bytes20 walletPubKeyHash,\n /// bytes32 mainUtxoTxHash,\n /// uint32 mainUtxoTxOutputIndex,\n /// uint64 mainUtxoTxOutputValue,\n /// bytes redeemerOutputScript\n /// ]\n ///\n /// - redeemer: The Ethereum address of the redeemer who will be able\n /// to claim Bank balance if anything goes wrong during the redemption.\n /// In the most basic case, when someone redeems their balance\n /// from the Bank, `balanceOwner` is the same as `redeemer`.\n /// However, when a Vault is redeeming part of its balance for some\n /// redeemer address (for example, someone who has earlier deposited\n /// into that Vault), `balanceOwner` is the Vault, and `redeemer` is\n /// the address for which the vault is redeeming its balance to,\n /// - walletPubKeyHash: The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key),\n /// - mainUtxoTxHash: Data of the wallet's main UTXO TX hash, as\n /// currently known on the Ethereum chain,\n /// - mainUtxoTxOutputIndex: Data of the wallet's main UTXO output\n /// index, as currently known on Ethereum chain,\n /// - mainUtxoTxOutputValue: Data of the wallet's main UTXO output\n /// value, as currently known on Ethereum chain,\n /// - redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC.\n /// @dev Requirements:\n /// - The caller must be the Bank,\n /// - Wallet behind `walletPubKeyHash` must be live,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain,\n /// - `redeemerOutputScript` must be a proper Bitcoin script,\n /// - `redeemerOutputScript` cannot have wallet PKH as payload,\n /// - `amount` must be above or equal the `redemptionDustThreshold`,\n /// - Given `walletPubKeyHash` and `redeemerOutputScript` pair can be\n /// used for only one pending request at the same time,\n /// - Wallet must have enough Bitcoin balance to process the request.\n ///\n /// Note on upgradeability:\n /// Bridge is an upgradeable contract deployed behind\n /// a TransparentUpgradeableProxy. Accepting redemption data as bytes\n /// provides great flexibility. The Bridge is just like any other\n /// contract with a balance approved in the Bank and can be upgraded\n /// to another version without being bound to a particular interface\n /// forever. This flexibility comes with the cost - developers\n /// integrating their vaults and dApps with `Bridge` using\n /// `approveBalanceAndCall` need to pay extra attention to\n /// `redemptionData` and adjust the code in case the expected structure\n /// of `redemptionData` changes.\n function receiveBalanceApproval(\n address balanceOwner,\n uint256 amount,\n bytes calldata redemptionData\n ) external override {\n require(msg.sender == address(self.bank), \"Caller is not the bank\");\n\n self.requestRedemption(\n balanceOwner,\n SafeCastUpgradeable.toUint64(amount),\n redemptionData\n );\n }\n\n /// @notice Used by the wallet to prove the BTC redemption transaction\n /// and to make the necessary bookkeeping. Redemption is only\n /// accepted if it satisfies SPV proof.\n ///\n /// The function is performing Bank balance updates by burning\n /// the total redeemed Bitcoin amount from Bridge balance and\n /// transferring the treasury fee sum to the treasury address.\n ///\n /// It is possible to prove the given redemption only one time.\n /// @param redemptionTx Bitcoin redemption transaction data.\n /// @param redemptionProof Bitcoin redemption proof data.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin\n /// HASH160 over the compressed ECDSA public key) of the wallet which\n /// performed the redemption transaction.\n /// @dev Requirements:\n /// - `redemptionTx` components must match the expected structure. See\n /// `BitcoinTx.Info` docs for reference. Their values must exactly\n /// correspond to appropriate Bitcoin transaction fields to produce\n /// a provable transaction hash,\n /// - The `redemptionTx` should represent a Bitcoin transaction with\n /// exactly 1 input that refers to the wallet's main UTXO. That\n /// transaction should have 1..n outputs handling existing pending\n /// redemption requests or pointing to reported timed out requests.\n /// There can be also 1 optional output representing the\n /// change and pointing back to the 20-byte wallet public key hash.\n /// The change should be always present if the redeemed value sum\n /// is lower than the total wallet's BTC balance,\n /// - `redemptionProof` components must match the expected structure.\n /// See `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`\n /// field must contain a valid number of block headers, not less\n /// than the `txProofDifficultyFactor` contract constant,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// Additionally, the recent main UTXO on Ethereum must be set,\n /// - `walletPubKeyHash` must be connected with the main UTXO used\n /// as transaction single input.\n /// Other remarks:\n /// - Putting the change output as the first transaction output can\n /// save some gas because the output processing loop begins each\n /// iteration by checking whether the given output is the change\n /// thus uses some gas for making the comparison. Once the change\n /// is identified, that check is omitted in further iterations.\n function submitRedemptionProof(\n BitcoinTx.Info calldata redemptionTx,\n BitcoinTx.Proof calldata redemptionProof,\n BitcoinTx.UTXO calldata mainUtxo,\n bytes20 walletPubKeyHash\n ) external {\n self.submitRedemptionProof(\n redemptionTx,\n redemptionProof,\n mainUtxo,\n walletPubKeyHash\n );\n }\n\n /// @notice Notifies that there is a pending redemption request associated\n /// with the given wallet, that has timed out. The redemption\n /// request is identified by the key built as\n /// `keccak256(keccak256(redeemerOutputScript) | walletPubKeyHash)`.\n /// The results of calling this function:\n /// - The pending redemptions value for the wallet will be decreased\n /// by the requested amount (minus treasury fee),\n /// - The tokens taken from the redeemer on redemption request will\n /// be returned to the redeemer,\n /// - The request will be moved from pending redemptions to\n /// timed-out redemptions,\n /// - If the state of the wallet is `Live` or `MovingFunds`, the\n /// wallet operators will be slashed and the notifier will be\n /// rewarded,\n /// - If the state of wallet is `Live`, the wallet will be closed or\n /// marked as `MovingFunds` (depending on the presence or absence\n /// of the wallet's main UTXO) and the wallet will no longer be\n /// marked as the active wallet (if it was marked as such).\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @param redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH).\n /// @dev Requirements:\n /// - The wallet must be in the Live or MovingFunds or Terminated state,\n /// - The redemption request identified by `walletPubKeyHash` and\n /// `redeemerOutputScript` must exist,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given `walletID`. Those IDs are not directly stored\n /// in the contract for gas efficiency purposes but they can be\n /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`\n /// events of the `WalletRegistry` contract,\n /// - The amount of time defined by `redemptionTimeout` must have\n /// passed since the redemption was requested (the request must be\n /// timed-out).\n function notifyRedemptionTimeout(\n bytes20 walletPubKeyHash,\n uint32[] calldata walletMembersIDs,\n bytes calldata redeemerOutputScript\n ) external {\n self.notifyRedemptionTimeout(\n walletPubKeyHash,\n walletMembersIDs,\n redeemerOutputScript\n );\n }\n\n /// @notice Submits the moving funds target wallets commitment.\n /// Once all requirements are met, that function registers the\n /// target wallets commitment and opens the way for moving funds\n /// proof submission.\n /// @param walletPubKeyHash 20-byte public key hash of the source wallet.\n /// @param walletMainUtxo Data of the source wallet's main UTXO, as\n /// currently known on the Ethereum chain.\n /// @param walletMembersIDs Identifiers of the source wallet signing group\n /// members.\n /// @param walletMemberIndex Position of the caller in the source wallet\n /// signing group members list.\n /// @param targetWallets List of 20-byte public key hashes of the target\n /// wallets that the source wallet commits to move the funds to.\n /// @dev Requirements:\n /// - The source wallet must be in the MovingFunds state,\n /// - The source wallet must not have pending redemption requests,\n /// - The source wallet must not have pending moved funds sweep requests,\n /// - The source wallet must not have submitted its commitment already,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given source wallet in the ECDSA registry. Those IDs are\n /// not directly stored in the contract for gas efficiency purposes\n /// but they can be read from appropriate `DkgResultSubmitted`\n /// and `DkgResultApproved` events,\n /// - The `walletMemberIndex` must be in range [1, walletMembersIDs.length],\n /// - The caller must be the member of the source wallet signing group\n /// at the position indicated by `walletMemberIndex` parameter,\n /// - The `walletMainUtxo` components must point to the recent main\n /// UTXO of the source wallet, as currently known on the Ethereum\n /// chain,\n /// - Source wallet BTC balance must be greater than zero,\n /// - At least one Live wallet must exist in the system,\n /// - Submitted target wallets count must match the expected count\n /// `N = min(liveWalletsCount, ceil(walletBtcBalance / walletMaxBtcTransfer))`\n /// where `N > 0`,\n /// - Each target wallet must be not equal to the source wallet,\n /// - Each target wallet must follow the expected order i.e. all\n /// target wallets 20-byte public key hashes represented as numbers\n /// must form a strictly increasing sequence without duplicates,\n /// - Each target wallet must be in Live state.\n function submitMovingFundsCommitment(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata walletMainUtxo,\n uint32[] calldata walletMembersIDs,\n uint256 walletMemberIndex,\n bytes20[] calldata targetWallets\n ) external {\n self.submitMovingFundsCommitment(\n walletPubKeyHash,\n walletMainUtxo,\n walletMembersIDs,\n walletMemberIndex,\n targetWallets\n );\n }\n\n /// @notice Resets the moving funds timeout for the given wallet if the\n /// target wallet commitment cannot be submitted due to a lack\n /// of live wallets in the system.\n /// @param walletPubKeyHash 20-byte public key hash of the moving funds wallet.\n /// @dev Requirements:\n /// - The wallet must be in the MovingFunds state,\n /// - The target wallets commitment must not be already submitted for\n /// the given moving funds wallet,\n /// - Live wallets count must be zero,\n /// - The moving funds timeout reset delay must be elapsed.\n function resetMovingFundsTimeout(bytes20 walletPubKeyHash) external {\n self.resetMovingFundsTimeout(walletPubKeyHash);\n }\n\n /// @notice Used by the wallet to prove the BTC moving funds transaction\n /// and to make the necessary state changes. Moving funds is only\n /// accepted if it satisfies SPV proof.\n ///\n /// The function validates the moving funds transaction structure\n /// by checking if it actually spends the main UTXO of the declared\n /// wallet and locks the value on the pre-committed target wallets\n /// using a reasonable transaction fee. If all preconditions are\n /// met, this functions closes the source wallet.\n ///\n /// It is possible to prove the given moving funds transaction only\n /// one time.\n /// @param movingFundsTx Bitcoin moving funds transaction data.\n /// @param movingFundsProof Bitcoin moving funds proof data.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin\n /// HASH160 over the compressed ECDSA public key) of the wallet\n /// which performed the moving funds transaction.\n /// @dev Requirements:\n /// - `movingFundsTx` components must match the expected structure. See\n /// `BitcoinTx.Info` docs for reference. Their values must exactly\n /// correspond to appropriate Bitcoin transaction fields to produce\n /// a provable transaction hash,\n /// - The `movingFundsTx` should represent a Bitcoin transaction with\n /// exactly 1 input that refers to the wallet's main UTXO. That\n /// transaction should have 1..n outputs corresponding to the\n /// pre-committed target wallets. Outputs must be ordered in the\n /// same way as their corresponding target wallets are ordered\n /// within the target wallets commitment,\n /// - `movingFundsProof` components must match the expected structure.\n /// See `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`\n /// field must contain a valid number of block headers, not less\n /// than the `txProofDifficultyFactor` contract constant,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// Additionally, the recent main UTXO on Ethereum must be set,\n /// - `walletPubKeyHash` must be connected with the main UTXO used\n /// as transaction single input,\n /// - The wallet that `walletPubKeyHash` points to must be in the\n /// MovingFunds state,\n /// - The target wallets commitment must be submitted by the wallet\n /// that `walletPubKeyHash` points to,\n /// - The total Bitcoin transaction fee must be lesser or equal\n /// to `movingFundsTxMaxTotalFee` governable parameter.\n function submitMovingFundsProof(\n BitcoinTx.Info calldata movingFundsTx,\n BitcoinTx.Proof calldata movingFundsProof,\n BitcoinTx.UTXO calldata mainUtxo,\n bytes20 walletPubKeyHash\n ) external {\n self.submitMovingFundsProof(\n movingFundsTx,\n movingFundsProof,\n mainUtxo,\n walletPubKeyHash\n );\n }\n\n /// @notice Notifies about a timed out moving funds process. Terminates\n /// the wallet and slashes signing group members as a result.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @dev Requirements:\n /// - The wallet must be in the MovingFunds state,\n /// - The moving funds timeout must be actually exceeded,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given `walletID`. Those IDs are not directly stored\n /// in the contract for gas efficiency purposes but they can be\n /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`\n /// events of the `WalletRegistry` contract.\n function notifyMovingFundsTimeout(\n bytes20 walletPubKeyHash,\n uint32[] calldata walletMembersIDs\n ) external {\n self.notifyMovingFundsTimeout(walletPubKeyHash, walletMembersIDs);\n }\n\n /// @notice Notifies about a moving funds wallet whose BTC balance is\n /// below the moving funds dust threshold. Ends the moving funds\n /// process and begins wallet closing immediately.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known\n /// on the Ethereum chain.\n /// @dev Requirements:\n /// - The wallet must be in the MovingFunds state,\n /// - The `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// If the wallet has no main UTXO, this parameter can be empty as it\n /// is ignored,\n /// - The wallet BTC balance must be below the moving funds threshold.\n function notifyMovingFundsBelowDust(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata mainUtxo\n ) external {\n self.notifyMovingFundsBelowDust(walletPubKeyHash, mainUtxo);\n }\n\n /// @notice Used by the wallet to prove the BTC moved funds sweep\n /// transaction and to make the necessary state changes. Moved\n /// funds sweep is only accepted if it satisfies SPV proof.\n ///\n /// The function validates the sweep transaction structure by\n /// checking if it actually spends the moved funds UTXO and the\n /// sweeping wallet's main UTXO (optionally), and if it locks the\n /// value on the sweeping wallet's 20-byte public key hash using a\n /// reasonable transaction fee. If all preconditions are\n /// met, this function updates the sweeping wallet main UTXO, thus\n /// their BTC balance.\n ///\n /// It is possible to prove the given sweep transaction only\n /// one time.\n /// @param sweepTx Bitcoin sweep funds transaction data.\n /// @param sweepProof Bitcoin sweep funds proof data.\n /// @param mainUtxo Data of the sweeping wallet's main UTXO, as currently\n /// known on the Ethereum chain.\n /// @dev Requirements:\n /// - `sweepTx` components must match the expected structure. See\n /// `BitcoinTx.Info` docs for reference. Their values must exactly\n /// correspond to appropriate Bitcoin transaction fields to produce\n /// a provable transaction hash,\n /// - The `sweepTx` should represent a Bitcoin transaction with\n /// the first input pointing to a moved funds sweep request targeted\n /// to the wallet, and optionally, the second input pointing to the\n /// wallet's main UTXO, if the sweeping wallet has a main UTXO set.\n /// There should be only one output locking funds on the sweeping\n /// wallet 20-byte public key hash,\n /// - `sweepProof` components must match the expected structure.\n /// See `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`\n /// field must contain a valid number of block headers, not less\n /// than the `txProofDifficultyFactor` contract constant,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the sweeping wallet, as currently known on the Ethereum chain.\n /// If there is no main UTXO, this parameter is ignored,\n /// - The sweeping wallet must be in the Live or MovingFunds state,\n /// - The total Bitcoin transaction fee must be lesser or equal\n /// to `movedFundsSweepTxMaxTotalFee` governable parameter.\n function submitMovedFundsSweepProof(\n BitcoinTx.Info calldata sweepTx,\n BitcoinTx.Proof calldata sweepProof,\n BitcoinTx.UTXO calldata mainUtxo\n ) external {\n self.submitMovedFundsSweepProof(sweepTx, sweepProof, mainUtxo);\n }\n\n /// @notice Notifies about a timed out moved funds sweep process. If the\n /// wallet is not terminated yet, that function terminates\n /// the wallet and slashes signing group members as a result.\n /// Marks the given sweep request as TimedOut.\n /// @param movingFundsTxHash 32-byte hash of the moving funds transaction\n /// that caused the sweep request to be created.\n /// @param movingFundsTxOutputIndex Index of the moving funds transaction\n /// output that is subject of the sweep request.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @dev Requirements:\n /// - The moved funds sweep request must be in the Pending state,\n /// - The moved funds sweep timeout must be actually exceeded,\n /// - The wallet must be either in the Live or MovingFunds or\n /// Terminated state,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given `walletID`. Those IDs are not directly stored\n /// in the contract for gas efficiency purposes but they can be\n /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`\n /// events of the `WalletRegistry` contract.\n function notifyMovedFundsSweepTimeout(\n bytes32 movingFundsTxHash,\n uint32 movingFundsTxOutputIndex,\n uint32[] calldata walletMembersIDs\n ) external {\n self.notifyMovedFundsSweepTimeout(\n movingFundsTxHash,\n movingFundsTxOutputIndex,\n walletMembersIDs\n );\n }\n\n /// @notice Requests creation of a new wallet. This function just\n /// forms a request and the creation process is performed\n /// asynchronously. Once a wallet is created, the ECDSA Wallet\n /// Registry will notify this contract by calling the\n /// `__ecdsaWalletCreatedCallback` function.\n /// @param activeWalletMainUtxo Data of the active wallet's main UTXO, as\n /// currently known on the Ethereum chain.\n /// @dev Requirements:\n /// - `activeWalletMainUtxo` components must point to the recent main\n /// UTXO of the given active wallet, as currently known on the\n /// Ethereum chain. If there is no active wallet at the moment, or\n /// the active wallet has no main UTXO, this parameter can be\n /// empty as it is ignored,\n /// - Wallet creation must not be in progress,\n /// - If the active wallet is set, one of the following\n /// conditions must be true:\n /// - The active wallet BTC balance is above the minimum threshold\n /// and the active wallet is old enough, i.e. the creation period\n /// was elapsed since its creation time,\n /// - The active wallet BTC balance is above the maximum threshold.\n function requestNewWallet(BitcoinTx.UTXO calldata activeWalletMainUtxo)\n external\n {\n self.requestNewWallet(activeWalletMainUtxo);\n }\n\n /// @notice A callback function that is called by the ECDSA Wallet Registry\n /// once a new ECDSA wallet is created.\n /// @param ecdsaWalletID Wallet's unique identifier.\n /// @param publicKeyX Wallet's public key's X coordinate.\n /// @param publicKeyY Wallet's public key's Y coordinate.\n /// @dev Requirements:\n /// - The only caller authorized to call this function is `registry`,\n /// - Given wallet data must not belong to an already registered wallet.\n function __ecdsaWalletCreatedCallback(\n bytes32 ecdsaWalletID,\n bytes32 publicKeyX,\n bytes32 publicKeyY\n ) external override {\n self.registerNewWallet(ecdsaWalletID, publicKeyX, publicKeyY);\n }\n\n /// @notice A callback function that is called by the ECDSA Wallet Registry\n /// once a wallet heartbeat failure is detected.\n /// @param publicKeyX Wallet's public key's X coordinate.\n /// @param publicKeyY Wallet's public key's Y coordinate.\n /// @dev Requirements:\n /// - The only caller authorized to call this function is `registry`,\n /// - Wallet must be in Live state.\n function __ecdsaWalletHeartbeatFailedCallback(\n bytes32,\n bytes32 publicKeyX,\n bytes32 publicKeyY\n ) external override {\n self.notifyWalletHeartbeatFailed(publicKeyX, publicKeyY);\n }\n\n /// @notice Notifies that the wallet is either old enough or has too few\n /// satoshi left and qualifies to be closed.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMainUtxo Data of the wallet's main UTXO, as currently\n /// known on the Ethereum chain.\n /// @dev Requirements:\n /// - Wallet must not be set as the current active wallet,\n /// - Wallet must exceed the wallet maximum age OR the wallet BTC\n /// balance must be lesser than the minimum threshold. If the latter\n /// case is true, the `walletMainUtxo` components must point to the\n /// recent main UTXO of the given wallet, as currently known on the\n /// Ethereum chain. If the wallet has no main UTXO, this parameter\n /// can be empty as it is ignored since the wallet balance is\n /// assumed to be zero,\n /// - Wallet must be in Live state.\n function notifyWalletCloseable(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata walletMainUtxo\n ) external {\n self.notifyWalletCloseable(walletPubKeyHash, walletMainUtxo);\n }\n\n /// @notice Notifies about the end of the closing period for the given wallet.\n /// Closes the wallet ultimately and notifies the ECDSA registry\n /// about this fact.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @dev Requirements:\n /// - The wallet must be in the Closing state,\n /// - The wallet closing period must have elapsed.\n function notifyWalletClosingPeriodElapsed(bytes20 walletPubKeyHash)\n external\n {\n self.notifyWalletClosingPeriodElapsed(walletPubKeyHash);\n }\n\n /// @notice Submits a fraud challenge indicating that a UTXO being under\n /// wallet control was unlocked by the wallet but was not used\n /// according to the protocol rules. That means the wallet signed\n /// a transaction input pointing to that UTXO and there is a unique\n /// sighash and signature pair associated with that input. This\n /// function uses those parameters to create a fraud accusation that\n /// proves a given transaction input unlocking the given UTXO was\n /// actually signed by the wallet. This function cannot determine\n /// whether the transaction was actually broadcast and the input was\n /// consumed in a fraudulent way so it just opens a challenge period\n /// during which the wallet can defeat the challenge by submitting\n /// proof of a transaction that consumes the given input according\n /// to protocol rules. To prevent spurious allegations, the caller\n /// must deposit ETH that is returned back upon justified fraud\n /// challenge or confiscated otherwise.\n /// @param walletPublicKey The public key of the wallet in the uncompressed\n /// and unprefixed format (64 bytes).\n /// @param preimageSha256 The hash that was generated by applying SHA-256\n /// one time over the preimage used during input signing. The preimage\n /// is a serialized subset of the transaction and its structure\n /// depends on the transaction input (see BIP-143 for reference).\n /// Notice that applying SHA-256 over the `preimageSha256` results\n /// in `sighash`. The path from `preimage` to `sighash` looks like\n /// this:\n /// preimage -> (SHA-256) -> preimageSha256 -> (SHA-256) -> sighash.\n /// @param signature Bitcoin signature in the R/S/V format.\n /// @dev Requirements:\n /// - Wallet behind `walletPublicKey` must be in Live or MovingFunds\n /// or Closing state,\n /// - The challenger must send appropriate amount of ETH used as\n /// fraud challenge deposit,\n /// - The signature (represented by r, s and v) must be generated by\n /// the wallet behind `walletPubKey` during signing of `sighash`\n /// which was calculated from `preimageSha256`,\n /// - Wallet can be challenged for the given signature only once.\n function submitFraudChallenge(\n bytes calldata walletPublicKey,\n bytes memory preimageSha256,\n BitcoinTx.RSVSignature calldata signature\n ) external payable {\n self.submitFraudChallenge(walletPublicKey, preimageSha256, signature);\n }\n\n /// @notice Allows to defeat a pending fraud challenge against a wallet if\n /// the transaction that spends the UTXO follows the protocol rules.\n /// In order to defeat the challenge the same `walletPublicKey` and\n /// signature (represented by `r`, `s` and `v`) must be provided as\n /// were used to calculate the sighash during input signing.\n /// The fraud challenge defeat attempt will only succeed if the\n /// inputs in the preimage are considered honestly spent by the\n /// wallet. Therefore the transaction spending the UTXO must be\n /// proven in the Bridge before a challenge defeat is called.\n /// If successfully defeated, the fraud challenge is marked as\n /// resolved and the amount of ether deposited by the challenger is\n /// sent to the treasury.\n /// @param walletPublicKey The public key of the wallet in the uncompressed\n /// and unprefixed format (64 bytes).\n /// @param preimage The preimage which produces sighash used to generate the\n /// ECDSA signature that is the subject of the fraud claim. It is a\n /// serialized subset of the transaction. The exact subset used as\n /// the preimage depends on the transaction input the signature is\n /// produced for. See BIP-143 for reference.\n /// @param witness Flag indicating whether the preimage was produced for a\n /// witness input. True for witness, false for non-witness input.\n /// @dev Requirements:\n /// - `walletPublicKey` and `sighash` calculated as `hash256(preimage)`\n /// must identify an open fraud challenge,\n /// - the preimage must be a valid preimage of a transaction generated\n /// according to the protocol rules and already proved in the Bridge,\n /// - before a defeat attempt is made the transaction that spends the\n /// given UTXO must be proven in the Bridge.\n function defeatFraudChallenge(\n bytes calldata walletPublicKey,\n bytes calldata preimage,\n bool witness\n ) external {\n self.defeatFraudChallenge(walletPublicKey, preimage, witness);\n }\n\n /// @notice Allows to defeat a pending fraud challenge against a wallet by\n /// proving the sighash and signature were produced for an off-chain\n /// wallet heartbeat message following a strict format.\n /// In order to defeat the challenge the same `walletPublicKey` and\n /// signature (represented by `r`, `s` and `v`) must be provided as\n /// were used to calculate the sighash during heartbeat message\n /// signing. The fraud challenge defeat attempt will only succeed if\n /// the signed message follows a strict format required for\n /// heartbeat messages. If successfully defeated, the fraud\n /// challenge is marked as resolved and the amount of ether\n /// deposited by the challenger is sent to the treasury.\n /// @param walletPublicKey The public key of the wallet in the uncompressed\n /// and unprefixed format (64 bytes).\n /// @param heartbeatMessage Off-chain heartbeat message meeting the heartbeat\n /// message format requirements which produces sighash used to\n /// generate the ECDSA signature that is the subject of the fraud\n /// claim.\n /// @dev Requirements:\n /// - `walletPublicKey` and `sighash` calculated as\n /// `hash256(heartbeatMessage)` must identify an open fraud challenge,\n /// - `heartbeatMessage` must follow a strict format of heartbeat\n /// messages.\n function defeatFraudChallengeWithHeartbeat(\n bytes calldata walletPublicKey,\n bytes calldata heartbeatMessage\n ) external {\n self.defeatFraudChallengeWithHeartbeat(\n walletPublicKey,\n heartbeatMessage\n );\n }\n\n /// @notice Notifies about defeat timeout for the given fraud challenge.\n /// Can be called only if there was a fraud challenge identified by\n /// the provided `walletPublicKey` and `sighash` and it was not\n /// defeated on time. The amount of time that needs to pass after\n /// a fraud challenge is reported is indicated by the\n /// `challengeDefeatTimeout`. After a successful fraud challenge\n /// defeat timeout notification the fraud challenge is marked as\n /// resolved, the stake of each operator is slashed, the ether\n /// deposited is returned to the challenger and the challenger is\n /// rewarded.\n /// @param walletPublicKey The public key of the wallet in the uncompressed\n /// and unprefixed format (64 bytes).\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @param preimageSha256 The hash that was generated by applying SHA-256\n /// one time over the preimage used during input signing. The preimage\n /// is a serialized subset of the transaction and its structure\n /// depends on the transaction input (see BIP-143 for reference).\n /// Notice that applying SHA-256 over the `preimageSha256` results\n /// in `sighash`. The path from `preimage` to `sighash` looks like\n /// this:\n /// preimage -> (SHA-256) -> preimageSha256 -> (SHA-256) -> sighash.\n /// @dev Requirements:\n /// - The wallet must be in the Live or MovingFunds or Closing or\n /// Terminated state,\n /// - The `walletPublicKey` and `sighash` calculated from\n /// `preimageSha256` must identify an open fraud challenge,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given `walletID`. Those IDs are not directly stored\n /// in the contract for gas efficiency purposes but they can be\n /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`\n /// events of the `WalletRegistry` contract,\n /// - The amount of time indicated by `challengeDefeatTimeout` must pass\n /// after the challenge was reported.\n function notifyFraudChallengeDefeatTimeout(\n bytes calldata walletPublicKey,\n uint32[] calldata walletMembersIDs,\n bytes memory preimageSha256\n ) external {\n self.notifyFraudChallengeDefeatTimeout(\n walletPublicKey,\n walletMembersIDs,\n preimageSha256\n );\n }\n\n /// @notice Allows the Governance to mark the given vault address as trusted\n /// or no longer trusted. Vaults are not trusted by default.\n /// Trusted vault must meet the following criteria:\n /// - `IVault.receiveBalanceIncrease` must have a known, low gas\n /// cost,\n /// - `IVault.receiveBalanceIncrease` must never revert.\n /// @dev Without restricting reveal only to trusted vaults, malicious\n /// vaults not meeting the criteria would be able to nuke sweep proof\n /// transactions executed by ECDSA wallet with deposits routed to\n /// them.\n /// @param vault The address of the vault.\n /// @param isTrusted flag indicating whether the vault is trusted or not.\n /// @dev Can only be called by the Governance.\n function setVaultStatus(address vault, bool isTrusted)\n external\n onlyGovernance\n {\n self.isVaultTrusted[vault] = isTrusted;\n emit VaultStatusUpdated(vault, isTrusted);\n }\n\n /// @notice Updates parameters of deposits.\n /// @param depositDustThreshold New value of the deposit dust threshold in\n /// satoshis. It is the minimal amount that can be requested to\n //// deposit. Value of this parameter must take into account the value\n /// of `depositTreasuryFeeDivisor` and `depositTxMaxFee` parameters\n /// in order to make requests that can incur the treasury and\n /// transaction fee and still satisfy the depositor.\n /// @param depositTreasuryFeeDivisor New value of the treasury fee divisor.\n /// It is the divisor used to compute the treasury fee taken from\n /// each deposit and transferred to the treasury upon sweep proof\n /// submission. That fee is computed as follows:\n /// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`\n /// For example, if the treasury fee needs to be 2% of each deposit,\n /// the `depositTreasuryFeeDivisor` should be set to `50`\n /// because `1/50 = 0.02 = 2%`.\n /// @param depositTxMaxFee New value of the deposit tx max fee in satoshis.\n /// It is the maximum amount of BTC transaction fee that can\n /// be incurred by each swept deposit being part of the given sweep\n /// transaction. If the maximum BTC transaction fee is exceeded,\n /// such transaction is considered a fraud.\n /// @dev Requirements:\n /// - Deposit dust threshold must be greater than zero,\n /// - Deposit treasury fee divisor must be greater than zero,\n /// - Deposit transaction max fee must be greater than zero.\n function updateDepositParameters(\n uint64 depositDustThreshold,\n uint64 depositTreasuryFeeDivisor,\n uint64 depositTxMaxFee\n ) external onlyGovernance {\n self.updateDepositParameters(\n depositDustThreshold,\n depositTreasuryFeeDivisor,\n depositTxMaxFee\n );\n }\n\n /// @notice Updates parameters of redemptions.\n /// @param redemptionDustThreshold New value of the redemption dust\n /// threshold in satoshis. It is the minimal amount that can be\n /// requested for redemption. Value of this parameter must take into\n /// account the value of `redemptionTreasuryFeeDivisor` and\n /// `redemptionTxMaxFee` parameters in order to make requests that\n /// can incur the treasury and transaction fee and still satisfy the\n /// redeemer.\n /// @param redemptionTreasuryFeeDivisor New value of the redemption\n /// treasury fee divisor. It is the divisor used to compute the\n /// treasury fee taken from each redemption request and transferred\n /// to the treasury upon successful request finalization. That fee is\n /// computed as follows:\n /// `treasuryFee = requestedAmount / redemptionTreasuryFeeDivisor`\n /// For example, if the treasury fee needs to be 2% of each\n /// redemption request, the `redemptionTreasuryFeeDivisor` should\n /// be set to `50` because `1/50 = 0.02 = 2%`.\n /// @param redemptionTxMaxFee New value of the redemption transaction max\n /// fee in satoshis. It is the maximum amount of BTC transaction fee\n /// that can be incurred by each redemption request being part of the\n /// given redemption transaction. If the maximum BTC transaction fee\n /// is exceeded, such transaction is considered a fraud.\n /// This is a per-redemption output max fee for the redemption\n /// transaction.\n /// @param redemptionTimeout New value of the redemption timeout in seconds.\n /// It is the time after which the redemption request can be reported\n /// as timed out. It is counted from the moment when the redemption\n /// request was created via `requestRedemption` call. Reported timed\n /// out requests are cancelled and locked balance is returned to the\n /// redeemer in full amount.\n /// @param redemptionTimeoutSlashingAmount New value of the redemption\n /// timeout slashing amount in T, it is the amount slashed from each\n /// wallet member for redemption timeout.\n /// @param redemptionTimeoutNotifierRewardMultiplier New value of the\n /// redemption timeout notifier reward multiplier as percentage,\n /// it determines the percentage of the notifier reward from the\n /// staking contact the notifier of a redemption timeout receives.\n /// The value must be in the range [0, 100].\n /// @dev Requirements:\n /// - Redemption dust threshold must be greater than moving funds dust\n /// threshold,\n /// - Redemption treasury fee divisor must be greater than zero,\n /// - Redemption transaction max fee must be greater than zero,\n /// - Redemption timeout must be greater than zero,\n /// - Redemption timeout notifier reward multiplier must be in the\n /// range [0, 100].\n function updateRedemptionParameters(\n uint64 redemptionDustThreshold,\n uint64 redemptionTreasuryFeeDivisor,\n uint64 redemptionTxMaxFee,\n uint32 redemptionTimeout,\n uint96 redemptionTimeoutSlashingAmount,\n uint32 redemptionTimeoutNotifierRewardMultiplier\n ) external onlyGovernance {\n self.updateRedemptionParameters(\n redemptionDustThreshold,\n redemptionTreasuryFeeDivisor,\n redemptionTxMaxFee,\n redemptionTimeout,\n redemptionTimeoutSlashingAmount,\n redemptionTimeoutNotifierRewardMultiplier\n );\n }\n\n /// @notice Updates parameters of moving funds.\n /// @param movingFundsTxMaxTotalFee New value of the moving funds transaction\n /// max total fee in satoshis. It is the maximum amount of the total\n /// BTC transaction fee that is acceptable in a single moving funds\n /// transaction. This is a _total_ max fee for the entire moving\n /// funds transaction.\n /// @param movingFundsDustThreshold New value of the moving funds dust\n /// threshold. It is the minimal satoshi amount that makes sense to\n /// be transferred during the moving funds process. Moving funds\n /// wallets having their BTC balance below that value can begin\n /// closing immediately as transferring such a low value may not be\n /// possible due to BTC network fees.\n /// @param movingFundsTimeoutResetDelay New value of the moving funds\n /// timeout reset delay in seconds. It is the time after which the\n /// moving funds timeout can be reset in case the target wallet\n /// commitment cannot be submitted due to a lack of live wallets\n /// in the system. It is counted from the moment when the wallet\n /// was requested to move their funds and switched to the MovingFunds\n /// state or from the moment the timeout was reset the last time.\n /// @param movingFundsTimeout New value of the moving funds timeout in\n /// seconds. It is the time after which the moving funds process can\n /// be reported as timed out. It is counted from the moment when the\n /// wallet was requested to move their funds and switched to the\n /// MovingFunds state.\n /// @param movingFundsTimeoutSlashingAmount New value of the moving funds\n /// timeout slashing amount in T, it is the amount slashed from each\n /// wallet member for moving funds timeout.\n /// @param movingFundsTimeoutNotifierRewardMultiplier New value of the\n /// moving funds timeout notifier reward multiplier as percentage,\n /// it determines the percentage of the notifier reward from the\n /// staking contact the notifier of a moving funds timeout receives.\n /// The value must be in the range [0, 100].\n /// @param movedFundsSweepTxMaxTotalFee New value of the moved funds sweep\n /// transaction max total fee in satoshis. It is the maximum amount\n /// of the total BTC transaction fee that is acceptable in a single\n /// moved funds sweep transaction. This is a _total_ max fee for the\n /// entire moved funds sweep transaction.\n /// @param movedFundsSweepTimeout New value of the moved funds sweep\n /// timeout in seconds. It is the time after which the moved funds\n /// sweep process can be reported as timed out. It is counted from\n /// the moment when the wallet was requested to sweep the received\n /// funds.\n /// @param movedFundsSweepTimeoutSlashingAmount New value of the moved\n /// funds sweep timeout slashing amount in T, it is the amount\n /// slashed from each wallet member for moved funds sweep timeout.\n /// @param movedFundsSweepTimeoutNotifierRewardMultiplier New value of\n /// the moved funds sweep timeout notifier reward multiplier as\n /// percentage, it determines the percentage of the notifier reward\n /// from the staking contact the notifier of a moved funds sweep\n /// timeout receives. The value must be in the range [0, 100].\n /// @dev Requirements:\n /// - Moving funds transaction max total fee must be greater than zero,\n /// - Moving funds dust threshold must be greater than zero and lower\n /// than the redemption dust threshold,\n /// - Moving funds timeout reset delay must be greater than zero,\n /// - Moving funds timeout must be greater than the moving funds\n /// timeout reset delay,\n /// - Moving funds timeout notifier reward multiplier must be in the\n /// range [0, 100],\n /// - Moved funds sweep transaction max total fee must be greater than zero,\n /// - Moved funds sweep timeout must be greater than zero,\n /// - Moved funds sweep timeout notifier reward multiplier must be in the\n /// range [0, 100].\n function updateMovingFundsParameters(\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n ) external onlyGovernance {\n self.updateMovingFundsParameters(\n movingFundsTxMaxTotalFee,\n movingFundsDustThreshold,\n movingFundsTimeoutResetDelay,\n movingFundsTimeout,\n movingFundsTimeoutSlashingAmount,\n movingFundsTimeoutNotifierRewardMultiplier,\n movedFundsSweepTxMaxTotalFee,\n movedFundsSweepTimeout,\n movedFundsSweepTimeoutSlashingAmount,\n movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n }\n\n /// @notice Updates parameters of wallets.\n /// @param walletCreationPeriod New value of the wallet creation period in\n /// seconds, determines how frequently a new wallet creation can be\n /// requested.\n /// @param walletCreationMinBtcBalance New value of the wallet minimum BTC\n /// balance in satoshi, used to decide about wallet creation.\n /// @param walletCreationMaxBtcBalance New value of the wallet maximum BTC\n /// balance in satoshi, used to decide about wallet creation.\n /// @param walletClosureMinBtcBalance New value of the wallet minimum BTC\n /// balance in satoshi, used to decide about wallet closure.\n /// @param walletMaxAge New value of the wallet maximum age in seconds,\n /// indicates the maximum age of a wallet in seconds, after which\n /// the wallet moving funds process can be requested.\n /// @param walletMaxBtcTransfer New value of the wallet maximum BTC transfer\n /// in satoshi, determines the maximum amount that can be transferred\n // to a single target wallet during the moving funds process.\n /// @param walletClosingPeriod New value of the wallet closing period in\n /// seconds, determines the length of the wallet closing period,\n // i.e. the period when the wallet remains in the Closing state\n // and can be subject of deposit fraud challenges.\n /// @dev Requirements:\n /// - Wallet minimum BTC balance must be greater than zero,\n /// - Wallet maximum BTC balance must be greater than the wallet\n /// minimum BTC balance,\n /// - Wallet maximum BTC transfer must be greater than zero,\n /// - Wallet closing period must be greater than zero.\n function updateWalletParameters(\n uint32 walletCreationPeriod,\n uint64 walletCreationMinBtcBalance,\n uint64 walletCreationMaxBtcBalance,\n uint64 walletClosureMinBtcBalance,\n uint32 walletMaxAge,\n uint64 walletMaxBtcTransfer,\n uint32 walletClosingPeriod\n ) external onlyGovernance {\n self.updateWalletParameters(\n walletCreationPeriod,\n walletCreationMinBtcBalance,\n walletCreationMaxBtcBalance,\n walletClosureMinBtcBalance,\n walletMaxAge,\n walletMaxBtcTransfer,\n walletClosingPeriod\n );\n }\n\n /// @notice Updates parameters related to frauds.\n /// @param fraudChallengeDepositAmount New value of the fraud challenge\n /// deposit amount in wei, it is the amount of ETH the party\n /// challenging the wallet for fraud needs to deposit.\n /// @param fraudChallengeDefeatTimeout New value of the challenge defeat\n /// timeout in seconds, it is the amount of time the wallet has to\n /// defeat a fraud challenge. The value must be greater than zero.\n /// @param fraudSlashingAmount New value of the fraud slashing amount in T,\n /// it is the amount slashed from each wallet member for committing\n /// a fraud.\n /// @param fraudNotifierRewardMultiplier New value of the fraud notifier\n /// reward multiplier as percentage, it determines the percentage of\n /// the notifier reward from the staking contact the notifier of\n /// a fraud receives. The value must be in the range [0, 100].\n /// @dev Requirements:\n /// - Fraud challenge defeat timeout must be greater than 0,\n /// - Fraud notifier reward multiplier must be in the range [0, 100].\n function updateFraudParameters(\n uint96 fraudChallengeDepositAmount,\n uint32 fraudChallengeDefeatTimeout,\n uint96 fraudSlashingAmount,\n uint32 fraudNotifierRewardMultiplier\n ) external onlyGovernance {\n self.updateFraudParameters(\n fraudChallengeDepositAmount,\n fraudChallengeDefeatTimeout,\n fraudSlashingAmount,\n fraudNotifierRewardMultiplier\n );\n }\n\n /// @notice Collection of all revealed deposits indexed by\n /// keccak256(fundingTxHash | fundingOutputIndex).\n /// The fundingTxHash is bytes32 (ordered as in Bitcoin internally)\n /// and fundingOutputIndex an uint32. This mapping may contain valid\n /// and invalid deposits and the wallet is responsible for\n /// validating them before attempting to execute a sweep.\n function deposits(uint256 depositKey)\n external\n view\n returns (Deposit.DepositRequest memory)\n {\n return self.deposits[depositKey];\n }\n\n /// @notice Collection of all pending redemption requests indexed by\n /// redemption key built as\n /// `keccak256(keccak256(redeemerOutputScript) | walletPubKeyHash)`.\n /// The walletPubKeyHash is the 20-byte wallet's public key hash\n /// (computed using Bitcoin HASH160 over the compressed ECDSA\n /// public key) and `redeemerOutputScript` is a Bitcoin script\n /// (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC as requested by the redeemer. Requests are added\n /// to this mapping by the `requestRedemption` method (duplicates\n /// not allowed) and are removed by one of the following methods:\n /// - `submitRedemptionProof` in case the request was handled\n /// successfully,\n /// - `notifyRedemptionTimeout` in case the request was reported\n /// to be timed out.\n function pendingRedemptions(uint256 redemptionKey)\n external\n view\n returns (Redemption.RedemptionRequest memory)\n {\n return self.pendingRedemptions[redemptionKey];\n }\n\n /// @notice Collection of all timed out redemptions requests indexed by\n /// redemption key built as\n /// `keccak256(keccak256(redeemerOutputScript) | walletPubKeyHash)`.\n /// The walletPubKeyHash is the 20-byte wallet's public key hash\n /// (computed using Bitcoin HASH160 over the compressed ECDSA\n /// public key) and `redeemerOutputScript` is the Bitcoin script\n /// (P2PKH, P2WPKH, P2SH or P2WSH) that is involved in the timed\n /// out request. Timed out requests are stored in this mapping to\n /// avoid slashing the wallets multiple times for the same timeout.\n /// Only one method can add to this mapping:\n /// - `notifyRedemptionTimeout` which puts the redemption key\n /// to this mapping basing on a timed out request stored\n /// previously in `pendingRedemptions` mapping.\n function timedOutRedemptions(uint256 redemptionKey)\n external\n view\n returns (Redemption.RedemptionRequest memory)\n {\n return self.timedOutRedemptions[redemptionKey];\n }\n\n /// @notice Collection of main UTXOs that are honestly spent indexed by\n /// keccak256(fundingTxHash | fundingOutputIndex). The fundingTxHash\n /// is bytes32 (ordered as in Bitcoin internally) and\n /// fundingOutputIndex an uint32. A main UTXO is considered honestly\n /// spent if it was used as an input of a transaction that have been\n /// proven in the Bridge.\n function spentMainUTXOs(uint256 utxoKey) external view returns (bool) {\n return self.spentMainUTXOs[utxoKey];\n }\n\n /// @notice Gets details about a registered wallet.\n /// @param walletPubKeyHash The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key).\n /// @return Wallet details.\n function wallets(bytes20 walletPubKeyHash)\n external\n view\n returns (Wallets.Wallet memory)\n {\n return self.registeredWallets[walletPubKeyHash];\n }\n\n /// @notice Gets the public key hash of the active wallet.\n /// @return The 20-byte public key hash (computed using Bitcoin HASH160\n /// over the compressed ECDSA public key) of the active wallet.\n /// Returns bytes20(0) if there is no active wallet at the moment.\n function activeWalletPubKeyHash() external view returns (bytes20) {\n return self.activeWalletPubKeyHash;\n }\n\n /// @notice Gets the live wallets count.\n /// @return The current count of wallets being in the Live state.\n function liveWalletsCount() external view returns (uint32) {\n return self.liveWalletsCount;\n }\n\n /// @notice Returns the fraud challenge identified by the given key built\n /// as keccak256(walletPublicKey|sighash).\n function fraudChallenges(uint256 challengeKey)\n external\n view\n returns (Fraud.FraudChallenge memory)\n {\n return self.fraudChallenges[challengeKey];\n }\n\n /// @notice Collection of all moved funds sweep requests indexed by\n /// `keccak256(movingFundsTxHash | movingFundsOutputIndex)`.\n /// The `movingFundsTxHash` is `bytes32` (ordered as in Bitcoin\n /// internally) and `movingFundsOutputIndex` an `uint32`. Each entry\n /// is actually an UTXO representing the moved funds and is supposed\n /// to be swept with the current main UTXO of the recipient wallet.\n /// @param requestKey Request key built as\n /// `keccak256(movingFundsTxHash | movingFundsOutputIndex)`.\n /// @return Details of the moved funds sweep request.\n function movedFundsSweepRequests(uint256 requestKey)\n external\n view\n returns (MovingFunds.MovedFundsSweepRequest memory)\n {\n return self.movedFundsSweepRequests[requestKey];\n }\n\n /// @notice Indicates if the vault with the given address is trusted or not.\n /// Depositors can route their revealed deposits only to trusted\n /// vaults and have trusted vaults notified about new deposits as\n /// soon as these deposits get swept. Vaults not trusted by the\n /// Bridge can still be used by Bank balance owners on their own\n /// responsibility - anyone can approve their Bank balance to any\n /// address.\n function isVaultTrusted(address vault) external view returns (bool) {\n return self.isVaultTrusted[vault];\n }\n\n /// @notice Returns the current values of Bridge deposit parameters.\n /// @return depositDustThreshold The minimal amount that can be requested\n /// to deposit. Value of this parameter must take into account the\n /// value of `depositTreasuryFeeDivisor` and `depositTxMaxFee`\n /// parameters in order to make requests that can incur the\n /// treasury and transaction fee and still satisfy the depositor.\n /// @return depositTreasuryFeeDivisor Divisor used to compute the treasury\n /// fee taken from each deposit and transferred to the treasury upon\n /// sweep proof submission. That fee is computed as follows:\n /// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`\n /// For example, if the treasury fee needs to be 2% of each deposit,\n /// the `depositTreasuryFeeDivisor` should be set to `50`\n /// because `1/50 = 0.02 = 2%`.\n /// @return depositTxMaxFee Maximum amount of BTC transaction fee that can\n /// be incurred by each swept deposit being part of the given sweep\n /// transaction. If the maximum BTC transaction fee is exceeded,\n /// such transaction is considered a fraud.\n function depositParameters()\n external\n view\n returns (\n uint64 depositDustThreshold,\n uint64 depositTreasuryFeeDivisor,\n uint64 depositTxMaxFee\n )\n {\n depositDustThreshold = self.depositDustThreshold;\n depositTreasuryFeeDivisor = self.depositTreasuryFeeDivisor;\n depositTxMaxFee = self.depositTxMaxFee;\n }\n\n /// @notice Returns the current values of Bridge redemption parameters.\n /// @return redemptionDustThreshold The minimal amount that can be requested\n /// for redemption. Value of this parameter must take into account\n /// the value of `redemptionTreasuryFeeDivisor` and `redemptionTxMaxFee`\n /// parameters in order to make requests that can incur the\n /// treasury and transaction fee and still satisfy the redeemer.\n /// @return redemptionTreasuryFeeDivisor Divisor used to compute the treasury\n /// fee taken from each redemption request and transferred to the\n /// treasury upon successful request finalization. That fee is\n /// computed as follows:\n /// `treasuryFee = requestedAmount / redemptionTreasuryFeeDivisor`\n /// For example, if the treasury fee needs to be 2% of each\n /// redemption request, the `redemptionTreasuryFeeDivisor` should\n /// be set to `50` because `1/50 = 0.02 = 2%`.\n /// @return redemptionTxMaxFee Maximum amount of BTC transaction fee that\n /// can be incurred by each redemption request being part of the\n /// given redemption transaction. If the maximum BTC transaction\n /// fee is exceeded, such transaction is considered a fraud.\n /// This is a per-redemption output max fee for the redemption\n /// transaction.\n /// @return redemptionTimeout Time after which the redemption request can be\n /// reported as timed out. It is counted from the moment when the\n /// redemption request was created via `requestRedemption` call.\n /// Reported timed out requests are cancelled and locked balance is\n /// returned to the redeemer in full amount.\n /// @return redemptionTimeoutSlashingAmount The amount of stake slashed\n /// from each member of a wallet for a redemption timeout.\n /// @return redemptionTimeoutNotifierRewardMultiplier The percentage of the\n /// notifier reward from the staking contract the notifier of a\n /// redemption timeout receives. The value is in the range [0, 100].\n function redemptionParameters()\n external\n view\n returns (\n uint64 redemptionDustThreshold,\n uint64 redemptionTreasuryFeeDivisor,\n uint64 redemptionTxMaxFee,\n uint32 redemptionTimeout,\n uint96 redemptionTimeoutSlashingAmount,\n uint32 redemptionTimeoutNotifierRewardMultiplier\n )\n {\n redemptionDustThreshold = self.redemptionDustThreshold;\n redemptionTreasuryFeeDivisor = self.redemptionTreasuryFeeDivisor;\n redemptionTxMaxFee = self.redemptionTxMaxFee;\n redemptionTimeout = self.redemptionTimeout;\n redemptionTimeoutSlashingAmount = self.redemptionTimeoutSlashingAmount;\n redemptionTimeoutNotifierRewardMultiplier = self\n .redemptionTimeoutNotifierRewardMultiplier;\n }\n\n /// @notice Returns the current values of Bridge moving funds between\n /// wallets parameters.\n /// @return movingFundsTxMaxTotalFee Maximum amount of the total BTC\n /// transaction fee that is acceptable in a single moving funds\n /// transaction. This is a _total_ max fee for the entire moving\n /// funds transaction.\n /// @return movingFundsDustThreshold The minimal satoshi amount that makes\n /// sense to be transferred during the moving funds process. Moving\n /// funds wallets having their BTC balance below that value can\n /// begin closing immediately as transferring such a low value may\n /// not be possible due to BTC network fees.\n /// @return movingFundsTimeoutResetDelay Time after which the moving funds\n /// timeout can be reset in case the target wallet commitment\n /// cannot be submitted due to a lack of live wallets in the system.\n /// It is counted from the moment when the wallet was requested to\n /// move their funds and switched to the MovingFunds state or from\n /// the moment the timeout was reset the last time. Value in seconds\n /// This value should be lower than the value of the\n /// `movingFundsTimeout`.\n /// @return movingFundsTimeout Time after which the moving funds process\n /// can be reported as timed out. It is counted from the moment\n /// when the wallet was requested to move their funds and switched\n /// to the MovingFunds state. Value in seconds.\n /// @return movingFundsTimeoutSlashingAmount The amount of stake slashed\n /// from each member of a wallet for a moving funds timeout.\n /// @return movingFundsTimeoutNotifierRewardMultiplier The percentage of the\n /// notifier reward from the staking contract the notifier of a\n /// moving funds timeout receives. The value is in the range [0, 100].\n /// @return movedFundsSweepTxMaxTotalFee Maximum amount of the total BTC\n /// transaction fee that is acceptable in a single moved funds\n /// sweep transaction. This is a _total_ max fee for the entire\n /// moved funds sweep transaction.\n /// @return movedFundsSweepTimeout Time after which the moved funds sweep\n /// process can be reported as timed out. It is counted from the\n /// moment when the wallet was requested to sweep the received funds.\n /// Value in seconds.\n /// @return movedFundsSweepTimeoutSlashingAmount The amount of stake slashed\n /// from each member of a wallet for a moved funds sweep timeout.\n /// @return movedFundsSweepTimeoutNotifierRewardMultiplier The percentage\n /// of the notifier reward from the staking contract the notifier\n /// of a moved funds sweep timeout receives. The value is in the\n /// range [0, 100].\n function movingFundsParameters()\n external\n view\n returns (\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n )\n {\n movingFundsTxMaxTotalFee = self.movingFundsTxMaxTotalFee;\n movingFundsDustThreshold = self.movingFundsDustThreshold;\n movingFundsTimeoutResetDelay = self.movingFundsTimeoutResetDelay;\n movingFundsTimeout = self.movingFundsTimeout;\n movingFundsTimeoutSlashingAmount = self\n .movingFundsTimeoutSlashingAmount;\n movingFundsTimeoutNotifierRewardMultiplier = self\n .movingFundsTimeoutNotifierRewardMultiplier;\n movedFundsSweepTxMaxTotalFee = self.movedFundsSweepTxMaxTotalFee;\n movedFundsSweepTimeout = self.movedFundsSweepTimeout;\n movedFundsSweepTimeoutSlashingAmount = self\n .movedFundsSweepTimeoutSlashingAmount;\n movedFundsSweepTimeoutNotifierRewardMultiplier = self\n .movedFundsSweepTimeoutNotifierRewardMultiplier;\n }\n\n /// @return walletCreationPeriod Determines how frequently a new wallet\n /// creation can be requested. Value in seconds.\n /// @return walletCreationMinBtcBalance The minimum BTC threshold in satoshi\n /// that is used to decide about wallet creation.\n /// @return walletCreationMaxBtcBalance The maximum BTC threshold in satoshi\n /// that is used to decide about wallet creation.\n /// @return walletClosureMinBtcBalance The minimum BTC threshold in satoshi\n /// that is used to decide about wallet closure.\n /// @return walletMaxAge The maximum age of a wallet in seconds, after which\n /// the wallet moving funds process can be requested.\n /// @return walletMaxBtcTransfer The maximum BTC amount in satoshi than\n /// can be transferred to a single target wallet during the moving\n /// funds process.\n /// @return walletClosingPeriod Determines the length of the wallet closing\n /// period, i.e. the period when the wallet remains in the Closing\n /// state and can be subject of deposit fraud challenges. Value\n /// in seconds.\n function walletParameters()\n external\n view\n returns (\n uint32 walletCreationPeriod,\n uint64 walletCreationMinBtcBalance,\n uint64 walletCreationMaxBtcBalance,\n uint64 walletClosureMinBtcBalance,\n uint32 walletMaxAge,\n uint64 walletMaxBtcTransfer,\n uint32 walletClosingPeriod\n )\n {\n walletCreationPeriod = self.walletCreationPeriod;\n walletCreationMinBtcBalance = self.walletCreationMinBtcBalance;\n walletCreationMaxBtcBalance = self.walletCreationMaxBtcBalance;\n walletClosureMinBtcBalance = self.walletClosureMinBtcBalance;\n walletMaxAge = self.walletMaxAge;\n walletMaxBtcTransfer = self.walletMaxBtcTransfer;\n walletClosingPeriod = self.walletClosingPeriod;\n }\n\n /// @notice Returns the current values of Bridge fraud parameters.\n /// @return fraudChallengeDepositAmount The amount of ETH in wei the party\n /// challenging the wallet for fraud needs to deposit.\n /// @return fraudChallengeDefeatTimeout The amount of time the wallet has to\n /// defeat a fraud challenge.\n /// @return fraudSlashingAmount The amount slashed from each wallet member\n /// for committing a fraud.\n /// @return fraudNotifierRewardMultiplier The percentage of the notifier\n /// reward from the staking contract the notifier of a fraud\n /// receives. The value is in the range [0, 100].\n function fraudParameters()\n external\n view\n returns (\n uint96 fraudChallengeDepositAmount,\n uint32 fraudChallengeDefeatTimeout,\n uint96 fraudSlashingAmount,\n uint32 fraudNotifierRewardMultiplier\n )\n {\n fraudChallengeDepositAmount = self.fraudChallengeDepositAmount;\n fraudChallengeDefeatTimeout = self.fraudChallengeDefeatTimeout;\n fraudSlashingAmount = self.fraudSlashingAmount;\n fraudNotifierRewardMultiplier = self.fraudNotifierRewardMultiplier;\n }\n\n /// @notice Returns the addresses of contracts Bridge is interacting with.\n /// @return bank Address of the Bank the Bridge belongs to.\n /// @return relay Address of the Bitcoin relay providing the current Bitcoin\n /// network difficulty.\n /// @return ecdsaWalletRegistry Address of the ECDSA Wallet Registry.\n function contractReferences()\n external\n view\n returns (\n Bank bank,\n IRelay relay,\n EcdsaWalletRegistry ecdsaWalletRegistry\n )\n {\n bank = self.bank;\n relay = self.relay;\n ecdsaWalletRegistry = self.ecdsaWalletRegistry;\n }\n\n /// @notice Address where the deposit treasury fees will be sent to.\n /// Treasury takes part in the operators rewarding process.\n function treasury() external view returns (address) {\n return self.treasury;\n }\n\n /// @notice The number of confirmations on the Bitcoin chain required to\n /// successfully evaluate an SPV proof.\n function txProofDifficultyFactor() external view returns (uint256) {\n return self.txProofDifficultyFactor;\n }\n}\n"
|
|
132
132
|
},
|
|
133
133
|
"@keep-network/random-beacon/contracts/Governable.sol": {
|
|
134
134
|
"content": "// SPDX-License-Identifier: MIT\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity ^0.8.9;\n\n/// @notice Governable contract.\n/// @dev A constructor is not defined, which makes the contract compatible with\n/// upgradable proxies. This requires calling explicitly `_transferGovernance`\n/// function in a child contract.\nabstract contract Governable {\n // Governance of the contract\n // The variable should be initialized by the implementing contract.\n // slither-disable-next-line uninitialized-state\n address public governance;\n\n // Reserved storage space in case we need to add more variables,\n // since there are upgradeable contracts that inherit from this one.\n // See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n // slither-disable-next-line unused-state\n uint256[49] private __gap;\n\n event GovernanceTransferred(address oldGovernance, address newGovernance);\n\n modifier onlyGovernance() virtual {\n require(governance == msg.sender, \"Caller is not the governance\");\n _;\n }\n\n /// @notice Transfers governance of the contract to `newGovernance`.\n function transferGovernance(address newGovernance)\n external\n virtual\n onlyGovernance\n {\n require(\n newGovernance != address(0),\n \"New governance is the zero address\"\n );\n _transferGovernance(newGovernance);\n }\n\n function _transferGovernance(address newGovernance) internal virtual {\n address oldGovernance = governance;\n governance = newGovernance;\n emit GovernanceTransferred(oldGovernance, newGovernance);\n }\n}\n"
|
|
@@ -152,7 +152,7 @@
|
|
|
152
152
|
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\nimport {BTCUtils} from \"@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol\";\n\nimport \"./BitcoinTx.sol\";\nimport \"./BridgeState.sol\";\nimport \"./Wallets.sol\";\n\nimport \"../bank/Bank.sol\";\n\n/// @title Bridge deposit sweep\n/// @notice The library handles the logic for sweeping transactions revealed to\n/// the Bridge\n/// @dev Bridge active wallet periodically signs a transaction that unlocks all\n/// of the valid, revealed deposits above the dust threshold, combines them\n/// into a single UTXO with the existing main wallet UTXO, and relocks\n/// those transactions without a 30-day refund clause to the same wallet.\n/// This has two main effects: it consolidates the UTXO set and it disables\n/// the refund. Balances of depositors in the Bank are increased when the\n/// SPV sweep proof is submitted to the Bridge.\nlibrary DepositSweep {\n using BridgeState for BridgeState.Storage;\n using BitcoinTx for BridgeState.Storage;\n\n using BTCUtils for bytes;\n\n /// @notice Represents temporary information needed during the processing\n /// of the deposit sweep Bitcoin transaction inputs. This structure\n /// is an internal one and should not be exported outside of the\n /// deposit sweep transaction processing code.\n /// @dev Allows to mitigate \"stack too deep\" errors on EVM.\n struct DepositSweepTxInputsProcessingInfo {\n // Input vector of the deposit sweep Bitcoin transaction. It is\n // assumed the vector's structure is valid so it must be validated\n // using e.g. `BTCUtils.validateVin` function before being used\n // during the processing. The validation is usually done as part\n // of the `BitcoinTx.validateProof` call that checks the SPV proof.\n bytes sweepTxInputVector;\n // Data of the wallet's main UTXO. If no main UTXO exists for the given\n // sweeping wallet, this parameter's fields should be zeroed to bypass\n // the main UTXO validation\n BitcoinTx.UTXO mainUtxo;\n // Address of the vault where all swept deposits should be routed to.\n // It is used to validate whether all swept deposits have been revealed\n // with the same `vault` parameter. It is an optional parameter.\n // Set to zero address if deposits are not routed to a vault.\n address vault;\n // This struct doesn't contain `__gap` property as the structure is not\n // stored, it is used as a function's memory argument.\n }\n\n /// @notice Represents an outcome of the sweep Bitcoin transaction\n /// inputs processing.\n struct DepositSweepTxInputsInfo {\n // Sum of all inputs values i.e. all deposits and main UTXO value,\n // if present.\n uint256 inputsTotalValue;\n // Addresses of depositors who performed processed deposits. Ordered in\n // the same order as deposits inputs in the input vector. Size of this\n // array is either equal to the number of inputs (main UTXO doesn't\n // exist) or less by one (main UTXO exists and is pointed by one of\n // the inputs).\n address[] depositors;\n // Amounts of deposits corresponding to processed deposits. Ordered in\n // the same order as deposits inputs in the input vector. Size of this\n // array is either equal to the number of inputs (main UTXO doesn't\n // exist) or less by one (main UTXO exists and is pointed by one of\n // the inputs).\n uint256[] depositedAmounts;\n // Values of the treasury fee corresponding to processed deposits.\n // Ordered in the same order as deposits inputs in the input vector.\n // Size of this array is either equal to the number of inputs (main\n // UTXO doesn't exist) or less by one (main UTXO exists and is pointed\n // by one of the inputs).\n uint256[] treasuryFees;\n // This struct doesn't contain `__gap` property as the structure is not\n // stored, it is used as a function's memory argument.\n }\n\n event DepositsSwept(bytes20 walletPubKeyHash, bytes32 sweepTxHash);\n\n /// @notice Used by the wallet to prove the BTC deposit sweep transaction\n /// and to update Bank balances accordingly. Sweep is only accepted\n /// if it satisfies SPV proof.\n ///\n /// The function is performing Bank balance updates by first\n /// computing the Bitcoin fee for the sweep transaction. The fee is\n /// divided evenly between all swept deposits. Each depositor\n /// receives a balance in the bank equal to the amount inferred\n /// during the reveal transaction, minus their fee share.\n ///\n /// It is possible to prove the given sweep only one time.\n /// @param sweepTx Bitcoin sweep transaction data.\n /// @param sweepProof Bitcoin sweep proof data.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain. If no main UTXO exists for the given wallet,\n /// this parameter is ignored.\n /// @param vault Optional address of the vault where all swept deposits\n /// should be routed to. All deposits swept as part of the transaction\n /// must have their `vault` parameters set to the same address.\n /// If this parameter is set to an address of a trusted vault, swept\n /// deposits are routed to that vault.\n /// If this parameter is set to the zero address or to an address\n /// of a non-trusted vault, swept deposits are not routed to a\n /// vault but depositors' balances are increased in the Bank\n /// individually.\n /// @dev Requirements:\n /// - `sweepTx` components must match the expected structure. See\n /// `BitcoinTx.Info` docs for reference. Their values must exactly\n /// correspond to appropriate Bitcoin transaction fields to produce\n /// a provable transaction hash,\n /// - The `sweepTx` should represent a Bitcoin transaction with 1..n\n /// inputs. If the wallet has no main UTXO, all n inputs should\n /// correspond to P2(W)SH revealed deposits UTXOs. If the wallet has\n /// an existing main UTXO, one of the n inputs must point to that\n /// main UTXO and remaining n-1 inputs should correspond to P2(W)SH\n /// revealed deposits UTXOs. That transaction must have only\n /// one P2(W)PKH output locking funds on the 20-byte wallet public\n /// key hash,\n /// - All revealed deposits that are swept by `sweepTx` must have\n /// their `vault` parameters set to the same address as the address\n /// passed in the `vault` function parameter,\n /// - `sweepProof` components must match the expected structure. See\n /// `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`\n /// field must contain a valid number of block headers, not less\n /// than the `txProofDifficultyFactor` contract constant,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// If there is no main UTXO, this parameter is ignored.\n function submitDepositSweepProof(\n BridgeState.Storage storage self,\n BitcoinTx.Info calldata sweepTx,\n BitcoinTx.Proof calldata sweepProof,\n BitcoinTx.UTXO calldata mainUtxo,\n address vault\n ) external {\n // Wallet state validation is performed in the\n // `resolveDepositSweepingWallet` function.\n\n // The actual transaction proof is performed here. After that point, we\n // can assume the transaction happened on Bitcoin chain and has\n // a sufficient number of confirmations as determined by\n // `txProofDifficultyFactor` constant.\n bytes32 sweepTxHash = self.validateProof(sweepTx, sweepProof);\n\n // Process sweep transaction output and extract its target wallet\n // public key hash and value.\n (\n bytes20 walletPubKeyHash,\n uint64 sweepTxOutputValue\n ) = processDepositSweepTxOutput(self, sweepTx.outputVector);\n\n (\n Wallets.Wallet storage wallet,\n BitcoinTx.UTXO memory resolvedMainUtxo\n ) = resolveDepositSweepingWallet(self, walletPubKeyHash, mainUtxo);\n\n // Process sweep transaction inputs and extract all information needed\n // to perform deposit bookkeeping.\n DepositSweepTxInputsInfo\n memory inputsInfo = processDepositSweepTxInputs(\n self,\n DepositSweepTxInputsProcessingInfo(\n sweepTx.inputVector,\n resolvedMainUtxo,\n vault\n )\n );\n\n // Helper variable that will hold the sum of treasury fees paid by\n // all deposits.\n uint256 totalTreasuryFee = 0;\n\n // Determine the transaction fee that should be incurred by each deposit\n // and the indivisible remainder that should be additionally incurred\n // by the last deposit.\n (\n uint256 depositTxFee,\n uint256 depositTxFeeRemainder\n ) = depositSweepTxFeeDistribution(\n inputsInfo.inputsTotalValue,\n sweepTxOutputValue,\n inputsInfo.depositedAmounts.length\n );\n\n // Make sure the highest value of the deposit transaction fee does not\n // exceed the maximum value limited by the governable parameter.\n require(\n depositTxFee + depositTxFeeRemainder <= self.depositTxMaxFee,\n \"Transaction fee is too high\"\n );\n\n // Reduce each deposit amount by treasury fee and transaction fee.\n for (uint256 i = 0; i < inputsInfo.depositedAmounts.length; i++) {\n // The last deposit should incur the deposit transaction fee\n // remainder.\n uint256 depositTxFeeIncurred = i ==\n inputsInfo.depositedAmounts.length - 1\n ? depositTxFee + depositTxFeeRemainder\n : depositTxFee;\n\n // There is no need to check whether\n // `inputsInfo.depositedAmounts[i] - inputsInfo.treasuryFees[i] - txFee > 0`\n // since the `depositDustThreshold` should force that condition\n // to be always true.\n inputsInfo.depositedAmounts[i] =\n inputsInfo.depositedAmounts[i] -\n inputsInfo.treasuryFees[i] -\n depositTxFeeIncurred;\n totalTreasuryFee += inputsInfo.treasuryFees[i];\n }\n\n // Record this sweep data and assign them to the wallet public key hash\n // as new main UTXO. Transaction output index is always 0 as sweep\n // transaction always contains only one output.\n wallet.mainUtxoHash = keccak256(\n abi.encodePacked(sweepTxHash, uint32(0), sweepTxOutputValue)\n );\n\n // slither-disable-next-line reentrancy-events\n emit DepositsSwept(walletPubKeyHash, sweepTxHash);\n\n if (vault != address(0) && self.isVaultTrusted[vault]) {\n // If the `vault` address is not zero and belongs to a trusted\n // vault, route the deposits to that vault.\n self.bank.increaseBalanceAndCall(\n vault,\n inputsInfo.depositors,\n inputsInfo.depositedAmounts\n );\n } else {\n // If the `vault` address is zero or belongs to a non-trusted\n // vault, increase balances in the Bank individually for each\n // depositor.\n self.bank.increaseBalances(\n inputsInfo.depositors,\n inputsInfo.depositedAmounts\n );\n }\n\n // Pass the treasury fee to the treasury address.\n self.bank.increaseBalance(self.treasury, totalTreasuryFee);\n }\n\n /// @notice Resolves sweeping wallet based on the provided wallet public key\n /// hash. Validates the wallet state and current main UTXO, as\n /// currently known on the Ethereum chain.\n /// @param walletPubKeyHash public key hash of the wallet proving the sweep\n /// Bitcoin transaction.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain. If no main UTXO exists for the given wallet,\n /// this parameter is ignored.\n /// @return wallet Data of the sweeping wallet.\n /// @return resolvedMainUtxo The actual main UTXO of the sweeping wallet\n /// resolved by cross-checking the `mainUtxo` parameter with\n /// the chain state. If the validation went well, this is the\n /// plain-text main UTXO corresponding to the `wallet.mainUtxoHash`.\n /// @dev Requirements:\n /// - Sweeping wallet must be either in Live or MovingFunds state,\n /// - If the main UTXO of the sweeping wallet exists in the storage,\n /// the passed `mainUTXO` parameter must be equal to the stored one.\n function resolveDepositSweepingWallet(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata mainUtxo\n )\n internal\n view\n returns (\n Wallets.Wallet storage wallet,\n BitcoinTx.UTXO memory resolvedMainUtxo\n )\n {\n wallet = self.registeredWallets[walletPubKeyHash];\n\n Wallets.WalletState walletState = wallet.state;\n require(\n walletState == Wallets.WalletState.Live ||\n walletState == Wallets.WalletState.MovingFunds,\n \"Wallet must be in Live or MovingFunds state\"\n );\n\n // Check if the main UTXO for given wallet exists. If so, validate\n // passed main UTXO data against the stored hash and use them for\n // further processing. If no main UTXO exists, use empty data.\n resolvedMainUtxo = BitcoinTx.UTXO(bytes32(0), 0, 0);\n bytes32 mainUtxoHash = wallet.mainUtxoHash;\n if (mainUtxoHash != bytes32(0)) {\n require(\n keccak256(\n abi.encodePacked(\n mainUtxo.txHash,\n mainUtxo.txOutputIndex,\n mainUtxo.txOutputValue\n )\n ) == mainUtxoHash,\n \"Invalid main UTXO data\"\n );\n resolvedMainUtxo = mainUtxo;\n }\n }\n\n /// @notice Processes the Bitcoin sweep transaction output vector by\n /// extracting the single output and using it to gain additional\n /// information required for further processing (e.g. value and\n /// wallet public key hash).\n /// @param sweepTxOutputVector Bitcoin sweep transaction output vector.\n /// This function assumes vector's structure is valid so it must be\n /// validated using e.g. `BTCUtils.validateVout` function before\n /// it is passed here.\n /// @return walletPubKeyHash 20-byte wallet public key hash.\n /// @return value 8-byte sweep transaction output value.\n function processDepositSweepTxOutput(\n BridgeState.Storage storage self,\n bytes memory sweepTxOutputVector\n ) internal view returns (bytes20 walletPubKeyHash, uint64 value) {\n // To determine the total number of sweep transaction outputs, we need to\n // parse the compactSize uint (VarInt) the output vector is prepended by.\n // That compactSize uint encodes the number of vector elements using the\n // format presented in:\n // https://developer.bitcoin.org/reference/transactions.html#compactsize-unsigned-integers\n // We don't need asserting the compactSize uint is parseable since it\n // was already checked during `validateVout` validation.\n // See `BitcoinTx.outputVector` docs for more details.\n (, uint256 outputsCount) = sweepTxOutputVector.parseVarInt();\n require(\n outputsCount == 1,\n \"Sweep transaction must have a single output\"\n );\n\n bytes memory output = sweepTxOutputVector.extractOutputAtIndex(0);\n walletPubKeyHash = self.extractPubKeyHash(output);\n value = output.extractValue();\n\n return (walletPubKeyHash, value);\n }\n\n /// @notice Processes the Bitcoin sweep transaction input vector. It\n /// extracts each input and tries to obtain associated deposit or\n /// main UTXO data, depending on the input type. Reverts\n /// if one of the inputs cannot be recognized as a pointer to a\n /// revealed deposit or expected main UTXO.\n /// This function also marks each processed deposit as swept.\n /// @return resultInfo Outcomes of the processing.\n function processDepositSweepTxInputs(\n BridgeState.Storage storage self,\n DepositSweepTxInputsProcessingInfo memory processInfo\n ) internal returns (DepositSweepTxInputsInfo memory resultInfo) {\n // If the passed `mainUtxo` parameter's values are zeroed, the main UTXO\n // for the given wallet doesn't exist and it is not expected to be\n // included in the sweep transaction input vector.\n bool mainUtxoExpected = processInfo.mainUtxo.txHash != bytes32(0);\n bool mainUtxoFound = false;\n\n // Determining the total number of sweep transaction inputs in the same\n // way as for number of outputs. See `BitcoinTx.inputVector` docs for\n // more details.\n (uint256 inputsCompactSizeUintLength, uint256 inputsCount) = processInfo\n .sweepTxInputVector\n .parseVarInt();\n\n // To determine the first input starting index, we must jump over\n // the compactSize uint which prepends the input vector. One byte\n // must be added because `BtcUtils.parseVarInt` does not include\n // compactSize uint tag in the returned length.\n //\n // For >= 0 && <= 252, `BTCUtils.determineVarIntDataLengthAt`\n // returns `0`, so we jump over one byte of compactSize uint.\n //\n // For >= 253 && <= 0xffff there is `0xfd` tag,\n // `BTCUtils.determineVarIntDataLengthAt` returns `2` (no\n // tag byte included) so we need to jump over 1+2 bytes of\n // compactSize uint.\n //\n // Please refer `BTCUtils` library and compactSize uint\n // docs in `BitcoinTx` library for more details.\n uint256 inputStartingIndex = 1 + inputsCompactSizeUintLength;\n\n // Determine the swept deposits count. If main UTXO is NOT expected,\n // all inputs should be deposits. If main UTXO is expected, one input\n // should point to that main UTXO.\n resultInfo.depositors = new address[](\n !mainUtxoExpected ? inputsCount : inputsCount - 1\n );\n resultInfo.depositedAmounts = new uint256[](\n resultInfo.depositors.length\n );\n resultInfo.treasuryFees = new uint256[](resultInfo.depositors.length);\n\n // Initialize helper variables.\n uint256 processedDepositsCount = 0;\n\n // Inputs processing loop.\n for (uint256 i = 0; i < inputsCount; i++) {\n (\n bytes32 outpointTxHash,\n uint32 outpointIndex,\n uint256 inputLength\n ) = parseDepositSweepTxInputAt(\n processInfo.sweepTxInputVector,\n inputStartingIndex\n );\n\n Deposit.DepositRequest storage deposit = self.deposits[\n uint256(\n keccak256(abi.encodePacked(outpointTxHash, outpointIndex))\n )\n ];\n\n if (deposit.revealedAt != 0) {\n // If we entered here, that means the input was identified as\n // a revealed deposit.\n require(deposit.sweptAt == 0, \"Deposit already swept\");\n\n require(\n deposit.vault == processInfo.vault,\n \"Deposit should be routed to another vault\"\n );\n\n if (processedDepositsCount == resultInfo.depositors.length) {\n // If this condition is true, that means a deposit input\n // took place of an expected main UTXO input.\n // In other words, there is no expected main UTXO\n // input and all inputs come from valid, revealed deposits.\n revert(\n \"Expected main UTXO not present in sweep transaction inputs\"\n );\n }\n\n /* solhint-disable-next-line not-rely-on-time */\n deposit.sweptAt = uint32(block.timestamp);\n\n resultInfo.depositors[processedDepositsCount] = deposit\n .depositor;\n resultInfo.depositedAmounts[processedDepositsCount] = deposit\n .amount;\n resultInfo.inputsTotalValue += resultInfo.depositedAmounts[\n processedDepositsCount\n ];\n resultInfo.treasuryFees[processedDepositsCount] = deposit\n .treasuryFee;\n\n processedDepositsCount++;\n } else if (\n mainUtxoExpected != mainUtxoFound &&\n processInfo.mainUtxo.txHash == outpointTxHash &&\n processInfo.mainUtxo.txOutputIndex == outpointIndex\n ) {\n // If we entered here, that means the input was identified as\n // the expected main UTXO.\n resultInfo.inputsTotalValue += processInfo\n .mainUtxo\n .txOutputValue;\n mainUtxoFound = true;\n\n // Main UTXO used as an input, mark it as spent.\n self.spentMainUTXOs[\n uint256(\n keccak256(\n abi.encodePacked(outpointTxHash, outpointIndex)\n )\n )\n ] = true;\n } else {\n revert(\"Unknown input type\");\n }\n\n // Make the `inputStartingIndex` pointing to the next input by\n // increasing it by current input's length.\n inputStartingIndex += inputLength;\n }\n\n // Construction of the input processing loop guarantees that:\n // `processedDepositsCount == resultInfo.depositors.length == resultInfo.depositedAmounts.length`\n // is always true at this point. We just use the first variable\n // to assert the total count of swept deposit is bigger than zero.\n require(\n processedDepositsCount > 0,\n \"Sweep transaction must process at least one deposit\"\n );\n\n // Assert the main UTXO was used as one of current sweep's inputs if\n // it was actually expected.\n require(\n mainUtxoExpected == mainUtxoFound,\n \"Expected main UTXO not present in sweep transaction inputs\"\n );\n\n return resultInfo;\n }\n\n /// @notice Parses a Bitcoin transaction input starting at the given index.\n /// @param inputVector Bitcoin transaction input vector.\n /// @param inputStartingIndex Index the given input starts at.\n /// @return outpointTxHash 32-byte hash of the Bitcoin transaction which is\n /// pointed in the given input's outpoint.\n /// @return outpointIndex 4-byte index of the Bitcoin transaction output\n /// which is pointed in the given input's outpoint.\n /// @return inputLength Byte length of the given input.\n /// @dev This function assumes vector's structure is valid so it must be\n /// validated using e.g. `BTCUtils.validateVin` function before it\n /// is passed here.\n function parseDepositSweepTxInputAt(\n bytes memory inputVector,\n uint256 inputStartingIndex\n )\n internal\n pure\n returns (\n bytes32 outpointTxHash,\n uint32 outpointIndex,\n uint256 inputLength\n )\n {\n outpointTxHash = inputVector.extractInputTxIdLeAt(inputStartingIndex);\n\n outpointIndex = BTCUtils.reverseUint32(\n uint32(inputVector.extractTxIndexLeAt(inputStartingIndex))\n );\n\n inputLength = inputVector.determineInputLengthAt(inputStartingIndex);\n\n return (outpointTxHash, outpointIndex, inputLength);\n }\n\n /// @notice Determines the distribution of the sweep transaction fee\n /// over swept deposits.\n /// @param sweepTxInputsTotalValue Total value of all sweep transaction inputs.\n /// @param sweepTxOutputValue Value of the sweep transaction output.\n /// @param depositsCount Count of the deposits swept by the sweep transaction.\n /// @return depositTxFee Transaction fee per deposit determined by evenly\n /// spreading the divisible part of the sweep transaction fee\n /// over all deposits.\n /// @return depositTxFeeRemainder The indivisible part of the sweep\n /// transaction fee than cannot be distributed over all deposits.\n /// @dev It is up to the caller to decide how the remainder should be\n /// counted in. This function only computes its value.\n function depositSweepTxFeeDistribution(\n uint256 sweepTxInputsTotalValue,\n uint256 sweepTxOutputValue,\n uint256 depositsCount\n )\n internal\n pure\n returns (uint256 depositTxFee, uint256 depositTxFeeRemainder)\n {\n // The sweep transaction fee is just the difference between inputs\n // amounts sum and the output amount.\n uint256 sweepTxFee = sweepTxInputsTotalValue - sweepTxOutputValue;\n // Compute the indivisible remainder that remains after dividing the\n // sweep transaction fee over all deposits evenly.\n depositTxFeeRemainder = sweepTxFee % depositsCount;\n // Compute the transaction fee per deposit by dividing the sweep\n // transaction fee (reduced by the remainder) by the number of deposits.\n depositTxFee = (sweepTxFee - depositTxFeeRemainder) / depositsCount;\n\n return (depositTxFee, depositTxFeeRemainder);\n }\n}\n"
|
|
153
153
|
},
|
|
154
154
|
"contracts/bridge/Redemption.sol": {
|
|
155
|
-
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\nimport {BTCUtils} from \"@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol\";\nimport {BytesLib} from \"@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol\";\n\nimport \"./BitcoinTx.sol\";\nimport \"./BridgeState.sol\";\nimport \"./Wallets.sol\";\n\nimport \"../bank/Bank.sol\";\n\n/// @notice Aggregates functions common to the redemption transaction proof\n/// validation and to the moving funds transaction proof validation.\nlibrary OutboundTx {\n using BTCUtils for bytes;\n\n /// @notice Checks whether an outbound Bitcoin transaction performed from\n /// the given wallet has an input vector that contains a single\n /// input referring to the wallet's main UTXO. Marks that main UTXO\n /// as correctly spent if the validation succeeds. Reverts otherwise.\n /// There are two outbound transactions from a wallet possible: a\n /// redemption transaction or a moving funds to another wallet\n /// transaction.\n /// @param walletOutboundTxInputVector Bitcoin outbound transaction's input\n /// vector. This function assumes vector's structure is valid so it\n /// must be validated using e.g. `BTCUtils.validateVin` function\n /// before it is passed here.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n function processWalletOutboundTxInput(\n BridgeState.Storage storage self,\n bytes memory walletOutboundTxInputVector,\n BitcoinTx.UTXO calldata mainUtxo\n ) internal {\n // Assert that the single outbound transaction input actually\n // refers to the wallet's main UTXO.\n (\n bytes32 outpointTxHash,\n uint32 outpointIndex\n ) = parseWalletOutboundTxInput(walletOutboundTxInputVector);\n require(\n mainUtxo.txHash == outpointTxHash &&\n mainUtxo.txOutputIndex == outpointIndex,\n \"Outbound transaction input must point to the wallet's main UTXO\"\n );\n\n // Main UTXO used as an input, mark it as spent.\n self.spentMainUTXOs[\n uint256(\n keccak256(\n abi.encodePacked(mainUtxo.txHash, mainUtxo.txOutputIndex)\n )\n )\n ] = true;\n }\n\n /// @notice Parses the input vector of an outbound Bitcoin transaction\n /// performed from the given wallet. It extracts the single input\n /// then the transaction hash and output index from its outpoint.\n /// There are two outbound transactions from a wallet possible: a\n /// redemption transaction or a moving funds to another wallet\n /// transaction.\n /// @param walletOutboundTxInputVector Bitcoin outbound transaction input\n /// vector. This function assumes vector's structure is valid so it\n /// must be validated using e.g. `BTCUtils.validateVin` function\n /// before it is passed here.\n /// @return outpointTxHash 32-byte hash of the Bitcoin transaction which is\n /// pointed in the input's outpoint.\n /// @return outpointIndex 4-byte index of the Bitcoin transaction output\n /// which is pointed in the input's outpoint.\n function parseWalletOutboundTxInput(\n bytes memory walletOutboundTxInputVector\n ) internal pure returns (bytes32 outpointTxHash, uint32 outpointIndex) {\n // To determine the total number of Bitcoin transaction inputs,\n // we need to parse the compactSize uint (VarInt) the input vector is\n // prepended by. That compactSize uint encodes the number of vector\n // elements using the format presented in:\n // https://developer.bitcoin.org/reference/transactions.html#compactsize-unsigned-integers\n // We don't need asserting the compactSize uint is parseable since it\n // was already checked during `validateVin` validation.\n // See `BitcoinTx.inputVector` docs for more details.\n (, uint256 inputsCount) = walletOutboundTxInputVector.parseVarInt();\n require(\n inputsCount == 1,\n \"Outbound transaction must have a single input\"\n );\n\n bytes memory input = walletOutboundTxInputVector.extractInputAtIndex(0);\n\n outpointTxHash = input.extractInputTxIdLE();\n\n outpointIndex = BTCUtils.reverseUint32(\n uint32(input.extractTxIndexLE())\n );\n\n // There is only one input in the transaction. Input has an outpoint\n // field that is a reference to the transaction being spent (see\n // `BitcoinTx` docs). The outpoint contains the hash of the transaction\n // to spend (`outpointTxHash`) and the index of the specific output\n // from that transaction (`outpointIndex`).\n return (outpointTxHash, outpointIndex);\n }\n}\n\n/// @title Bridge redemption\n/// @notice The library handles the logic for redeeming Bitcoin balances from\n/// the Bridge.\n/// @dev To initiate a redemption, a user with a Bank balance supplies\n/// a Bitcoin address. Then, the system calculates the redemption fee, and\n/// releases balance to the provided Bitcoin address. Just like in case of\n/// sweeps of revealed deposits, redemption requests are processed in\n/// batches and require SPV proof to be submitted to the Bridge.\nlibrary Redemption {\n using BridgeState for BridgeState.Storage;\n using Wallets for BridgeState.Storage;\n using BitcoinTx for BridgeState.Storage;\n\n using BTCUtils for bytes;\n using BytesLib for bytes;\n\n /// @notice Represents a redemption request.\n struct RedemptionRequest {\n // ETH address of the redeemer who created the request.\n address redeemer;\n // Requested TBTC amount in satoshi.\n uint64 requestedAmount;\n // Treasury TBTC fee in satoshi at the moment of request creation.\n uint64 treasuryFee;\n // Transaction maximum BTC fee in satoshi at the moment of request\n // creation.\n uint64 txMaxFee;\n // UNIX timestamp the request was created at.\n uint32 requestedAt;\n // This struct doesn't contain `__gap` property as the structure is stored\n // in a mapping, mappings store values in different slots and they are\n // not contiguous with other values.\n }\n\n /// @notice Represents an outcome of the redemption Bitcoin transaction\n /// outputs processing.\n struct RedemptionTxOutputsInfo {\n // Total TBTC value in satoshi that should be burned by the Bridge.\n // It includes the total amount of all BTC redeemed in the transaction\n // and the fee paid to BTC miners for the redemption transaction.\n uint64 totalBurnableValue;\n // Total TBTC value in satoshi that should be transferred to\n // the treasury. It is a sum of all treasury fees paid by all\n // redeemers included in the redemption transaction.\n uint64 totalTreasuryFee;\n // Index of the change output. The change output becomes\n // the new main wallet's UTXO.\n uint32 changeIndex;\n // Value in satoshi of the change output.\n uint64 changeValue;\n // This struct doesn't contain `__gap` property as the structure is not\n // stored, it is used as a function's memory argument.\n }\n\n /// @notice Represents temporary information needed during the processing of\n /// the redemption Bitcoin transaction outputs. This structure is an\n /// internal one and should not be exported outside of the redemption\n /// transaction processing code.\n /// @dev Allows to mitigate \"stack too deep\" errors on EVM.\n struct RedemptionTxOutputsProcessingInfo {\n // The first output starting index in the transaction.\n uint256 outputStartingIndex;\n // The number of outputs in the transaction.\n uint256 outputsCount;\n // P2PKH script for the wallet. Needed to determine the change output.\n bytes32 walletP2PKHScriptKeccak;\n // P2WPKH script for the wallet. Needed to determine the change output.\n bytes32 walletP2WPKHScriptKeccak;\n // This struct doesn't contain `__gap` property as the structure is not\n // stored, it is used as a function's memory argument.\n }\n\n event RedemptionRequested(\n bytes20 indexed walletPubKeyHash,\n bytes redeemerOutputScript,\n address indexed redeemer,\n uint64 requestedAmount,\n uint64 treasuryFee,\n uint64 txMaxFee\n );\n\n event RedemptionsCompleted(\n bytes20 indexed walletPubKeyHash,\n bytes32 redemptionTxHash\n );\n\n event RedemptionTimedOut(\n bytes20 indexed walletPubKeyHash,\n bytes redeemerOutputScript\n );\n\n /// @notice Requests redemption of the given amount from the specified\n /// wallet to the redeemer Bitcoin output script.\n /// This function handles the simplest case, where balance owner is\n /// the redeemer.\n /// @param walletPubKeyHash The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key).\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param balanceOwner The address of the Bank balance owner whose balance\n /// is getting redeemed. Balance owner address is stored as\n /// a redemeer address who will be able co claim back the Bank\n /// balance if anything goes wrong during the redemption.\n /// @param redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC.\n /// @param amount Requested amount in satoshi. This is also the Bank balance\n /// that is taken from the `balanceOwner` upon request.\n /// Once the request is handled, the actual amount of BTC locked\n /// on the redeemer output script will be always lower than this value\n /// since the treasury and Bitcoin transaction fees must be incurred.\n /// The minimal amount satisfying the request can be computed as:\n /// `amount - (amount / redemptionTreasuryFeeDivisor) - redemptionTxMaxFee`.\n /// Fees values are taken at the moment of request creation.\n /// @dev Requirements:\n /// - Wallet behind `walletPubKeyHash` must be live,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain,\n /// - `redeemerOutputScript` must be a proper Bitcoin script,\n /// - `redeemerOutputScript` cannot have wallet PKH as payload,\n /// - `amount` must be above or equal the `redemptionDustThreshold`,\n /// - Given `walletPubKeyHash` and `redeemerOutputScript` pair can be\n /// used for only one pending request at the same time,\n /// - Wallet must have enough Bitcoin balance to proceed the request,\n /// - Balance owner must make an allowance in the Bank that the Bridge\n /// contract can spend the given `amount`.\n function requestRedemption(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata mainUtxo,\n address balanceOwner,\n bytes calldata redeemerOutputScript,\n uint64 amount\n ) external {\n requestRedemption(\n self,\n walletPubKeyHash,\n mainUtxo,\n balanceOwner,\n balanceOwner,\n redeemerOutputScript,\n amount\n );\n }\n\n /// @notice Requests redemption of the given amount from the specified\n /// wallet to the redeemer Bitcoin output script. Used by\n /// `Bridge.receiveBalanceApproval`. Can handle more complex cases\n /// where balance owner may be someone else than the redeemer.\n /// @param balanceOwner The address of the Bank balance owner whose balance\n /// is getting redeemed.\n /// @param amount Requested amount in satoshi. This is also the Bank balance\n /// that is taken from the `balanceOwner` upon request.\n /// Once the request is handled, the actual amount of BTC locked\n /// on the redeemer output script will be always lower than this value\n /// since the treasury and Bitcoin transaction fees must be incurred.\n /// The minimal amount satisfying the request can be computed as:\n /// `amount - (amount / redemptionTreasuryFeeDivisor) - redemptionTxMaxFee`.\n /// Fees values are taken at the moment of request creation.\n /// @param redemptionData ABI-encoded redemption data:\n /// [\n /// address redeemer,\n /// bytes20 walletPubKeyHash,\n /// bytes32 mainUtxoTxHash,\n /// uint32 mainUtxoTxOutputIndex,\n /// uint64 mainUtxoTxOutputValue,\n /// bytes redeemerOutputScript\n /// ]\n ///\n /// - redeemer: The Ethereum address of the redeemer who will be able\n /// to claim Bank balance if anything goes wrong during the redemption.\n /// In the most basic case, when someone redeems their Bitcoin\n /// balance from the Bank, `balanceOwner` is the same as `redemeer`.\n /// However, when a Vault is redeeming part of its balance for some\n /// redeemer address (for example, someone who has earlier deposited\n /// into that Vault), `balanceOwner` is the Vault, and `redemeer` is\n /// the address for which the vault is redeeming its balance to,\n /// - walletPubKeyHash: The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key),\n /// - mainUtxoTxHash: Data of the wallet's main UTXO TX hash, as\n /// currently known on the Ethereum chain,\n /// - mainUtxoTxOutputIndex: Data of the wallet's main UTXO output\n /// index, as currently known on Ethereum chain,\n /// - mainUtxoTxOutputValue: Data of the wallet's main UTXO output\n /// value, as currently known on Ethereum chain,\n /// - redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC.\n /// @dev Requirements:\n /// - Wallet behind `walletPubKeyHash` must be live,\n /// - `mainUtxo*` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain,\n /// - `redeemerOutputScript` must be a proper Bitcoin script,\n /// - `redeemerOutputScript` cannot have wallet PKH as payload,\n /// - `amount` must be above or equal the `redemptionDustThreshold`,\n /// - Given `walletPubKeyHash` and `redeemerOutputScript` pair can be\n /// used for only one pending request at the same time,\n /// - Wallet must have enough Bitcoin balance to proceed the request,\n /// - Balance owner must make an allowance in the Bank that the Bridge\n /// contract can spend the given `amount`.\n function requestRedemption(\n BridgeState.Storage storage self,\n address balanceOwner,\n uint64 amount,\n bytes calldata redemptionData\n ) external {\n (\n address redeemer,\n bytes20 walletPubKeyHash,\n bytes32 mainUtxoTxHash,\n uint32 mainUtxoTxOutputIndex,\n uint64 mainUtxoTxOutputValue,\n bytes memory redeemerOutputScript\n ) = abi.decode(\n redemptionData,\n (address, bytes20, bytes32, uint32, uint64, bytes)\n );\n\n requestRedemption(\n self,\n walletPubKeyHash,\n BitcoinTx.UTXO(\n mainUtxoTxHash,\n mainUtxoTxOutputIndex,\n mainUtxoTxOutputValue\n ),\n balanceOwner,\n redeemer,\n redeemerOutputScript,\n amount\n );\n }\n\n /// @notice Requests redemption of the given amount from the specified\n /// wallet to the redeemer Bitcoin output script.\n /// @param walletPubKeyHash The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key).\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param balanceOwner The address of the Bank balance owner whose balance\n /// is getting redeemed.\n /// @param redeemer The Ethereum address of the redeemer who will be able to\n /// claim Bank balance if anything goes wrong during the redemption.\n /// In the most basic case, when someone redeems their Bitcoin\n /// balance from the Bank, `balanceOwner` is the same as `redeemer`.\n /// However, when a Vault is redeeming part of its balance for some\n /// redeemer address (for example, someone who has earlier deposited\n /// into that Vault), `balanceOwner` is the Vault, and `redeemer` is\n /// the address for which the vault is redeeming its balance to.\n /// @param redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC.\n /// @param amount Requested amount in satoshi. This is also the Bank balance\n /// that is taken from the `balanceOwner` upon request.\n /// Once the request is handled, the actual amount of BTC locked\n /// on the redeemer output script will be always lower than this value\n /// since the treasury and Bitcoin transaction fees must be incurred.\n /// The minimal amount satisfying the request can be computed as:\n /// `amount - (amount / redemptionTreasuryFeeDivisor) - redemptionTxMaxFee`.\n /// Fees values are taken at the moment of request creation.\n /// @dev Requirements:\n /// - Wallet behind `walletPubKeyHash` must be live,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain,\n /// - `redeemerOutputScript` must be a proper Bitcoin script,\n /// - `redeemerOutputScript` cannot have wallet PKH as payload,\n /// - `amount` must be above or equal the `redemptionDustThreshold`,\n /// - Given `walletPubKeyHash` and `redeemerOutputScript` pair can be\n /// used for only one pending request at the same time,\n /// - Wallet must have enough Bitcoin balance to proceed the request,\n /// - Balance owner must make an allowance in the Bank that the Bridge\n /// contract can spend the given `amount`.\n function requestRedemption(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO memory mainUtxo,\n address balanceOwner,\n address redeemer,\n bytes memory redeemerOutputScript,\n uint64 amount\n ) internal {\n Wallets.Wallet storage wallet = self.registeredWallets[\n walletPubKeyHash\n ];\n\n require(\n wallet.state == Wallets.WalletState.Live,\n \"Wallet must be in Live state\"\n );\n\n bytes32 mainUtxoHash = wallet.mainUtxoHash;\n require(\n mainUtxoHash != bytes32(0),\n \"No main UTXO for the given wallet\"\n );\n require(\n keccak256(\n abi.encodePacked(\n mainUtxo.txHash,\n mainUtxo.txOutputIndex,\n mainUtxo.txOutputValue\n )\n ) == mainUtxoHash,\n \"Invalid main UTXO data\"\n );\n\n // Validate if redeemer output script is a correct standard type\n // (P2PKH, P2WPKH, P2SH or P2WSH). This is done by using\n // `BTCUtils.extractHashAt` on it. Such a function extracts the payload\n // properly only from standard outputs so if it succeeds, we have a\n // guarantee the redeemer output script is proper. The underlying way\n // of validation is the same as in tBTC v1.\n bytes memory redeemerOutputScriptPayload = redeemerOutputScript\n .extractHashAt(0, redeemerOutputScript.length);\n\n require(\n redeemerOutputScriptPayload.length > 0,\n \"Redeemer output script must be a standard type\"\n );\n // Check if the redeemer output script payload does not point to the\n // wallet public key hash.\n require(\n redeemerOutputScriptPayload.length != 20 ||\n walletPubKeyHash != redeemerOutputScriptPayload.slice20(0),\n \"Redeemer output script must not point to the wallet PKH\"\n );\n\n require(\n amount >= self.redemptionDustThreshold,\n \"Redemption amount too small\"\n );\n\n // The redemption key is built on top of the wallet public key hash\n // and redeemer output script pair. That means there can be only one\n // request asking for redemption from the given wallet to the given\n // BTC script at the same time.\n uint256 redemptionKey = getRedemptionKey(\n walletPubKeyHash,\n redeemerOutputScript\n );\n\n // Check if given redemption key is not used by a pending redemption.\n // There is no need to check for existence in `timedOutRedemptions`\n // since the wallet's state is changed to other than Live after\n // first time out is reported so making new requests is not possible.\n // slither-disable-next-line incorrect-equality\n require(\n self.pendingRedemptions[redemptionKey].requestedAt == 0,\n \"There is a pending redemption request from this wallet to the same address\"\n );\n\n // No need to check whether `amount - treasuryFee - txMaxFee > 0`\n // since the `redemptionDustThreshold` should force that condition\n // to be always true.\n uint64 treasuryFee = self.redemptionTreasuryFeeDivisor > 0\n ? amount / self.redemptionTreasuryFeeDivisor\n : 0;\n uint64 txMaxFee = self.redemptionTxMaxFee;\n\n // The main wallet UTXO's value doesn't include all pending redemptions.\n // To determine if the requested redemption can be performed by the\n // wallet we need to subtract the total value of all pending redemptions\n // from that wallet's main UTXO value. Given that the treasury fee is\n // not redeemed from the wallet, we are subtracting it.\n wallet.pendingRedemptionsValue += amount - treasuryFee;\n require(\n mainUtxo.txOutputValue >= wallet.pendingRedemptionsValue,\n \"Insufficient wallet funds\"\n );\n\n self.pendingRedemptions[redemptionKey] = RedemptionRequest(\n redeemer,\n amount,\n treasuryFee,\n txMaxFee,\n /* solhint-disable-next-line not-rely-on-time */\n uint32(block.timestamp)\n );\n\n // slither-disable-next-line reentrancy-events\n emit RedemptionRequested(\n walletPubKeyHash,\n redeemerOutputScript,\n redeemer,\n amount,\n treasuryFee,\n txMaxFee\n );\n\n self.bank.transferBalanceFrom(balanceOwner, address(this), amount);\n }\n\n /// @notice Used by the wallet to prove the BTC redemption transaction\n /// and to make the necessary bookkeeping. Redemption is only\n /// accepted if it satisfies SPV proof.\n ///\n /// The function is performing Bank balance updates by burning\n /// the total redeemed Bitcoin amount from Bridge balance and\n /// transferring the treasury fee sum to the treasury address.\n ///\n /// It is possible to prove the given redemption only one time.\n /// @param redemptionTx Bitcoin redemption transaction data.\n /// @param redemptionProof Bitcoin redemption proof data.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin\n /// HASH160 over the compressed ECDSA public key) of the wallet which\n /// performed the redemption transaction.\n /// @dev Requirements:\n /// - `redemptionTx` components must match the expected structure. See\n /// `BitcoinTx.Info` docs for reference. Their values must exactly\n /// correspond to appropriate Bitcoin transaction fields to produce\n /// a provable transaction hash,\n /// - The `redemptionTx` should represent a Bitcoin transaction with\n /// exactly 1 input that refers to the wallet's main UTXO. That\n /// transaction should have 1..n outputs handling existing pending\n /// redemption requests or pointing to reported timed out requests.\n /// There can be also 1 optional output representing the\n /// change and pointing back to the 20-byte wallet public key hash.\n /// The change should be always present if the redeemed value sum\n /// is lower than the total wallet's BTC balance,\n /// - `redemptionProof` components must match the expected structure.\n /// See `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`\n /// field must contain a valid number of block headers, not less\n /// than the `txProofDifficultyFactor` contract constant,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// Additionally, the recent main UTXO on Ethereum must be set,\n /// - `walletPubKeyHash` must be connected with the main UTXO used\n /// as transaction single input.\n /// Other remarks:\n /// - Putting the change output as the first transaction output can\n /// save some gas because the output processing loop begins each\n /// iteration by checking whether the given output is the change\n /// thus uses some gas for making the comparison. Once the change\n /// is identified, that check is omitted in further iterations.\n function submitRedemptionProof(\n BridgeState.Storage storage self,\n BitcoinTx.Info calldata redemptionTx,\n BitcoinTx.Proof calldata redemptionProof,\n BitcoinTx.UTXO calldata mainUtxo,\n bytes20 walletPubKeyHash\n ) external {\n // Wallet state validation is performed in the `resolveRedeemingWallet`\n // function.\n\n // The actual transaction proof is performed here. After that point, we\n // can assume the transaction happened on Bitcoin chain and has\n // a sufficient number of confirmations as determined by\n // `txProofDifficultyFactor` constant.\n bytes32 redemptionTxHash = self.validateProof(\n redemptionTx,\n redemptionProof\n );\n\n Wallets.Wallet storage wallet = resolveRedeemingWallet(\n self,\n walletPubKeyHash,\n mainUtxo\n );\n\n // Process the redemption transaction input. Specifically, check if it\n // refers to the expected wallet's main UTXO.\n OutboundTx.processWalletOutboundTxInput(\n self,\n redemptionTx.inputVector,\n mainUtxo\n );\n\n // Process redemption transaction outputs to extract some info required\n // for further processing.\n RedemptionTxOutputsInfo memory outputsInfo = processRedemptionTxOutputs(\n self,\n redemptionTx.outputVector,\n walletPubKeyHash\n );\n\n if (outputsInfo.changeValue > 0) {\n // If the change value is grater than zero, it means the change\n // output exists and can be used as new wallet's main UTXO.\n wallet.mainUtxoHash = keccak256(\n abi.encodePacked(\n redemptionTxHash,\n outputsInfo.changeIndex,\n outputsInfo.changeValue\n )\n );\n } else {\n // If the change value is zero, it means the change output doesn't\n // exists and no funds left on the wallet. Delete the main UTXO\n // for that wallet to represent that state in a proper way.\n delete wallet.mainUtxoHash;\n }\n\n wallet.pendingRedemptionsValue -= outputsInfo.totalBurnableValue;\n\n emit RedemptionsCompleted(walletPubKeyHash, redemptionTxHash);\n\n self.bank.decreaseBalance(outputsInfo.totalBurnableValue);\n self.bank.transferBalance(self.treasury, outputsInfo.totalTreasuryFee);\n }\n\n /// @notice Resolves redeeming wallet based on the provided wallet public\n /// key hash. Validates the wallet state and current main UTXO, as\n /// currently known on the Ethereum chain.\n /// @param walletPubKeyHash public key hash of the wallet proving the sweep\n /// Bitcoin transaction.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @return wallet Data of the sweeping wallet.\n /// @dev Requirements:\n /// - Sweeping wallet must be either in Live or MovingFunds state,\n /// - Main UTXO of the redeeming wallet must exists in the storage,\n /// - The passed `mainUTXO` parameter must be equal to the stored one.\n function resolveRedeemingWallet(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata mainUtxo\n ) internal view returns (Wallets.Wallet storage wallet) {\n wallet = self.registeredWallets[walletPubKeyHash];\n\n // Assert that main UTXO for passed wallet exists in storage.\n bytes32 mainUtxoHash = wallet.mainUtxoHash;\n require(mainUtxoHash != bytes32(0), \"No main UTXO for given wallet\");\n\n // Assert that passed main UTXO parameter is the same as in storage and\n // can be used for further processing.\n require(\n keccak256(\n abi.encodePacked(\n mainUtxo.txHash,\n mainUtxo.txOutputIndex,\n mainUtxo.txOutputValue\n )\n ) == mainUtxoHash,\n \"Invalid main UTXO data\"\n );\n\n Wallets.WalletState walletState = wallet.state;\n require(\n walletState == Wallets.WalletState.Live ||\n walletState == Wallets.WalletState.MovingFunds,\n \"Wallet must be in Live or MovingFunds state\"\n );\n }\n\n /// @notice Processes the Bitcoin redemption transaction output vector.\n /// It extracts each output and tries to identify it as a pending\n /// redemption request, reported timed out request, or change.\n /// Reverts if one of the outputs cannot be recognized properly.\n /// This function also marks each request as processed by removing\n /// them from `pendingRedemptions` mapping.\n /// @param redemptionTxOutputVector Bitcoin redemption transaction output\n /// vector. This function assumes vector's structure is valid so it\n /// must be validated using e.g. `BTCUtils.validateVout` function\n /// before it is passed here.\n /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin\n /// HASH160 over the compressed ECDSA public key) of the wallet which\n /// performed the redemption transaction.\n /// @return info Outcomes of the processing.\n function processRedemptionTxOutputs(\n BridgeState.Storage storage self,\n bytes memory redemptionTxOutputVector,\n bytes20 walletPubKeyHash\n ) internal returns (RedemptionTxOutputsInfo memory info) {\n // Determining the total number of redemption transaction outputs in\n // the same way as for number of inputs. See `BitcoinTx.outputVector`\n // docs for more details.\n (\n uint256 outputsCompactSizeUintLength,\n uint256 outputsCount\n ) = redemptionTxOutputVector.parseVarInt();\n\n // To determine the first output starting index, we must jump over\n // the compactSize uint which prepends the output vector. One byte\n // must be added because `BtcUtils.parseVarInt` does not include\n // compactSize uint tag in the returned length.\n //\n // For >= 0 && <= 252, `BTCUtils.determineVarIntDataLengthAt`\n // returns `0`, so we jump over one byte of compactSize uint.\n //\n // For >= 253 && <= 0xffff there is `0xfd` tag,\n // `BTCUtils.determineVarIntDataLengthAt` returns `2` (no\n // tag byte included) so we need to jump over 1+2 bytes of\n // compactSize uint.\n //\n // Please refer `BTCUtils` library and compactSize uint\n // docs in `BitcoinTx` library for more details.\n uint256 outputStartingIndex = 1 + outputsCompactSizeUintLength;\n\n // Calculate the keccak256 for two possible wallet's P2PKH or P2WPKH\n // scripts that can be used to lock the change. This is done upfront to\n // save on gas. Both scripts have a strict format defined by Bitcoin.\n //\n // The P2PKH script has the byte format: <0x1976a914> <20-byte PKH> <0x88ac>.\n // According to https://en.bitcoin.it/wiki/Script#Opcodes this translates to:\n // - 0x19: Byte length of the entire script\n // - 0x76: OP_DUP\n // - 0xa9: OP_HASH160\n // - 0x14: Byte length of the public key hash\n // - 0x88: OP_EQUALVERIFY\n // - 0xac: OP_CHECKSIG\n // which matches the P2PKH structure as per:\n // https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash\n bytes32 walletP2PKHScriptKeccak = keccak256(\n abi.encodePacked(BitcoinTx.makeP2PKHScript(walletPubKeyHash))\n );\n // The P2WPKH script has the byte format: <0x160014> <20-byte PKH>.\n // According to https://en.bitcoin.it/wiki/Script#Opcodes this translates to:\n // - 0x16: Byte length of the entire script\n // - 0x00: OP_0\n // - 0x14: Byte length of the public key hash\n // which matches the P2WPKH structure as per:\n // https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#P2WPKH\n bytes32 walletP2WPKHScriptKeccak = keccak256(\n abi.encodePacked(BitcoinTx.makeP2WPKHScript(walletPubKeyHash))\n );\n\n return\n processRedemptionTxOutputs(\n self,\n redemptionTxOutputVector,\n walletPubKeyHash,\n RedemptionTxOutputsProcessingInfo(\n outputStartingIndex,\n outputsCount,\n walletP2PKHScriptKeccak,\n walletP2WPKHScriptKeccak\n )\n );\n }\n\n /// @notice Processes all outputs from the redemption transaction. Tries to\n /// identify output as a change output, pending redemption request\n /// or reported redemption. Reverts if one of the outputs cannot be\n /// recognized properly. Marks each request as processed by removing\n /// them from `pendingRedemptions` mapping.\n /// @param redemptionTxOutputVector Bitcoin redemption transaction output\n /// vector. This function assumes vector's structure is valid so it\n /// must be validated using e.g. `BTCUtils.validateVout` function\n /// before it is passed here.\n /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin\n /// HASH160 over the compressed ECDSA public key) of the wallet which\n /// performed the redemption transaction.\n /// @param processInfo RedemptionTxOutputsProcessingInfo identifying output\n /// starting index, the number of outputs and possible wallet change\n /// P2PKH and P2WPKH scripts.\n function processRedemptionTxOutputs(\n BridgeState.Storage storage self,\n bytes memory redemptionTxOutputVector,\n bytes20 walletPubKeyHash,\n RedemptionTxOutputsProcessingInfo memory processInfo\n ) internal returns (RedemptionTxOutputsInfo memory resultInfo) {\n // Helper flag indicating whether there was at least one redemption\n // output present (redemption must be either pending or reported as\n // timed out).\n bool redemptionPresent = false;\n\n // Outputs processing loop.\n for (uint256 i = 0; i < processInfo.outputsCount; i++) {\n uint256 outputLength = redemptionTxOutputVector\n .determineOutputLengthAt(processInfo.outputStartingIndex);\n\n // Extract the value from given output.\n uint64 outputValue = redemptionTxOutputVector.extractValueAt(\n processInfo.outputStartingIndex\n );\n\n // The output consists of an 8-byte value and a variable length\n // script. To hash that script we slice the output starting from\n // 9th byte until the end.\n uint256 scriptLength = outputLength - 8;\n uint256 outputScriptStart = processInfo.outputStartingIndex + 8;\n\n bytes32 outputScriptHash;\n /* solhint-disable-next-line no-inline-assembly */\n assembly {\n // The first argument to assembly keccak256 is the pointer.\n // We point to `redemptionTxOutputVector` but at the position\n // indicated by `outputScriptStart`. To load that position, we\n // need to call `add(outputScriptStart, 32)` because\n // `outputScriptStart` has 32 bytes.\n outputScriptHash := keccak256(\n add(redemptionTxOutputVector, add(outputScriptStart, 32)),\n scriptLength\n )\n }\n\n if (\n resultInfo.changeValue == 0 &&\n (outputScriptHash == processInfo.walletP2PKHScriptKeccak ||\n outputScriptHash == processInfo.walletP2WPKHScriptKeccak) &&\n outputValue > 0\n ) {\n // If we entered here, that means the change output with a\n // proper non-zero value was found.\n resultInfo.changeIndex = uint32(i);\n resultInfo.changeValue = outputValue;\n } else {\n // If we entered here, that the means the given output is\n // supposed to represent a redemption.\n (\n uint64 burnableValue,\n uint64 treasuryFee\n ) = processNonChangeRedemptionTxOutput(\n self,\n _getRedemptionKey(walletPubKeyHash, outputScriptHash),\n outputValue\n );\n resultInfo.totalBurnableValue += burnableValue;\n resultInfo.totalTreasuryFee += treasuryFee;\n redemptionPresent = true;\n }\n\n // Make the `outputStartingIndex` pointing to the next output by\n // increasing it by current output's length.\n processInfo.outputStartingIndex += outputLength;\n }\n\n // Protect against the cases when there is only a single change output\n // referring back to the wallet PKH and just burning main UTXO value\n // for transaction fees.\n require(\n redemptionPresent,\n \"Redemption transaction must process at least one redemption\"\n );\n }\n\n /// @notice Processes a single redemption transaction output. Tries to\n /// identify output as a pending redemption request or reported\n /// redemption timeout. Output script passed to this function must\n /// not be the change output. Such output needs to be identified\n /// separately before calling this function.\n /// Reverts if output is neither requested pending redemption nor\n /// requested and reported timed-out redemption.\n /// This function also marks each pending request as processed by\n /// removing them from `pendingRedemptions` mapping.\n /// @param redemptionKey Redemption key of the output being processed.\n /// @param outputValue Value of the output being processed.\n /// @return burnableValue The value burnable as a result of processing this\n /// single redemption output. This value needs to be summed up with\n /// burnable values of all other outputs to evaluate total burnable\n /// value for the entire redemption transaction. This value is 0\n /// for a timed-out redemption request.\n /// @return treasuryFee The treasury fee from this single redemption output.\n /// This value needs to be summed up with treasury fees of all other\n /// outputs to evaluate the total treasury fee for the entire\n /// redemption transaction. This value is 0 for a timed-out\n /// redemption request.\n /// @dev Requirements:\n /// - This function should be called only if the given output\n /// represents redemption. It must not be the change output.\n function processNonChangeRedemptionTxOutput(\n BridgeState.Storage storage self,\n uint256 redemptionKey,\n uint64 outputValue\n ) internal returns (uint64 burnableValue, uint64 treasuryFee) {\n if (self.pendingRedemptions[redemptionKey].requestedAt != 0) {\n // If we entered here, that means the output was identified\n // as a pending redemption request.\n RedemptionRequest storage request = self.pendingRedemptions[\n redemptionKey\n ];\n // Compute the request's redeemable amount as the requested\n // amount reduced by the treasury fee. The request's\n // minimal amount is then the redeemable amount reduced by\n // the maximum transaction fee.\n uint64 redeemableAmount = request.requestedAmount -\n request.treasuryFee;\n // Output value must fit between the request's redeemable\n // and minimal amounts to be deemed valid.\n require(\n redeemableAmount - request.txMaxFee <= outputValue &&\n outputValue <= redeemableAmount,\n \"Output value is not within the acceptable range of the pending request\"\n );\n // Add the redeemable amount to the total burnable value\n // the Bridge will use to decrease its balance in the Bank.\n burnableValue = redeemableAmount;\n // Add the request's treasury fee to the total treasury fee\n // value the Bridge will transfer to the treasury.\n treasuryFee = request.treasuryFee;\n // Request was properly handled so remove its redemption\n // key from the mapping to make it reusable for further\n // requests.\n delete self.pendingRedemptions[redemptionKey];\n } else {\n // If we entered here, the output is not a redemption\n // request but there is still a chance the given output is\n // related to a reported timed out redemption request.\n // If so, check if the output value matches the request\n // amount to confirm this is an overdue request fulfillment\n // then bypass this output and process the subsequent\n // ones. That also means the wallet was already punished\n // for the inactivity. Otherwise, just revert.\n RedemptionRequest storage request = self.timedOutRedemptions[\n redemptionKey\n ];\n\n require(\n request.requestedAt != 0,\n \"Output is a non-requested redemption\"\n );\n\n uint64 redeemableAmount = request.requestedAmount -\n request.treasuryFee;\n\n require(\n redeemableAmount - request.txMaxFee <= outputValue &&\n outputValue <= redeemableAmount,\n \"Output value is not within the acceptable range of the timed out request\"\n );\n }\n }\n\n /// @notice Notifies that there is a pending redemption request associated\n /// with the given wallet, that has timed out. The redemption\n /// request is identified by the key built as\n /// `keccak256(keccak256(redeemerOutputScript) | walletPubKeyHash)`.\n /// The results of calling this function:\n /// - the pending redemptions value for the wallet will be decreased\n /// by the requested amount (minus treasury fee),\n /// - the tokens taken from the redeemer on redemption request will\n /// be returned to the redeemer,\n /// - the request will be moved from pending redemptions to\n /// timed-out redemptions,\n /// - if the state of the wallet is `Live` or `MovingFunds`, the\n /// wallet operators will be slashed and the notifier will be\n /// rewarded,\n /// - if the state of wallet is `Live`, the wallet will be closed or\n /// marked as `MovingFunds` (depending on the presence or absence\n /// of the wallet's main UTXO) and the wallet will no longer be\n /// marked as the active wallet (if it was marked as such).\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @param redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH).\n /// @dev Requirements:\n /// - The wallet must be in the Live or MovingFunds or Terminated state,\n /// - The redemption request identified by `walletPubKeyHash` and\n /// `redeemerOutputScript` must exist,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given `walletID`. Those IDs are not directly stored\n /// in the contract for gas efficiency purposes but they can be\n /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`\n /// events of the `WalletRegistry` contract,\n /// - The amount of time defined by `redemptionTimeout` must have\n /// passed since the redemption was requested (the request must be\n /// timed-out).\n function notifyRedemptionTimeout(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n uint32[] calldata walletMembersIDs,\n bytes calldata redeemerOutputScript\n ) external {\n // Wallet state is validated in `notifyWalletRedemptionTimeout`.\n uint256 redemptionKey = getRedemptionKey(\n walletPubKeyHash,\n redeemerOutputScript\n );\n Redemption.RedemptionRequest memory request = self.pendingRedemptions[\n redemptionKey\n ];\n\n require(request.requestedAt > 0, \"Redemption request does not exist\");\n require(\n /* solhint-disable-next-line not-rely-on-time */\n request.requestedAt + self.redemptionTimeout < block.timestamp,\n \"Redemption request has not timed out\"\n );\n\n // Update the wallet's pending redemptions value\n Wallets.Wallet storage wallet = self.registeredWallets[\n walletPubKeyHash\n ];\n wallet.pendingRedemptionsValue -=\n request.requestedAmount -\n request.treasuryFee;\n\n // It is worth noting that there is no need to check if\n // `timedOutRedemption` mapping already contains the given redemption\n // key. There is no possibility to re-use a key of a reported timed-out\n // redemption because the wallet responsible for causing the timeout is\n // moved to a state that prevents it to receive new redemption requests.\n\n // Propagate timeout consequences to the wallet\n self.notifyWalletRedemptionTimeout(walletPubKeyHash, walletMembersIDs);\n\n // Move the redemption from pending redemptions to timed-out redemptions\n self.timedOutRedemptions[redemptionKey] = request;\n delete self.pendingRedemptions[redemptionKey];\n\n // slither-disable-next-line reentrancy-events\n emit RedemptionTimedOut(walletPubKeyHash, redeemerOutputScript);\n\n // Return the requested amount of tokens to the redeemer\n self.bank.transferBalance(request.redeemer, request.requestedAmount);\n }\n\n /// @notice Calculate redemption key without allocations.\n /// @param walletPubKeyHash the pubkey hash of the wallet.\n /// @param script the output script of the redemption.\n /// @return The key = keccak256(keccak256(script) | walletPubKeyHash).\n function getRedemptionKey(bytes20 walletPubKeyHash, bytes memory script)\n internal\n pure\n returns (uint256)\n {\n bytes32 scriptHash = keccak256(script);\n uint256 key;\n /* solhint-disable-next-line no-inline-assembly */\n assembly {\n mstore(0, scriptHash)\n mstore(32, walletPubKeyHash)\n key := keccak256(0, 52)\n }\n return key;\n }\n\n /// @notice Finish calculating redemption key without allocations.\n /// @param walletPubKeyHash the pubkey hash of the wallet.\n /// @param scriptHash the output script hash of the redemption.\n /// @return The key = keccak256(scriptHash | walletPubKeyHash).\n function _getRedemptionKey(bytes20 walletPubKeyHash, bytes32 scriptHash)\n internal\n pure\n returns (uint256)\n {\n uint256 key;\n /* solhint-disable-next-line no-inline-assembly */\n assembly {\n mstore(0, scriptHash)\n mstore(32, walletPubKeyHash)\n key := keccak256(0, 52)\n }\n return key;\n }\n}\n"
|
|
155
|
+
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\nimport {BTCUtils} from \"@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol\";\nimport {BytesLib} from \"@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol\";\n\nimport \"./BitcoinTx.sol\";\nimport \"./BridgeState.sol\";\nimport \"./Wallets.sol\";\n\nimport \"../bank/Bank.sol\";\n\n/// @notice Aggregates functions common to the redemption transaction proof\n/// validation and to the moving funds transaction proof validation.\nlibrary OutboundTx {\n using BTCUtils for bytes;\n\n /// @notice Checks whether an outbound Bitcoin transaction performed from\n /// the given wallet has an input vector that contains a single\n /// input referring to the wallet's main UTXO. Marks that main UTXO\n /// as correctly spent if the validation succeeds. Reverts otherwise.\n /// There are two outbound transactions from a wallet possible: a\n /// redemption transaction or a moving funds to another wallet\n /// transaction.\n /// @param walletOutboundTxInputVector Bitcoin outbound transaction's input\n /// vector. This function assumes vector's structure is valid so it\n /// must be validated using e.g. `BTCUtils.validateVin` function\n /// before it is passed here.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n function processWalletOutboundTxInput(\n BridgeState.Storage storage self,\n bytes memory walletOutboundTxInputVector,\n BitcoinTx.UTXO calldata mainUtxo\n ) internal {\n // Assert that the single outbound transaction input actually\n // refers to the wallet's main UTXO.\n (\n bytes32 outpointTxHash,\n uint32 outpointIndex\n ) = parseWalletOutboundTxInput(walletOutboundTxInputVector);\n require(\n mainUtxo.txHash == outpointTxHash &&\n mainUtxo.txOutputIndex == outpointIndex,\n \"Outbound transaction input must point to the wallet's main UTXO\"\n );\n\n // Main UTXO used as an input, mark it as spent.\n self.spentMainUTXOs[\n uint256(\n keccak256(\n abi.encodePacked(mainUtxo.txHash, mainUtxo.txOutputIndex)\n )\n )\n ] = true;\n }\n\n /// @notice Parses the input vector of an outbound Bitcoin transaction\n /// performed from the given wallet. It extracts the single input\n /// then the transaction hash and output index from its outpoint.\n /// There are two outbound transactions from a wallet possible: a\n /// redemption transaction or a moving funds to another wallet\n /// transaction.\n /// @param walletOutboundTxInputVector Bitcoin outbound transaction input\n /// vector. This function assumes vector's structure is valid so it\n /// must be validated using e.g. `BTCUtils.validateVin` function\n /// before it is passed here.\n /// @return outpointTxHash 32-byte hash of the Bitcoin transaction which is\n /// pointed in the input's outpoint.\n /// @return outpointIndex 4-byte index of the Bitcoin transaction output\n /// which is pointed in the input's outpoint.\n function parseWalletOutboundTxInput(\n bytes memory walletOutboundTxInputVector\n ) internal pure returns (bytes32 outpointTxHash, uint32 outpointIndex) {\n // To determine the total number of Bitcoin transaction inputs,\n // we need to parse the compactSize uint (VarInt) the input vector is\n // prepended by. That compactSize uint encodes the number of vector\n // elements using the format presented in:\n // https://developer.bitcoin.org/reference/transactions.html#compactsize-unsigned-integers\n // We don't need asserting the compactSize uint is parseable since it\n // was already checked during `validateVin` validation.\n // See `BitcoinTx.inputVector` docs for more details.\n (, uint256 inputsCount) = walletOutboundTxInputVector.parseVarInt();\n require(\n inputsCount == 1,\n \"Outbound transaction must have a single input\"\n );\n\n bytes memory input = walletOutboundTxInputVector.extractInputAtIndex(0);\n\n outpointTxHash = input.extractInputTxIdLE();\n\n outpointIndex = BTCUtils.reverseUint32(\n uint32(input.extractTxIndexLE())\n );\n\n // There is only one input in the transaction. Input has an outpoint\n // field that is a reference to the transaction being spent (see\n // `BitcoinTx` docs). The outpoint contains the hash of the transaction\n // to spend (`outpointTxHash`) and the index of the specific output\n // from that transaction (`outpointIndex`).\n return (outpointTxHash, outpointIndex);\n }\n}\n\n/// @title Bridge redemption\n/// @notice The library handles the logic for redeeming Bitcoin balances from\n/// the Bridge.\n/// @dev To initiate a redemption, a user with a Bank balance supplies\n/// a Bitcoin address. Then, the system calculates the redemption fee, and\n/// releases balance to the provided Bitcoin address. Just like in case of\n/// sweeps of revealed deposits, redemption requests are processed in\n/// batches and require SPV proof to be submitted to the Bridge.\nlibrary Redemption {\n using BridgeState for BridgeState.Storage;\n using Wallets for BridgeState.Storage;\n using BitcoinTx for BridgeState.Storage;\n\n using BTCUtils for bytes;\n using BytesLib for bytes;\n\n /// @notice Represents a redemption request.\n struct RedemptionRequest {\n // ETH address of the redeemer who created the request.\n address redeemer;\n // Requested TBTC amount in satoshi.\n uint64 requestedAmount;\n // Treasury TBTC fee in satoshi at the moment of request creation.\n uint64 treasuryFee;\n // Transaction maximum BTC fee in satoshi at the moment of request\n // creation.\n uint64 txMaxFee;\n // UNIX timestamp the request was created at.\n uint32 requestedAt;\n // This struct doesn't contain `__gap` property as the structure is stored\n // in a mapping, mappings store values in different slots and they are\n // not contiguous with other values.\n }\n\n /// @notice Represents an outcome of the redemption Bitcoin transaction\n /// outputs processing.\n struct RedemptionTxOutputsInfo {\n // Total TBTC value in satoshi that should be burned by the Bridge.\n // It includes the total amount of all BTC redeemed in the transaction\n // and the fee paid to BTC miners for the redemption transaction.\n uint64 totalBurnableValue;\n // Total TBTC value in satoshi that should be transferred to\n // the treasury. It is a sum of all treasury fees paid by all\n // redeemers included in the redemption transaction.\n uint64 totalTreasuryFee;\n // Index of the change output. The change output becomes\n // the new main wallet's UTXO.\n uint32 changeIndex;\n // Value in satoshi of the change output.\n uint64 changeValue;\n // This struct doesn't contain `__gap` property as the structure is not\n // stored, it is used as a function's memory argument.\n }\n\n /// @notice Represents temporary information needed during the processing of\n /// the redemption Bitcoin transaction outputs. This structure is an\n /// internal one and should not be exported outside of the redemption\n /// transaction processing code.\n /// @dev Allows to mitigate \"stack too deep\" errors on EVM.\n struct RedemptionTxOutputsProcessingInfo {\n // The first output starting index in the transaction.\n uint256 outputStartingIndex;\n // The number of outputs in the transaction.\n uint256 outputsCount;\n // P2PKH script for the wallet. Needed to determine the change output.\n bytes32 walletP2PKHScriptKeccak;\n // P2WPKH script for the wallet. Needed to determine the change output.\n bytes32 walletP2WPKHScriptKeccak;\n // This struct doesn't contain `__gap` property as the structure is not\n // stored, it is used as a function's memory argument.\n }\n\n event RedemptionRequested(\n bytes20 indexed walletPubKeyHash,\n bytes redeemerOutputScript,\n address indexed redeemer,\n uint64 requestedAmount,\n uint64 treasuryFee,\n uint64 txMaxFee\n );\n\n event RedemptionsCompleted(\n bytes20 indexed walletPubKeyHash,\n bytes32 redemptionTxHash\n );\n\n event RedemptionTimedOut(\n bytes20 indexed walletPubKeyHash,\n bytes redeemerOutputScript\n );\n\n /// @notice Requests redemption of the given amount from the specified\n /// wallet to the redeemer Bitcoin output script.\n /// This function handles the simplest case, where balance owner is\n /// the redeemer.\n /// @param walletPubKeyHash The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key).\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param balanceOwner The address of the Bank balance owner whose balance\n /// is getting redeemed. Balance owner address is stored as\n /// a redeemer address who will be able co claim back the Bank\n /// balance if anything goes wrong during the redemption.\n /// @param redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC.\n /// @param amount Requested amount in satoshi. This is also the Bank balance\n /// that is taken from the `balanceOwner` upon request.\n /// Once the request is handled, the actual amount of BTC locked\n /// on the redeemer output script will be always lower than this value\n /// since the treasury and Bitcoin transaction fees must be incurred.\n /// The minimal amount satisfying the request can be computed as:\n /// `amount - (amount / redemptionTreasuryFeeDivisor) - redemptionTxMaxFee`.\n /// Fees values are taken at the moment of request creation.\n /// @dev Requirements:\n /// - Wallet behind `walletPubKeyHash` must be live,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain,\n /// - `redeemerOutputScript` must be a proper Bitcoin script,\n /// - `redeemerOutputScript` cannot have wallet PKH as payload,\n /// - `amount` must be above or equal the `redemptionDustThreshold`,\n /// - Given `walletPubKeyHash` and `redeemerOutputScript` pair can be\n /// used for only one pending request at the same time,\n /// - Wallet must have enough Bitcoin balance to proceed the request,\n /// - Balance owner must make an allowance in the Bank that the Bridge\n /// contract can spend the given `amount`.\n function requestRedemption(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata mainUtxo,\n address balanceOwner,\n bytes calldata redeemerOutputScript,\n uint64 amount\n ) external {\n requestRedemption(\n self,\n walletPubKeyHash,\n mainUtxo,\n balanceOwner,\n balanceOwner,\n redeemerOutputScript,\n amount\n );\n }\n\n /// @notice Requests redemption of the given amount from the specified\n /// wallet to the redeemer Bitcoin output script. Used by\n /// `Bridge.receiveBalanceApproval`. Can handle more complex cases\n /// where balance owner may be someone else than the redeemer.\n /// @param balanceOwner The address of the Bank balance owner whose balance\n /// is getting redeemed.\n /// @param amount Requested amount in satoshi. This is also the Bank balance\n /// that is taken from the `balanceOwner` upon request.\n /// Once the request is handled, the actual amount of BTC locked\n /// on the redeemer output script will be always lower than this value\n /// since the treasury and Bitcoin transaction fees must be incurred.\n /// The minimal amount satisfying the request can be computed as:\n /// `amount - (amount / redemptionTreasuryFeeDivisor) - redemptionTxMaxFee`.\n /// Fees values are taken at the moment of request creation.\n /// @param redemptionData ABI-encoded redemption data:\n /// [\n /// address redeemer,\n /// bytes20 walletPubKeyHash,\n /// bytes32 mainUtxoTxHash,\n /// uint32 mainUtxoTxOutputIndex,\n /// uint64 mainUtxoTxOutputValue,\n /// bytes redeemerOutputScript\n /// ]\n ///\n /// - redeemer: The Ethereum address of the redeemer who will be able\n /// to claim Bank balance if anything goes wrong during the redemption.\n /// In the most basic case, when someone redeems their Bitcoin\n /// balance from the Bank, `balanceOwner` is the same as `redeemer`.\n /// However, when a Vault is redeeming part of its balance for some\n /// redeemer address (for example, someone who has earlier deposited\n /// into that Vault), `balanceOwner` is the Vault, and `redeemer` is\n /// the address for which the vault is redeeming its balance to,\n /// - walletPubKeyHash: The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key),\n /// - mainUtxoTxHash: Data of the wallet's main UTXO TX hash, as\n /// currently known on the Ethereum chain,\n /// - mainUtxoTxOutputIndex: Data of the wallet's main UTXO output\n /// index, as currently known on Ethereum chain,\n /// - mainUtxoTxOutputValue: Data of the wallet's main UTXO output\n /// value, as currently known on Ethereum chain,\n /// - redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC.\n /// @dev Requirements:\n /// - Wallet behind `walletPubKeyHash` must be live,\n /// - `mainUtxo*` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain,\n /// - `redeemerOutputScript` must be a proper Bitcoin script,\n /// - `redeemerOutputScript` cannot have wallet PKH as payload,\n /// - `amount` must be above or equal the `redemptionDustThreshold`,\n /// - Given `walletPubKeyHash` and `redeemerOutputScript` pair can be\n /// used for only one pending request at the same time,\n /// - Wallet must have enough Bitcoin balance to proceed the request,\n /// - Balance owner must make an allowance in the Bank that the Bridge\n /// contract can spend the given `amount`.\n function requestRedemption(\n BridgeState.Storage storage self,\n address balanceOwner,\n uint64 amount,\n bytes calldata redemptionData\n ) external {\n (\n address redeemer,\n bytes20 walletPubKeyHash,\n bytes32 mainUtxoTxHash,\n uint32 mainUtxoTxOutputIndex,\n uint64 mainUtxoTxOutputValue,\n bytes memory redeemerOutputScript\n ) = abi.decode(\n redemptionData,\n (address, bytes20, bytes32, uint32, uint64, bytes)\n );\n\n requestRedemption(\n self,\n walletPubKeyHash,\n BitcoinTx.UTXO(\n mainUtxoTxHash,\n mainUtxoTxOutputIndex,\n mainUtxoTxOutputValue\n ),\n balanceOwner,\n redeemer,\n redeemerOutputScript,\n amount\n );\n }\n\n /// @notice Requests redemption of the given amount from the specified\n /// wallet to the redeemer Bitcoin output script.\n /// @param walletPubKeyHash The 20-byte wallet public key hash (computed\n /// using Bitcoin HASH160 over the compressed ECDSA public key).\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param balanceOwner The address of the Bank balance owner whose balance\n /// is getting redeemed.\n /// @param redeemer The Ethereum address of the redeemer who will be able to\n /// claim Bank balance if anything goes wrong during the redemption.\n /// In the most basic case, when someone redeems their Bitcoin\n /// balance from the Bank, `balanceOwner` is the same as `redeemer`.\n /// However, when a Vault is redeeming part of its balance for some\n /// redeemer address (for example, someone who has earlier deposited\n /// into that Vault), `balanceOwner` is the Vault, and `redeemer` is\n /// the address for which the vault is redeeming its balance to.\n /// @param redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH) that will be used to lock\n /// redeemed BTC.\n /// @param amount Requested amount in satoshi. This is also the Bank balance\n /// that is taken from the `balanceOwner` upon request.\n /// Once the request is handled, the actual amount of BTC locked\n /// on the redeemer output script will be always lower than this value\n /// since the treasury and Bitcoin transaction fees must be incurred.\n /// The minimal amount satisfying the request can be computed as:\n /// `amount - (amount / redemptionTreasuryFeeDivisor) - redemptionTxMaxFee`.\n /// Fees values are taken at the moment of request creation.\n /// @dev Requirements:\n /// - Wallet behind `walletPubKeyHash` must be live,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain,\n /// - `redeemerOutputScript` must be a proper Bitcoin script,\n /// - `redeemerOutputScript` cannot have wallet PKH as payload,\n /// - `amount` must be above or equal the `redemptionDustThreshold`,\n /// - Given `walletPubKeyHash` and `redeemerOutputScript` pair can be\n /// used for only one pending request at the same time,\n /// - Wallet must have enough Bitcoin balance to proceed the request,\n /// - Balance owner must make an allowance in the Bank that the Bridge\n /// contract can spend the given `amount`.\n function requestRedemption(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO memory mainUtxo,\n address balanceOwner,\n address redeemer,\n bytes memory redeemerOutputScript,\n uint64 amount\n ) internal {\n Wallets.Wallet storage wallet = self.registeredWallets[\n walletPubKeyHash\n ];\n\n require(\n wallet.state == Wallets.WalletState.Live,\n \"Wallet must be in Live state\"\n );\n\n bytes32 mainUtxoHash = wallet.mainUtxoHash;\n require(\n mainUtxoHash != bytes32(0),\n \"No main UTXO for the given wallet\"\n );\n require(\n keccak256(\n abi.encodePacked(\n mainUtxo.txHash,\n mainUtxo.txOutputIndex,\n mainUtxo.txOutputValue\n )\n ) == mainUtxoHash,\n \"Invalid main UTXO data\"\n );\n\n // Validate if redeemer output script is a correct standard type\n // (P2PKH, P2WPKH, P2SH or P2WSH). This is done by using\n // `BTCUtils.extractHashAt` on it. Such a function extracts the payload\n // properly only from standard outputs so if it succeeds, we have a\n // guarantee the redeemer output script is proper. The underlying way\n // of validation is the same as in tBTC v1.\n bytes memory redeemerOutputScriptPayload = redeemerOutputScript\n .extractHashAt(0, redeemerOutputScript.length);\n\n require(\n redeemerOutputScriptPayload.length > 0,\n \"Redeemer output script must be a standard type\"\n );\n // Check if the redeemer output script payload does not point to the\n // wallet public key hash.\n require(\n redeemerOutputScriptPayload.length != 20 ||\n walletPubKeyHash != redeemerOutputScriptPayload.slice20(0),\n \"Redeemer output script must not point to the wallet PKH\"\n );\n\n require(\n amount >= self.redemptionDustThreshold,\n \"Redemption amount too small\"\n );\n\n // The redemption key is built on top of the wallet public key hash\n // and redeemer output script pair. That means there can be only one\n // request asking for redemption from the given wallet to the given\n // BTC script at the same time.\n uint256 redemptionKey = getRedemptionKey(\n walletPubKeyHash,\n redeemerOutputScript\n );\n\n // Check if given redemption key is not used by a pending redemption.\n // There is no need to check for existence in `timedOutRedemptions`\n // since the wallet's state is changed to other than Live after\n // first time out is reported so making new requests is not possible.\n // slither-disable-next-line incorrect-equality\n require(\n self.pendingRedemptions[redemptionKey].requestedAt == 0,\n \"There is a pending redemption request from this wallet to the same address\"\n );\n\n // No need to check whether `amount - treasuryFee - txMaxFee > 0`\n // since the `redemptionDustThreshold` should force that condition\n // to be always true.\n uint64 treasuryFee = self.redemptionTreasuryFeeDivisor > 0\n ? amount / self.redemptionTreasuryFeeDivisor\n : 0;\n uint64 txMaxFee = self.redemptionTxMaxFee;\n\n // The main wallet UTXO's value doesn't include all pending redemptions.\n // To determine if the requested redemption can be performed by the\n // wallet we need to subtract the total value of all pending redemptions\n // from that wallet's main UTXO value. Given that the treasury fee is\n // not redeemed from the wallet, we are subtracting it.\n wallet.pendingRedemptionsValue += amount - treasuryFee;\n require(\n mainUtxo.txOutputValue >= wallet.pendingRedemptionsValue,\n \"Insufficient wallet funds\"\n );\n\n self.pendingRedemptions[redemptionKey] = RedemptionRequest(\n redeemer,\n amount,\n treasuryFee,\n txMaxFee,\n /* solhint-disable-next-line not-rely-on-time */\n uint32(block.timestamp)\n );\n\n // slither-disable-next-line reentrancy-events\n emit RedemptionRequested(\n walletPubKeyHash,\n redeemerOutputScript,\n redeemer,\n amount,\n treasuryFee,\n txMaxFee\n );\n\n self.bank.transferBalanceFrom(balanceOwner, address(this), amount);\n }\n\n /// @notice Used by the wallet to prove the BTC redemption transaction\n /// and to make the necessary bookkeeping. Redemption is only\n /// accepted if it satisfies SPV proof.\n ///\n /// The function is performing Bank balance updates by burning\n /// the total redeemed Bitcoin amount from Bridge balance and\n /// transferring the treasury fee sum to the treasury address.\n ///\n /// It is possible to prove the given redemption only one time.\n /// @param redemptionTx Bitcoin redemption transaction data.\n /// @param redemptionProof Bitcoin redemption proof data.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin\n /// HASH160 over the compressed ECDSA public key) of the wallet which\n /// performed the redemption transaction.\n /// @dev Requirements:\n /// - `redemptionTx` components must match the expected structure. See\n /// `BitcoinTx.Info` docs for reference. Their values must exactly\n /// correspond to appropriate Bitcoin transaction fields to produce\n /// a provable transaction hash,\n /// - The `redemptionTx` should represent a Bitcoin transaction with\n /// exactly 1 input that refers to the wallet's main UTXO. That\n /// transaction should have 1..n outputs handling existing pending\n /// redemption requests or pointing to reported timed out requests.\n /// There can be also 1 optional output representing the\n /// change and pointing back to the 20-byte wallet public key hash.\n /// The change should be always present if the redeemed value sum\n /// is lower than the total wallet's BTC balance,\n /// - `redemptionProof` components must match the expected structure.\n /// See `BitcoinTx.Proof` docs for reference. The `bitcoinHeaders`\n /// field must contain a valid number of block headers, not less\n /// than the `txProofDifficultyFactor` contract constant,\n /// - `mainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// Additionally, the recent main UTXO on Ethereum must be set,\n /// - `walletPubKeyHash` must be connected with the main UTXO used\n /// as transaction single input.\n /// Other remarks:\n /// - Putting the change output as the first transaction output can\n /// save some gas because the output processing loop begins each\n /// iteration by checking whether the given output is the change\n /// thus uses some gas for making the comparison. Once the change\n /// is identified, that check is omitted in further iterations.\n function submitRedemptionProof(\n BridgeState.Storage storage self,\n BitcoinTx.Info calldata redemptionTx,\n BitcoinTx.Proof calldata redemptionProof,\n BitcoinTx.UTXO calldata mainUtxo,\n bytes20 walletPubKeyHash\n ) external {\n // Wallet state validation is performed in the `resolveRedeemingWallet`\n // function.\n\n // The actual transaction proof is performed here. After that point, we\n // can assume the transaction happened on Bitcoin chain and has\n // a sufficient number of confirmations as determined by\n // `txProofDifficultyFactor` constant.\n bytes32 redemptionTxHash = self.validateProof(\n redemptionTx,\n redemptionProof\n );\n\n Wallets.Wallet storage wallet = resolveRedeemingWallet(\n self,\n walletPubKeyHash,\n mainUtxo\n );\n\n // Process the redemption transaction input. Specifically, check if it\n // refers to the expected wallet's main UTXO.\n OutboundTx.processWalletOutboundTxInput(\n self,\n redemptionTx.inputVector,\n mainUtxo\n );\n\n // Process redemption transaction outputs to extract some info required\n // for further processing.\n RedemptionTxOutputsInfo memory outputsInfo = processRedemptionTxOutputs(\n self,\n redemptionTx.outputVector,\n walletPubKeyHash\n );\n\n if (outputsInfo.changeValue > 0) {\n // If the change value is grater than zero, it means the change\n // output exists and can be used as new wallet's main UTXO.\n wallet.mainUtxoHash = keccak256(\n abi.encodePacked(\n redemptionTxHash,\n outputsInfo.changeIndex,\n outputsInfo.changeValue\n )\n );\n } else {\n // If the change value is zero, it means the change output doesn't\n // exists and no funds left on the wallet. Delete the main UTXO\n // for that wallet to represent that state in a proper way.\n delete wallet.mainUtxoHash;\n }\n\n wallet.pendingRedemptionsValue -= outputsInfo.totalBurnableValue;\n\n emit RedemptionsCompleted(walletPubKeyHash, redemptionTxHash);\n\n self.bank.decreaseBalance(outputsInfo.totalBurnableValue);\n self.bank.transferBalance(self.treasury, outputsInfo.totalTreasuryFee);\n }\n\n /// @notice Resolves redeeming wallet based on the provided wallet public\n /// key hash. Validates the wallet state and current main UTXO, as\n /// currently known on the Ethereum chain.\n /// @param walletPubKeyHash public key hash of the wallet proving the sweep\n /// Bitcoin transaction.\n /// @param mainUtxo Data of the wallet's main UTXO, as currently known on\n /// the Ethereum chain.\n /// @return wallet Data of the sweeping wallet.\n /// @dev Requirements:\n /// - Sweeping wallet must be either in Live or MovingFunds state,\n /// - Main UTXO of the redeeming wallet must exists in the storage,\n /// - The passed `mainUTXO` parameter must be equal to the stored one.\n function resolveRedeemingWallet(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata mainUtxo\n ) internal view returns (Wallets.Wallet storage wallet) {\n wallet = self.registeredWallets[walletPubKeyHash];\n\n // Assert that main UTXO for passed wallet exists in storage.\n bytes32 mainUtxoHash = wallet.mainUtxoHash;\n require(mainUtxoHash != bytes32(0), \"No main UTXO for given wallet\");\n\n // Assert that passed main UTXO parameter is the same as in storage and\n // can be used for further processing.\n require(\n keccak256(\n abi.encodePacked(\n mainUtxo.txHash,\n mainUtxo.txOutputIndex,\n mainUtxo.txOutputValue\n )\n ) == mainUtxoHash,\n \"Invalid main UTXO data\"\n );\n\n Wallets.WalletState walletState = wallet.state;\n require(\n walletState == Wallets.WalletState.Live ||\n walletState == Wallets.WalletState.MovingFunds,\n \"Wallet must be in Live or MovingFunds state\"\n );\n }\n\n /// @notice Processes the Bitcoin redemption transaction output vector.\n /// It extracts each output and tries to identify it as a pending\n /// redemption request, reported timed out request, or change.\n /// Reverts if one of the outputs cannot be recognized properly.\n /// This function also marks each request as processed by removing\n /// them from `pendingRedemptions` mapping.\n /// @param redemptionTxOutputVector Bitcoin redemption transaction output\n /// vector. This function assumes vector's structure is valid so it\n /// must be validated using e.g. `BTCUtils.validateVout` function\n /// before it is passed here.\n /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin\n /// HASH160 over the compressed ECDSA public key) of the wallet which\n /// performed the redemption transaction.\n /// @return info Outcomes of the processing.\n function processRedemptionTxOutputs(\n BridgeState.Storage storage self,\n bytes memory redemptionTxOutputVector,\n bytes20 walletPubKeyHash\n ) internal returns (RedemptionTxOutputsInfo memory info) {\n // Determining the total number of redemption transaction outputs in\n // the same way as for number of inputs. See `BitcoinTx.outputVector`\n // docs for more details.\n (\n uint256 outputsCompactSizeUintLength,\n uint256 outputsCount\n ) = redemptionTxOutputVector.parseVarInt();\n\n // To determine the first output starting index, we must jump over\n // the compactSize uint which prepends the output vector. One byte\n // must be added because `BtcUtils.parseVarInt` does not include\n // compactSize uint tag in the returned length.\n //\n // For >= 0 && <= 252, `BTCUtils.determineVarIntDataLengthAt`\n // returns `0`, so we jump over one byte of compactSize uint.\n //\n // For >= 253 && <= 0xffff there is `0xfd` tag,\n // `BTCUtils.determineVarIntDataLengthAt` returns `2` (no\n // tag byte included) so we need to jump over 1+2 bytes of\n // compactSize uint.\n //\n // Please refer `BTCUtils` library and compactSize uint\n // docs in `BitcoinTx` library for more details.\n uint256 outputStartingIndex = 1 + outputsCompactSizeUintLength;\n\n // Calculate the keccak256 for two possible wallet's P2PKH or P2WPKH\n // scripts that can be used to lock the change. This is done upfront to\n // save on gas. Both scripts have a strict format defined by Bitcoin.\n //\n // The P2PKH script has the byte format: <0x1976a914> <20-byte PKH> <0x88ac>.\n // According to https://en.bitcoin.it/wiki/Script#Opcodes this translates to:\n // - 0x19: Byte length of the entire script\n // - 0x76: OP_DUP\n // - 0xa9: OP_HASH160\n // - 0x14: Byte length of the public key hash\n // - 0x88: OP_EQUALVERIFY\n // - 0xac: OP_CHECKSIG\n // which matches the P2PKH structure as per:\n // https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash\n bytes32 walletP2PKHScriptKeccak = keccak256(\n abi.encodePacked(BitcoinTx.makeP2PKHScript(walletPubKeyHash))\n );\n // The P2WPKH script has the byte format: <0x160014> <20-byte PKH>.\n // According to https://en.bitcoin.it/wiki/Script#Opcodes this translates to:\n // - 0x16: Byte length of the entire script\n // - 0x00: OP_0\n // - 0x14: Byte length of the public key hash\n // which matches the P2WPKH structure as per:\n // https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#P2WPKH\n bytes32 walletP2WPKHScriptKeccak = keccak256(\n abi.encodePacked(BitcoinTx.makeP2WPKHScript(walletPubKeyHash))\n );\n\n return\n processRedemptionTxOutputs(\n self,\n redemptionTxOutputVector,\n walletPubKeyHash,\n RedemptionTxOutputsProcessingInfo(\n outputStartingIndex,\n outputsCount,\n walletP2PKHScriptKeccak,\n walletP2WPKHScriptKeccak\n )\n );\n }\n\n /// @notice Processes all outputs from the redemption transaction. Tries to\n /// identify output as a change output, pending redemption request\n /// or reported redemption. Reverts if one of the outputs cannot be\n /// recognized properly. Marks each request as processed by removing\n /// them from `pendingRedemptions` mapping.\n /// @param redemptionTxOutputVector Bitcoin redemption transaction output\n /// vector. This function assumes vector's structure is valid so it\n /// must be validated using e.g. `BTCUtils.validateVout` function\n /// before it is passed here.\n /// @param walletPubKeyHash 20-byte public key hash (computed using Bitcoin\n /// HASH160 over the compressed ECDSA public key) of the wallet which\n /// performed the redemption transaction.\n /// @param processInfo RedemptionTxOutputsProcessingInfo identifying output\n /// starting index, the number of outputs and possible wallet change\n /// P2PKH and P2WPKH scripts.\n function processRedemptionTxOutputs(\n BridgeState.Storage storage self,\n bytes memory redemptionTxOutputVector,\n bytes20 walletPubKeyHash,\n RedemptionTxOutputsProcessingInfo memory processInfo\n ) internal returns (RedemptionTxOutputsInfo memory resultInfo) {\n // Helper flag indicating whether there was at least one redemption\n // output present (redemption must be either pending or reported as\n // timed out).\n bool redemptionPresent = false;\n\n // Outputs processing loop.\n for (uint256 i = 0; i < processInfo.outputsCount; i++) {\n uint256 outputLength = redemptionTxOutputVector\n .determineOutputLengthAt(processInfo.outputStartingIndex);\n\n // Extract the value from given output.\n uint64 outputValue = redemptionTxOutputVector.extractValueAt(\n processInfo.outputStartingIndex\n );\n\n // The output consists of an 8-byte value and a variable length\n // script. To hash that script we slice the output starting from\n // 9th byte until the end.\n uint256 scriptLength = outputLength - 8;\n uint256 outputScriptStart = processInfo.outputStartingIndex + 8;\n\n bytes32 outputScriptHash;\n /* solhint-disable-next-line no-inline-assembly */\n assembly {\n // The first argument to assembly keccak256 is the pointer.\n // We point to `redemptionTxOutputVector` but at the position\n // indicated by `outputScriptStart`. To load that position, we\n // need to call `add(outputScriptStart, 32)` because\n // `outputScriptStart` has 32 bytes.\n outputScriptHash := keccak256(\n add(redemptionTxOutputVector, add(outputScriptStart, 32)),\n scriptLength\n )\n }\n\n if (\n resultInfo.changeValue == 0 &&\n (outputScriptHash == processInfo.walletP2PKHScriptKeccak ||\n outputScriptHash == processInfo.walletP2WPKHScriptKeccak) &&\n outputValue > 0\n ) {\n // If we entered here, that means the change output with a\n // proper non-zero value was found.\n resultInfo.changeIndex = uint32(i);\n resultInfo.changeValue = outputValue;\n } else {\n // If we entered here, that the means the given output is\n // supposed to represent a redemption.\n (\n uint64 burnableValue,\n uint64 treasuryFee\n ) = processNonChangeRedemptionTxOutput(\n self,\n _getRedemptionKey(walletPubKeyHash, outputScriptHash),\n outputValue\n );\n resultInfo.totalBurnableValue += burnableValue;\n resultInfo.totalTreasuryFee += treasuryFee;\n redemptionPresent = true;\n }\n\n // Make the `outputStartingIndex` pointing to the next output by\n // increasing it by current output's length.\n processInfo.outputStartingIndex += outputLength;\n }\n\n // Protect against the cases when there is only a single change output\n // referring back to the wallet PKH and just burning main UTXO value\n // for transaction fees.\n require(\n redemptionPresent,\n \"Redemption transaction must process at least one redemption\"\n );\n }\n\n /// @notice Processes a single redemption transaction output. Tries to\n /// identify output as a pending redemption request or reported\n /// redemption timeout. Output script passed to this function must\n /// not be the change output. Such output needs to be identified\n /// separately before calling this function.\n /// Reverts if output is neither requested pending redemption nor\n /// requested and reported timed-out redemption.\n /// This function also marks each pending request as processed by\n /// removing them from `pendingRedemptions` mapping.\n /// @param redemptionKey Redemption key of the output being processed.\n /// @param outputValue Value of the output being processed.\n /// @return burnableValue The value burnable as a result of processing this\n /// single redemption output. This value needs to be summed up with\n /// burnable values of all other outputs to evaluate total burnable\n /// value for the entire redemption transaction. This value is 0\n /// for a timed-out redemption request.\n /// @return treasuryFee The treasury fee from this single redemption output.\n /// This value needs to be summed up with treasury fees of all other\n /// outputs to evaluate the total treasury fee for the entire\n /// redemption transaction. This value is 0 for a timed-out\n /// redemption request.\n /// @dev Requirements:\n /// - This function should be called only if the given output\n /// represents redemption. It must not be the change output.\n function processNonChangeRedemptionTxOutput(\n BridgeState.Storage storage self,\n uint256 redemptionKey,\n uint64 outputValue\n ) internal returns (uint64 burnableValue, uint64 treasuryFee) {\n if (self.pendingRedemptions[redemptionKey].requestedAt != 0) {\n // If we entered here, that means the output was identified\n // as a pending redemption request.\n RedemptionRequest storage request = self.pendingRedemptions[\n redemptionKey\n ];\n // Compute the request's redeemable amount as the requested\n // amount reduced by the treasury fee. The request's\n // minimal amount is then the redeemable amount reduced by\n // the maximum transaction fee.\n uint64 redeemableAmount = request.requestedAmount -\n request.treasuryFee;\n // Output value must fit between the request's redeemable\n // and minimal amounts to be deemed valid.\n require(\n redeemableAmount - request.txMaxFee <= outputValue &&\n outputValue <= redeemableAmount,\n \"Output value is not within the acceptable range of the pending request\"\n );\n // Add the redeemable amount to the total burnable value\n // the Bridge will use to decrease its balance in the Bank.\n burnableValue = redeemableAmount;\n // Add the request's treasury fee to the total treasury fee\n // value the Bridge will transfer to the treasury.\n treasuryFee = request.treasuryFee;\n // Request was properly handled so remove its redemption\n // key from the mapping to make it reusable for further\n // requests.\n delete self.pendingRedemptions[redemptionKey];\n } else {\n // If we entered here, the output is not a redemption\n // request but there is still a chance the given output is\n // related to a reported timed out redemption request.\n // If so, check if the output value matches the request\n // amount to confirm this is an overdue request fulfillment\n // then bypass this output and process the subsequent\n // ones. That also means the wallet was already punished\n // for the inactivity. Otherwise, just revert.\n RedemptionRequest storage request = self.timedOutRedemptions[\n redemptionKey\n ];\n\n require(\n request.requestedAt != 0,\n \"Output is a non-requested redemption\"\n );\n\n uint64 redeemableAmount = request.requestedAmount -\n request.treasuryFee;\n\n require(\n redeemableAmount - request.txMaxFee <= outputValue &&\n outputValue <= redeemableAmount,\n \"Output value is not within the acceptable range of the timed out request\"\n );\n }\n }\n\n /// @notice Notifies that there is a pending redemption request associated\n /// with the given wallet, that has timed out. The redemption\n /// request is identified by the key built as\n /// `keccak256(keccak256(redeemerOutputScript) | walletPubKeyHash)`.\n /// The results of calling this function:\n /// - the pending redemptions value for the wallet will be decreased\n /// by the requested amount (minus treasury fee),\n /// - the tokens taken from the redeemer on redemption request will\n /// be returned to the redeemer,\n /// - the request will be moved from pending redemptions to\n /// timed-out redemptions,\n /// - if the state of the wallet is `Live` or `MovingFunds`, the\n /// wallet operators will be slashed and the notifier will be\n /// rewarded,\n /// - if the state of wallet is `Live`, the wallet will be closed or\n /// marked as `MovingFunds` (depending on the presence or absence\n /// of the wallet's main UTXO) and the wallet will no longer be\n /// marked as the active wallet (if it was marked as such).\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @param redeemerOutputScript The redeemer's length-prefixed output\n /// script (P2PKH, P2WPKH, P2SH or P2WSH).\n /// @dev Requirements:\n /// - The wallet must be in the Live or MovingFunds or Terminated state,\n /// - The redemption request identified by `walletPubKeyHash` and\n /// `redeemerOutputScript` must exist,\n /// - The expression `keccak256(abi.encode(walletMembersIDs))` must\n /// be exactly the same as the hash stored under `membersIdsHash`\n /// for the given `walletID`. Those IDs are not directly stored\n /// in the contract for gas efficiency purposes but they can be\n /// read from appropriate `DkgResultSubmitted` and `DkgResultApproved`\n /// events of the `WalletRegistry` contract,\n /// - The amount of time defined by `redemptionTimeout` must have\n /// passed since the redemption was requested (the request must be\n /// timed-out).\n function notifyRedemptionTimeout(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n uint32[] calldata walletMembersIDs,\n bytes calldata redeemerOutputScript\n ) external {\n // Wallet state is validated in `notifyWalletRedemptionTimeout`.\n uint256 redemptionKey = getRedemptionKey(\n walletPubKeyHash,\n redeemerOutputScript\n );\n Redemption.RedemptionRequest memory request = self.pendingRedemptions[\n redemptionKey\n ];\n\n require(request.requestedAt > 0, \"Redemption request does not exist\");\n require(\n /* solhint-disable-next-line not-rely-on-time */\n request.requestedAt + self.redemptionTimeout < block.timestamp,\n \"Redemption request has not timed out\"\n );\n\n // Update the wallet's pending redemptions value\n Wallets.Wallet storage wallet = self.registeredWallets[\n walletPubKeyHash\n ];\n wallet.pendingRedemptionsValue -=\n request.requestedAmount -\n request.treasuryFee;\n\n // It is worth noting that there is no need to check if\n // `timedOutRedemption` mapping already contains the given redemption\n // key. There is no possibility to re-use a key of a reported timed-out\n // redemption because the wallet responsible for causing the timeout is\n // moved to a state that prevents it to receive new redemption requests.\n\n // Propagate timeout consequences to the wallet\n self.notifyWalletRedemptionTimeout(walletPubKeyHash, walletMembersIDs);\n\n // Move the redemption from pending redemptions to timed-out redemptions\n self.timedOutRedemptions[redemptionKey] = request;\n delete self.pendingRedemptions[redemptionKey];\n\n // slither-disable-next-line reentrancy-events\n emit RedemptionTimedOut(walletPubKeyHash, redeemerOutputScript);\n\n // Return the requested amount of tokens to the redeemer\n self.bank.transferBalance(request.redeemer, request.requestedAmount);\n }\n\n /// @notice Calculate redemption key without allocations.\n /// @param walletPubKeyHash the pubkey hash of the wallet.\n /// @param script the output script of the redemption.\n /// @return The key = keccak256(keccak256(script) | walletPubKeyHash).\n function getRedemptionKey(bytes20 walletPubKeyHash, bytes memory script)\n internal\n pure\n returns (uint256)\n {\n bytes32 scriptHash = keccak256(script);\n uint256 key;\n /* solhint-disable-next-line no-inline-assembly */\n assembly {\n mstore(0, scriptHash)\n mstore(32, walletPubKeyHash)\n key := keccak256(0, 52)\n }\n return key;\n }\n\n /// @notice Finish calculating redemption key without allocations.\n /// @param walletPubKeyHash the pubkey hash of the wallet.\n /// @param scriptHash the output script hash of the redemption.\n /// @return The key = keccak256(scriptHash | walletPubKeyHash).\n function _getRedemptionKey(bytes20 walletPubKeyHash, bytes32 scriptHash)\n internal\n pure\n returns (uint256)\n {\n uint256 key;\n /* solhint-disable-next-line no-inline-assembly */\n assembly {\n mstore(0, scriptHash)\n mstore(32, walletPubKeyHash)\n key := keccak256(0, 52)\n }\n return key;\n }\n}\n"
|
|
156
156
|
},
|
|
157
157
|
"contracts/bridge/BitcoinTx.sol": {
|
|
158
158
|
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\nimport {BTCUtils} from \"@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol\";\nimport {BytesLib} from \"@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol\";\nimport {ValidateSPV} from \"@keep-network/bitcoin-spv-sol/contracts/ValidateSPV.sol\";\n\nimport \"./BridgeState.sol\";\n\n/// @title Bitcoin transaction\n/// @notice Allows to reference Bitcoin raw transaction in Solidity.\n/// @dev See https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format\n///\n/// Raw Bitcoin transaction data:\n///\n/// | Bytes | Name | BTC type | Description |\n/// |--------|--------------|------------------------|---------------------------|\n/// | 4 | version | int32_t (LE) | TX version number |\n/// | varies | tx_in_count | compactSize uint (LE) | Number of TX inputs |\n/// | varies | tx_in | txIn[] | TX inputs |\n/// | varies | tx_out_count | compactSize uint (LE) | Number of TX outputs |\n/// | varies | tx_out | txOut[] | TX outputs |\n/// | 4 | lock_time | uint32_t (LE) | Unix time or block number |\n///\n//\n/// Non-coinbase transaction input (txIn):\n///\n/// | Bytes | Name | BTC type | Description |\n/// |--------|------------------|------------------------|---------------------------------------------|\n/// | 36 | previous_output | outpoint | The previous outpoint being spent |\n/// | varies | script_bytes | compactSize uint (LE) | The number of bytes in the signature script |\n/// | varies | signature_script | char[] | The signature script, empty for P2WSH |\n/// | 4 | sequence | uint32_t (LE) | Sequence number |\n///\n///\n/// The reference to transaction being spent (outpoint):\n///\n/// | Bytes | Name | BTC type | Description |\n/// |-------|-------|---------------|------------------------------------------|\n/// | 32 | hash | char[32] | Hash of the transaction to spend |\n/// | 4 | index | uint32_t (LE) | Index of the specific output from the TX |\n///\n///\n/// Transaction output (txOut):\n///\n/// | Bytes | Name | BTC type | Description |\n/// |--------|-----------------|-----------------------|--------------------------------------|\n/// | 8 | value | int64_t (LE) | Number of satoshis to spend |\n/// | 1+ | pk_script_bytes | compactSize uint (LE) | Number of bytes in the pubkey script |\n/// | varies | pk_script | char[] | Pubkey script |\n///\n/// compactSize uint format:\n///\n/// | Value | Bytes | Format |\n/// |-----------------------------------------|-------|----------------------------------------------|\n/// | >= 0 && <= 252 | 1 | uint8_t |\n/// | >= 253 && <= 0xffff | 3 | 0xfd followed by the number as uint16_t (LE) |\n/// | >= 0x10000 && <= 0xffffffff | 5 | 0xfe followed by the number as uint32_t (LE) |\n/// | >= 0x100000000 && <= 0xffffffffffffffff | 9 | 0xff followed by the number as uint64_t (LE) |\n///\n/// (*) compactSize uint is often references as VarInt)\n///\n/// Coinbase transaction input (txIn):\n///\n/// | Bytes | Name | BTC type | Description |\n/// |--------|------------------|------------------------|---------------------------------------------|\n/// | 32 | hash | char[32] | A 32-byte 0x0 null (no previous_outpoint) |\n/// | 4 | index | uint32_t (LE) | 0xffffffff (no previous_outpoint) |\n/// | varies | script_bytes | compactSize uint (LE) | The number of bytes in the coinbase script |\n/// | varies | height | char[] | The block height of this block (BIP34) (*) |\n/// | varies | coinbase_script | none | Arbitrary data, max 100 bytes |\n/// | 4 | sequence | uint32_t (LE) | Sequence number\n///\n/// (*) Uses script language: starts with a data-pushing opcode that indicates how many bytes to push to\n/// the stack followed by the block height as a little-endian unsigned integer. This script must be as\n/// short as possible, otherwise it may be rejected. The data-pushing opcode will be 0x03 and the total\n/// size four bytes until block 16,777,216 about 300 years from now.\nlibrary BitcoinTx {\n using BTCUtils for bytes;\n using BTCUtils for uint256;\n using BytesLib for bytes;\n using ValidateSPV for bytes;\n using ValidateSPV for bytes32;\n\n /// @notice Represents Bitcoin transaction data.\n struct Info {\n /// @notice Bitcoin transaction version.\n /// @dev `version` from raw Bitcoin transaction data.\n /// Encoded as 4-bytes signed integer, little endian.\n bytes4 version;\n /// @notice All Bitcoin transaction inputs, prepended by the number of\n /// transaction inputs.\n /// @dev `tx_in_count | tx_in` from raw Bitcoin transaction data.\n ///\n /// The number of transaction inputs encoded as compactSize\n /// unsigned integer, little-endian.\n ///\n /// Note that some popular block explorers reverse the order of\n /// bytes from `outpoint`'s `hash` and display it as big-endian.\n /// Solidity code of Bridge expects hashes in little-endian, just\n /// like they are represented in a raw Bitcoin transaction.\n bytes inputVector;\n /// @notice All Bitcoin transaction outputs prepended by the number of\n /// transaction outputs.\n /// @dev `tx_out_count | tx_out` from raw Bitcoin transaction data.\n ///\n /// The number of transaction outputs encoded as a compactSize\n /// unsigned integer, little-endian.\n bytes outputVector;\n /// @notice Bitcoin transaction locktime.\n ///\n /// @dev `lock_time` from raw Bitcoin transaction data.\n /// Encoded as 4-bytes unsigned integer, little endian.\n bytes4 locktime;\n // This struct doesn't contain `__gap` property as the structure is not\n // stored, it is used as a function's calldata argument.\n }\n\n /// @notice Represents data needed to perform a Bitcoin SPV proof.\n struct Proof {\n /// @notice The merkle proof of transaction inclusion in a block.\n bytes merkleProof;\n /// @notice Transaction index in the block (0-indexed).\n uint256 txIndexInBlock;\n /// @notice Single byte-string of 80-byte bitcoin headers,\n /// lowest height first.\n bytes bitcoinHeaders;\n // This struct doesn't contain `__gap` property as the structure is not\n // stored, it is used as a function's calldata argument.\n }\n\n /// @notice Represents info about an unspent transaction output.\n struct UTXO {\n /// @notice Hash of the transaction the output belongs to.\n /// @dev Byte order corresponds to the Bitcoin internal byte order.\n bytes32 txHash;\n /// @notice Index of the transaction output (0-indexed).\n uint32 txOutputIndex;\n /// @notice Value of the transaction output.\n uint64 txOutputValue;\n // This struct doesn't contain `__gap` property as the structure is not\n // stored, it is used as a function's calldata argument.\n }\n\n /// @notice Represents Bitcoin signature in the R/S/V format.\n struct RSVSignature {\n /// @notice Signature r value.\n bytes32 r;\n /// @notice Signature s value.\n bytes32 s;\n /// @notice Signature recovery value.\n uint8 v;\n // This struct doesn't contain `__gap` property as the structure is not\n // stored, it is used as a function's calldata argument.\n }\n\n /// @notice Validates the SPV proof of the Bitcoin transaction.\n /// Reverts in case the validation or proof verification fail.\n /// @param txInfo Bitcoin transaction data.\n /// @param proof Bitcoin proof data.\n /// @return txHash Proven 32-byte transaction hash.\n function validateProof(\n BridgeState.Storage storage self,\n Info calldata txInfo,\n Proof calldata proof\n ) internal view returns (bytes32 txHash) {\n require(\n txInfo.inputVector.validateVin(),\n \"Invalid input vector provided\"\n );\n require(\n txInfo.outputVector.validateVout(),\n \"Invalid output vector provided\"\n );\n\n txHash = abi\n .encodePacked(\n txInfo.version,\n txInfo.inputVector,\n txInfo.outputVector,\n txInfo.locktime\n )\n .hash256View();\n\n require(\n txHash.prove(\n proof.bitcoinHeaders.extractMerkleRootLE(),\n proof.merkleProof,\n proof.txIndexInBlock\n ),\n \"Tx merkle proof is not valid for provided header and tx hash\"\n );\n\n evaluateProofDifficulty(self, proof.bitcoinHeaders);\n\n return txHash;\n }\n\n /// @notice Evaluates the given Bitcoin proof difficulty against the actual\n /// Bitcoin chain difficulty provided by the relay oracle.\n /// Reverts in case the evaluation fails.\n /// @param bitcoinHeaders Bitcoin headers chain being part of the SPV\n /// proof. Used to extract the observed proof difficulty.\n function evaluateProofDifficulty(\n BridgeState.Storage storage self,\n bytes memory bitcoinHeaders\n ) internal view {\n IRelay relay = self.relay;\n uint256 currentEpochDifficulty = relay.getCurrentEpochDifficulty();\n uint256 previousEpochDifficulty = relay.getPrevEpochDifficulty();\n\n uint256 requestedDiff = 0;\n uint256 firstHeaderDiff = bitcoinHeaders\n .extractTarget()\n .calculateDifficulty();\n\n if (firstHeaderDiff == currentEpochDifficulty) {\n requestedDiff = currentEpochDifficulty;\n } else if (firstHeaderDiff == previousEpochDifficulty) {\n requestedDiff = previousEpochDifficulty;\n } else {\n revert(\"Not at current or previous difficulty\");\n }\n\n uint256 observedDiff = bitcoinHeaders.validateHeaderChain();\n\n require(\n observedDiff != ValidateSPV.getErrBadLength(),\n \"Invalid length of the headers chain\"\n );\n require(\n observedDiff != ValidateSPV.getErrInvalidChain(),\n \"Invalid headers chain\"\n );\n require(\n observedDiff != ValidateSPV.getErrLowWork(),\n \"Insufficient work in a header\"\n );\n\n require(\n observedDiff >= requestedDiff * self.txProofDifficultyFactor,\n \"Insufficient accumulated difficulty in header chain\"\n );\n }\n\n /// @notice Extracts public key hash from the provided P2PKH or P2WPKH output.\n /// Reverts if the validation fails.\n /// @param output The transaction output.\n /// @return pubKeyHash 20-byte public key hash the output locks funds on.\n /// @dev Requirements:\n /// - The output must be of P2PKH or P2WPKH type and lock the funds\n /// on a 20-byte public key hash.\n function extractPubKeyHash(BridgeState.Storage storage, bytes memory output)\n internal\n pure\n returns (bytes20 pubKeyHash)\n {\n bytes memory pubKeyHashBytes = output.extractHash();\n\n require(\n pubKeyHashBytes.length == 20,\n \"Output's public key hash must have 20 bytes\"\n );\n\n pubKeyHash = pubKeyHashBytes.slice20(0);\n\n // The output consists of an 8-byte value and a variable length script.\n // To extract just the script, we ignore the first 8 bytes.\n uint256 scriptLen = output.length - 8;\n\n // The P2PKH script is 26 bytes long.\n // The P2WPKH script is 23 bytes long.\n // A valid script must have one of these lengths,\n // and we can identify the expected script type by the length.\n require(\n scriptLen == 26 || scriptLen == 23,\n \"Output must be P2PKH or P2WPKH\"\n );\n\n if (scriptLen == 26) {\n // Compare to the expected P2PKH script.\n bytes26 script = bytes26(output.slice32(8));\n\n require(\n script == makeP2PKHScript(pubKeyHash),\n \"Invalid P2PKH script\"\n );\n }\n\n if (scriptLen == 23) {\n // Compare to the expected P2WPKH script.\n bytes23 script = bytes23(output.slice32(8));\n\n require(\n script == makeP2WPKHScript(pubKeyHash),\n \"Invalid P2WPKH script\"\n );\n }\n\n return pubKeyHash;\n }\n\n /// @notice Build the P2PKH script from the given public key hash.\n /// @param pubKeyHash The 20-byte public key hash.\n /// @return The P2PKH script.\n /// @dev The P2PKH script has the following byte format:\n /// <0x1976a914> <20-byte PKH> <0x88ac>. According to\n /// https://en.bitcoin.it/wiki/Script#Opcodes this translates to:\n /// - 0x19: Byte length of the entire script\n /// - 0x76: OP_DUP\n /// - 0xa9: OP_HASH160\n /// - 0x14: Byte length of the public key hash\n /// - 0x88: OP_EQUALVERIFY\n /// - 0xac: OP_CHECKSIG\n /// which matches the P2PKH structure as per:\n /// https://en.bitcoin.it/wiki/Transaction#Pay-to-PubkeyHash\n function makeP2PKHScript(bytes20 pubKeyHash)\n internal\n pure\n returns (bytes26)\n {\n bytes26 P2PKHScriptMask = hex\"1976a914000000000000000000000000000000000000000088ac\";\n\n return ((bytes26(pubKeyHash) >> 32) | P2PKHScriptMask);\n }\n\n /// @notice Build the P2WPKH script from the given public key hash.\n /// @param pubKeyHash The 20-byte public key hash.\n /// @return The P2WPKH script.\n /// @dev The P2WPKH script has the following format:\n /// <0x160014> <20-byte PKH>. According to\n /// https://en.bitcoin.it/wiki/Script#Opcodes this translates to:\n /// - 0x16: Byte length of the entire script\n /// - 0x00: OP_0\n /// - 0x14: Byte length of the public key hash\n /// which matches the P2WPKH structure as per:\n /// https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#P2WPKH\n function makeP2WPKHScript(bytes20 pubKeyHash)\n internal\n pure\n returns (bytes23)\n {\n bytes23 P2WPKHScriptMask = hex\"1600140000000000000000000000000000000000000000\";\n\n return ((bytes23(pubKeyHash) >> 24) | P2WPKHScriptMask);\n }\n}\n"
|
|
@@ -199,23 +199,17 @@
|
|
|
199
199
|
"contracts/test/BridgeStub.sol": {
|
|
200
200
|
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\n\nimport \"../bridge/BitcoinTx.sol\";\nimport \"../bridge/Bridge.sol\";\nimport \"../bridge/MovingFunds.sol\";\nimport \"../bridge/Wallets.sol\";\n\ncontract BridgeStub is Bridge {\n function setSweptDeposits(BitcoinTx.UTXO[] calldata utxos) external {\n for (uint256 i = 0; i < utxos.length; i++) {\n uint256 utxoKey = uint256(\n keccak256(\n abi.encodePacked(utxos[i].txHash, utxos[i].txOutputIndex)\n )\n );\n self.deposits[utxoKey].sweptAt = 1641650400;\n }\n }\n\n function setSpentMainUtxos(BitcoinTx.UTXO[] calldata utxos) external {\n for (uint256 i = 0; i < utxos.length; i++) {\n uint256 utxoKey = uint256(\n keccak256(\n abi.encodePacked(utxos[i].txHash, utxos[i].txOutputIndex)\n )\n );\n self.spentMainUTXOs[utxoKey] = true;\n }\n }\n\n function setProcessedMovedFundsSweepRequests(\n BitcoinTx.UTXO[] calldata utxos\n ) external {\n for (uint256 i = 0; i < utxos.length; i++) {\n uint256 utxoKey = uint256(\n keccak256(\n abi.encodePacked(utxos[i].txHash, utxos[i].txOutputIndex)\n )\n );\n self.movedFundsSweepRequests[utxoKey].state = MovingFunds\n .MovedFundsSweepRequestState\n .Processed;\n }\n }\n\n function setActiveWallet(bytes20 activeWalletPubKeyHash) external {\n self.activeWalletPubKeyHash = activeWalletPubKeyHash;\n }\n\n function setWalletMainUtxo(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata utxo\n ) external {\n self.registeredWallets[walletPubKeyHash].mainUtxoHash = keccak256(\n abi.encodePacked(\n utxo.txHash,\n utxo.txOutputIndex,\n utxo.txOutputValue\n )\n );\n }\n\n function setWallet(bytes20 walletPubKeyHash, Wallets.Wallet calldata wallet)\n external\n {\n self.registeredWallets[walletPubKeyHash] = wallet;\n\n if (wallet.state == Wallets.WalletState.Live) {\n self.liveWalletsCount++;\n }\n }\n\n function setDepositDustThreshold(uint64 _depositDustThreshold) external {\n self.depositDustThreshold = _depositDustThreshold;\n }\n\n function setDepositTxMaxFee(uint64 _depositTxMaxFee) external {\n self.depositTxMaxFee = _depositTxMaxFee;\n }\n\n function setRedemptionDustThreshold(uint64 _redemptionDustThreshold)\n external\n {\n self.redemptionDustThreshold = _redemptionDustThreshold;\n }\n\n function setRedemptionTxMaxFee(uint64 _redemptionTxMaxFee) external {\n self.redemptionTxMaxFee = _redemptionTxMaxFee;\n }\n\n function setRedemptionTreasuryFeeDivisor(\n uint64 _redemptionTreasuryFeeDivisor\n ) external {\n self.redemptionTreasuryFeeDivisor = _redemptionTreasuryFeeDivisor;\n }\n\n function setMovingFundsTxMaxTotalFee(uint64 _movingFundsTxMaxTotalFee)\n external\n {\n self.movingFundsTxMaxTotalFee = _movingFundsTxMaxTotalFee;\n }\n\n function setPendingMovedFundsSweepRequest(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata utxo\n ) external {\n uint256 requestKey = uint256(\n keccak256(abi.encodePacked(utxo.txHash, utxo.txOutputIndex))\n );\n\n self.movedFundsSweepRequests[requestKey] = MovingFunds\n .MovedFundsSweepRequest(\n walletPubKeyHash,\n utxo.txOutputValue,\n /* solhint-disable-next-line not-rely-on-time */\n uint32(block.timestamp),\n MovingFunds.MovedFundsSweepRequestState.Pending\n );\n\n self\n .registeredWallets[walletPubKeyHash]\n .pendingMovedFundsSweepRequestsCount++;\n }\n\n function processPendingMovedFundsSweepRequest(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata utxo\n ) external {\n uint256 requestKey = uint256(\n keccak256(abi.encodePacked(utxo.txHash, utxo.txOutputIndex))\n );\n\n MovingFunds.MovedFundsSweepRequest storage request = self\n .movedFundsSweepRequests[requestKey];\n\n require(\n request.state == MovingFunds.MovedFundsSweepRequestState.Pending,\n \"Stub sweep request must be in Pending state\"\n );\n\n request.state = MovingFunds.MovedFundsSweepRequestState.Processed;\n\n self\n .registeredWallets[walletPubKeyHash]\n .pendingMovedFundsSweepRequestsCount--;\n }\n\n function timeoutPendingMovedFundsSweepRequest(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata utxo\n ) external {\n uint256 requestKey = uint256(\n keccak256(abi.encodePacked(utxo.txHash, utxo.txOutputIndex))\n );\n\n MovingFunds.MovedFundsSweepRequest storage request = self\n .movedFundsSweepRequests[requestKey];\n\n require(\n request.state == MovingFunds.MovedFundsSweepRequestState.Pending,\n \"Stub sweep request must be in Pending state\"\n );\n\n request.state = MovingFunds.MovedFundsSweepRequestState.TimedOut;\n\n self\n .registeredWallets[walletPubKeyHash]\n .pendingMovedFundsSweepRequestsCount--;\n }\n\n function setMovedFundsSweepTxMaxTotalFee(\n uint64 _movedFundsSweepTxMaxTotalFee\n ) external {\n self.movedFundsSweepTxMaxTotalFee = _movedFundsSweepTxMaxTotalFee;\n }\n}\n"
|
|
201
201
|
},
|
|
202
|
-
"contracts/
|
|
203
|
-
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\n\nimport \"../bridge/Heartbeat.sol\";\n\n/// @dev This is a contract implemented to test Heartbeat library directly.\ncontract HeartbeatStub {\n function isValidHeartbeatMessage(bytes calldata message)\n public\n pure\n returns (bool)\n {\n return Heartbeat.isValidHeartbeatMessage(message);\n }\n}\n"
|
|
204
|
-
},
|
|
205
|
-
"contracts/test/TestEcdsaLib.sol": {
|
|
206
|
-
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\n\nimport \"../bridge/EcdsaLib.sol\";\n\n// TODO: Rename to EcdsLibStub\n/// @dev This is a contract implemented to test EcdsaLib library directly.\ncontract TestEcdsaLib {\n function compressPublicKey(bytes32 x, bytes32 y)\n public\n pure\n returns (bytes memory)\n {\n return EcdsaLib.compressPublicKey(x, y);\n }\n}\n"
|
|
202
|
+
"contracts/maintainer/MaintainerProxy.sol": {
|
|
203
|
+
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@keep-network/random-beacon/contracts/Reimbursable.sol\";\nimport \"@keep-network/random-beacon/contracts/ReimbursementPool.sol\";\n\nimport \"../bridge/BitcoinTx.sol\";\nimport \"../bridge/Bridge.sol\";\n\n/// @title Maintainer Proxy\n/// @notice Maintainers are the willing off-chain clients approved by the governance.\n/// Maintainers proxy calls to the Bridge contract via 'MaintainerProxy'\n/// and are refunded for the spent gas from the Reimbursement Pool.\n/// Only the authorized maintainers can call 'MaintainerProxy' functions.\ncontract MaintainerProxy is Ownable, Reimbursable {\n Bridge public bridge;\n\n /// @notice Authorized maintainers that can interact with the set of functions\n /// for maintainers only. Authorization can be granted and removed by\n /// the governance.\n /// @dev 'Key' is the address of the maintainer. 'Value' represents an index+1\n /// in the 'maintainers' array. 1 was added so the maintainer index can\n /// never be 0 which is a reserved index for a non-existent maintainer\n /// in this map.\n mapping(address => uint256) public isAuthorized;\n\n /// @notice This list of maintainers keeps the order of which maintainer should\n /// be submitting a next transaction. It does not enforce the order\n /// but only tracks who should be next in line.\n address[] public maintainers;\n\n /// @notice Gas that is meant to balance the submission of deposit sweep proof\n /// overall cost. Can be updated by the governance based on the current\n /// market conditions.\n uint256 public submitDepositSweepProofGasOffset;\n\n /// @notice Gas that is meant to balance the submission of redemption proof\n /// overall cost. Can be updated by the governance based on the current\n /// market conditions.\n uint256 public submitRedemptionProofGasOffset;\n\n /// @notice Gas that is meant to balance the submission of moving funds commitment\n /// overall cost. Can be updated by the governance based on the current\n /// market conditions.\n uint256 public submitMovingFundsCommitmentGasOffset;\n\n /// @notice Gas that is meant to balance the reset of moving funds timeout\n /// overall cost. Can be updated by the governance based on the current\n /// market conditions.\n uint256 public resetMovingFundsTimeoutGasOffset;\n\n /// @notice Gas that is meant to balance the submission of moving funds proof\n /// overall cost. Can be updated by the governance based on the current\n /// market conditions.\n uint256 public submitMovingFundsProofGasOffset;\n\n /// @notice Gas that is meant to balance the notification of moving funds below\n /// dust overall cost. Can be updated by the governance based on the\n /// current market conditions.\n uint256 public notifyMovingFundsBelowDustGasOffset;\n\n /// @notice Gas that is meant to balance the submission of moved funds sweep\n /// proof overall cost. Can be updated by the governance based on the\n /// current market conditions.\n uint256 public submitMovedFundsSweepProofGasOffset;\n\n /// @notice Gas that is meant to balance the request of a new wallet overall\n /// cost. Can be updated by the governance based on the current\n /// market conditions.\n uint256 public requestNewWalletGasOffset;\n\n /// @notice Gas that is meant to balance the notification of closeable wallet\n /// overall cost. Can be updated by the governance based on the current\n /// market conditions.\n uint256 public notifyWalletCloseableGasOffset;\n\n /// @notice Gas that is meant to balance the notification of wallet closing\n /// period elapsed overall cost. Can be updated by the governance\n /// based on the current market conditions.\n uint256 public notifyWalletClosingPeriodElapsedGasOffset;\n\n /// @notice Gas that is meant to balance the defeat fraud challenge\n /// overall cost. Can be updated by the governance based on the current\n /// market conditions.\n uint256 public defeatFraudChallengeGasOffset;\n\n /// @notice Gas that is meant to balance the defeat fraud challenge with heartbeat\n /// overall cost. Can be updated by the governance based on the current\n /// market conditions.\n uint256 public defeatFraudChallengeWithHeartbeatGasOffset;\n\n event MaintainerAuthorized(address indexed maintainer);\n\n event MaintainerUnauthorized(address indexed maintainer);\n\n event BridgeUpdated(address newBridge);\n\n event GasOffsetParametersUpdated(\n uint256 submitDepositSweepProofGasOffset,\n uint256 submitRedemptionProofGasOffset,\n uint256 submitMovingFundsCommitmentGasOffset,\n uint256 resetMovingFundsTimeoutGasOffset,\n uint256 submitMovingFundsProofGasOffset,\n uint256 notifyMovingFundsBelowDustGasOffset,\n uint256 submitMovedFundsSweepProofGasOffset,\n uint256 requestNewWalletGasOffset,\n uint256 notifyWalletCloseableGasOffset,\n uint256 notifyWalletClosingPeriodElapsedGasOffset,\n uint256 defeatFraudChallengeGasOffset,\n uint256 defeatFraudChallengeWithHeartbeatGasOffset\n );\n\n modifier onlyMaintainer() {\n require(isAuthorized[msg.sender] != 0, \"Caller is not authorized\");\n _;\n }\n\n modifier onlyReimbursableAdmin() override {\n require(owner() == msg.sender, \"Caller is not the owner\");\n _;\n }\n\n constructor(Bridge _bridge, ReimbursementPool _reimbursementPool) {\n bridge = _bridge;\n reimbursementPool = _reimbursementPool;\n submitDepositSweepProofGasOffset = 27000;\n submitRedemptionProofGasOffset = 9750;\n submitMovingFundsCommitmentGasOffset = 8000;\n resetMovingFundsTimeoutGasOffset = 1000;\n submitMovingFundsProofGasOffset = 15000;\n notifyMovingFundsBelowDustGasOffset = 3500;\n submitMovedFundsSweepProofGasOffset = 22000;\n requestNewWalletGasOffset = 3500;\n notifyWalletCloseableGasOffset = 4000;\n notifyWalletClosingPeriodElapsedGasOffset = 3000;\n defeatFraudChallengeGasOffset = 10000;\n defeatFraudChallengeWithHeartbeatGasOffset = 5000;\n }\n\n /// @notice Wraps `Bridge.submitDepositSweepProof` call and reimburses the\n /// caller's transaction cost.\n /// @dev See `Bridge.submitDepositSweepProof` function documentation.\n function submitDepositSweepProof(\n BitcoinTx.Info calldata sweepTx,\n BitcoinTx.Proof calldata sweepProof,\n BitcoinTx.UTXO calldata mainUtxo,\n address vault\n ) external onlyMaintainer {\n uint256 gasStart = gasleft();\n\n bridge.submitDepositSweepProof(sweepTx, sweepProof, mainUtxo, vault);\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + submitDepositSweepProofGasOffset,\n msg.sender\n );\n }\n\n /// @notice Wraps `Bridge.submitRedemptionProof` call and reimburses the\n /// caller's transaction cost.\n /// @dev See `Bridge.submitRedemptionProof` function documentation.\n function submitRedemptionProof(\n BitcoinTx.Info calldata redemptionTx,\n BitcoinTx.Proof calldata redemptionProof,\n BitcoinTx.UTXO calldata mainUtxo,\n bytes20 walletPubKeyHash\n ) external onlyMaintainer {\n uint256 gasStart = gasleft();\n\n bridge.submitRedemptionProof(\n redemptionTx,\n redemptionProof,\n mainUtxo,\n walletPubKeyHash\n );\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + submitRedemptionProofGasOffset,\n msg.sender\n );\n }\n\n /// @notice Wraps `Bridge.submitMovingFundsCommitment` call and reimburses the\n /// caller's transaction cost.\n /// @dev See `Bridge.submitMovingFundsCommitment` function documentation.\n function submitMovingFundsCommitment(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata walletMainUtxo,\n uint32[] calldata walletMembersIDs,\n uint256 walletMemberIndex,\n bytes20[] calldata targetWallets\n ) external {\n uint256 gasStart = gasleft();\n\n bridge.submitMovingFundsCommitment(\n walletPubKeyHash,\n walletMainUtxo,\n walletMembersIDs,\n walletMemberIndex,\n targetWallets\n );\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + submitMovingFundsCommitmentGasOffset,\n msg.sender\n );\n }\n\n /// @notice Wraps `Bridge.resetMovingFundsTimeout` call and reimburses the\n /// caller's transaction cost.\n /// @dev See `Bridge.resetMovingFundsTimeout` function documentation.\n function resetMovingFundsTimeout(bytes20 walletPubKeyHash) external {\n uint256 gasStart = gasleft();\n\n bridge.resetMovingFundsTimeout(walletPubKeyHash);\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + resetMovingFundsTimeoutGasOffset,\n msg.sender\n );\n }\n\n /// @notice Wraps `Bridge.submitMovingFundsProof` call and reimburses the\n /// caller's transaction cost.\n /// @dev See `Bridge.submitMovingFundsProof` function documentation.\n function submitMovingFundsProof(\n BitcoinTx.Info calldata movingFundsTx,\n BitcoinTx.Proof calldata movingFundsProof,\n BitcoinTx.UTXO calldata mainUtxo,\n bytes20 walletPubKeyHash\n ) external onlyMaintainer {\n uint256 gasStart = gasleft();\n\n bridge.submitMovingFundsProof(\n movingFundsTx,\n movingFundsProof,\n mainUtxo,\n walletPubKeyHash\n );\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + submitMovingFundsProofGasOffset,\n msg.sender\n );\n }\n\n /// @notice Wraps `Bridge.notifyMovingFundsBelowDust` call and reimburses the\n /// caller's transaction cost.\n /// @dev See `Bridge.notifyMovingFundsBelowDust` function documentation.\n function notifyMovingFundsBelowDust(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata mainUtxo\n ) external onlyMaintainer {\n uint256 gasStart = gasleft();\n\n bridge.notifyMovingFundsBelowDust(walletPubKeyHash, mainUtxo);\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + notifyMovingFundsBelowDustGasOffset,\n msg.sender\n );\n }\n\n /// @notice Wraps `Bridge.submitMovedFundsSweepProof` call and reimburses the\n /// caller's transaction cost.\n /// @dev See `Bridge.submitMovedFundsSweepProof` function documentation.\n function submitMovedFundsSweepProof(\n BitcoinTx.Info calldata sweepTx,\n BitcoinTx.Proof calldata sweepProof,\n BitcoinTx.UTXO calldata mainUtxo\n ) external onlyMaintainer {\n uint256 gasStart = gasleft();\n\n bridge.submitMovedFundsSweepProof(sweepTx, sweepProof, mainUtxo);\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + submitMovedFundsSweepProofGasOffset,\n msg.sender\n );\n }\n\n /// @notice Wraps `Bridge.requestNewWallet` call and reimburses the\n /// caller's transaction cost.\n /// @dev See `Bridge.requestNewWallet` function documentation.\n function requestNewWallet(BitcoinTx.UTXO calldata activeWalletMainUtxo)\n external\n onlyMaintainer\n {\n uint256 gasStart = gasleft();\n\n bridge.requestNewWallet(activeWalletMainUtxo);\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + requestNewWalletGasOffset,\n msg.sender\n );\n }\n\n /// @notice Wraps `Bridge.notifyWalletCloseable` call and reimburses the\n /// caller's transaction cost.\n /// @dev See `Bridge.notifyWalletCloseable` function documentation.\n function notifyWalletCloseable(\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata walletMainUtxo\n ) external onlyMaintainer {\n uint256 gasStart = gasleft();\n\n bridge.notifyWalletCloseable(walletPubKeyHash, walletMainUtxo);\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + notifyWalletCloseableGasOffset,\n msg.sender\n );\n }\n\n /// @notice Wraps `Bridge.notifyWalletClosingPeriodElapsed` call and reimburses\n /// the caller's transaction cost.\n /// @dev See `Bridge.notifyWalletClosingPeriodElapsed` function documentation.\n function notifyWalletClosingPeriodElapsed(bytes20 walletPubKeyHash)\n external\n onlyMaintainer\n {\n uint256 gasStart = gasleft();\n\n bridge.notifyWalletClosingPeriodElapsed(walletPubKeyHash);\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + notifyWalletClosingPeriodElapsedGasOffset,\n msg.sender\n );\n }\n\n /// @notice Wraps `Bridge.defeatFraudChallenge` call and reimburses the\n /// caller's transaction cost.\n /// @dev See `Bridge.defeatFraudChallenge` function documentation.\n function defeatFraudChallenge(\n bytes calldata walletPublicKey,\n bytes calldata preimage,\n bool witness\n ) external {\n uint256 gasStart = gasleft();\n\n bridge.defeatFraudChallenge(walletPublicKey, preimage, witness);\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + defeatFraudChallengeGasOffset,\n msg.sender\n );\n }\n\n /// @notice Wraps `Bridge.defeatFraudChallengeWithHeartbeat` call and\n /// reimburses the caller's transaction cost.\n /// @dev See `Bridge.defeatFraudChallengeWithHeartbeat` function documentation.\n function defeatFraudChallengeWithHeartbeat(\n bytes calldata walletPublicKey,\n bytes calldata heartbeatMessage\n ) external {\n uint256 gasStart = gasleft();\n\n bridge.defeatFraudChallengeWithHeartbeat(\n walletPublicKey,\n heartbeatMessage\n );\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + defeatFraudChallengeWithHeartbeatGasOffset,\n msg.sender\n );\n }\n\n /// @notice Authorize a maintainer that can interact with this reimbursement pool.\n /// Can be authorized by the owner only.\n /// @param maintainer Maintainer to authorize.\n function authorize(address maintainer) external onlyOwner {\n maintainers.push(maintainer);\n isAuthorized[maintainer] = maintainers.length;\n\n emit MaintainerAuthorized(maintainer);\n }\n\n /// @notice Unauthorize a maintainer that was previously authorized to interact\n /// with the Maintainer Proxy contract. Can be unauthorized by the\n /// owner only.\n /// @dev The last maintainer is swapped with the one to be unauthorized.\n /// The unauthorized maintainer is then removed from the list. An index\n /// of the last maintainer is changed with the removed maintainer.\n /// Ex.\n /// 'maintainers' list: [0x1, 0x2, 0x3, 0x4, 0x5]\n /// 'isAuthorized' map: [0x1 -> 1, 0x2 -> 2, 0x3 -> 3, 0x4 -> 4, 0x5 -> 5]\n /// unauthorize: 0x3\n /// new 'maintainers' list: [0x1, 0x2, 0x5, 0x4]\n /// new 'isAuthorized' map: [0x1 -> 1, 0x2 -> 2, 0x4 -> 4, 0x5 -> 3]\n /// @param maintainerToUnauthorize Maintainer to unauthorize.\n function unauthorize(address maintainerToUnauthorize) external onlyOwner {\n uint256 maintainerIdToUnauthorize = isAuthorized[\n maintainerToUnauthorize\n ];\n\n require(maintainerIdToUnauthorize != 0, \"No maintainer to unauthorize\");\n\n address lastMaintainerAddress = maintainers[maintainers.length - 1];\n\n maintainers[maintainerIdToUnauthorize - 1] = lastMaintainerAddress;\n maintainers.pop();\n\n isAuthorized[lastMaintainerAddress] = maintainerIdToUnauthorize;\n\n delete isAuthorized[maintainerToUnauthorize];\n\n emit MaintainerUnauthorized(maintainerToUnauthorize);\n }\n\n /// @notice Allows the Governance to upgrade the Bridge address.\n /// @dev The function does not implement any governance delay and does not\n /// check the status of the Bridge. The Governance implementation needs\n /// to ensure all requirements for the upgrade are satisfied before\n /// executing this function.\n function updateBridge(Bridge _bridge) external onlyOwner {\n bridge = _bridge;\n\n emit BridgeUpdated(address(_bridge));\n }\n\n /// @notice Updates the values of gas offset parameters.\n /// @dev Can be called only by the contract owner. The caller is responsible\n /// for validating parameters.\n /// @param newSubmitDepositSweepProofGasOffset New submit deposit sweep\n /// proof gas offset.\n /// @param newSubmitRedemptionProofGasOffset New submit redemption proof gas\n /// offset.\n /// @param newSubmitMovingFundsCommitmentGasOffset New submit moving funds\n /// commitment gas offset.\n /// @param newResetMovingFundsTimeoutGasOffset New reset moving funds\n /// timeout gas offset.\n /// @param newSubmitMovingFundsProofGasOffset New submit moving funds proof\n /// gas offset.\n /// @param newNotifyMovingFundsBelowDustGasOffset New notify moving funds\n /// below dust gas offset.\n /// @param newSubmitMovedFundsSweepProofGasOffset New submit moved funds\n /// sweep proof gas offset.\n /// @param newRequestNewWalletGasOffset New request new wallet gas offset.\n /// @param newNotifyWalletCloseableGasOffset New notify closeable wallet gas\n /// offset.\n /// @param newNotifyWalletClosingPeriodElapsedGasOffset New notify wallet\n /// closing period elapsed gas offset.\n /// @param newDefeatFraudChallengeGasOffset New defeat fraud challenge gas\n /// offset.\n /// @param newDefeatFraudChallengeWithHeartbeatGasOffset New defeat fraud\n /// challenge with heartbeat gas offset.\n function updateGasOffsetParameters(\n uint256 newSubmitDepositSweepProofGasOffset,\n uint256 newSubmitRedemptionProofGasOffset,\n uint256 newSubmitMovingFundsCommitmentGasOffset,\n uint256 newResetMovingFundsTimeoutGasOffset,\n uint256 newSubmitMovingFundsProofGasOffset,\n uint256 newNotifyMovingFundsBelowDustGasOffset,\n uint256 newSubmitMovedFundsSweepProofGasOffset,\n uint256 newRequestNewWalletGasOffset,\n uint256 newNotifyWalletCloseableGasOffset,\n uint256 newNotifyWalletClosingPeriodElapsedGasOffset,\n uint256 newDefeatFraudChallengeGasOffset,\n uint256 newDefeatFraudChallengeWithHeartbeatGasOffset\n ) external onlyOwner {\n submitDepositSweepProofGasOffset = newSubmitDepositSweepProofGasOffset;\n submitRedemptionProofGasOffset = newSubmitRedemptionProofGasOffset;\n submitMovingFundsCommitmentGasOffset = newSubmitMovingFundsCommitmentGasOffset;\n resetMovingFundsTimeoutGasOffset = newResetMovingFundsTimeoutGasOffset;\n submitMovingFundsProofGasOffset = newSubmitMovingFundsProofGasOffset;\n notifyMovingFundsBelowDustGasOffset = newNotifyMovingFundsBelowDustGasOffset;\n submitMovedFundsSweepProofGasOffset = newSubmitMovedFundsSweepProofGasOffset;\n requestNewWalletGasOffset = newRequestNewWalletGasOffset;\n notifyWalletCloseableGasOffset = newNotifyWalletCloseableGasOffset;\n notifyWalletClosingPeriodElapsedGasOffset = newNotifyWalletClosingPeriodElapsedGasOffset;\n defeatFraudChallengeGasOffset = newDefeatFraudChallengeGasOffset;\n defeatFraudChallengeWithHeartbeatGasOffset = newDefeatFraudChallengeWithHeartbeatGasOffset;\n\n emit GasOffsetParametersUpdated(\n submitDepositSweepProofGasOffset,\n submitRedemptionProofGasOffset,\n submitMovingFundsCommitmentGasOffset,\n resetMovingFundsTimeoutGasOffset,\n submitMovingFundsProofGasOffset,\n notifyMovingFundsBelowDustGasOffset,\n submitMovedFundsSweepProofGasOffset,\n requestNewWalletGasOffset,\n notifyWalletCloseableGasOffset,\n notifyWalletClosingPeriodElapsedGasOffset,\n defeatFraudChallengeGasOffset,\n defeatFraudChallengeWithHeartbeatGasOffset\n );\n }\n\n /// @notice Gets an entire array of maintainer addresses.\n function allMaintainers() external view returns (address[] memory) {\n return maintainers;\n }\n}\n"
|
|
207
204
|
},
|
|
208
|
-
"@keep-network/
|
|
209
|
-
"content": "// SPDX-License-Identifier: MIT\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity ^0.8.9;\n\nimport \"
|
|
210
|
-
},
|
|
211
|
-
"@keep-network/ecdsa/contracts/libraries/Wallets.sol": {
|
|
212
|
-
"content": "// SPDX-License-Identifier: MIT\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity ^0.8.9;\n\nlibrary Wallets {\n struct Wallet {\n // Keccak256 hash of group members identifiers array. Group members do not\n // include operators selected by the sortition pool that misbehaved during DKG.\n bytes32 membersIdsHash;\n // Uncompressed ECDSA public key stored as X and Y coordinates (32 bytes each).\n bytes32 publicKeyX;\n bytes32 publicKeyY;\n // This struct doesn't contain `__gap` property as the structure is stored\n // in a mapping, mappings store values in different slots and they are\n // not contiguous with other values.\n }\n\n struct Data {\n // Mapping of keccak256 hashes of wallet public keys to wallet details.\n // Hash of public key is considered an unique wallet identifier.\n mapping(bytes32 => Wallet) registry;\n // Reserved storage space in case we need to add more variables.\n // See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n // slither-disable-next-line unused-state\n uint256[49] __gap;\n }\n\n /// @notice Registers a new wallet.\n /// @dev Uses a public key hash as a unique identifier of a wallet.\n /// @param membersIdsHash Keccak256 hash of group members identifiers array\n /// @param publicKey Uncompressed public key\n /// @return walletID Wallet's ID\n /// @return publicKeyX Wallet's public key's X coordinate\n /// @return publicKeyY Wallet's public key's Y coordinate\n function addWallet(\n Data storage self,\n bytes32 membersIdsHash,\n bytes calldata publicKey\n )\n internal\n returns (\n bytes32 walletID,\n bytes32 publicKeyX,\n bytes32 publicKeyY\n )\n {\n walletID = keccak256(publicKey);\n\n require(\n self.registry[walletID].publicKeyX == bytes32(0),\n \"Wallet with the given public key already exists\"\n );\n require(publicKey.length == 64, \"Invalid length of the public key\");\n\n publicKeyX = bytes32(publicKey[:32]);\n publicKeyY = bytes32(publicKey[32:]);\n\n self.registry[walletID].membersIdsHash = membersIdsHash;\n self.registry[walletID].publicKeyX = publicKeyX;\n self.registry[walletID].publicKeyY = publicKeyY;\n }\n\n /// @notice Deletes wallet with the given ID from the registry. Reverts\n /// if wallet with the given ID has not been registered or if it\n /// has already been closed.\n function deleteWallet(Data storage self, bytes32 walletID) internal {\n require(\n isWalletRegistered(self, walletID),\n \"Wallet with the given ID has not been registered\"\n );\n\n delete self.registry[walletID];\n }\n\n /// @notice Checks if a wallet with the given ID is registered.\n /// @param walletID Wallet's ID\n /// @return True if a wallet is registered, false otherwise\n function isWalletRegistered(Data storage self, bytes32 walletID)\n internal\n view\n returns (bool)\n {\n return self.registry[walletID].publicKeyX != bytes32(0);\n }\n\n /// @notice Returns Keccak256 hash of the wallet signing group members\n /// identifiers array. Group members do not include operators\n /// selected by the sortition pool that misbehaved during DKG.\n /// Reverts if wallet with the given ID is not registered.\n /// @param walletID ID of the wallet\n /// @return Wallet signing group members hash\n function getWalletMembersIdsHash(Data storage self, bytes32 walletID)\n internal\n view\n returns (bytes32)\n {\n require(\n isWalletRegistered(self, walletID),\n \"Wallet with the given ID has not been registered\"\n );\n\n return self.registry[walletID].membersIdsHash;\n }\n\n /// @notice Gets public key of a wallet with the given wallet ID.\n /// The public key is returned as X and Y coordinates.\n /// Reverts if wallet with the given ID is not registered.\n /// @param walletID ID of the wallet\n /// @return x Public key X coordinate\n /// @return y Public key Y coordinate\n function getWalletPublicKeyCoordinates(Data storage self, bytes32 walletID)\n internal\n view\n returns (bytes32 x, bytes32 y)\n {\n require(\n isWalletRegistered(self, walletID),\n \"Wallet with the given ID has not been registered\"\n );\n\n Wallet storage wallet = self.registry[walletID];\n\n return (wallet.publicKeyX, wallet.publicKeyY);\n }\n\n /// @notice Gets public key of a wallet with the given wallet ID.\n /// The public key is returned in an uncompressed format as a 64-byte\n /// concatenation of X and Y coordinates.\n /// Reverts if wallet with the given ID is not registered.\n /// @param walletID ID of the wallet\n /// @return Uncompressed public key of the wallet\n function getWalletPublicKey(Data storage self, bytes32 walletID)\n internal\n view\n returns (bytes memory)\n {\n (bytes32 x, bytes32 y) = getWalletPublicKeyCoordinates(self, walletID);\n return bytes.concat(x, y);\n }\n}\n"
|
|
205
|
+
"@keep-network/random-beacon/contracts/Reimbursable.sol": {
|
|
206
|
+
"content": "// SPDX-License-Identifier: MIT\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity ^0.8.9;\n\nimport \"./ReimbursementPool.sol\";\n\nabstract contract Reimbursable {\n // The variable should be initialized by the implementing contract.\n // slither-disable-next-line uninitialized-state\n ReimbursementPool public reimbursementPool;\n\n // Reserved storage space in case we need to add more variables,\n // since there are upgradeable contracts that inherit from this one.\n // See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n // slither-disable-next-line unused-state\n uint256[49] private __gap;\n\n event ReimbursementPoolUpdated(address newReimbursementPool);\n\n modifier refundable(address receiver) {\n uint256 gasStart = gasleft();\n _;\n reimbursementPool.refund(gasStart - gasleft(), receiver);\n }\n\n modifier onlyReimbursableAdmin() virtual {\n _;\n }\n\n function updateReimbursementPool(ReimbursementPool _reimbursementPool)\n external\n onlyReimbursableAdmin\n {\n emit ReimbursementPoolUpdated(address(_reimbursementPool));\n\n reimbursementPool = _reimbursementPool;\n }\n}\n"
|
|
213
207
|
},
|
|
214
|
-
"@keep-network/
|
|
215
|
-
"content": "// SPDX-License-Identifier: MIT\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n//\n\npragma solidity ^0.8.9;\n\nimport \"@keep-network/sortition-pools/contracts/SortitionPool.sol\";\nimport \"@threshold-network/solidity-contracts/contracts/staking/IStaking.sol\";\n\n/// @notice Library managing the state of stake authorizations for ECDSA\n/// operator contract and the presence of operators in the sortition\n/// pool based on the stake authorized for them.\nlibrary EcdsaAuthorization {\n struct Parameters {\n // The minimum authorization required by ECDSA application so that\n // operator can join the sortition pool and do the work.\n uint96 minimumAuthorization;\n // Authorization decrease delay in seconds between the time\n // authorization decrease is requested and the time the authorization\n // decrease can be approved. It is always the same value, no matter if\n // authorization decrease amount is small, significant, or if it is\n // a decrease to zero.\n uint64 authorizationDecreaseDelay;\n // The time period before the authorization decrease delay end,\n // during which the authorization decrease request can be overwritten.\n //\n // When the request is overwritten, the authorization decrease delay is\n // reset.\n //\n // For example, if `authorizationDecraseChangePeriod` is set to 4\n // days, `authorizationDecreaseDelay` is set to 14 days, and someone\n // requested authorization decrease, it means they can not\n // request another decrease for the first 10 days. After 10 days pass,\n // they can request again and overwrite the previous authorization\n // decrease request. The delay time will reset for them and they\n // will have to wait another 10 days to alter it and 14 days to\n // approve it.\n //\n // This value protects against malicious operators who manipulate\n // their weight by overwriting authorization decrease request, and\n // lowering or increasing their eligible stake this way.\n //\n // If set to a value equal to `authorizationDecreaseDelay, it means\n // that authorization decrease request can be always overwritten.\n // If set to zero, it means authorization decrease request can not be\n // overwritten until the delay end, and one needs to wait for the entire\n // authorization decrease delay to approve their decrease and request\n // for another one or to overwrite the pending one.\n //\n // (1) authorization decrease requested timestamp\n // (2) from this moment authorization decrease request can be\n // overwritten\n // (3) from this moment authorization decrease request can be\n // approved, assuming it was NOT overwritten in (2)\n //\n // (1) (2) (3)\n // --x------------------------------x--------------------------x---->\n // | \\________________________/\n // | authorizationDecreaseChangePeriod\n // \\______________________________________________________/\n // authorizationDecreaseDelay\n //\n uint64 authorizationDecreaseChangePeriod;\n // This struct doesn't contain `__gap` property as the structure is\n // stored inside `Data` struct, that already have a gap that can be used\n // on upgrade.\n }\n\n struct AuthorizationDecrease {\n uint96 decreasingBy; // amount\n uint64 decreasingAt; // timestamp\n }\n\n struct Data {\n Parameters parameters;\n mapping(address => address) stakingProviderToOperator;\n mapping(address => address) operatorToStakingProvider;\n mapping(address => AuthorizationDecrease) pendingDecreases;\n // Reserved storage space in case we need to add more variables.\n // See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n // slither-disable-next-line unused-state\n uint256[46] __gap;\n }\n\n event OperatorRegistered(\n address indexed stakingProvider,\n address indexed operator\n );\n\n event AuthorizationIncreased(\n address indexed stakingProvider,\n address indexed operator,\n uint96 fromAmount,\n uint96 toAmount\n );\n\n event AuthorizationDecreaseRequested(\n address indexed stakingProvider,\n address indexed operator,\n uint96 fromAmount,\n uint96 toAmount,\n uint64 decreasingAt\n );\n\n event AuthorizationDecreaseApproved(address indexed stakingProvider);\n\n event InvoluntaryAuthorizationDecreaseFailed(\n address indexed stakingProvider,\n address indexed operator,\n uint96 fromAmount,\n uint96 toAmount\n );\n\n event OperatorJoinedSortitionPool(\n address indexed stakingProvider,\n address indexed operator\n );\n\n event OperatorStatusUpdated(\n address indexed stakingProvider,\n address indexed operator\n );\n\n /// @notice Sets the minimum authorization for ECDSA application. Without\n /// at least the minimum authorization, staking provider is not\n /// eligible to join and operate in the network.\n function setMinimumAuthorization(\n Data storage self,\n uint96 _minimumAuthorization\n ) internal {\n self.parameters.minimumAuthorization = _minimumAuthorization;\n }\n\n /// @notice Sets the authorization decrease delay. It is the time in seconds\n /// that needs to pass between the time authorization decrease is\n /// requested and the time the authorization decrease can be\n /// approved, no matter the authorization decrease amount.\n function setAuthorizationDecreaseDelay(\n Data storage self,\n uint64 _authorizationDecreaseDelay\n ) internal {\n self\n .parameters\n .authorizationDecreaseDelay = _authorizationDecreaseDelay;\n }\n\n /// @notice Sets the authorization decrease change period. It is the time\n /// period before the authorization decrease delay end,\n /// during which the authorization decrease request can be\n /// overwritten.\n function setAuthorizationDecreaseChangePeriod(\n Data storage self,\n uint64 _authorizationDecreaseChangePeriod\n ) internal {\n self\n .parameters\n .authorizationDecreaseChangePeriod = _authorizationDecreaseChangePeriod;\n }\n\n /// @notice Used by staking provider to set operator address that will\n /// operate ECDSA node. The given staking provider can set operator\n /// address only one time. The operator address can not be changed\n /// and must be unique. Reverts if the operator is already set for\n /// the staking provider or if the operator address is already in\n /// use. Reverts if there is a pending authorization decrease for\n /// the staking provider.\n function registerOperator(Data storage self, address operator) internal {\n address stakingProvider = msg.sender;\n\n require(operator != address(0), \"Operator can not be zero address\");\n require(\n self.stakingProviderToOperator[stakingProvider] == address(0),\n \"Operator already set for the staking provider\"\n );\n require(\n self.operatorToStakingProvider[operator] == address(0),\n \"Operator address already in use\"\n );\n\n // Authorization request for a staking provider who has not yet\n // registered their operator can be approved immediately.\n // We need to make sure that the approval happens before operator\n // is registered to do not let the operator join the sortition pool\n // with an unresolved authorization decrease request that can be\n // approved at any point.\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n require(\n decrease.decreasingAt == 0,\n \"There is a pending authorization decrease request\"\n );\n\n emit OperatorRegistered(stakingProvider, operator);\n\n self.stakingProviderToOperator[stakingProvider] = operator;\n self.operatorToStakingProvider[operator] = stakingProvider;\n }\n\n /// @notice Used by T staking contract to inform the application that the\n /// authorized stake amount for the given staking provider increased.\n ///\n /// Reverts if the authorization amount is below the minimum.\n ///\n /// The function is not updating the sortition pool. Sortition pool\n /// state needs to be updated by the operator with a call to\n /// `joinSortitionPool` or `updateOperatorStatus`.\n ///\n /// @dev Should only be callable by T staking contract.\n function authorizationIncreased(\n Data storage self,\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) internal {\n require(\n toAmount >= self.parameters.minimumAuthorization,\n \"Authorization below the minimum\"\n );\n\n // Note that this function does not require the operator address to be\n // set for the given staking provider. This allows the stake owner\n // who is also an authorizer to increase the authorization before the\n // staking provider sets the operator. This allows delegating stake\n // and increasing authorization immediately one after another without\n // having to wait for the staking provider to do their part.\n\n address operator = self.stakingProviderToOperator[stakingProvider];\n emit AuthorizationIncreased(\n stakingProvider,\n operator,\n fromAmount,\n toAmount\n );\n }\n\n /// @notice Used by T staking contract to inform the application that the\n /// authorization decrease for the given staking provider has been\n /// requested.\n ///\n /// Reverts if the amount after deauthorization would be non-zero\n /// and lower than the minimum authorization.\n ///\n /// Reverts if another authorization decrease request is pending for\n /// the staking provider and not enough time passed since the\n /// original request (see `authorizationDecreaseChangePeriod`).\n ///\n /// If the operator is not known (`registerOperator` was not called)\n /// it lets to `approveAuthorizationDecrease` immediately. If the\n /// operator is known (`registerOperator` was called), the operator\n /// needs to update state of the sortition pool with a call to\n /// `joinSortitionPool` or `updateOperatorStatus`. After the\n /// sortition pool state is in sync, authorization decrease delay\n /// starts.\n ///\n /// After authorization decrease delay passes, authorization\n /// decrease request needs to be approved with a call to\n /// `approveAuthorizationDecrease` function.\n ///\n /// If there is a pending authorization decrease request, it is\n /// overwritten, but only if enough time passed since the original\n /// request. Otherwise, the function reverts.\n ///\n /// @dev Should only be callable by T staking contract.\n function authorizationDecreaseRequested(\n Data storage self,\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) internal {\n require(\n toAmount == 0 || toAmount >= self.parameters.minimumAuthorization,\n \"Authorization amount should be 0 or above the minimum\"\n );\n\n address operator = self.stakingProviderToOperator[stakingProvider];\n\n uint64 decreasingAt;\n\n if (operator == address(0)) {\n // Operator is not known. It means `registerOperator` was not\n // called yet, and there is no chance the operator could\n // call `joinSortitionPool`. We can let to approve authorization\n // decrease immediately because that operator was never in the\n // sortition pool.\n\n // solhint-disable-next-line not-rely-on-time\n decreasingAt = uint64(block.timestamp);\n } else {\n // Operator is known. It means that this operator is or was in\n // the sortition pool. Before authorization decrease delay starts,\n // the operator needs to update the state of the sortition pool\n // with a call to `joinSortitionPool` or `updateOperatorStatus`.\n // For now, we set `decreasingAt` as \"never decreasing\" and let\n // it be updated by `joinSortitionPool` or `updateOperatorStatus`\n // once we know the sortition pool is in sync.\n decreasingAt = type(uint64).max;\n }\n\n uint96 decreasingBy = fromAmount - toAmount;\n\n AuthorizationDecrease storage decreaseRequest = self.pendingDecreases[\n stakingProvider\n ];\n\n uint64 pendingDecreaseAt = decreaseRequest.decreasingAt;\n if (pendingDecreaseAt != 0 && pendingDecreaseAt != type(uint64).max) {\n // If there is already a pending authorization decrease request for\n // this staking provider and that request has been activated\n // (sortition pool was updated), require enough time to pass before\n // it can be overwritten.\n require(\n // solhint-disable-next-line not-rely-on-time\n block.timestamp >=\n pendingDecreaseAt -\n self.parameters.authorizationDecreaseChangePeriod,\n \"Not enough time passed since the original request\"\n );\n }\n\n decreaseRequest.decreasingBy = decreasingBy;\n decreaseRequest.decreasingAt = decreasingAt;\n\n emit AuthorizationDecreaseRequested(\n stakingProvider,\n operator,\n fromAmount,\n toAmount,\n decreasingAt\n );\n }\n\n /// @notice Approves the previously registered authorization decrease\n /// request. Reverts if authorization decrease delay have not passed\n /// yet or if the authorization decrease was not requested for the\n /// given staking provider.\n function approveAuthorizationDecrease(\n Data storage self,\n IStaking tokenStaking,\n address stakingProvider\n ) internal {\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n require(\n decrease.decreasingAt > 0,\n \"Authorization decrease not requested\"\n );\n require(\n decrease.decreasingAt != type(uint64).max,\n \"Authorization decrease request not activated\"\n );\n require(\n // solhint-disable-next-line not-rely-on-time\n block.timestamp >= decrease.decreasingAt,\n \"Authorization decrease delay not passed\"\n );\n\n emit AuthorizationDecreaseApproved(stakingProvider);\n\n // slither-disable-next-line unused-return\n tokenStaking.approveAuthorizationDecrease(stakingProvider);\n delete self.pendingDecreases[stakingProvider];\n }\n\n /// @notice Used by T staking contract to inform the application the\n /// authorization has been decreased for the given staking provider\n /// involuntarily, as a result of slashing.\n ///\n /// If the operator is not known (`registerOperator` was not called)\n /// the function does nothing. The operator was never in a sortition\n /// pool so there is nothing to update.\n ///\n /// If the operator is known, sortition pool is unlocked, and the\n /// operator is in the sortition pool, the sortition pool state is\n /// updated. If the sortition pool is locked, update needs to be\n /// postponed. Every other staker is incentivized to call\n /// `updateOperatorStatus` for the problematic operator to increase\n /// their own rewards in the pool.\n ///\n /// @dev Should only be callable by T staking contract.\n function involuntaryAuthorizationDecrease(\n Data storage self,\n IStaking tokenStaking,\n SortitionPool sortitionPool,\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) internal {\n address operator = self.stakingProviderToOperator[stakingProvider];\n\n if (operator == address(0)) {\n // Operator is not known. It means `registerOperator` was not\n // called yet, and there is no chance the operator could\n // call `joinSortitionPool`. We can just ignore this update because\n // operator was never in the sortition pool.\n return;\n } else {\n // Operator is known. It means that this operator is or was in the\n // sortition pool and the sortition pool may need to be updated.\n //\n // If the sortition pool is not locked and the operator is in the\n // sortition pool, we are updating it.\n //\n // To keep stakes synchronized between applications when staking\n // providers are slashed, without the risk of running out of gas,\n // the staking contract queues up slashings and let users process\n // the transactions. When an application slashes one or more staking\n // providers, it adds them to the slashing queue on the staking\n // contract. A queue entry contains the staking provider’s address\n // and the amount they are due to be slashed.\n //\n // When there is at least one staking provider in the slashing\n // queue, any account can submit a transaction processing one or\n // more staking providers' slashings, and collecting a reward for\n // doing so. A queued slashing is processed by updating the staking\n // provider’s stake to the post-slashing amount, updating authorized\n // amount for each affected application, and notifying all affected\n // applications that the staking provider’s authorized stake has\n // been reduced due to slashing.\n //\n // The entire idea is that the process transaction is expensive\n // because each application needs to be updated, so the reward for\n // the processor is hefty and comes from the slashed tokens.\n // Practically, it means that if the sortition pool is unlocked, and\n // can be updated, it should be updated because we already paid\n // someone for updating it.\n //\n // If the sortition pool is locked, update needs to wait. Other\n // sortition pool members are incentivized to call\n // `updateOperatorStatus` for the problematic operator because they\n // will increase their rewards this way.\n if (sortitionPool.isOperatorInPool(operator)) {\n if (sortitionPool.isLocked()) {\n emit InvoluntaryAuthorizationDecreaseFailed(\n stakingProvider,\n operator,\n fromAmount,\n toAmount\n );\n } else {\n updateOperatorStatus(\n self,\n tokenStaking,\n sortitionPool,\n operator\n );\n }\n }\n }\n }\n\n /// @notice Lets the operator join the sortition pool. The operator address\n /// must be known - before calling this function, it has to be\n /// appointed by the staking provider by calling `registerOperator`.\n /// Also, the operator must have the minimum authorization required\n /// by ECDSA. Function reverts if there is no minimum stake\n /// authorized or if the operator is not known. If there was an\n /// authorization decrease requested, it is activated by starting\n /// the authorization decrease delay.\n function joinSortitionPool(\n Data storage self,\n IStaking tokenStaking,\n SortitionPool sortitionPool\n ) internal {\n address operator = msg.sender;\n\n address stakingProvider = self.operatorToStakingProvider[operator];\n require(stakingProvider != address(0), \"Unknown operator\");\n\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n\n uint96 _eligibleStake = eligibleStake(\n self,\n tokenStaking,\n stakingProvider,\n decrease.decreasingBy\n );\n\n require(_eligibleStake != 0, \"Authorization below the minimum\");\n\n emit OperatorJoinedSortitionPool(stakingProvider, operator);\n\n sortitionPool.insertOperator(operator, _eligibleStake);\n\n // If there is a pending authorization decrease request, activate it.\n // At this point, the sortition pool state is up to date so the\n // authorization decrease delay can start counting.\n if (decrease.decreasingAt == type(uint64).max) {\n decrease.decreasingAt =\n // solhint-disable-next-line not-rely-on-time\n uint64(block.timestamp) +\n self.parameters.authorizationDecreaseDelay;\n }\n }\n\n /// @notice Updates status of the operator in the sortition pool. If there\n /// was an authorization decrease requested, it is activated by\n /// starting the authorization decrease delay.\n /// Function reverts if the operator is not known.\n function updateOperatorStatus(\n Data storage self,\n IStaking tokenStaking,\n SortitionPool sortitionPool,\n address operator\n ) internal {\n address stakingProvider = self.operatorToStakingProvider[operator];\n require(stakingProvider != address(0), \"Unknown operator\");\n\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n\n emit OperatorStatusUpdated(stakingProvider, operator);\n\n if (sortitionPool.isOperatorInPool(operator)) {\n uint96 _eligibleStake = eligibleStake(\n self,\n tokenStaking,\n stakingProvider,\n decrease.decreasingBy\n );\n\n sortitionPool.updateOperatorStatus(operator, _eligibleStake);\n }\n\n // If there is a pending authorization decrease request, activate it.\n // At this point, the sortition pool state is up to date so the\n // authorization decrease delay can start counting.\n if (decrease.decreasingAt == type(uint64).max) {\n decrease.decreasingAt =\n // solhint-disable-next-line not-rely-on-time\n uint64(block.timestamp) +\n self.parameters.authorizationDecreaseDelay;\n }\n }\n\n /// @notice Checks if the operator's authorized stake is in sync with\n /// operator's weight in the sortition pool.\n /// If the operator is not in the sortition pool and their\n /// authorized stake is non-zero, function returns false.\n function isOperatorUpToDate(\n Data storage self,\n IStaking tokenStaking,\n SortitionPool sortitionPool,\n address operator\n ) internal view returns (bool) {\n address stakingProvider = self.operatorToStakingProvider[operator];\n require(stakingProvider != address(0), \"Unknown operator\");\n\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n\n uint96 _eligibleStake = eligibleStake(\n self,\n tokenStaking,\n stakingProvider,\n decrease.decreasingBy\n );\n\n if (!sortitionPool.isOperatorInPool(operator)) {\n return _eligibleStake == 0;\n } else {\n return sortitionPool.isOperatorUpToDate(operator, _eligibleStake);\n }\n }\n\n /// @notice Returns the current value of the staking provider's eligible\n /// stake. Eligible stake is defined as the currently authorized\n /// stake minus the pending authorization decrease. Eligible stake\n /// is what is used for operator's weight in the pool. If the\n /// authorized stake minus the pending authorization decrease is\n /// below the minimum authorization, eligible stake is 0.\n /// @dev This function can be exposed to the public in contrast to the\n /// second variant accepting `decreasingBy` as a parameter.\n function eligibleStake(\n Data storage self,\n IStaking tokenStaking,\n address stakingProvider\n ) internal view returns (uint96) {\n return\n eligibleStake(\n self,\n tokenStaking,\n stakingProvider,\n pendingAuthorizationDecrease(self, stakingProvider)\n );\n }\n\n /// @notice Returns the current value of the staking provider's eligible\n /// stake. Eligible stake is defined as the currently authorized\n /// stake minus the pending authorization decrease. Eligible stake\n /// is what is used for operator's weight in the pool. If the\n /// authorized stake minus the pending authorization decrease is\n /// below the minimum authorization, eligible stake is 0.\n /// @dev This function is not intended to be exposes to the public.\n /// `decreasingBy` must be fetched from `pendingDecreases` mapping and\n /// it is passed as a parameter to optimize gas usage of functions that\n /// call `eligibleStake` and need to use `AuthorizationDecrease`\n /// fetched from `pendingDecreases` for some additional logic.\n function eligibleStake(\n Data storage self,\n IStaking tokenStaking,\n address stakingProvider,\n uint96 decreasingBy\n ) internal view returns (uint96) {\n uint96 authorizedStake = tokenStaking.authorizedStake(\n stakingProvider,\n address(this)\n );\n\n uint96 _eligibleStake = authorizedStake > decreasingBy\n ? authorizedStake - decreasingBy\n : 0;\n\n if (_eligibleStake < self.parameters.minimumAuthorization) {\n return 0;\n } else {\n return _eligibleStake;\n }\n }\n\n /// @notice Returns the amount of stake that is pending authorization\n /// decrease for the given staking provider. If no authorization\n /// decrease has been requested, returns zero.\n function pendingAuthorizationDecrease(\n Data storage self,\n address stakingProvider\n ) internal view returns (uint96) {\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n\n return decrease.decreasingBy;\n }\n\n /// @notice Returns the remaining time in seconds that needs to pass before\n /// the requested authorization decrease can be approved.\n /// If the sortition pool state was not updated yet by the operator\n /// after requesting the authorization decrease, returns\n /// `type(uint64).max`.\n function remainingAuthorizationDecreaseDelay(\n Data storage self,\n address stakingProvider\n ) internal view returns (uint64) {\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n\n if (decrease.decreasingAt == type(uint64).max) {\n return type(uint64).max;\n }\n\n // solhint-disable-next-line not-rely-on-time\n uint64 _now = uint64(block.timestamp);\n return _now > decrease.decreasingAt ? 0 : decrease.decreasingAt - _now;\n }\n}\n"
|
|
208
|
+
"@keep-network/random-beacon/contracts/ReimbursementPool.sol": {
|
|
209
|
+
"content": "// SPDX-License-Identifier: MIT\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity ^0.8.9;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\n\ncontract ReimbursementPool is Ownable, ReentrancyGuard {\n /// @notice Authorized contracts that can interact with the reimbursment pool.\n /// Authorization can be granted and removed by the owner.\n mapping(address => bool) public isAuthorized;\n\n /// @notice Static gas includes:\n /// - cost of the refund function\n /// - base transaction cost\n uint256 public staticGas;\n\n /// @notice Max gas price used to reimburse a transaction submitter. Protects\n /// against malicious operator-miners.\n uint256 public maxGasPrice;\n\n event StaticGasUpdated(uint256 newStaticGas);\n\n event MaxGasPriceUpdated(uint256 newMaxGasPrice);\n\n event SendingEtherFailed(uint256 refundAmount, address receiver);\n\n event AuthorizedContract(address thirdPartyContract);\n\n event UnauthorizedContract(address thirdPartyContract);\n\n event FundsWithdrawn(uint256 withdrawnAmount, address receiver);\n\n constructor(uint256 _staticGas, uint256 _maxGasPrice) {\n staticGas = _staticGas;\n maxGasPrice = _maxGasPrice;\n }\n\n /// @notice Receive ETH\n receive() external payable {}\n\n /// @notice Refunds ETH to a spender for executing specific transactions.\n /// @dev Ignoring the result of sending ETH to a receiver is made on purpose.\n /// For EOA receiving ETH should always work. If a receiver is a smart\n /// contract, then we do not want to fail a transaction, because in some\n /// cases the refund is done at the very end of multiple calls where all\n /// the previous calls were already paid off. It is a receiver's smart\n /// contract resposibility to make sure it can receive ETH.\n /// @dev Only authorized contracts are allowed calling this function.\n /// @param gasSpent Gas spent on a transaction that needs to be reimbursed.\n /// @param receiver Address where the reimbursment is sent.\n function refund(uint256 gasSpent, address receiver) external nonReentrant {\n require(\n isAuthorized[msg.sender],\n \"Contract is not authorized for a refund\"\n );\n require(receiver != address(0), \"Receiver's address cannot be zero\");\n\n uint256 gasPrice = tx.gasprice < maxGasPrice\n ? tx.gasprice\n : maxGasPrice;\n\n uint256 refundAmount = (gasSpent + staticGas) * gasPrice;\n\n /* solhint-disable avoid-low-level-calls */\n // slither-disable-next-line low-level-calls,unchecked-lowlevel\n (bool sent, ) = receiver.call{value: refundAmount}(\"\");\n /* solhint-enable avoid-low-level-calls */\n if (!sent) {\n // slither-disable-next-line reentrancy-events\n emit SendingEtherFailed(refundAmount, receiver);\n }\n }\n\n /// @notice Authorize a contract that can interact with this reimbursment pool.\n /// Can be authorized by the owner only.\n /// @param _contract Authorized contract.\n function authorize(address _contract) external onlyOwner {\n isAuthorized[_contract] = true;\n\n emit AuthorizedContract(_contract);\n }\n\n /// @notice Unauthorize a contract that was previously authorized to interact\n /// with this reimbursment pool. Can be unauthorized by the\n /// owner only.\n /// @param _contract Authorized contract.\n function unauthorize(address _contract) external onlyOwner {\n delete isAuthorized[_contract];\n\n emit UnauthorizedContract(_contract);\n }\n\n /// @notice Setting a static gas cost for executing a transaction. Can be set\n /// by the owner only.\n /// @param _staticGas Static gas cost.\n function setStaticGas(uint256 _staticGas) external onlyOwner {\n staticGas = _staticGas;\n\n emit StaticGasUpdated(_staticGas);\n }\n\n /// @notice Setting a max gas price for transactions. Can be set by the\n /// owner only.\n /// @param _maxGasPrice Max gas price used to reimburse tx submitters.\n function setMaxGasPrice(uint256 _maxGasPrice) external onlyOwner {\n maxGasPrice = _maxGasPrice;\n\n emit MaxGasPriceUpdated(_maxGasPrice);\n }\n\n /// @notice Withdraws all ETH from this pool which are sent to a given\n /// address. Can be set by the owner only.\n /// @param receiver An address where ETH is sent.\n function withdrawAll(address receiver) external onlyOwner {\n withdraw(address(this).balance, receiver);\n }\n\n /// @notice Withdraws ETH amount from this pool which are sent to a given\n /// address. Can be set by the owner only.\n /// @param amount Amount to withdraw from the pool.\n /// @param receiver An address where ETH is sent.\n function withdraw(uint256 amount, address receiver) public onlyOwner {\n require(\n address(this).balance >= amount,\n \"Insufficient contract balance\"\n );\n require(receiver != address(0), \"Receiver's address cannot be zero\");\n\n emit FundsWithdrawn(amount, receiver);\n\n /* solhint-disable avoid-low-level-calls */\n // slither-disable-next-line low-level-calls,arbitrary-send\n (bool sent, ) = receiver.call{value: amount}(\"\");\n /* solhint-enable avoid-low-level-calls */\n require(sent, \"Failed to send Ether\");\n }\n}\n"
|
|
216
210
|
},
|
|
217
|
-
"@
|
|
218
|
-
"content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity 0.8.9;\n\n/// @title Interface of Threshold Network staking contract\n/// @notice The staking contract enables T owners to have their wallets offline\n/// and their stake managed by staking providers on their behalf.\n/// The staking contract does not define operator role. The operator\n/// responsible for running off-chain client software is appointed by\n/// the staking provider in the particular application utilizing the\n/// staking contract. All off-chain client software should be able\n/// to run without exposing operator's or staking provider’s private\n/// key and should not require any owner’s keys at all. The stake\n/// delegation optimizes the network throughput without compromising the\n/// security of the owners’ stake.\ninterface IStaking {\n enum StakeType {\n NU,\n KEEP,\n T\n }\n\n //\n //\n // Delegating a stake\n //\n //\n\n /// @notice Creates a delegation with `msg.sender` owner with the given\n /// staking provider, beneficiary, and authorizer. Transfers the\n /// given amount of T to the staking contract.\n /// @dev The owner of the delegation needs to have the amount approved to\n /// transfer to the staking contract.\n function stake(\n address stakingProvider,\n address payable beneficiary,\n address authorizer,\n uint96 amount\n ) external;\n\n /// @notice Copies delegation from the legacy KEEP staking contract to T\n /// staking contract. No tokens are transferred. Caches the active\n /// stake amount from KEEP staking contract. Can be called by\n /// anyone.\n /// @dev The staking provider in T staking contract is the legacy KEEP\n /// staking contract operator.\n function stakeKeep(address stakingProvider) external;\n\n /// @notice Copies delegation from the legacy NU staking contract to T\n /// staking contract, additionally appointing staking provider,\n /// beneficiary and authorizer roles. Caches the amount staked in NU\n /// staking contract. Can be called only by the original delegation\n /// owner.\n function stakeNu(\n address stakingProvider,\n address payable beneficiary,\n address authorizer\n ) external;\n\n /// @notice Refresh Keep stake owner. Can be called only by the old owner.\n /// @dev The staking provider in T staking contract is the legacy KEEP\n /// staking contract operator.\n function refreshKeepStakeOwner(address stakingProvider) external;\n\n /// @notice Allows the Governance to set the minimum required stake amount.\n /// This amount is required to protect against griefing the staking\n /// contract and individual applications are allowed to require\n /// higher minimum stakes if necessary.\n function setMinimumStakeAmount(uint96 amount) external;\n\n //\n //\n // Authorizing an application\n //\n //\n\n /// @notice Allows the Governance to approve the particular application\n /// before individual stake authorizers are able to authorize it.\n function approveApplication(address application) external;\n\n /// @notice Increases the authorization of the given staking provider for\n /// the given application by the given amount. Can only be called by\n /// the authorizer for that staking provider.\n /// @dev Calls `authorizationIncreased(address stakingProvider, uint256 amount)`\n /// on the given application to notify the application about\n /// authorization change. See `IApplication`.\n function increaseAuthorization(\n address stakingProvider,\n address application,\n uint96 amount\n ) external;\n\n /// @notice Requests decrease of the authorization for the given staking\n /// provider on the given application by the provided amount.\n /// It may not change the authorized amount immediatelly. When\n /// it happens depends on the application. Can only be called by the\n /// given staking provider’s authorizer. Overwrites pending\n /// authorization decrease for the given staking provider and\n /// application.\n /// @dev Calls `authorizationDecreaseRequested(address stakingProvider, uint256 amount)`\n /// on the given application. See `IApplication`.\n function requestAuthorizationDecrease(\n address stakingProvider,\n address application,\n uint96 amount\n ) external;\n\n /// @notice Requests decrease of all authorizations for the given staking\n /// provider on all applications by all authorized amount.\n /// It may not change the authorized amount immediatelly. When\n /// it happens depends on the application. Can only be called by the\n /// given staking provider’s authorizer. Overwrites pending\n /// authorization decrease for the given staking provider and\n /// application.\n /// @dev Calls `authorizationDecreaseRequested(address stakingProvider, uint256 amount)`\n /// for each authorized application. See `IApplication`.\n function requestAuthorizationDecrease(address stakingProvider) external;\n\n /// @notice Called by the application at its discretion to approve the\n /// previously requested authorization decrease request. Can only be\n /// called by the application that was previously requested to\n /// decrease the authorization for that staking provider.\n /// Returns resulting authorized amount for the application.\n function approveAuthorizationDecrease(address stakingProvider)\n external\n returns (uint96);\n\n /// @notice Decreases the authorization for the given `stakingProvider` on\n /// the given disabled `application`, for all authorized amount.\n /// Can be called by anyone.\n function forceDecreaseAuthorization(\n address stakingProvider,\n address application\n ) external;\n\n /// @notice Pauses the given application’s eligibility to slash stakes.\n /// Besides that stakers can't change authorization to the application.\n /// Can be called only by the Panic Button of the particular\n /// application. The paused application can not slash stakes until\n /// it is approved again by the Governance using `approveApplication`\n /// function. Should be used only in case of an emergency.\n function pauseApplication(address application) external;\n\n /// @notice Disables the given application. The disabled application can't\n /// slash stakers. Also stakers can't increase authorization to that\n /// application but can decrease without waiting by calling\n /// `requestAuthorizationDecrease` at any moment. Can be called only\n /// by the governance. The disabled application can't be approved\n /// again. Should be used only in case of an emergency.\n function disableApplication(address application) external;\n\n /// @notice Sets the Panic Button role for the given application to the\n /// provided address. Can only be called by the Governance. If the\n /// Panic Button for the given application should be disabled, the\n /// role address should be set to 0x0 address.\n function setPanicButton(address application, address panicButton) external;\n\n /// @notice Sets the maximum number of applications one staking provider can\n /// have authorized. Used to protect against DoSing slashing queue.\n /// Can only be called by the Governance.\n function setAuthorizationCeiling(uint256 ceiling) external;\n\n //\n //\n // Stake top-up\n //\n //\n\n /// @notice Increases the amount of the stake for the given staking provider.\n /// Can be called only by the owner or the staking provider.\n /// @dev The sender of this transaction needs to have the amount approved to\n /// transfer to the staking contract.\n function topUp(address stakingProvider, uint96 amount) external;\n\n /// @notice Propagates information about stake top-up from the legacy KEEP\n /// staking contract to T staking contract. Can be called only by\n /// the owner or the staking provider.\n function topUpKeep(address stakingProvider) external;\n\n /// @notice Propagates information about stake top-up from the legacy NU\n /// staking contract to T staking contract. Can be called only by\n /// the owner or the staking provider.\n function topUpNu(address stakingProvider) external;\n\n //\n //\n // Undelegating a stake (unstaking)\n //\n //\n\n /// @notice Reduces the liquid T stake amount by the provided amount and\n /// withdraws T to the owner. Reverts if there is at least one\n /// authorization higher than the sum of the legacy stake and\n /// remaining liquid T stake or if the unstake amount is higher than\n /// the liquid T stake amount. Can be called only by the delegation\n /// owner or the staking provider.\n function unstakeT(address stakingProvider, uint96 amount) external;\n\n /// @notice Sets the legacy KEEP staking contract active stake amount cached\n /// in T staking contract to 0. Reverts if the amount of liquid T\n /// staked in T staking contract is lower than the highest\n /// application authorization. This function allows to unstake from\n /// KEEP staking contract and still being able to operate in T\n /// network and earning rewards based on the liquid T staked. Can be\n /// called only by the delegation owner or the staking provider.\n function unstakeKeep(address stakingProvider) external;\n\n /// @notice Reduces cached legacy NU stake amount by the provided amount.\n /// Reverts if there is at least one authorization higher than the\n /// sum of remaining legacy NU stake and liquid T stake for that\n /// staking provider or if the untaked amount is higher than the\n /// cached legacy stake amount. If succeeded, the legacy NU stake\n /// can be partially or fully undelegated on the legacy staking\n /// contract. This function allows to unstake from NU staking\n /// contract and still being able to operate in T network and\n /// earning rewards based on the liquid T staked. Can be called only\n /// by the delegation owner or the staking provider.\n function unstakeNu(address stakingProvider, uint96 amount) external;\n\n /// @notice Sets cached legacy stake amount to 0, sets the liquid T stake\n /// amount to 0 and withdraws all liquid T from the stake to the\n /// owner. Reverts if there is at least one non-zero authorization.\n /// Can be called only by the delegation owner or the staking\n /// provider.\n function unstakeAll(address stakingProvider) external;\n\n //\n //\n // Keeping information in sync\n //\n //\n\n /// @notice Notifies about the discrepancy between legacy KEEP active stake\n /// and the amount cached in T staking contract. Slashes the staking\n /// provider in case the amount cached is higher than the actual\n /// active stake amount in KEEP staking contract. Needs to update\n /// authorizations of all affected applications and execute an\n /// involuntary allocation decrease on all affected applications.\n /// Can be called by anyone, notifier receives a reward.\n function notifyKeepStakeDiscrepancy(address stakingProvider) external;\n\n /// @notice Notifies about the discrepancy between legacy NU active stake\n /// and the amount cached in T staking contract. Slashes the\n /// staking provider in case the amount cached is higher than the\n /// actual active stake amount in NU staking contract. Needs to\n /// update authorizations of all affected applications and execute\n /// an involuntary allocation decrease on all affected applications.\n /// Can be called by anyone, notifier receives a reward.\n function notifyNuStakeDiscrepancy(address stakingProvider) external;\n\n /// @notice Sets the penalty amount for stake discrepancy and reward\n /// multiplier for reporting it. The penalty is seized from the\n /// delegated stake, and 5% of the penalty, scaled by the\n /// multiplier, is given to the notifier. The rest of the tokens are\n /// burned. Can only be called by the Governance. See `seize` function.\n function setStakeDiscrepancyPenalty(\n uint96 penalty,\n uint256 rewardMultiplier\n ) external;\n\n /// @notice Sets reward in T tokens for notification of misbehaviour\n /// of one staking provider. Can only be called by the governance.\n function setNotificationReward(uint96 reward) external;\n\n /// @notice Transfer some amount of T tokens as reward for notifications\n /// of misbehaviour\n function pushNotificationReward(uint96 reward) external;\n\n /// @notice Withdraw some amount of T tokens from notifiers treasury.\n /// Can only be called by the governance.\n function withdrawNotificationReward(address recipient, uint96 amount)\n external;\n\n /// @notice Adds staking providers to the slashing queue along with the\n /// amount that should be slashed from each one of them. Can only be\n /// called by application authorized for all staking providers in\n /// the array.\n function slash(uint96 amount, address[] memory stakingProviders) external;\n\n /// @notice Adds staking providers to the slashing queue along with the\n /// amount. The notifier will receive reward per each staking\n /// provider from notifiers treasury. Can only be called by\n /// application authorized for all staking providers in the array.\n function seize(\n uint96 amount,\n uint256 rewardMultipier,\n address notifier,\n address[] memory stakingProviders\n ) external;\n\n /// @notice Takes the given number of queued slashing operations and\n /// processes them. Receives 5% of the slashed amount.\n /// Executes `involuntaryAllocationDecrease` function on each\n /// affected application.\n function processSlashing(uint256 count) external;\n\n //\n //\n // Auxiliary functions\n //\n //\n\n /// @notice Returns the authorized stake amount of the staking provider for\n /// the application.\n function authorizedStake(address stakingProvider, address application)\n external\n view\n returns (uint96);\n\n /// @notice Returns staked amount of T, Keep and Nu for the specified\n /// staking provider.\n /// @dev All values are in T denomination\n function stakes(address stakingProvider)\n external\n view\n returns (\n uint96 tStake,\n uint96 keepInTStake,\n uint96 nuInTStake\n );\n\n /// @notice Returns start staking timestamp.\n /// @dev This value is set at most once.\n function getStartStakingTimestamp(address stakingProvider)\n external\n view\n returns (uint256);\n\n /// @notice Returns staked amount of NU for the specified staking provider.\n function stakedNu(address stakingProvider) external view returns (uint256);\n\n /// @notice Gets the stake owner, the beneficiary and the authorizer\n /// for the specified staking provider address.\n /// @return owner Stake owner address.\n /// @return beneficiary Beneficiary address.\n /// @return authorizer Authorizer address.\n function rolesOf(address stakingProvider)\n external\n view\n returns (\n address owner,\n address payable beneficiary,\n address authorizer\n );\n\n /// @notice Returns length of application array\n function getApplicationsLength() external view returns (uint256);\n\n /// @notice Returns length of slashing queue\n function getSlashingQueueLength() external view returns (uint256);\n\n /// @notice Returns minimum possible stake for T, KEEP or NU in T\n /// denomination.\n /// @dev For example, suppose the given staking provider has 10 T, 20 T\n /// worth of KEEP, and 30 T worth of NU all staked, and the maximum\n /// application authorization is 40 T, then `getMinStaked` for\n /// that staking provider returns:\n /// * 0 T if KEEP stake type specified i.e.\n /// min = 40 T max - (10 T + 30 T worth of NU) = 0 T\n /// * 10 T if NU stake type specified i.e.\n /// min = 40 T max - (10 T + 20 T worth of KEEP) = 10 T\n /// * 0 T if T stake type specified i.e.\n /// min = 40 T max - (20 T worth of KEEP + 30 T worth of NU) < 0 T\n /// In other words, the minimum stake amount for the specified\n /// stake type is the minimum amount of stake of the given type\n /// needed to satisfy the maximum application authorization given the\n /// staked amounts of the other stake types for that staking provider.\n function getMinStaked(address stakingProvider, StakeType stakeTypes)\n external\n view\n returns (uint96);\n\n /// @notice Returns available amount to authorize for the specified application\n function getAvailableToAuthorize(\n address stakingProvider,\n address application\n ) external view returns (uint96);\n}\n"
|
|
211
|
+
"@openzeppelin/contracts/security/ReentrancyGuard.sol": {
|
|
212
|
+
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n constructor() {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and making it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n // On the first call to nonReentrant, _notEntered will be true\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n}\n"
|
|
219
213
|
},
|
|
220
214
|
"@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol": {
|
|
221
215
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/transparent/ProxyAdmin.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./TransparentUpgradeableProxy.sol\";\nimport \"../../access/Ownable.sol\";\n\n/**\n * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an\n * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.\n */\ncontract ProxyAdmin is Ownable {\n /**\n * @dev Returns the current implementation of `proxy`.\n *\n * Requirements:\n *\n * - This contract must be the admin of `proxy`.\n */\n function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {\n // We need to manually run the static call since the getter cannot be flagged as view\n // bytes4(keccak256(\"implementation()\")) == 0x5c60da1b\n (bool success, bytes memory returndata) = address(proxy).staticcall(hex\"5c60da1b\");\n require(success);\n return abi.decode(returndata, (address));\n }\n\n /**\n * @dev Returns the current admin of `proxy`.\n *\n * Requirements:\n *\n * - This contract must be the admin of `proxy`.\n */\n function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {\n // We need to manually run the static call since the getter cannot be flagged as view\n // bytes4(keccak256(\"admin()\")) == 0xf851a440\n (bool success, bytes memory returndata) = address(proxy).staticcall(hex\"f851a440\");\n require(success);\n return abi.decode(returndata, (address));\n }\n\n /**\n * @dev Changes the admin of `proxy` to `newAdmin`.\n *\n * Requirements:\n *\n * - This contract must be the current admin of `proxy`.\n */\n function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {\n proxy.changeAdmin(newAdmin);\n }\n\n /**\n * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.\n *\n * Requirements:\n *\n * - This contract must be the admin of `proxy`.\n */\n function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {\n proxy.upgradeTo(implementation);\n }\n\n /**\n * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See\n * {TransparentUpgradeableProxy-upgradeToAndCall}.\n *\n * Requirements:\n *\n * - This contract must be the admin of `proxy`.\n */\n function upgradeAndCall(\n TransparentUpgradeableProxy proxy,\n address implementation,\n bytes memory data\n ) public payable virtual onlyOwner {\n proxy.upgradeToAndCall{value: msg.value}(implementation, data);\n }\n}\n"
|
|
@@ -247,17 +241,32 @@
|
|
|
247
241
|
"contracts/test/TestERC20.sol": {
|
|
248
242
|
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\n\nimport \"@thesis/solidity-contracts/contracts/token/ERC20WithPermit.sol\";\n\ncontract TestERC20 is ERC20WithPermit {\n string public constant NAME = \"Test ERC20 Token\";\n string public constant SYMBOL = \"TT\";\n\n constructor() ERC20WithPermit(NAME, SYMBOL) {}\n}\n"
|
|
249
243
|
},
|
|
244
|
+
"@keep-network/ecdsa/contracts/libraries/EcdsaInactivity.sol": {
|
|
245
|
+
"content": "// SPDX-License-Identifier: MIT\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity ^0.8.9;\n\nimport \"@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol\";\n\nimport \"@keep-network/random-beacon/contracts/libraries/BytesLib.sol\";\nimport \"@keep-network/sortition-pools/contracts/SortitionPool.sol\";\n\nimport \"./Wallets.sol\";\n\nlibrary EcdsaInactivity {\n using BytesLib for bytes;\n using ECDSAUpgradeable for bytes32;\n\n struct Claim {\n // ID of the wallet whose signing group is raising the inactivity claim.\n bytes32 walletID;\n // Indices of group members accused of being inactive. Indices must be in\n // range [1, groupMembers.length], unique, and sorted in ascending order.\n uint256[] inactiveMembersIndices;\n // Indicates if inactivity claim is a wallet-wide heartbeat failure.\n // If wallet failed a heartbeat, this is signalled to the wallet owner\n // who may decide to move responsibilities to another wallet\n // given that the wallet who failed the heartbeat is at risk of not\n // being able to sign messages soon.\n bool heartbeatFailed;\n // Concatenation of signatures from members supporting the claim.\n // The message to be signed by each member is keccak256 hash of the\n // concatenation of inactivity claim nonce for the given wallet, wallet\n // public key, inactive members indices, and boolean flag indicating\n // if this is a wallet-wide heartbeat failure. The calculated hash should\n // be prefixed with `\\x19Ethereum signed message:\\n` before signing, so\n // the message to sign is:\n // `\\x19Ethereum signed message:\\n${keccak256(\n // nonce | walletPubKey | inactiveMembersIndices | heartbeatFailed\n // )}`\n bytes signatures;\n // Indices of members corresponding to each signature. Indices must be\n // in range [1, groupMembers.length], unique, and sorted in ascending\n // order.\n uint256[] signingMembersIndices;\n // This struct doesn't contain `__gap` property as the structure is not\n // stored, it is used as a function's calldata argument.\n }\n\n /// @notice The minimum number of wallet signing group members needed to\n /// interact according to the protocol to produce a valid inactivity\n /// claim.\n uint256 public constant groupThreshold = 51;\n\n /// @notice Size in bytes of a single signature produced by member\n /// supporting the inactivity claim.\n uint256 public constant signatureByteSize = 65;\n\n /// @notice Verifies the inactivity claim according to the rules defined in\n /// `Claim` struct documentation. Reverts if verification fails.\n /// @dev Wallet signing group members hash is validated upstream in\n /// `WalletRegistry.notifyOperatorInactivity()`\n /// @param sortitionPool Sortition pool reference\n /// @param claim Inactivity claim\n /// @param walletPubKey Public key of the wallet\n /// @param nonce Current inactivity nonce for wallet used in the claim\n /// @param groupMembers Identifiers of group members\n /// @return inactiveMembers Identifiers of members who are inactive\n function verifyClaim(\n SortitionPool sortitionPool,\n Claim calldata claim,\n bytes memory walletPubKey,\n uint256 nonce,\n uint32[] calldata groupMembers\n ) external view returns (uint32[] memory inactiveMembers) {\n // Validate inactive members indices. Maximum indices count is equal to\n // the group size and is not limited deliberately to leave a theoretical\n // possibility to accuse more members than `groupSize - groupThreshold`.\n validateMembersIndices(\n claim.inactiveMembersIndices,\n groupMembers.length\n );\n\n // Validate signatures array is properly formed and number of\n // signatures and signers is correct.\n uint256 signaturesCount = claim.signatures.length / signatureByteSize;\n require(claim.signatures.length != 0, \"No signatures provided\");\n require(\n claim.signatures.length % signatureByteSize == 0,\n \"Malformed signatures array\"\n );\n require(\n signaturesCount == claim.signingMembersIndices.length,\n \"Unexpected signatures count\"\n );\n require(signaturesCount >= groupThreshold, \"Too few signatures\");\n require(signaturesCount <= groupMembers.length, \"Too many signatures\");\n\n // Validate signing members indices. Note that `signingMembersIndices`\n // were already partially validated during `signatures` parameter\n // validation.\n validateMembersIndices(\n claim.signingMembersIndices,\n groupMembers.length\n );\n\n bytes32 signedMessageHash = keccak256(\n abi.encodePacked(\n nonce,\n walletPubKey,\n claim.inactiveMembersIndices,\n claim.heartbeatFailed\n )\n ).toEthSignedMessageHash();\n\n address[] memory groupMembersAddresses = sortitionPool.getIDOperators(\n groupMembers\n );\n\n // Verify each signature.\n bytes memory checkedSignature;\n bool senderSignatureExists = false;\n for (uint256 i = 0; i < signaturesCount; i++) {\n uint256 memberIndex = claim.signingMembersIndices[i];\n checkedSignature = claim.signatures.slice(\n signatureByteSize * i,\n signatureByteSize\n );\n address recoveredAddress = signedMessageHash.recover(\n checkedSignature\n );\n\n require(\n groupMembersAddresses[memberIndex - 1] == recoveredAddress,\n \"Invalid signature\"\n );\n\n if (!senderSignatureExists && msg.sender == recoveredAddress) {\n senderSignatureExists = true;\n }\n }\n\n require(senderSignatureExists, \"Sender must be claim signer\");\n\n inactiveMembers = new uint32[](claim.inactiveMembersIndices.length);\n for (uint256 i = 0; i < claim.inactiveMembersIndices.length; i++) {\n uint256 memberIndex = claim.inactiveMembersIndices[i];\n inactiveMembers[i] = groupMembers[memberIndex - 1];\n }\n\n return inactiveMembers;\n }\n\n /// @notice Validates members indices array. Array is considered valid\n /// if its size and each single index are in [1, groupSize] range,\n /// indexes are unique, and sorted in an ascending order.\n /// Reverts if validation fails.\n /// @param indices Array to validate.\n /// @param groupSize Group size used as reference.\n function validateMembersIndices(\n uint256[] calldata indices,\n uint256 groupSize\n ) internal view {\n require(\n indices.length > 0 && indices.length <= groupSize,\n \"Corrupted members indices\"\n );\n\n // Check if first and last indices are in range [1, groupSize].\n // This check combined with the loop below makes sure every single\n // index is in the correct range.\n require(\n indices[0] > 0 && indices[indices.length - 1] <= groupSize,\n \"Corrupted members indices\"\n );\n\n for (uint256 i = 0; i < indices.length - 1; i++) {\n // Check whether given index is smaller than the next one. This\n // way we are sure indexes are ordered in the ascending order\n // and there are no duplicates.\n require(indices[i] < indices[i + 1], \"Corrupted members indices\");\n }\n }\n}\n"
|
|
246
|
+
},
|
|
247
|
+
"@keep-network/ecdsa/contracts/libraries/Wallets.sol": {
|
|
248
|
+
"content": "// SPDX-License-Identifier: MIT\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity ^0.8.9;\n\nlibrary Wallets {\n struct Wallet {\n // Keccak256 hash of group members identifiers array. Group members do not\n // include operators selected by the sortition pool that misbehaved during DKG.\n bytes32 membersIdsHash;\n // Uncompressed ECDSA public key stored as X and Y coordinates (32 bytes each).\n bytes32 publicKeyX;\n bytes32 publicKeyY;\n // This struct doesn't contain `__gap` property as the structure is stored\n // in a mapping, mappings store values in different slots and they are\n // not contiguous with other values.\n }\n\n struct Data {\n // Mapping of keccak256 hashes of wallet public keys to wallet details.\n // Hash of public key is considered an unique wallet identifier.\n mapping(bytes32 => Wallet) registry;\n // Reserved storage space in case we need to add more variables.\n // See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n // slither-disable-next-line unused-state\n uint256[49] __gap;\n }\n\n /// @notice Registers a new wallet.\n /// @dev Uses a public key hash as a unique identifier of a wallet.\n /// @param membersIdsHash Keccak256 hash of group members identifiers array\n /// @param publicKey Uncompressed public key\n /// @return walletID Wallet's ID\n /// @return publicKeyX Wallet's public key's X coordinate\n /// @return publicKeyY Wallet's public key's Y coordinate\n function addWallet(\n Data storage self,\n bytes32 membersIdsHash,\n bytes calldata publicKey\n )\n internal\n returns (\n bytes32 walletID,\n bytes32 publicKeyX,\n bytes32 publicKeyY\n )\n {\n walletID = keccak256(publicKey);\n\n require(\n self.registry[walletID].publicKeyX == bytes32(0),\n \"Wallet with the given public key already exists\"\n );\n require(publicKey.length == 64, \"Invalid length of the public key\");\n\n publicKeyX = bytes32(publicKey[:32]);\n publicKeyY = bytes32(publicKey[32:]);\n\n self.registry[walletID].membersIdsHash = membersIdsHash;\n self.registry[walletID].publicKeyX = publicKeyX;\n self.registry[walletID].publicKeyY = publicKeyY;\n }\n\n /// @notice Deletes wallet with the given ID from the registry. Reverts\n /// if wallet with the given ID has not been registered or if it\n /// has already been closed.\n function deleteWallet(Data storage self, bytes32 walletID) internal {\n require(\n isWalletRegistered(self, walletID),\n \"Wallet with the given ID has not been registered\"\n );\n\n delete self.registry[walletID];\n }\n\n /// @notice Checks if a wallet with the given ID is registered.\n /// @param walletID Wallet's ID\n /// @return True if a wallet is registered, false otherwise\n function isWalletRegistered(Data storage self, bytes32 walletID)\n internal\n view\n returns (bool)\n {\n return self.registry[walletID].publicKeyX != bytes32(0);\n }\n\n /// @notice Returns Keccak256 hash of the wallet signing group members\n /// identifiers array. Group members do not include operators\n /// selected by the sortition pool that misbehaved during DKG.\n /// Reverts if wallet with the given ID is not registered.\n /// @param walletID ID of the wallet\n /// @return Wallet signing group members hash\n function getWalletMembersIdsHash(Data storage self, bytes32 walletID)\n internal\n view\n returns (bytes32)\n {\n require(\n isWalletRegistered(self, walletID),\n \"Wallet with the given ID has not been registered\"\n );\n\n return self.registry[walletID].membersIdsHash;\n }\n\n /// @notice Gets public key of a wallet with the given wallet ID.\n /// The public key is returned as X and Y coordinates.\n /// Reverts if wallet with the given ID is not registered.\n /// @param walletID ID of the wallet\n /// @return x Public key X coordinate\n /// @return y Public key Y coordinate\n function getWalletPublicKeyCoordinates(Data storage self, bytes32 walletID)\n internal\n view\n returns (bytes32 x, bytes32 y)\n {\n require(\n isWalletRegistered(self, walletID),\n \"Wallet with the given ID has not been registered\"\n );\n\n Wallet storage wallet = self.registry[walletID];\n\n return (wallet.publicKeyX, wallet.publicKeyY);\n }\n\n /// @notice Gets public key of a wallet with the given wallet ID.\n /// The public key is returned in an uncompressed format as a 64-byte\n /// concatenation of X and Y coordinates.\n /// Reverts if wallet with the given ID is not registered.\n /// @param walletID ID of the wallet\n /// @return Uncompressed public key of the wallet\n function getWalletPublicKey(Data storage self, bytes32 walletID)\n internal\n view\n returns (bytes memory)\n {\n (bytes32 x, bytes32 y) = getWalletPublicKeyCoordinates(self, walletID);\n return bytes.concat(x, y);\n }\n}\n"
|
|
249
|
+
},
|
|
250
|
+
"@keep-network/ecdsa/contracts/libraries/EcdsaAuthorization.sol": {
|
|
251
|
+
"content": "// SPDX-License-Identifier: MIT\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n//\n\npragma solidity ^0.8.9;\n\nimport \"@keep-network/sortition-pools/contracts/SortitionPool.sol\";\nimport \"@threshold-network/solidity-contracts/contracts/staking/IStaking.sol\";\n\n/// @notice Library managing the state of stake authorizations for ECDSA\n/// operator contract and the presence of operators in the sortition\n/// pool based on the stake authorized for them.\nlibrary EcdsaAuthorization {\n struct Parameters {\n // The minimum authorization required by ECDSA application so that\n // operator can join the sortition pool and do the work.\n uint96 minimumAuthorization;\n // Authorization decrease delay in seconds between the time\n // authorization decrease is requested and the time the authorization\n // decrease can be approved. It is always the same value, no matter if\n // authorization decrease amount is small, significant, or if it is\n // a decrease to zero.\n uint64 authorizationDecreaseDelay;\n // The time period before the authorization decrease delay end,\n // during which the authorization decrease request can be overwritten.\n //\n // When the request is overwritten, the authorization decrease delay is\n // reset.\n //\n // For example, if `authorizationDecraseChangePeriod` is set to 4\n // days, `authorizationDecreaseDelay` is set to 14 days, and someone\n // requested authorization decrease, it means they can not\n // request another decrease for the first 10 days. After 10 days pass,\n // they can request again and overwrite the previous authorization\n // decrease request. The delay time will reset for them and they\n // will have to wait another 10 days to alter it and 14 days to\n // approve it.\n //\n // This value protects against malicious operators who manipulate\n // their weight by overwriting authorization decrease request, and\n // lowering or increasing their eligible stake this way.\n //\n // If set to a value equal to `authorizationDecreaseDelay, it means\n // that authorization decrease request can be always overwritten.\n // If set to zero, it means authorization decrease request can not be\n // overwritten until the delay end, and one needs to wait for the entire\n // authorization decrease delay to approve their decrease and request\n // for another one or to overwrite the pending one.\n //\n // (1) authorization decrease requested timestamp\n // (2) from this moment authorization decrease request can be\n // overwritten\n // (3) from this moment authorization decrease request can be\n // approved, assuming it was NOT overwritten in (2)\n //\n // (1) (2) (3)\n // --x------------------------------x--------------------------x---->\n // | \\________________________/\n // | authorizationDecreaseChangePeriod\n // \\______________________________________________________/\n // authorizationDecreaseDelay\n //\n uint64 authorizationDecreaseChangePeriod;\n // This struct doesn't contain `__gap` property as the structure is\n // stored inside `Data` struct, that already have a gap that can be used\n // on upgrade.\n }\n\n struct AuthorizationDecrease {\n uint96 decreasingBy; // amount\n uint64 decreasingAt; // timestamp\n }\n\n struct Data {\n Parameters parameters;\n mapping(address => address) stakingProviderToOperator;\n mapping(address => address) operatorToStakingProvider;\n mapping(address => AuthorizationDecrease) pendingDecreases;\n // Reserved storage space in case we need to add more variables.\n // See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n // slither-disable-next-line unused-state\n uint256[46] __gap;\n }\n\n event OperatorRegistered(\n address indexed stakingProvider,\n address indexed operator\n );\n\n event AuthorizationIncreased(\n address indexed stakingProvider,\n address indexed operator,\n uint96 fromAmount,\n uint96 toAmount\n );\n\n event AuthorizationDecreaseRequested(\n address indexed stakingProvider,\n address indexed operator,\n uint96 fromAmount,\n uint96 toAmount,\n uint64 decreasingAt\n );\n\n event AuthorizationDecreaseApproved(address indexed stakingProvider);\n\n event InvoluntaryAuthorizationDecreaseFailed(\n address indexed stakingProvider,\n address indexed operator,\n uint96 fromAmount,\n uint96 toAmount\n );\n\n event OperatorJoinedSortitionPool(\n address indexed stakingProvider,\n address indexed operator\n );\n\n event OperatorStatusUpdated(\n address indexed stakingProvider,\n address indexed operator\n );\n\n /// @notice Sets the minimum authorization for ECDSA application. Without\n /// at least the minimum authorization, staking provider is not\n /// eligible to join and operate in the network.\n function setMinimumAuthorization(\n Data storage self,\n uint96 _minimumAuthorization\n ) internal {\n self.parameters.minimumAuthorization = _minimumAuthorization;\n }\n\n /// @notice Sets the authorization decrease delay. It is the time in seconds\n /// that needs to pass between the time authorization decrease is\n /// requested and the time the authorization decrease can be\n /// approved, no matter the authorization decrease amount.\n function setAuthorizationDecreaseDelay(\n Data storage self,\n uint64 _authorizationDecreaseDelay\n ) internal {\n self\n .parameters\n .authorizationDecreaseDelay = _authorizationDecreaseDelay;\n }\n\n /// @notice Sets the authorization decrease change period. It is the time\n /// period before the authorization decrease delay end,\n /// during which the authorization decrease request can be\n /// overwritten.\n function setAuthorizationDecreaseChangePeriod(\n Data storage self,\n uint64 _authorizationDecreaseChangePeriod\n ) internal {\n self\n .parameters\n .authorizationDecreaseChangePeriod = _authorizationDecreaseChangePeriod;\n }\n\n /// @notice Used by staking provider to set operator address that will\n /// operate ECDSA node. The given staking provider can set operator\n /// address only one time. The operator address can not be changed\n /// and must be unique. Reverts if the operator is already set for\n /// the staking provider or if the operator address is already in\n /// use. Reverts if there is a pending authorization decrease for\n /// the staking provider.\n function registerOperator(Data storage self, address operator) internal {\n address stakingProvider = msg.sender;\n\n require(operator != address(0), \"Operator can not be zero address\");\n require(\n self.stakingProviderToOperator[stakingProvider] == address(0),\n \"Operator already set for the staking provider\"\n );\n require(\n self.operatorToStakingProvider[operator] == address(0),\n \"Operator address already in use\"\n );\n\n // Authorization request for a staking provider who has not yet\n // registered their operator can be approved immediately.\n // We need to make sure that the approval happens before operator\n // is registered to do not let the operator join the sortition pool\n // with an unresolved authorization decrease request that can be\n // approved at any point.\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n require(\n decrease.decreasingAt == 0,\n \"There is a pending authorization decrease request\"\n );\n\n emit OperatorRegistered(stakingProvider, operator);\n\n self.stakingProviderToOperator[stakingProvider] = operator;\n self.operatorToStakingProvider[operator] = stakingProvider;\n }\n\n /// @notice Used by T staking contract to inform the application that the\n /// authorized stake amount for the given staking provider increased.\n ///\n /// Reverts if the authorization amount is below the minimum.\n ///\n /// The function is not updating the sortition pool. Sortition pool\n /// state needs to be updated by the operator with a call to\n /// `joinSortitionPool` or `updateOperatorStatus`.\n ///\n /// @dev Should only be callable by T staking contract.\n function authorizationIncreased(\n Data storage self,\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) internal {\n require(\n toAmount >= self.parameters.minimumAuthorization,\n \"Authorization below the minimum\"\n );\n\n // Note that this function does not require the operator address to be\n // set for the given staking provider. This allows the stake owner\n // who is also an authorizer to increase the authorization before the\n // staking provider sets the operator. This allows delegating stake\n // and increasing authorization immediately one after another without\n // having to wait for the staking provider to do their part.\n\n address operator = self.stakingProviderToOperator[stakingProvider];\n emit AuthorizationIncreased(\n stakingProvider,\n operator,\n fromAmount,\n toAmount\n );\n }\n\n /// @notice Used by T staking contract to inform the application that the\n /// authorization decrease for the given staking provider has been\n /// requested.\n ///\n /// Reverts if the amount after deauthorization would be non-zero\n /// and lower than the minimum authorization.\n ///\n /// Reverts if another authorization decrease request is pending for\n /// the staking provider and not enough time passed since the\n /// original request (see `authorizationDecreaseChangePeriod`).\n ///\n /// If the operator is not known (`registerOperator` was not called)\n /// it lets to `approveAuthorizationDecrease` immediately. If the\n /// operator is known (`registerOperator` was called), the operator\n /// needs to update state of the sortition pool with a call to\n /// `joinSortitionPool` or `updateOperatorStatus`. After the\n /// sortition pool state is in sync, authorization decrease delay\n /// starts.\n ///\n /// After authorization decrease delay passes, authorization\n /// decrease request needs to be approved with a call to\n /// `approveAuthorizationDecrease` function.\n ///\n /// If there is a pending authorization decrease request, it is\n /// overwritten, but only if enough time passed since the original\n /// request. Otherwise, the function reverts.\n ///\n /// @dev Should only be callable by T staking contract.\n function authorizationDecreaseRequested(\n Data storage self,\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) internal {\n require(\n toAmount == 0 || toAmount >= self.parameters.minimumAuthorization,\n \"Authorization amount should be 0 or above the minimum\"\n );\n\n address operator = self.stakingProviderToOperator[stakingProvider];\n\n uint64 decreasingAt;\n\n if (operator == address(0)) {\n // Operator is not known. It means `registerOperator` was not\n // called yet, and there is no chance the operator could\n // call `joinSortitionPool`. We can let to approve authorization\n // decrease immediately because that operator was never in the\n // sortition pool.\n\n // solhint-disable-next-line not-rely-on-time\n decreasingAt = uint64(block.timestamp);\n } else {\n // Operator is known. It means that this operator is or was in\n // the sortition pool. Before authorization decrease delay starts,\n // the operator needs to update the state of the sortition pool\n // with a call to `joinSortitionPool` or `updateOperatorStatus`.\n // For now, we set `decreasingAt` as \"never decreasing\" and let\n // it be updated by `joinSortitionPool` or `updateOperatorStatus`\n // once we know the sortition pool is in sync.\n decreasingAt = type(uint64).max;\n }\n\n uint96 decreasingBy = fromAmount - toAmount;\n\n AuthorizationDecrease storage decreaseRequest = self.pendingDecreases[\n stakingProvider\n ];\n\n uint64 pendingDecreaseAt = decreaseRequest.decreasingAt;\n if (pendingDecreaseAt != 0 && pendingDecreaseAt != type(uint64).max) {\n // If there is already a pending authorization decrease request for\n // this staking provider and that request has been activated\n // (sortition pool was updated), require enough time to pass before\n // it can be overwritten.\n require(\n // solhint-disable-next-line not-rely-on-time\n block.timestamp >=\n pendingDecreaseAt -\n self.parameters.authorizationDecreaseChangePeriod,\n \"Not enough time passed since the original request\"\n );\n }\n\n decreaseRequest.decreasingBy = decreasingBy;\n decreaseRequest.decreasingAt = decreasingAt;\n\n emit AuthorizationDecreaseRequested(\n stakingProvider,\n operator,\n fromAmount,\n toAmount,\n decreasingAt\n );\n }\n\n /// @notice Approves the previously registered authorization decrease\n /// request. Reverts if authorization decrease delay have not passed\n /// yet or if the authorization decrease was not requested for the\n /// given staking provider.\n function approveAuthorizationDecrease(\n Data storage self,\n IStaking tokenStaking,\n address stakingProvider\n ) internal {\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n require(\n decrease.decreasingAt > 0,\n \"Authorization decrease not requested\"\n );\n require(\n decrease.decreasingAt != type(uint64).max,\n \"Authorization decrease request not activated\"\n );\n require(\n // solhint-disable-next-line not-rely-on-time\n block.timestamp >= decrease.decreasingAt,\n \"Authorization decrease delay not passed\"\n );\n\n emit AuthorizationDecreaseApproved(stakingProvider);\n\n // slither-disable-next-line unused-return\n tokenStaking.approveAuthorizationDecrease(stakingProvider);\n delete self.pendingDecreases[stakingProvider];\n }\n\n /// @notice Used by T staking contract to inform the application the\n /// authorization has been decreased for the given staking provider\n /// involuntarily, as a result of slashing.\n ///\n /// If the operator is not known (`registerOperator` was not called)\n /// the function does nothing. The operator was never in a sortition\n /// pool so there is nothing to update.\n ///\n /// If the operator is known, sortition pool is unlocked, and the\n /// operator is in the sortition pool, the sortition pool state is\n /// updated. If the sortition pool is locked, update needs to be\n /// postponed. Every other staker is incentivized to call\n /// `updateOperatorStatus` for the problematic operator to increase\n /// their own rewards in the pool.\n ///\n /// @dev Should only be callable by T staking contract.\n function involuntaryAuthorizationDecrease(\n Data storage self,\n IStaking tokenStaking,\n SortitionPool sortitionPool,\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) internal {\n address operator = self.stakingProviderToOperator[stakingProvider];\n\n if (operator == address(0)) {\n // Operator is not known. It means `registerOperator` was not\n // called yet, and there is no chance the operator could\n // call `joinSortitionPool`. We can just ignore this update because\n // operator was never in the sortition pool.\n return;\n } else {\n // Operator is known. It means that this operator is or was in the\n // sortition pool and the sortition pool may need to be updated.\n //\n // If the sortition pool is not locked and the operator is in the\n // sortition pool, we are updating it.\n //\n // To keep stakes synchronized between applications when staking\n // providers are slashed, without the risk of running out of gas,\n // the staking contract queues up slashings and let users process\n // the transactions. When an application slashes one or more staking\n // providers, it adds them to the slashing queue on the staking\n // contract. A queue entry contains the staking provider’s address\n // and the amount they are due to be slashed.\n //\n // When there is at least one staking provider in the slashing\n // queue, any account can submit a transaction processing one or\n // more staking providers' slashings, and collecting a reward for\n // doing so. A queued slashing is processed by updating the staking\n // provider’s stake to the post-slashing amount, updating authorized\n // amount for each affected application, and notifying all affected\n // applications that the staking provider’s authorized stake has\n // been reduced due to slashing.\n //\n // The entire idea is that the process transaction is expensive\n // because each application needs to be updated, so the reward for\n // the processor is hefty and comes from the slashed tokens.\n // Practically, it means that if the sortition pool is unlocked, and\n // can be updated, it should be updated because we already paid\n // someone for updating it.\n //\n // If the sortition pool is locked, update needs to wait. Other\n // sortition pool members are incentivized to call\n // `updateOperatorStatus` for the problematic operator because they\n // will increase their rewards this way.\n if (sortitionPool.isOperatorInPool(operator)) {\n if (sortitionPool.isLocked()) {\n emit InvoluntaryAuthorizationDecreaseFailed(\n stakingProvider,\n operator,\n fromAmount,\n toAmount\n );\n } else {\n updateOperatorStatus(\n self,\n tokenStaking,\n sortitionPool,\n operator\n );\n }\n }\n }\n }\n\n /// @notice Lets the operator join the sortition pool. The operator address\n /// must be known - before calling this function, it has to be\n /// appointed by the staking provider by calling `registerOperator`.\n /// Also, the operator must have the minimum authorization required\n /// by ECDSA. Function reverts if there is no minimum stake\n /// authorized or if the operator is not known. If there was an\n /// authorization decrease requested, it is activated by starting\n /// the authorization decrease delay.\n function joinSortitionPool(\n Data storage self,\n IStaking tokenStaking,\n SortitionPool sortitionPool\n ) internal {\n address operator = msg.sender;\n\n address stakingProvider = self.operatorToStakingProvider[operator];\n require(stakingProvider != address(0), \"Unknown operator\");\n\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n\n uint96 _eligibleStake = eligibleStake(\n self,\n tokenStaking,\n stakingProvider,\n decrease.decreasingBy\n );\n\n require(_eligibleStake != 0, \"Authorization below the minimum\");\n\n emit OperatorJoinedSortitionPool(stakingProvider, operator);\n\n sortitionPool.insertOperator(operator, _eligibleStake);\n\n // If there is a pending authorization decrease request, activate it.\n // At this point, the sortition pool state is up to date so the\n // authorization decrease delay can start counting.\n if (decrease.decreasingAt == type(uint64).max) {\n decrease.decreasingAt =\n // solhint-disable-next-line not-rely-on-time\n uint64(block.timestamp) +\n self.parameters.authorizationDecreaseDelay;\n }\n }\n\n /// @notice Updates status of the operator in the sortition pool. If there\n /// was an authorization decrease requested, it is activated by\n /// starting the authorization decrease delay.\n /// Function reverts if the operator is not known.\n function updateOperatorStatus(\n Data storage self,\n IStaking tokenStaking,\n SortitionPool sortitionPool,\n address operator\n ) internal {\n address stakingProvider = self.operatorToStakingProvider[operator];\n require(stakingProvider != address(0), \"Unknown operator\");\n\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n\n emit OperatorStatusUpdated(stakingProvider, operator);\n\n if (sortitionPool.isOperatorInPool(operator)) {\n uint96 _eligibleStake = eligibleStake(\n self,\n tokenStaking,\n stakingProvider,\n decrease.decreasingBy\n );\n\n sortitionPool.updateOperatorStatus(operator, _eligibleStake);\n }\n\n // If there is a pending authorization decrease request, activate it.\n // At this point, the sortition pool state is up to date so the\n // authorization decrease delay can start counting.\n if (decrease.decreasingAt == type(uint64).max) {\n decrease.decreasingAt =\n // solhint-disable-next-line not-rely-on-time\n uint64(block.timestamp) +\n self.parameters.authorizationDecreaseDelay;\n }\n }\n\n /// @notice Checks if the operator's authorized stake is in sync with\n /// operator's weight in the sortition pool.\n /// If the operator is not in the sortition pool and their\n /// authorized stake is non-zero, function returns false.\n function isOperatorUpToDate(\n Data storage self,\n IStaking tokenStaking,\n SortitionPool sortitionPool,\n address operator\n ) internal view returns (bool) {\n address stakingProvider = self.operatorToStakingProvider[operator];\n require(stakingProvider != address(0), \"Unknown operator\");\n\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n\n uint96 _eligibleStake = eligibleStake(\n self,\n tokenStaking,\n stakingProvider,\n decrease.decreasingBy\n );\n\n if (!sortitionPool.isOperatorInPool(operator)) {\n return _eligibleStake == 0;\n } else {\n return sortitionPool.isOperatorUpToDate(operator, _eligibleStake);\n }\n }\n\n /// @notice Returns the current value of the staking provider's eligible\n /// stake. Eligible stake is defined as the currently authorized\n /// stake minus the pending authorization decrease. Eligible stake\n /// is what is used for operator's weight in the pool. If the\n /// authorized stake minus the pending authorization decrease is\n /// below the minimum authorization, eligible stake is 0.\n /// @dev This function can be exposed to the public in contrast to the\n /// second variant accepting `decreasingBy` as a parameter.\n function eligibleStake(\n Data storage self,\n IStaking tokenStaking,\n address stakingProvider\n ) internal view returns (uint96) {\n return\n eligibleStake(\n self,\n tokenStaking,\n stakingProvider,\n pendingAuthorizationDecrease(self, stakingProvider)\n );\n }\n\n /// @notice Returns the current value of the staking provider's eligible\n /// stake. Eligible stake is defined as the currently authorized\n /// stake minus the pending authorization decrease. Eligible stake\n /// is what is used for operator's weight in the pool. If the\n /// authorized stake minus the pending authorization decrease is\n /// below the minimum authorization, eligible stake is 0.\n /// @dev This function is not intended to be exposes to the public.\n /// `decreasingBy` must be fetched from `pendingDecreases` mapping and\n /// it is passed as a parameter to optimize gas usage of functions that\n /// call `eligibleStake` and need to use `AuthorizationDecrease`\n /// fetched from `pendingDecreases` for some additional logic.\n function eligibleStake(\n Data storage self,\n IStaking tokenStaking,\n address stakingProvider,\n uint96 decreasingBy\n ) internal view returns (uint96) {\n uint96 authorizedStake = tokenStaking.authorizedStake(\n stakingProvider,\n address(this)\n );\n\n uint96 _eligibleStake = authorizedStake > decreasingBy\n ? authorizedStake - decreasingBy\n : 0;\n\n if (_eligibleStake < self.parameters.minimumAuthorization) {\n return 0;\n } else {\n return _eligibleStake;\n }\n }\n\n /// @notice Returns the amount of stake that is pending authorization\n /// decrease for the given staking provider. If no authorization\n /// decrease has been requested, returns zero.\n function pendingAuthorizationDecrease(\n Data storage self,\n address stakingProvider\n ) internal view returns (uint96) {\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n\n return decrease.decreasingBy;\n }\n\n /// @notice Returns the remaining time in seconds that needs to pass before\n /// the requested authorization decrease can be approved.\n /// If the sortition pool state was not updated yet by the operator\n /// after requesting the authorization decrease, returns\n /// `type(uint64).max`.\n function remainingAuthorizationDecreaseDelay(\n Data storage self,\n address stakingProvider\n ) internal view returns (uint64) {\n AuthorizationDecrease storage decrease = self.pendingDecreases[\n stakingProvider\n ];\n\n if (decrease.decreasingAt == type(uint64).max) {\n return type(uint64).max;\n }\n\n // solhint-disable-next-line not-rely-on-time\n uint64 _now = uint64(block.timestamp);\n return _now > decrease.decreasingAt ? 0 : decrease.decreasingAt - _now;\n }\n}\n"
|
|
252
|
+
},
|
|
253
|
+
"@threshold-network/solidity-contracts/contracts/staking/IStaking.sol": {
|
|
254
|
+
"content": "// SPDX-License-Identifier: GPL-3.0-or-later\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity 0.8.9;\n\n/// @title Interface of Threshold Network staking contract\n/// @notice The staking contract enables T owners to have their wallets offline\n/// and their stake managed by staking providers on their behalf.\n/// The staking contract does not define operator role. The operator\n/// responsible for running off-chain client software is appointed by\n/// the staking provider in the particular application utilizing the\n/// staking contract. All off-chain client software should be able\n/// to run without exposing operator's or staking provider’s private\n/// key and should not require any owner’s keys at all. The stake\n/// delegation optimizes the network throughput without compromising the\n/// security of the owners’ stake.\ninterface IStaking {\n enum StakeType {\n NU,\n KEEP,\n T\n }\n\n //\n //\n // Delegating a stake\n //\n //\n\n /// @notice Creates a delegation with `msg.sender` owner with the given\n /// staking provider, beneficiary, and authorizer. Transfers the\n /// given amount of T to the staking contract.\n /// @dev The owner of the delegation needs to have the amount approved to\n /// transfer to the staking contract.\n function stake(\n address stakingProvider,\n address payable beneficiary,\n address authorizer,\n uint96 amount\n ) external;\n\n /// @notice Copies delegation from the legacy KEEP staking contract to T\n /// staking contract. No tokens are transferred. Caches the active\n /// stake amount from KEEP staking contract. Can be called by\n /// anyone.\n /// @dev The staking provider in T staking contract is the legacy KEEP\n /// staking contract operator.\n function stakeKeep(address stakingProvider) external;\n\n /// @notice Copies delegation from the legacy NU staking contract to T\n /// staking contract, additionally appointing staking provider,\n /// beneficiary and authorizer roles. Caches the amount staked in NU\n /// staking contract. Can be called only by the original delegation\n /// owner.\n function stakeNu(\n address stakingProvider,\n address payable beneficiary,\n address authorizer\n ) external;\n\n /// @notice Refresh Keep stake owner. Can be called only by the old owner.\n /// @dev The staking provider in T staking contract is the legacy KEEP\n /// staking contract operator.\n function refreshKeepStakeOwner(address stakingProvider) external;\n\n /// @notice Allows the Governance to set the minimum required stake amount.\n /// This amount is required to protect against griefing the staking\n /// contract and individual applications are allowed to require\n /// higher minimum stakes if necessary.\n function setMinimumStakeAmount(uint96 amount) external;\n\n //\n //\n // Authorizing an application\n //\n //\n\n /// @notice Allows the Governance to approve the particular application\n /// before individual stake authorizers are able to authorize it.\n function approveApplication(address application) external;\n\n /// @notice Increases the authorization of the given staking provider for\n /// the given application by the given amount. Can only be called by\n /// the authorizer for that staking provider.\n /// @dev Calls `authorizationIncreased(address stakingProvider, uint256 amount)`\n /// on the given application to notify the application about\n /// authorization change. See `IApplication`.\n function increaseAuthorization(\n address stakingProvider,\n address application,\n uint96 amount\n ) external;\n\n /// @notice Requests decrease of the authorization for the given staking\n /// provider on the given application by the provided amount.\n /// It may not change the authorized amount immediatelly. When\n /// it happens depends on the application. Can only be called by the\n /// given staking provider’s authorizer. Overwrites pending\n /// authorization decrease for the given staking provider and\n /// application.\n /// @dev Calls `authorizationDecreaseRequested(address stakingProvider, uint256 amount)`\n /// on the given application. See `IApplication`.\n function requestAuthorizationDecrease(\n address stakingProvider,\n address application,\n uint96 amount\n ) external;\n\n /// @notice Requests decrease of all authorizations for the given staking\n /// provider on all applications by all authorized amount.\n /// It may not change the authorized amount immediatelly. When\n /// it happens depends on the application. Can only be called by the\n /// given staking provider’s authorizer. Overwrites pending\n /// authorization decrease for the given staking provider and\n /// application.\n /// @dev Calls `authorizationDecreaseRequested(address stakingProvider, uint256 amount)`\n /// for each authorized application. See `IApplication`.\n function requestAuthorizationDecrease(address stakingProvider) external;\n\n /// @notice Called by the application at its discretion to approve the\n /// previously requested authorization decrease request. Can only be\n /// called by the application that was previously requested to\n /// decrease the authorization for that staking provider.\n /// Returns resulting authorized amount for the application.\n function approveAuthorizationDecrease(address stakingProvider)\n external\n returns (uint96);\n\n /// @notice Decreases the authorization for the given `stakingProvider` on\n /// the given disabled `application`, for all authorized amount.\n /// Can be called by anyone.\n function forceDecreaseAuthorization(\n address stakingProvider,\n address application\n ) external;\n\n /// @notice Pauses the given application’s eligibility to slash stakes.\n /// Besides that stakers can't change authorization to the application.\n /// Can be called only by the Panic Button of the particular\n /// application. The paused application can not slash stakes until\n /// it is approved again by the Governance using `approveApplication`\n /// function. Should be used only in case of an emergency.\n function pauseApplication(address application) external;\n\n /// @notice Disables the given application. The disabled application can't\n /// slash stakers. Also stakers can't increase authorization to that\n /// application but can decrease without waiting by calling\n /// `requestAuthorizationDecrease` at any moment. Can be called only\n /// by the governance. The disabled application can't be approved\n /// again. Should be used only in case of an emergency.\n function disableApplication(address application) external;\n\n /// @notice Sets the Panic Button role for the given application to the\n /// provided address. Can only be called by the Governance. If the\n /// Panic Button for the given application should be disabled, the\n /// role address should be set to 0x0 address.\n function setPanicButton(address application, address panicButton) external;\n\n /// @notice Sets the maximum number of applications one staking provider can\n /// have authorized. Used to protect against DoSing slashing queue.\n /// Can only be called by the Governance.\n function setAuthorizationCeiling(uint256 ceiling) external;\n\n //\n //\n // Stake top-up\n //\n //\n\n /// @notice Increases the amount of the stake for the given staking provider.\n /// Can be called only by the owner or the staking provider.\n /// @dev The sender of this transaction needs to have the amount approved to\n /// transfer to the staking contract.\n function topUp(address stakingProvider, uint96 amount) external;\n\n /// @notice Propagates information about stake top-up from the legacy KEEP\n /// staking contract to T staking contract. Can be called only by\n /// the owner or the staking provider.\n function topUpKeep(address stakingProvider) external;\n\n /// @notice Propagates information about stake top-up from the legacy NU\n /// staking contract to T staking contract. Can be called only by\n /// the owner or the staking provider.\n function topUpNu(address stakingProvider) external;\n\n //\n //\n // Undelegating a stake (unstaking)\n //\n //\n\n /// @notice Reduces the liquid T stake amount by the provided amount and\n /// withdraws T to the owner. Reverts if there is at least one\n /// authorization higher than the sum of the legacy stake and\n /// remaining liquid T stake or if the unstake amount is higher than\n /// the liquid T stake amount. Can be called only by the delegation\n /// owner or the staking provider.\n function unstakeT(address stakingProvider, uint96 amount) external;\n\n /// @notice Sets the legacy KEEP staking contract active stake amount cached\n /// in T staking contract to 0. Reverts if the amount of liquid T\n /// staked in T staking contract is lower than the highest\n /// application authorization. This function allows to unstake from\n /// KEEP staking contract and still being able to operate in T\n /// network and earning rewards based on the liquid T staked. Can be\n /// called only by the delegation owner or the staking provider.\n function unstakeKeep(address stakingProvider) external;\n\n /// @notice Reduces cached legacy NU stake amount by the provided amount.\n /// Reverts if there is at least one authorization higher than the\n /// sum of remaining legacy NU stake and liquid T stake for that\n /// staking provider or if the untaked amount is higher than the\n /// cached legacy stake amount. If succeeded, the legacy NU stake\n /// can be partially or fully undelegated on the legacy staking\n /// contract. This function allows to unstake from NU staking\n /// contract and still being able to operate in T network and\n /// earning rewards based on the liquid T staked. Can be called only\n /// by the delegation owner or the staking provider.\n function unstakeNu(address stakingProvider, uint96 amount) external;\n\n /// @notice Sets cached legacy stake amount to 0, sets the liquid T stake\n /// amount to 0 and withdraws all liquid T from the stake to the\n /// owner. Reverts if there is at least one non-zero authorization.\n /// Can be called only by the delegation owner or the staking\n /// provider.\n function unstakeAll(address stakingProvider) external;\n\n //\n //\n // Keeping information in sync\n //\n //\n\n /// @notice Notifies about the discrepancy between legacy KEEP active stake\n /// and the amount cached in T staking contract. Slashes the staking\n /// provider in case the amount cached is higher than the actual\n /// active stake amount in KEEP staking contract. Needs to update\n /// authorizations of all affected applications and execute an\n /// involuntary allocation decrease on all affected applications.\n /// Can be called by anyone, notifier receives a reward.\n function notifyKeepStakeDiscrepancy(address stakingProvider) external;\n\n /// @notice Notifies about the discrepancy between legacy NU active stake\n /// and the amount cached in T staking contract. Slashes the\n /// staking provider in case the amount cached is higher than the\n /// actual active stake amount in NU staking contract. Needs to\n /// update authorizations of all affected applications and execute\n /// an involuntary allocation decrease on all affected applications.\n /// Can be called by anyone, notifier receives a reward.\n function notifyNuStakeDiscrepancy(address stakingProvider) external;\n\n /// @notice Sets the penalty amount for stake discrepancy and reward\n /// multiplier for reporting it. The penalty is seized from the\n /// delegated stake, and 5% of the penalty, scaled by the\n /// multiplier, is given to the notifier. The rest of the tokens are\n /// burned. Can only be called by the Governance. See `seize` function.\n function setStakeDiscrepancyPenalty(\n uint96 penalty,\n uint256 rewardMultiplier\n ) external;\n\n /// @notice Sets reward in T tokens for notification of misbehaviour\n /// of one staking provider. Can only be called by the governance.\n function setNotificationReward(uint96 reward) external;\n\n /// @notice Transfer some amount of T tokens as reward for notifications\n /// of misbehaviour\n function pushNotificationReward(uint96 reward) external;\n\n /// @notice Withdraw some amount of T tokens from notifiers treasury.\n /// Can only be called by the governance.\n function withdrawNotificationReward(address recipient, uint96 amount)\n external;\n\n /// @notice Adds staking providers to the slashing queue along with the\n /// amount that should be slashed from each one of them. Can only be\n /// called by application authorized for all staking providers in\n /// the array.\n function slash(uint96 amount, address[] memory stakingProviders) external;\n\n /// @notice Adds staking providers to the slashing queue along with the\n /// amount. The notifier will receive reward per each staking\n /// provider from notifiers treasury. Can only be called by\n /// application authorized for all staking providers in the array.\n function seize(\n uint96 amount,\n uint256 rewardMultipier,\n address notifier,\n address[] memory stakingProviders\n ) external;\n\n /// @notice Takes the given number of queued slashing operations and\n /// processes them. Receives 5% of the slashed amount.\n /// Executes `involuntaryAllocationDecrease` function on each\n /// affected application.\n function processSlashing(uint256 count) external;\n\n //\n //\n // Auxiliary functions\n //\n //\n\n /// @notice Returns the authorized stake amount of the staking provider for\n /// the application.\n function authorizedStake(address stakingProvider, address application)\n external\n view\n returns (uint96);\n\n /// @notice Returns staked amount of T, Keep and Nu for the specified\n /// staking provider.\n /// @dev All values are in T denomination\n function stakes(address stakingProvider)\n external\n view\n returns (\n uint96 tStake,\n uint96 keepInTStake,\n uint96 nuInTStake\n );\n\n /// @notice Returns start staking timestamp.\n /// @dev This value is set at most once.\n function getStartStakingTimestamp(address stakingProvider)\n external\n view\n returns (uint256);\n\n /// @notice Returns staked amount of NU for the specified staking provider.\n function stakedNu(address stakingProvider) external view returns (uint256);\n\n /// @notice Gets the stake owner, the beneficiary and the authorizer\n /// for the specified staking provider address.\n /// @return owner Stake owner address.\n /// @return beneficiary Beneficiary address.\n /// @return authorizer Authorizer address.\n function rolesOf(address stakingProvider)\n external\n view\n returns (\n address owner,\n address payable beneficiary,\n address authorizer\n );\n\n /// @notice Returns length of application array\n function getApplicationsLength() external view returns (uint256);\n\n /// @notice Returns length of slashing queue\n function getSlashingQueueLength() external view returns (uint256);\n\n /// @notice Returns minimum possible stake for T, KEEP or NU in T\n /// denomination.\n /// @dev For example, suppose the given staking provider has 10 T, 20 T\n /// worth of KEEP, and 30 T worth of NU all staked, and the maximum\n /// application authorization is 40 T, then `getMinStaked` for\n /// that staking provider returns:\n /// * 0 T if KEEP stake type specified i.e.\n /// min = 40 T max - (10 T + 30 T worth of NU) = 0 T\n /// * 10 T if NU stake type specified i.e.\n /// min = 40 T max - (10 T + 20 T worth of KEEP) = 10 T\n /// * 0 T if T stake type specified i.e.\n /// min = 40 T max - (20 T worth of KEEP + 30 T worth of NU) < 0 T\n /// In other words, the minimum stake amount for the specified\n /// stake type is the minimum amount of stake of the given type\n /// needed to satisfy the maximum application authorization given the\n /// staked amounts of the other stake types for that staking provider.\n function getMinStaked(address stakingProvider, StakeType stakeTypes)\n external\n view\n returns (uint96);\n\n /// @notice Returns available amount to authorize for the specified application\n function getAvailableToAuthorize(\n address stakingProvider,\n address application\n ) external view returns (uint96);\n}\n"
|
|
255
|
+
},
|
|
250
256
|
"contracts/hardhat-dependency-compiler/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol": {
|
|
251
257
|
"content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity >0.0.0;\nimport '@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol';\n"
|
|
252
258
|
},
|
|
253
|
-
"
|
|
254
|
-
"content": "// SPDX-License-Identifier: MIT\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity ^0.8.9;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\n\ncontract ReimbursementPool is Ownable, ReentrancyGuard {\n /// @notice Authorized contracts that can interact with the reimbursment pool.\n /// Authorization can be granted and removed by the owner.\n mapping(address => bool) public isAuthorized;\n\n /// @notice Static gas includes:\n /// - cost of the refund function\n /// - base transaction cost\n uint256 public staticGas;\n\n /// @notice Max gas price used to reimburse a transaction submitter. Protects\n /// against malicious operator-miners.\n uint256 public maxGasPrice;\n\n event StaticGasUpdated(uint256 newStaticGas);\n\n event MaxGasPriceUpdated(uint256 newMaxGasPrice);\n\n event SendingEtherFailed(uint256 refundAmount, address receiver);\n\n event AuthorizedContract(address thirdPartyContract);\n\n event UnauthorizedContract(address thirdPartyContract);\n\n event FundsWithdrawn(uint256 withdrawnAmount, address receiver);\n\n constructor(uint256 _staticGas, uint256 _maxGasPrice) {\n staticGas = _staticGas;\n maxGasPrice = _maxGasPrice;\n }\n\n /// @notice Receive ETH\n receive() external payable {}\n\n /// @notice Refunds ETH to a spender for executing specific transactions.\n /// @dev Ignoring the result of sending ETH to a receiver is made on purpose.\n /// For EOA receiving ETH should always work. If a receiver is a smart\n /// contract, then we do not want to fail a transaction, because in some\n /// cases the refund is done at the very end of multiple calls where all\n /// the previous calls were already paid off. It is a receiver's smart\n /// contract resposibility to make sure it can receive ETH.\n /// @dev Only authorized contracts are allowed calling this function.\n /// @param gasSpent Gas spent on a transaction that needs to be reimbursed.\n /// @param receiver Address where the reimbursment is sent.\n function refund(uint256 gasSpent, address receiver) external nonReentrant {\n require(\n isAuthorized[msg.sender],\n \"Contract is not authorized for a refund\"\n );\n require(receiver != address(0), \"Receiver's address cannot be zero\");\n\n uint256 gasPrice = tx.gasprice < maxGasPrice\n ? tx.gasprice\n : maxGasPrice;\n\n uint256 refundAmount = (gasSpent + staticGas) * gasPrice;\n\n /* solhint-disable avoid-low-level-calls */\n // slither-disable-next-line low-level-calls,unchecked-lowlevel\n (bool sent, ) = receiver.call{value: refundAmount}(\"\");\n /* solhint-enable avoid-low-level-calls */\n if (!sent) {\n // slither-disable-next-line reentrancy-events\n emit SendingEtherFailed(refundAmount, receiver);\n }\n }\n\n /// @notice Authorize a contract that can interact with this reimbursment pool.\n /// Can be authorized by the owner only.\n /// @param _contract Authorized contract.\n function authorize(address _contract) external onlyOwner {\n isAuthorized[_contract] = true;\n\n emit AuthorizedContract(_contract);\n }\n\n /// @notice Unauthorize a contract that was previously authorized to interact\n /// with this reimbursment pool. Can be unauthorized by the\n /// owner only.\n /// @param _contract Authorized contract.\n function unauthorize(address _contract) external onlyOwner {\n delete isAuthorized[_contract];\n\n emit UnauthorizedContract(_contract);\n }\n\n /// @notice Setting a static gas cost for executing a transaction. Can be set\n /// by the owner only.\n /// @param _staticGas Static gas cost.\n function setStaticGas(uint256 _staticGas) external onlyOwner {\n staticGas = _staticGas;\n\n emit StaticGasUpdated(_staticGas);\n }\n\n /// @notice Setting a max gas price for transactions. Can be set by the\n /// owner only.\n /// @param _maxGasPrice Max gas price used to reimburse tx submitters.\n function setMaxGasPrice(uint256 _maxGasPrice) external onlyOwner {\n maxGasPrice = _maxGasPrice;\n\n emit MaxGasPriceUpdated(_maxGasPrice);\n }\n\n /// @notice Withdraws all ETH from this pool which are sent to a given\n /// address. Can be set by the owner only.\n /// @param receiver An address where ETH is sent.\n function withdrawAll(address receiver) external onlyOwner {\n withdraw(address(this).balance, receiver);\n }\n\n /// @notice Withdraws ETH amount from this pool which are sent to a given\n /// address. Can be set by the owner only.\n /// @param amount Amount to withdraw from the pool.\n /// @param receiver An address where ETH is sent.\n function withdraw(uint256 amount, address receiver) public onlyOwner {\n require(\n address(this).balance >= amount,\n \"Insufficient contract balance\"\n );\n require(receiver != address(0), \"Receiver's address cannot be zero\");\n\n emit FundsWithdrawn(amount, receiver);\n\n /* solhint-disable avoid-low-level-calls */\n // slither-disable-next-line low-level-calls,arbitrary-send\n (bool sent, ) = receiver.call{value: amount}(\"\");\n /* solhint-enable avoid-low-level-calls */\n require(sent, \"Failed to send Ether\");\n }\n}\n"
|
|
259
|
+
"contracts/bridge/BridgeGovernance.sol": {
|
|
260
|
+
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"./BridgeGovernanceParameters.sol\";\n\nimport \"./Bridge.sol\";\n\n/// @title Bridge Governance\n/// @notice Owns the `Bridge` contract and is responsible for updating\n/// its governable parameters in respect to governance delay individual\n/// for each parameter. The other responsibility is marking a vault\n/// address as trusted or no longer trusted.\ncontract BridgeGovernance is Ownable {\n using BridgeGovernanceParameters for BridgeGovernanceParameters.DepositData;\n using BridgeGovernanceParameters for BridgeGovernanceParameters.RedemptionData;\n using BridgeGovernanceParameters for BridgeGovernanceParameters.MovingFundsData;\n using BridgeGovernanceParameters for BridgeGovernanceParameters.WalletData;\n using BridgeGovernanceParameters for BridgeGovernanceParameters.FraudData;\n\n BridgeGovernanceParameters.DepositData internal depositData;\n BridgeGovernanceParameters.RedemptionData internal redemptionData;\n BridgeGovernanceParameters.MovingFundsData internal movingFundsData;\n BridgeGovernanceParameters.WalletData internal walletData;\n BridgeGovernanceParameters.FraudData internal fraudData;\n\n Bridge internal bridge;\n\n // Array is used to mitigate the problem with the contract size limit.\n // governanceDelays[0] -> governanceDelay\n // governanceDelays[1] -> newGovernanceDelay\n // governanceDelays[2] -> governanceDelayChangeInitiated\n uint256[3] public governanceDelays;\n\n uint256 public bridgeGovernanceTransferChangeInitiated;\n address internal newBridgeGovernance;\n\n // We skip emitting event on *Update to go down with the contract size\n // limit. The reason why we leave *Started but not including *Updated is\n // because Bridge governance transferred event can also be read from the\n // Governable bridge contract 'GovernanceTransferred(old, new)'.\n event BridgeGovernanceTransferStarted(\n address newBridgeGovernance,\n uint256 timestamp\n );\n\n event DepositDustThresholdUpdateStarted(\n uint64 newDepositDustThreshold,\n uint256 timestamp\n );\n event DepositDustThresholdUpdated(uint64 depositDustThreshold);\n\n event DepositTreasuryFeeDivisorUpdateStarted(\n uint64 depositTreasuryFeeDivisor,\n uint256 timestamp\n );\n event DepositTreasuryFeeDivisorUpdated(uint64 depositTreasuryFeeDivisor);\n\n event DepositTxMaxFeeUpdateStarted(\n uint64 newDepositTxMaxFee,\n uint256 timestamp\n );\n event DepositTxMaxFeeUpdated(uint64 depositTxMaxFee);\n\n event RedemptionDustThresholdUpdateStarted(\n uint64 newRedemptionDustThreshold,\n uint256 timestamp\n );\n event RedemptionDustThresholdUpdated(uint64 redemptionDustThreshold);\n\n event RedemptionTreasuryFeeDivisorUpdateStarted(\n uint64 newRedemptionTreasuryFeeDivisor,\n uint256 timestamp\n );\n event RedemptionTreasuryFeeDivisorUpdated(\n uint64 redemptionTreasuryFeeDivisor\n );\n\n event RedemptionTxMaxFeeUpdateStarted(\n uint64 newRedemptionTxMaxFee,\n uint256 timestamp\n );\n event RedemptionTxMaxFeeUpdated(uint64 redemptionTxMaxFee);\n\n event RedemptionTimeoutUpdateStarted(\n uint32 newRedemptionTimeout,\n uint256 timestamp\n );\n event RedemptionTimeoutUpdated(uint32 redemptionTimeout);\n\n event RedemptionTimeoutSlashingAmountUpdateStarted(\n uint96 newRedemptionTimeoutSlashingAmount,\n uint256 timestamp\n );\n event RedemptionTimeoutSlashingAmountUpdated(\n uint96 redemptionTimeoutSlashingAmount\n );\n\n event RedemptionTimeoutNotifierRewardMultiplierUpdateStarted(\n uint32 newRedemptionTimeoutNotifierRewardMultiplier,\n uint256 timestamp\n );\n event RedemptionTimeoutNotifierRewardMultiplierUpdated(\n uint32 redemptionTimeoutNotifierRewardMultiplier\n );\n\n event MovingFundsTxMaxTotalFeeUpdateStarted(\n uint64 newMovingFundsTxMaxTotalFee,\n uint256 timestamp\n );\n event MovingFundsTxMaxTotalFeeUpdated(uint64 movingFundsTxMaxTotalFee);\n\n event MovingFundsDustThresholdUpdateStarted(\n uint64 newMovingFundsDustThreshold,\n uint256 timestamp\n );\n event MovingFundsDustThresholdUpdated(uint64 movingFundsDustThreshold);\n\n event MovingFundsTimeoutResetDelayUpdateStarted(\n uint32 newMovingFundsTimeoutResetDelay,\n uint256 timestamp\n );\n event MovingFundsTimeoutResetDelayUpdated(\n uint32 movingFundsTimeoutResetDelay\n );\n\n event MovingFundsTimeoutUpdateStarted(\n uint32 newMovingFundsTimeout,\n uint256 timestamp\n );\n event MovingFundsTimeoutUpdated(uint32 movingFundsTimeout);\n\n event MovingFundsTimeoutSlashingAmountUpdateStarted(\n uint96 newMovingFundsTimeoutSlashingAmount,\n uint256 timestamp\n );\n event MovingFundsTimeoutSlashingAmountUpdated(\n uint96 movingFundsTimeoutSlashingAmount\n );\n\n event MovingFundsTimeoutNotifierRewardMultiplierUpdateStarted(\n uint32 newMovingFundsTimeoutNotifierRewardMultiplier,\n uint256 timestamp\n );\n event MovingFundsTimeoutNotifierRewardMultiplierUpdated(\n uint32 movingFundsTimeoutNotifierRewardMultiplier\n );\n\n event MovedFundsSweepTxMaxTotalFeeUpdateStarted(\n uint64 newMovedFundsSweepTxMaxTotalFee,\n uint256 timestamp\n );\n event MovedFundsSweepTxMaxTotalFeeUpdated(\n uint64 movedFundsSweepTxMaxTotalFee\n );\n\n event MovedFundsSweepTimeoutUpdateStarted(\n uint32 newMovedFundsSweepTimeout,\n uint256 timestamp\n );\n event MovedFundsSweepTimeoutUpdated(uint32 movedFundsSweepTimeout);\n\n event MovedFundsSweepTimeoutSlashingAmountUpdateStarted(\n uint96 newMovedFundsSweepTimeoutSlashingAmount,\n uint256 timestamp\n );\n event MovedFundsSweepTimeoutSlashingAmountUpdated(\n uint96 movedFundsSweepTimeoutSlashingAmount\n );\n\n event MovedFundsSweepTimeoutNotifierRewardMultiplierUpdateStarted(\n uint32 newMovedFundsSweepTimeoutNotifierRewardMultiplier,\n uint256 timestamp\n );\n event MovedFundsSweepTimeoutNotifierRewardMultiplierUpdated(\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n\n event WalletCreationPeriodUpdateStarted(\n uint32 newWalletCreationPeriod,\n uint256 timestamp\n );\n event WalletCreationPeriodUpdated(uint32 walletCreationPeriod);\n\n event WalletCreationMinBtcBalanceUpdateStarted(\n uint64 newWalletCreationMinBtcBalance,\n uint256 timestamp\n );\n event WalletCreationMinBtcBalanceUpdated(\n uint64 walletCreationMinBtcBalance\n );\n\n event WalletCreationMaxBtcBalanceUpdateStarted(\n uint64 newWalletCreationMaxBtcBalance,\n uint256 timestamp\n );\n event WalletCreationMaxBtcBalanceUpdated(\n uint64 walletCreationMaxBtcBalance\n );\n\n event WalletClosureMinBtcBalanceUpdateStarted(\n uint64 newWalletClosureMinBtcBalance,\n uint256 timestamp\n );\n event WalletClosureMinBtcBalanceUpdated(uint64 walletClosureMinBtcBalance);\n\n event WalletMaxAgeUpdateStarted(uint32 newWalletMaxAge, uint256 timestamp);\n event WalletMaxAgeUpdated(uint32 walletMaxAge);\n\n event WalletMaxBtcTransferUpdateStarted(\n uint64 newWalletMaxBtcTransfer,\n uint256 timestamp\n );\n event WalletMaxBtcTransferUpdated(uint64 walletMaxBtcTransfer);\n\n event WalletClosingPeriodUpdateStarted(\n uint32 newWalletClosingPeriod,\n uint256 timestamp\n );\n event WalletClosingPeriodUpdated(uint32 walletClosingPeriod);\n\n event FraudChallengeDepositAmountUpdateStarted(\n uint96 newFraudChallengeDepositAmount,\n uint256 timestamp\n );\n event FraudChallengeDepositAmountUpdated(\n uint96 fraudChallengeDepositAmount\n );\n\n event FraudChallengeDefeatTimeoutUpdateStarted(\n uint32 newFraudChallengeDefeatTimeout,\n uint256 timestamp\n );\n event FraudChallengeDefeatTimeoutUpdated(\n uint32 fraudChallengeDefeatTimeout\n );\n\n event FraudSlashingAmountUpdateStarted(\n uint96 newFraudSlashingAmount,\n uint256 timestamp\n );\n event FraudSlashingAmountUpdated(uint96 fraudSlashingAmount);\n\n event FraudNotifierRewardMultiplierUpdateStarted(\n uint32 newFraudNotifierRewardMultiplier,\n uint256 timestamp\n );\n event FraudNotifierRewardMultiplierUpdated(\n uint32 fraudNotifierRewardMultiplier\n );\n\n constructor(Bridge _bridge, uint256 _governanceDelay) {\n bridge = _bridge;\n governanceDelays[0] = _governanceDelay;\n }\n\n /// @notice Allows the Governance to mark the given vault address as trusted\n /// or no longer trusted. Vaults are not trusted by default.\n /// Trusted vault must meet the following criteria:\n /// - `IVault.receiveBalanceIncrease` must have a known, low gas\n /// cost,\n /// - `IVault.receiveBalanceIncrease` must never revert.\n /// @param vault The address of the vault.\n /// @param isTrusted flag indicating whether the vault is trusted or not.\n function setVaultStatus(address vault, bool isTrusted) external onlyOwner {\n bridge.setVaultStatus(vault, isTrusted);\n }\n\n /// @notice Begins the governance delay update process.\n /// @dev Can be called only by the contract owner. The event that informs about\n /// the start of the governance delay was skipped on purpose to trim\n /// the contract size. All the params inside of the `governanceDelays`\n /// array are public and can be easily fetched.\n /// @param _newGovernanceDelay New governance delay\n function beginGovernanceDelayUpdate(uint256 _newGovernanceDelay)\n external\n onlyOwner\n {\n governanceDelays[1] = _newGovernanceDelay;\n /* solhint-disable not-rely-on-time */\n governanceDelays[2] = block.timestamp;\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the governance delay update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses. Updated event was skipped on purpose to trim the\n /// contract size. All the params inside of the `governanceDelays`\n /// array are public and can be easily fetched.\n function finalizeGovernanceDelayUpdate() external onlyOwner {\n require(governanceDelays[2] > 0, \"Change not initiated\");\n /* solhint-disable not-rely-on-time */\n require(\n block.timestamp - governanceDelays[2] > governanceDelay(),\n \"Governance delay has not elapsed\"\n );\n /* solhint-enable not-rely-on-time */\n governanceDelays[0] = governanceDelays[1];\n governanceDelays[1] = 0;\n governanceDelays[2] = 0;\n }\n\n /// @notice Begins the Bridge governance transfer process.\n /// @dev Can be called only by the contract owner. It is the governance\n /// responsibility to validate the correctness of the new Bridge\n /// Governance contract. The other reason for not adding this check is\n /// to go down with the contract size and leaving only the essential code.\n function beginBridgeGovernanceTransfer(address _newBridgeGovernance)\n external\n onlyOwner\n {\n // slither-disable-next-line missing-zero-check\n newBridgeGovernance = _newBridgeGovernance;\n /* solhint-disable not-rely-on-time */\n bridgeGovernanceTransferChangeInitiated = block.timestamp;\n emit BridgeGovernanceTransferStarted(\n _newBridgeGovernance,\n bridgeGovernanceTransferChangeInitiated\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the bridge governance transfer process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses. Bridge governance transferred event can be read\n /// from the Governable bridge contract 'GovernanceTransferred(old, new)'.\n /// Event that informs about the transfer in this function is skipped on\n /// purpose to go down with the contract size.\n function finalizeBridgeGovernanceTransfer() external onlyOwner {\n require(\n bridgeGovernanceTransferChangeInitiated > 0,\n \"Change not initiated\"\n );\n /* solhint-disable not-rely-on-time */\n require(\n block.timestamp - bridgeGovernanceTransferChangeInitiated >=\n governanceDelay(),\n \"Governance delay has not elapsed\"\n );\n /* solhint-enable not-rely-on-time */\n // slither-disable-next-line reentrancy-no-eth\n bridge.transferGovernance(newBridgeGovernance);\n bridgeGovernanceTransferChangeInitiated = 0;\n newBridgeGovernance = address(0);\n }\n\n // --- Deposit\n\n /// @notice Begins the deposit dust threshold amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newDepositDustThreshold New deposit dust threshold amount.\n function beginDepositDustThresholdUpdate(uint64 _newDepositDustThreshold)\n external\n onlyOwner\n {\n depositData.beginDepositDustThresholdUpdate(_newDepositDustThreshold);\n }\n\n /// @notice Finalizes the deposit dust threshold amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeDepositDustThresholdUpdate() external onlyOwner {\n (, uint64 depositTreasuryFeeDivisor, uint64 depositTxMaxFee) = bridge\n .depositParameters();\n bridge.updateDepositParameters(\n depositData.getNewDepositDustThreshold(),\n depositTreasuryFeeDivisor,\n depositTxMaxFee\n );\n depositData.finalizeDepositDustThresholdUpdate(governanceDelay());\n }\n\n /// @notice Begins the deposit treasury fee divisor amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newDepositTreasuryFeeDivisor New deposit treasury fee divisor.\n function beginDepositTreasuryFeeDivisorUpdate(\n uint64 _newDepositTreasuryFeeDivisor\n ) external onlyOwner {\n depositData.beginDepositTreasuryFeeDivisorUpdate(\n _newDepositTreasuryFeeDivisor\n );\n }\n\n /// @notice Finalizes the deposit treasury fee divisor amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeDepositTreasuryFeeDivisorUpdate() external onlyOwner {\n (uint64 depositDustThreshold, , uint64 depositTxMaxFee) = bridge\n .depositParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateDepositParameters(\n depositDustThreshold,\n depositData.getNewDepositTreasuryFeeDivisor(),\n depositTxMaxFee\n );\n depositData.finalizeDepositTreasuryFeeDivisorUpdate(governanceDelay());\n }\n\n /// @notice Begins the deposit tx max fee amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newDepositTxMaxFee New deposit tx max fee.\n function beginDepositTxMaxFeeUpdate(uint64 _newDepositTxMaxFee)\n external\n onlyOwner\n {\n depositData.beginDepositTxMaxFeeUpdate(_newDepositTxMaxFee);\n }\n\n /// @notice Finalizes the deposit tx max fee amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeDepositTxMaxFeeUpdate() external onlyOwner {\n (\n uint64 depositDustThreshold,\n uint64 depositTreasuryFeeDivisor,\n\n ) = bridge.depositParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateDepositParameters(\n depositDustThreshold,\n depositTreasuryFeeDivisor,\n depositData.getNewDepositTxMaxFee()\n );\n depositData.finalizeDepositTxMaxFeeUpdate(governanceDelay());\n }\n\n // --- Redemption\n\n /// @notice Begins the redemption dust threshold amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionDustThreshold New redemption dust threshold.\n function beginRedemptionDustThresholdUpdate(\n uint64 _newRedemptionDustThreshold\n ) external onlyOwner {\n redemptionData.beginRedemptionDustThresholdUpdate(\n _newRedemptionDustThreshold\n );\n }\n\n /// @notice Finalizes the dust threshold amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionDustThresholdUpdate() external onlyOwner {\n (\n ,\n uint64 redemptionTreasuryFeeDivisor,\n uint64 redemptionTxMaxFee,\n uint32 redemptionTimeout,\n uint96 redemptionTimeoutSlashingAmount,\n uint32 redemptionTimeoutNotifierRewardMultiplier\n ) = bridge.redemptionParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateRedemptionParameters(\n redemptionData.getNewRedemptionDustThreshold(),\n redemptionTreasuryFeeDivisor,\n redemptionTxMaxFee,\n redemptionTimeout,\n redemptionTimeoutSlashingAmount,\n redemptionTimeoutNotifierRewardMultiplier\n );\n\n redemptionData.finalizeRedemptionDustThresholdUpdate(governanceDelay());\n }\n\n /// @notice Begins the redemption treasury fee divisor amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionTreasuryFeeDivisor New redemption treasury fee divisor.\n function beginRedemptionTreasuryFeeDivisorUpdate(\n uint64 _newRedemptionTreasuryFeeDivisor\n ) external onlyOwner {\n redemptionData.beginRedemptionTreasuryFeeDivisorUpdate(\n _newRedemptionTreasuryFeeDivisor\n );\n }\n\n /// @notice Finalizes the redemption treasury fee divisor amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionTreasuryFeeDivisorUpdate() external onlyOwner {\n (\n uint64 redemptionDustThreshold,\n ,\n uint64 redemptionTxMaxFee,\n uint32 redemptionTimeout,\n uint96 redemptionTimeoutSlashingAmount,\n uint32 redemptionTimeoutNotifierRewardMultiplier\n ) = bridge.redemptionParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateRedemptionParameters(\n redemptionDustThreshold,\n redemptionData.getNewRedemptionTreasuryFeeDivisor(),\n redemptionTxMaxFee,\n redemptionTimeout,\n redemptionTimeoutSlashingAmount,\n redemptionTimeoutNotifierRewardMultiplier\n );\n\n redemptionData.finalizeRedemptionTreasuryFeeDivisorUpdate(\n governanceDelay()\n );\n }\n\n /// @notice Begins the redemption tx max fee amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionTxMaxFee New redemption tx max fee.\n function beginRedemptionTxMaxFeeUpdate(uint64 _newRedemptionTxMaxFee)\n external\n onlyOwner\n {\n redemptionData.beginRedemptionTxMaxFeeUpdate(_newRedemptionTxMaxFee);\n }\n\n /// @notice Finalizes the redemption tx max fee amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionTxMaxFeeUpdate() external onlyOwner {\n (\n uint64 redemptionDustThreshold,\n uint64 redemptionTreasuryFeeDivisor,\n ,\n uint32 redemptionTimeout,\n uint96 redemptionTimeoutSlashingAmount,\n uint32 redemptionTimeoutNotifierRewardMultiplier\n ) = bridge.redemptionParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateRedemptionParameters(\n redemptionDustThreshold,\n redemptionTreasuryFeeDivisor,\n redemptionData.getNewRedemptionTxMaxFee(),\n redemptionTimeout,\n redemptionTimeoutSlashingAmount,\n redemptionTimeoutNotifierRewardMultiplier\n );\n\n redemptionData.finalizeRedemptionTxMaxFeeUpdate(governanceDelay());\n }\n\n /// @notice Begins the redemption timeout amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionTimeout New redemption timeout.\n function beginRedemptionTimeoutUpdate(uint32 _newRedemptionTimeout)\n external\n onlyOwner\n {\n redemptionData.beginRedemptionTimeoutUpdate(_newRedemptionTimeout);\n }\n\n /// @notice Finalizes the redemption timeout amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionTimeoutUpdate() external onlyOwner {\n (\n uint64 redemptionDustThreshold,\n uint64 redemptionTreasuryFeeDivisor,\n uint64 redemptionTxMaxFee,\n ,\n uint96 redemptionTimeoutSlashingAmount,\n uint32 redemptionTimeoutNotifierRewardMultiplier\n ) = bridge.redemptionParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateRedemptionParameters(\n redemptionDustThreshold,\n redemptionTreasuryFeeDivisor,\n redemptionTxMaxFee,\n redemptionData.getNewRedemptionTimeout(),\n redemptionTimeoutSlashingAmount,\n redemptionTimeoutNotifierRewardMultiplier\n );\n\n redemptionData.finalizeRedemptionTimeoutUpdate(governanceDelay());\n }\n\n /// @notice Begins the redemption timeout slashing amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionTimeoutSlashingAmount New redemption timeout slashing\n /// amount.\n function beginRedemptionTimeoutSlashingAmountUpdate(\n uint96 _newRedemptionTimeoutSlashingAmount\n ) external onlyOwner {\n redemptionData.beginRedemptionTimeoutSlashingAmountUpdate(\n _newRedemptionTimeoutSlashingAmount\n );\n }\n\n /// @notice Finalizes the redemption timeout slashing amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionTimeoutSlashingAmountUpdate()\n external\n onlyOwner\n {\n (\n uint64 redemptionDustThreshold,\n uint64 redemptionTreasuryFeeDivisor,\n uint64 redemptionTxMaxFee,\n uint32 redemptionTimeout,\n ,\n uint32 redemptionTimeoutNotifierRewardMultiplier\n ) = bridge.redemptionParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateRedemptionParameters(\n redemptionDustThreshold,\n redemptionTreasuryFeeDivisor,\n redemptionTxMaxFee,\n redemptionTimeout,\n redemptionData.getNewRedemptionTimeoutSlashingAmount(),\n redemptionTimeoutNotifierRewardMultiplier\n );\n\n redemptionData.finalizeRedemptionTimeoutSlashingAmountUpdate(\n governanceDelay()\n );\n }\n\n /// @notice Begins the redemption timeout notifier reward multiplier amount\n /// update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionTimeoutNotifierRewardMultiplier New redemption timeout\n /// notifier reward multiplier.\n function beginRedemptionTimeoutNotifierRewardMultiplierUpdate(\n uint32 _newRedemptionTimeoutNotifierRewardMultiplier\n ) external onlyOwner {\n redemptionData.beginRedemptionTimeoutNotifierRewardMultiplierUpdate(\n _newRedemptionTimeoutNotifierRewardMultiplier\n );\n }\n\n /// @notice Finalizes the redemption timeout notifier reward multiplier amount\n /// update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionTimeoutNotifierRewardMultiplierUpdate()\n external\n onlyOwner\n {\n (\n uint64 redemptionDustThreshold,\n uint64 redemptionTreasuryFeeDivisor,\n uint64 redemptionTxMaxFee,\n uint32 redemptionTimeout,\n uint96 redemptionTimeoutSlashingAmount,\n\n ) = bridge.redemptionParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateRedemptionParameters(\n redemptionDustThreshold,\n redemptionTreasuryFeeDivisor,\n redemptionTxMaxFee,\n redemptionTimeout,\n redemptionTimeoutSlashingAmount,\n redemptionData.getNewRedemptionTimeoutNotifierRewardMultiplier()\n );\n\n redemptionData.finalizeRedemptionTimeoutNotifierRewardMultiplierUpdate(\n governanceDelay()\n );\n }\n\n // --- Moving funds\n\n /// @notice Begins the moving funds tx max total fee update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsTxMaxTotalFee New moving funds tx max total fee.\n function beginMovingFundsTxMaxTotalFeeUpdate(\n uint64 _newMovingFundsTxMaxTotalFee\n ) external onlyOwner {\n movingFundsData.beginMovingFundsTxMaxTotalFeeUpdate(\n _newMovingFundsTxMaxTotalFee\n );\n }\n\n /// @notice Finalizes the moving funds tx max total fee update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsTxMaxTotalFeeUpdate() external onlyOwner {\n (\n ,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n ) = bridge.movingFundsParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateMovingFundsParameters(\n movingFundsData.getNewMovingFundsTxMaxTotalFee(),\n movingFundsDustThreshold,\n movingFundsTimeoutResetDelay,\n movingFundsTimeout,\n movingFundsTimeoutSlashingAmount,\n movingFundsTimeoutNotifierRewardMultiplier,\n movedFundsSweepTxMaxTotalFee,\n movedFundsSweepTimeout,\n movedFundsSweepTimeoutSlashingAmount,\n movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n movingFundsData.finalizeMovingFundsTxMaxTotalFeeUpdate(\n governanceDelay()\n );\n }\n\n /// @notice Begins the moving funds dust threshold update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsDustThreshold New moving funds dust threshold.\n function beginMovingFundsDustThresholdUpdate(\n uint64 _newMovingFundsDustThreshold\n ) external onlyOwner {\n movingFundsData.beginMovingFundsDustThresholdUpdate(\n _newMovingFundsDustThreshold\n );\n }\n\n /// @notice Finalizes the moving funds dust threshold update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsDustThresholdUpdate() external onlyOwner {\n (\n uint64 movingFundsTxMaxTotalFee,\n ,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n ) = bridge.movingFundsParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateMovingFundsParameters(\n movingFundsTxMaxTotalFee,\n movingFundsData.getNewMovingFundsDustThreshold(),\n movingFundsTimeoutResetDelay,\n movingFundsTimeout,\n movingFundsTimeoutSlashingAmount,\n movingFundsTimeoutNotifierRewardMultiplier,\n movedFundsSweepTxMaxTotalFee,\n movedFundsSweepTimeout,\n movedFundsSweepTimeoutSlashingAmount,\n movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n movingFundsData.finalizeMovingFundsDustThresholdUpdate(\n governanceDelay()\n );\n }\n\n /// @notice Begins the moving funds timeout reset delay update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsTimeoutResetDelay New moving funds timeout reset\n /// delay.\n function beginMovingFundsTimeoutResetDelayUpdate(\n uint32 _newMovingFundsTimeoutResetDelay\n ) external onlyOwner {\n movingFundsData.beginMovingFundsTimeoutResetDelayUpdate(\n _newMovingFundsTimeoutResetDelay\n );\n }\n\n /// @notice Finalizes the moving funds timeout reset delay update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsTimeoutResetDelayUpdate() external onlyOwner {\n (\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n ,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n ) = bridge.movingFundsParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateMovingFundsParameters(\n movingFundsTxMaxTotalFee,\n movingFundsDustThreshold,\n movingFundsData.getNewMovingFundsTimeoutResetDelay(),\n movingFundsTimeout,\n movingFundsTimeoutSlashingAmount,\n movingFundsTimeoutNotifierRewardMultiplier,\n movedFundsSweepTxMaxTotalFee,\n movedFundsSweepTimeout,\n movedFundsSweepTimeoutSlashingAmount,\n movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n movingFundsData.finalizeMovingFundsTimeoutResetDelayUpdate(\n governanceDelay()\n );\n }\n\n /// @notice Begins the moving funds timeout update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsTimeout New moving funds timeout.\n function beginMovingFundsTimeoutUpdate(uint32 _newMovingFundsTimeout)\n external\n onlyOwner\n {\n movingFundsData.beginMovingFundsTimeoutUpdate(_newMovingFundsTimeout);\n }\n\n /// @notice Finalizes the moving funds timeout update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsTimeoutUpdate() external onlyOwner {\n (\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n ,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n ) = bridge.movingFundsParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateMovingFundsParameters(\n movingFundsTxMaxTotalFee,\n movingFundsDustThreshold,\n movingFundsTimeoutResetDelay,\n movingFundsData.getNewMovingFundsTimeout(),\n movingFundsTimeoutSlashingAmount,\n movingFundsTimeoutNotifierRewardMultiplier,\n movedFundsSweepTxMaxTotalFee,\n movedFundsSweepTimeout,\n movedFundsSweepTimeoutSlashingAmount,\n movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n movingFundsData.finalizeMovingFundsTimeoutUpdate(governanceDelay());\n }\n\n /// @notice Begins the moving funds timeout slashing amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsTimeoutSlashingAmount New moving funds timeout\n /// slashing amount.\n function beginMovingFundsTimeoutSlashingAmountUpdate(\n uint96 _newMovingFundsTimeoutSlashingAmount\n ) external onlyOwner {\n movingFundsData.beginMovingFundsTimeoutSlashingAmountUpdate(\n _newMovingFundsTimeoutSlashingAmount\n );\n }\n\n /// @notice Finalizes the moving funds timeout slashing amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsTimeoutSlashingAmountUpdate()\n external\n onlyOwner\n {\n (\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n ,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n ) = bridge.movingFundsParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateMovingFundsParameters(\n movingFundsTxMaxTotalFee,\n movingFundsDustThreshold,\n movingFundsTimeoutResetDelay,\n movingFundsTimeout,\n movingFundsData.getNewMovingFundsTimeoutSlashingAmount(),\n movingFundsTimeoutNotifierRewardMultiplier,\n movedFundsSweepTxMaxTotalFee,\n movedFundsSweepTimeout,\n movedFundsSweepTimeoutSlashingAmount,\n movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n movingFundsData.finalizeMovingFundsTimeoutSlashingAmountUpdate(\n governanceDelay()\n );\n }\n\n /// @notice Begins the moving funds timeout notifier reward multiplier update\n /// process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsTimeoutNotifierRewardMultiplier New moving funds\n /// timeout notifier reward multiplier.\n function beginMovingFundsTimeoutNotifierRewardMultiplierUpdate(\n uint32 _newMovingFundsTimeoutNotifierRewardMultiplier\n ) external onlyOwner {\n movingFundsData.beginMovingFundsTimeoutNotifierRewardMultiplierUpdate(\n _newMovingFundsTimeoutNotifierRewardMultiplier\n );\n }\n\n /// @notice Finalizes the moving funds timeout notifier reward multiplier\n /// update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsTimeoutNotifierRewardMultiplierUpdate()\n external\n onlyOwner\n {\n (\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n ,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n ) = bridge.movingFundsParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateMovingFundsParameters(\n movingFundsTxMaxTotalFee,\n movingFundsDustThreshold,\n movingFundsTimeoutResetDelay,\n movingFundsTimeout,\n movingFundsTimeoutSlashingAmount,\n movingFundsData.getNewMovingFundsTimeoutNotifierRewardMultiplier(),\n movedFundsSweepTxMaxTotalFee,\n movedFundsSweepTimeout,\n movedFundsSweepTimeoutSlashingAmount,\n movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n movingFundsData\n .finalizeMovingFundsTimeoutNotifierRewardMultiplierUpdate(\n governanceDelay()\n );\n }\n\n /// @notice Begins the moved funds sweep tx max total fee update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovedFundsSweepTxMaxTotalFee New moved funds sweep tx max total\n /// fee.\n function beginMovedFundsSweepTxMaxTotalFeeUpdate(\n uint64 _newMovedFundsSweepTxMaxTotalFee\n ) external onlyOwner {\n movingFundsData.beginMovedFundsSweepTxMaxTotalFeeUpdate(\n _newMovedFundsSweepTxMaxTotalFee\n );\n }\n\n /// @notice Finalizes the moved funds sweep tx max total fee update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovedFundsSweepTxMaxTotalFeeUpdate() external onlyOwner {\n (\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n ,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n ) = bridge.movingFundsParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateMovingFundsParameters(\n movingFundsTxMaxTotalFee,\n movingFundsDustThreshold,\n movingFundsTimeoutResetDelay,\n movingFundsTimeout,\n movingFundsTimeoutSlashingAmount,\n movingFundsTimeoutNotifierRewardMultiplier,\n movingFundsData.getNewMovedFundsSweepTxMaxTotalFee(),\n movedFundsSweepTimeout,\n movedFundsSweepTimeoutSlashingAmount,\n movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n movingFundsData.finalizeMovedFundsSweepTxMaxTotalFeeUpdate(\n governanceDelay()\n );\n }\n\n /// @notice Begins the moved funds sweep timeout update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovedFundsSweepTimeout New moved funds sweep timeout.\n function beginMovedFundsSweepTimeoutUpdate(\n uint32 _newMovedFundsSweepTimeout\n ) external onlyOwner {\n movingFundsData.beginMovedFundsSweepTimeoutUpdate(\n _newMovedFundsSweepTimeout\n );\n }\n\n /// @notice Finalizes the moved funds sweep timeout update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovedFundsSweepTimeoutUpdate() external onlyOwner {\n (\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n ,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n ) = bridge.movingFundsParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateMovingFundsParameters(\n movingFundsTxMaxTotalFee,\n movingFundsDustThreshold,\n movingFundsTimeoutResetDelay,\n movingFundsTimeout,\n movingFundsTimeoutSlashingAmount,\n movingFundsTimeoutNotifierRewardMultiplier,\n movedFundsSweepTxMaxTotalFee,\n movingFundsData.getNewMovedFundsSweepTimeout(),\n movedFundsSweepTimeoutSlashingAmount,\n movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n movingFundsData.finalizeMovedFundsSweepTimeoutUpdate(governanceDelay());\n }\n\n /// @notice Begins the moved funds sweep timeout slashing amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovedFundsSweepTimeoutSlashingAmount New moved funds sweep\n /// timeout slashing amount.\n function beginMovedFundsSweepTimeoutSlashingAmountUpdate(\n uint96 _newMovedFundsSweepTimeoutSlashingAmount\n ) external onlyOwner {\n movingFundsData.beginMovedFundsSweepTimeoutSlashingAmountUpdate(\n _newMovedFundsSweepTimeoutSlashingAmount\n );\n }\n\n /// @notice Finalizes the moved funds sweep timeout slashing amount update\n /// process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovedFundsSweepTimeoutSlashingAmountUpdate()\n external\n onlyOwner\n {\n (\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n ,\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n ) = bridge.movingFundsParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateMovingFundsParameters(\n movingFundsTxMaxTotalFee,\n movingFundsDustThreshold,\n movingFundsTimeoutResetDelay,\n movingFundsTimeout,\n movingFundsTimeoutSlashingAmount,\n movingFundsTimeoutNotifierRewardMultiplier,\n movedFundsSweepTxMaxTotalFee,\n movedFundsSweepTimeout,\n movingFundsData.getNewMovedFundsSweepTimeoutSlashingAmount(),\n movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n movingFundsData.finalizeMovedFundsSweepTimeoutSlashingAmountUpdate(\n governanceDelay()\n );\n }\n\n /// @notice Begins the moved funds sweep timeout notifier reward multiplier\n /// update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovedFundsSweepTimeoutNotifierRewardMultiplier New moved funds\n /// sweep timeout notifier reward multiplier.\n function beginMovedFundsSweepTimeoutNotifierRewardMultiplierUpdate(\n uint32 _newMovedFundsSweepTimeoutNotifierRewardMultiplier\n ) external onlyOwner {\n movingFundsData\n .beginMovedFundsSweepTimeoutNotifierRewardMultiplierUpdate(\n _newMovedFundsSweepTimeoutNotifierRewardMultiplier\n );\n }\n\n /// @notice Finalizes the moved funds sweep timeout notifier reward multiplier\n /// update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovedFundsSweepTimeoutNotifierRewardMultiplierUpdate()\n external\n onlyOwner\n {\n (\n uint64 movingFundsTxMaxTotalFee,\n uint64 movingFundsDustThreshold,\n uint32 movingFundsTimeoutResetDelay,\n uint32 movingFundsTimeout,\n uint96 movingFundsTimeoutSlashingAmount,\n uint32 movingFundsTimeoutNotifierRewardMultiplier,\n uint64 movedFundsSweepTxMaxTotalFee,\n uint32 movedFundsSweepTimeout,\n uint96 movedFundsSweepTimeoutSlashingAmount,\n\n ) = bridge.movingFundsParameters();\n bridge.updateMovingFundsParameters(\n movingFundsTxMaxTotalFee,\n movingFundsDustThreshold,\n movingFundsTimeoutResetDelay,\n movingFundsTimeout,\n movingFundsTimeoutSlashingAmount,\n movingFundsTimeoutNotifierRewardMultiplier,\n movedFundsSweepTxMaxTotalFee,\n movedFundsSweepTimeout,\n movedFundsSweepTimeoutSlashingAmount,\n movingFundsData\n .getNewMovedFundsSweepTimeoutNotifierRewardMultiplier()\n );\n movingFundsData\n .finalizeMovedFundsSweepTimeoutNotifierRewardMultiplierUpdate(\n governanceDelay()\n );\n }\n\n // --- Wallet creation\n\n /// @notice Begins the wallet creation period update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletCreationPeriod New wallet creation period.\n function beginWalletCreationPeriodUpdate(uint32 _newWalletCreationPeriod)\n external\n onlyOwner\n {\n walletData.beginWalletCreationPeriodUpdate(_newWalletCreationPeriod);\n }\n\n /// @notice Finalizes the wallet creation period update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletCreationPeriodUpdate() external onlyOwner {\n (\n ,\n uint64 walletCreationMinBtcBalance,\n uint64 walletCreationMaxBtcBalance,\n uint64 walletClosureMinBtcBalance,\n uint32 walletMaxAge,\n uint64 walletMaxBtcTransfer,\n uint32 walletClosingPeriod\n ) = bridge.walletParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateWalletParameters(\n walletData.getNewWalletCreationPeriod(),\n walletCreationMinBtcBalance,\n walletCreationMaxBtcBalance,\n walletClosureMinBtcBalance,\n walletMaxAge,\n walletMaxBtcTransfer,\n walletClosingPeriod\n );\n walletData.finalizeWalletCreationPeriodUpdate(governanceDelay());\n }\n\n /// @notice Begins the wallet creation min btc balance update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletCreationMinBtcBalance New wallet creation min btc balance.\n function beginWalletCreationMinBtcBalanceUpdate(\n uint64 _newWalletCreationMinBtcBalance\n ) external onlyOwner {\n walletData.beginWalletCreationMinBtcBalanceUpdate(\n _newWalletCreationMinBtcBalance\n );\n }\n\n /// @notice Finalizes the wallet creation min btc balance update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletCreationMinBtcBalanceUpdate() external onlyOwner {\n (\n uint32 walletCreationPeriod,\n ,\n uint64 walletCreationMaxBtcBalance,\n uint64 walletClosureMinBtcBalance,\n uint32 walletMaxAge,\n uint64 walletMaxBtcTransfer,\n uint32 walletClosingPeriod\n ) = bridge.walletParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateWalletParameters(\n walletCreationPeriod,\n walletData.getNewWalletCreationMinBtcBalance(),\n walletCreationMaxBtcBalance,\n walletClosureMinBtcBalance,\n walletMaxAge,\n walletMaxBtcTransfer,\n walletClosingPeriod\n );\n walletData.finalizeWalletCreationMinBtcBalanceUpdate(governanceDelay());\n }\n\n /// @notice Begins the wallet creation max btc balance update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletCreationMaxBtcBalance New wallet creation max btc\n /// balance.\n function beginWalletCreationMaxBtcBalanceUpdate(\n uint64 _newWalletCreationMaxBtcBalance\n ) external onlyOwner {\n walletData.beginWalletCreationMaxBtcBalanceUpdate(\n _newWalletCreationMaxBtcBalance\n );\n }\n\n /// @notice Finalizes the wallet creation max btc balance update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletCreationMaxBtcBalanceUpdate() external onlyOwner {\n (\n uint32 walletCreationPeriod,\n uint64 walletCreationMinBtcBalance,\n ,\n uint64 walletClosureMinBtcBalance,\n uint32 walletMaxAge,\n uint64 walletMaxBtcTransfer,\n uint32 walletClosingPeriod\n ) = bridge.walletParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateWalletParameters(\n walletCreationPeriod,\n walletCreationMinBtcBalance,\n walletData.getNewWalletCreationMaxBtcBalance(),\n walletClosureMinBtcBalance,\n walletMaxAge,\n walletMaxBtcTransfer,\n walletClosingPeriod\n );\n walletData.finalizeWalletCreationMaxBtcBalanceUpdate(governanceDelay());\n }\n\n /// @notice Begins the wallet closure min btc balance update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletClosureMinBtcBalance New wallet closure min btc balance.\n function beginWalletClosureMinBtcBalanceUpdate(\n uint64 _newWalletClosureMinBtcBalance\n ) external onlyOwner {\n walletData.beginWalletClosureMinBtcBalanceUpdate(\n _newWalletClosureMinBtcBalance\n );\n }\n\n /// @notice Finalizes the wallet closure min btc balance update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletClosureMinBtcBalanceUpdate() external onlyOwner {\n (\n uint32 walletCreationPeriod,\n uint64 walletCreationMinBtcBalance,\n uint64 walletCreationMaxBtcBalance,\n ,\n uint32 walletMaxAge,\n uint64 walletMaxBtcTransfer,\n uint32 walletClosingPeriod\n ) = bridge.walletParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateWalletParameters(\n walletCreationPeriod,\n walletCreationMinBtcBalance,\n walletCreationMaxBtcBalance,\n walletData.getNewWalletClosureMinBtcBalance(),\n walletMaxAge,\n walletMaxBtcTransfer,\n walletClosingPeriod\n );\n walletData.finalizeWalletClosureMinBtcBalanceUpdate(governanceDelay());\n }\n\n /// @notice Begins the wallet max age update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletMaxAge New wallet max age.\n function beginWalletMaxAgeUpdate(uint32 _newWalletMaxAge)\n external\n onlyOwner\n {\n walletData.beginWalletMaxAgeUpdate(_newWalletMaxAge);\n }\n\n /// @notice Finalizes the wallet max age update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletMaxAgeUpdate() external onlyOwner {\n (\n uint32 walletCreationPeriod,\n uint64 walletCreationMinBtcBalance,\n uint64 walletCreationMaxBtcBalance,\n uint64 walletClosureMinBtcBalance,\n ,\n uint64 walletMaxBtcTransfer,\n uint32 walletClosingPeriod\n ) = bridge.walletParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateWalletParameters(\n walletCreationPeriod,\n walletCreationMinBtcBalance,\n walletCreationMaxBtcBalance,\n walletClosureMinBtcBalance,\n walletData.getNewWalletMaxAge(),\n walletMaxBtcTransfer,\n walletClosingPeriod\n );\n walletData.finalizeWalletMaxAgeUpdate(governanceDelay());\n }\n\n /// @notice Begins the wallet max btc transfer amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletMaxBtcTransfer New wallet max btc transfer.\n function beginWalletMaxBtcTransferUpdate(uint64 _newWalletMaxBtcTransfer)\n external\n onlyOwner\n {\n walletData.beginWalletMaxBtcTransferUpdate(_newWalletMaxBtcTransfer);\n }\n\n /// @notice Finalizes the wallet max btc transfer amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletMaxBtcTransferUpdate() external onlyOwner {\n (\n uint32 walletCreationPeriod,\n uint64 walletCreationMinBtcBalance,\n uint64 walletCreationMaxBtcBalance,\n uint64 walletClosureMinBtcBalance,\n uint32 walletMaxAge,\n ,\n uint32 walletClosingPeriod\n ) = bridge.walletParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateWalletParameters(\n walletCreationPeriod,\n walletCreationMinBtcBalance,\n walletCreationMaxBtcBalance,\n walletClosureMinBtcBalance,\n walletMaxAge,\n walletData.getNewWalletMaxBtcTransfer(),\n walletClosingPeriod\n );\n walletData.finalizeWalletMaxBtcTransferUpdate(governanceDelay());\n }\n\n /// @notice Begins the wallet closing period update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletClosingPeriod New wallet closing period.\n function beginWalletClosingPeriodUpdate(uint32 _newWalletClosingPeriod)\n external\n onlyOwner\n {\n walletData.beginWalletClosingPeriodUpdate(_newWalletClosingPeriod);\n }\n\n /// @notice Finalizes the wallet closing period update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletClosingPeriodUpdate() external onlyOwner {\n (\n uint32 walletCreationPeriod,\n uint64 walletCreationMinBtcBalance,\n uint64 walletCreationMaxBtcBalance,\n uint64 walletClosureMinBtcBalance,\n uint32 walletMaxAge,\n uint64 walletMaxBtcTransfer,\n\n ) = bridge.walletParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateWalletParameters(\n walletCreationPeriod,\n walletCreationMinBtcBalance,\n walletCreationMaxBtcBalance,\n walletClosureMinBtcBalance,\n walletMaxAge,\n walletMaxBtcTransfer,\n walletData.getNewWalletClosingPeriod()\n );\n walletData.finalizeWalletClosingPeriodUpdate(governanceDelay());\n }\n\n // --- Fraud\n\n /// @notice Begins the fraud challenge deposit amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newFraudChallengeDepositAmount New fraud challenge deposit amount.\n function beginFraudChallengeDepositAmountUpdate(\n uint96 _newFraudChallengeDepositAmount\n ) external onlyOwner {\n fraudData.beginFraudChallengeDepositAmountUpdate(\n _newFraudChallengeDepositAmount\n );\n }\n\n /// @notice Finalizes the fraud challenge deposit amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeFraudChallengeDepositAmountUpdate() external onlyOwner {\n (\n ,\n uint32 fraudChallengeDefeatTimeout,\n uint96 fraudSlashingAmount,\n uint32 fraudNotifierRewardMultiplier\n ) = bridge.fraudParameters();\n // slither-disable-next-line reentrancy-no-eth\n bridge.updateFraudParameters(\n fraudData.getNewFraudChallengeDepositAmount(),\n fraudChallengeDefeatTimeout,\n fraudSlashingAmount,\n fraudNotifierRewardMultiplier\n );\n fraudData.finalizeFraudChallengeDepositAmountUpdate(governanceDelay());\n }\n\n /// @notice Begins the fraud challenge defeat timeout update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newFraudChallengeDefeatTimeout New fraud challenge defeat timeout.\n function beginFraudChallengeDefeatTimeoutUpdate(\n uint32 _newFraudChallengeDefeatTimeout\n ) external onlyOwner {\n fraudData.beginFraudChallengeDefeatTimeoutUpdate(\n _newFraudChallengeDefeatTimeout\n );\n }\n\n /// @notice Finalizes the fraud challenge defeat timeout update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeFraudChallengeDefeatTimeoutUpdate() external onlyOwner {\n (\n uint96 fraudChallengeDepositAmount,\n ,\n uint96 fraudSlashingAmount,\n uint32 fraudNotifierRewardMultiplier\n ) = bridge.fraudParameters();\n bridge.updateFraudParameters(\n fraudChallengeDepositAmount,\n fraudData.getNewFraudChallengeDefeatTimeout(),\n fraudSlashingAmount,\n fraudNotifierRewardMultiplier\n );\n fraudData.finalizeFraudChallengeDefeatTimeoutUpdate(governanceDelay());\n }\n\n /// @notice Begins the fraud slashing amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newFraudSlashingAmount New fraud slashing amount.\n function beginFraudSlashingAmountUpdate(uint96 _newFraudSlashingAmount)\n external\n onlyOwner\n {\n fraudData.beginFraudSlashingAmountUpdate(_newFraudSlashingAmount);\n }\n\n /// @notice Finalizes the fraud slashing amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeFraudSlashingAmountUpdate() external onlyOwner {\n (\n uint96 fraudChallengeDepositAmount,\n uint32 fraudChallengeDefeatTimeout,\n ,\n uint32 fraudNotifierRewardMultiplier\n ) = bridge.fraudParameters();\n bridge.updateFraudParameters(\n fraudChallengeDepositAmount,\n fraudChallengeDefeatTimeout,\n fraudData.getNewFraudSlashingAmount(),\n fraudNotifierRewardMultiplier\n );\n fraudData.finalizeFraudSlashingAmountUpdate(governanceDelay());\n }\n\n /// @notice Begins the fraud notifier reward multiplier update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newFraudNotifierRewardMultiplier New fraud notifier reward\n /// multiplier.\n function beginFraudNotifierRewardMultiplierUpdate(\n uint32 _newFraudNotifierRewardMultiplier\n ) external onlyOwner {\n fraudData.beginFraudNotifierRewardMultiplierUpdate(\n _newFraudNotifierRewardMultiplier\n );\n }\n\n /// @notice Finalizes the fraud notifier reward multiplier update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeFraudNotifierRewardMultiplierUpdate() external onlyOwner {\n (\n uint96 fraudChallengeDepositAmount,\n uint32 fraudChallengeDefeatTimeout,\n uint96 fraudSlashingAmount,\n\n ) = bridge.fraudParameters();\n bridge.updateFraudParameters(\n fraudChallengeDepositAmount,\n fraudChallengeDefeatTimeout,\n fraudSlashingAmount,\n fraudData.getNewFraudNotifierRewardMultiplier()\n );\n fraudData.finalizeFraudNotifierRewardMultiplierUpdate(\n governanceDelay()\n );\n }\n\n /// @notice Gets the governance delay parameter.\n function governanceDelay() internal view returns (uint256) {\n return governanceDelays[0];\n }\n}\n"
|
|
255
261
|
},
|
|
256
|
-
"
|
|
257
|
-
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n // Booleans are more expensive than uint256 or any type that takes up a full\n // word because each write operation emits an extra SLOAD to first read the\n // slot's contents, replace the bits taken up by the boolean, and then write\n // back. This is the compiler's defense against contract upgrades and\n // pointer aliasing, and it cannot be disabled.\n\n // The values being non-zero value makes deployment a bit more expensive,\n // but in exchange the refund on every call to nonReentrant will be lower in\n // amount. Since refunds are capped to a percentage of the total\n // transaction's gas, it is best to keep them low in cases like this one, to\n // increase the likelihood of the full refund coming into effect.\n uint256 private constant _NOT_ENTERED = 1;\n uint256 private constant _ENTERED = 2;\n\n uint256 private _status;\n\n constructor() {\n _status = _NOT_ENTERED;\n }\n\n /**\n * @dev Prevents a contract from calling itself, directly or indirectly.\n * Calling a `nonReentrant` function from another `nonReentrant`\n * function is not supported. It is possible to prevent this from happening\n * by making the `nonReentrant` function external, and making it call a\n * `private` function that does the actual work.\n */\n modifier nonReentrant() {\n // On the first call to nonReentrant, _notEntered will be true\n require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n // Any calls to nonReentrant after this point will fail\n _status = _ENTERED;\n\n _;\n\n // By storing the original value once again, a refund is triggered (see\n // https://eips.ethereum.org/EIPS/eip-2200)\n _status = _NOT_ENTERED;\n }\n}\n"
|
|
262
|
+
"contracts/bridge/BridgeGovernanceParameters.sol": {
|
|
263
|
+
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\n/// @title Bridge Governance library for storing updatable parameters.\nlibrary BridgeGovernanceParameters {\n struct DepositData {\n uint64 newDepositDustThreshold;\n uint256 depositDustThresholdChangeInitiated;\n uint64 newDepositTreasuryFeeDivisor;\n uint256 depositTreasuryFeeDivisorChangeInitiated;\n uint64 newDepositTxMaxFee;\n uint256 depositTxMaxFeeChangeInitiated;\n }\n\n struct RedemptionData {\n uint64 newRedemptionDustThreshold;\n uint256 redemptionDustThresholdChangeInitiated;\n uint64 newRedemptionTreasuryFeeDivisor;\n uint256 redemptionTreasuryFeeDivisorChangeInitiated;\n uint64 newRedemptionTxMaxFee;\n uint256 redemptionTxMaxFeeChangeInitiated;\n uint32 newRedemptionTimeout;\n uint256 redemptionTimeoutChangeInitiated;\n uint96 newRedemptionTimeoutSlashingAmount;\n uint256 redemptionTimeoutSlashingAmountChangeInitiated;\n uint32 newRedemptionTimeoutNotifierRewardMultiplier;\n uint256 redemptionTimeoutNotifierRewardMultiplierChangeInitiated;\n }\n\n struct MovingFundsData {\n uint64 newMovingFundsTxMaxTotalFee;\n uint256 movingFundsTxMaxTotalFeeChangeInitiated;\n uint64 newMovingFundsDustThreshold;\n uint256 movingFundsDustThresholdChangeInitiated;\n uint32 newMovingFundsTimeoutResetDelay;\n uint256 movingFundsTimeoutResetDelayChangeInitiated;\n uint32 newMovingFundsTimeout;\n uint256 movingFundsTimeoutChangeInitiated;\n uint96 newMovingFundsTimeoutSlashingAmount;\n uint256 movingFundsTimeoutSlashingAmountChangeInitiated;\n uint32 newMovingFundsTimeoutNotifierRewardMultiplier;\n uint256 movingFundsTimeoutNotifierRewardMultiplierChangeInitiated;\n uint64 newMovedFundsSweepTxMaxTotalFee;\n uint256 movedFundsSweepTxMaxTotalFeeChangeInitiated;\n uint32 newMovedFundsSweepTimeout;\n uint256 movedFundsSweepTimeoutChangeInitiated;\n uint96 newMovedFundsSweepTimeoutSlashingAmount;\n uint256 movedFundsSweepTimeoutSlashingAmountChangeInitiated;\n uint32 newMovedFundsSweepTimeoutNotifierRewardMultiplier;\n uint256 movedFundsSweepTimeoutNotifierRewardMultiplierChangeInitiated;\n }\n\n struct WalletData {\n uint32 newWalletCreationPeriod;\n uint256 walletCreationPeriodChangeInitiated;\n uint64 newWalletCreationMinBtcBalance;\n uint256 walletCreationMinBtcBalanceChangeInitiated;\n uint64 newWalletCreationMaxBtcBalance;\n uint256 walletCreationMaxBtcBalanceChangeInitiated;\n uint64 newWalletClosureMinBtcBalance;\n uint256 walletClosureMinBtcBalanceChangeInitiated;\n uint32 newWalletMaxAge;\n uint256 walletMaxAgeChangeInitiated;\n uint64 newWalletMaxBtcTransfer;\n uint256 walletMaxBtcTransferChangeInitiated;\n uint32 newWalletClosingPeriod;\n uint256 walletClosingPeriodChangeInitiated;\n }\n\n struct FraudData {\n uint96 newFraudChallengeDepositAmount;\n uint256 fraudChallengeDepositAmountChangeInitiated;\n uint32 newFraudChallengeDefeatTimeout;\n uint256 fraudChallengeDefeatTimeoutChangeInitiated;\n uint96 newFraudSlashingAmount;\n uint256 fraudSlashingAmountChangeInitiated;\n uint32 newFraudNotifierRewardMultiplier;\n uint256 fraudNotifierRewardMultiplierChangeInitiated;\n }\n\n event DepositDustThresholdUpdateStarted(\n uint64 newDepositDustThreshold,\n uint256 timestamp\n );\n event DepositDustThresholdUpdated(uint64 depositDustThreshold);\n\n event DepositTreasuryFeeDivisorUpdateStarted(\n uint64 depositTreasuryFeeDivisor,\n uint256 timestamp\n );\n event DepositTreasuryFeeDivisorUpdated(uint64 depositTreasuryFeeDivisor);\n\n event DepositTxMaxFeeUpdateStarted(\n uint64 newDepositTxMaxFee,\n uint256 timestamp\n );\n event DepositTxMaxFeeUpdated(uint64 depositTxMaxFee);\n\n event RedemptionDustThresholdUpdateStarted(\n uint64 newRedemptionDustThreshold,\n uint256 timestamp\n );\n event RedemptionDustThresholdUpdated(uint64 redemptionDustThreshold);\n\n event RedemptionTreasuryFeeDivisorUpdateStarted(\n uint64 newRedemptionTreasuryFeeDivisor,\n uint256 timestamp\n );\n event RedemptionTreasuryFeeDivisorUpdated(\n uint64 redemptionTreasuryFeeDivisor\n );\n\n event RedemptionTxMaxFeeUpdateStarted(\n uint64 newRedemptionTxMaxFee,\n uint256 timestamp\n );\n event RedemptionTxMaxFeeUpdated(uint64 redemptionTxMaxFee);\n\n event RedemptionTimeoutUpdateStarted(\n uint32 newRedemptionTimeout,\n uint256 timestamp\n );\n event RedemptionTimeoutUpdated(uint32 redemptionTimeout);\n\n event RedemptionTimeoutSlashingAmountUpdateStarted(\n uint96 newRedemptionTimeoutSlashingAmount,\n uint256 timestamp\n );\n event RedemptionTimeoutSlashingAmountUpdated(\n uint96 redemptionTimeoutSlashingAmount\n );\n\n event RedemptionTimeoutNotifierRewardMultiplierUpdateStarted(\n uint32 newRedemptionTimeoutNotifierRewardMultiplier,\n uint256 timestamp\n );\n event RedemptionTimeoutNotifierRewardMultiplierUpdated(\n uint32 redemptionTimeoutNotifierRewardMultiplier\n );\n\n event MovingFundsTxMaxTotalFeeUpdateStarted(\n uint64 newMovingFundsTxMaxTotalFee,\n uint256 timestamp\n );\n event MovingFundsTxMaxTotalFeeUpdated(uint64 movingFundsTxMaxTotalFee);\n\n event MovingFundsDustThresholdUpdateStarted(\n uint64 newMovingFundsDustThreshold,\n uint256 timestamp\n );\n event MovingFundsDustThresholdUpdated(uint64 movingFundsDustThreshold);\n\n event MovingFundsTimeoutResetDelayUpdateStarted(\n uint32 newMovingFundsTimeoutResetDelay,\n uint256 timestamp\n );\n event MovingFundsTimeoutResetDelayUpdated(\n uint32 movingFundsTimeoutResetDelay\n );\n\n event MovingFundsTimeoutUpdateStarted(\n uint32 newMovingFundsTimeout,\n uint256 timestamp\n );\n event MovingFundsTimeoutUpdated(uint32 movingFundsTimeout);\n\n event MovingFundsTimeoutSlashingAmountUpdateStarted(\n uint96 newMovingFundsTimeoutSlashingAmount,\n uint256 timestamp\n );\n event MovingFundsTimeoutSlashingAmountUpdated(\n uint96 movingFundsTimeoutSlashingAmount\n );\n\n event MovingFundsTimeoutNotifierRewardMultiplierUpdateStarted(\n uint32 newMovingFundsTimeoutNotifierRewardMultiplier,\n uint256 timestamp\n );\n event MovingFundsTimeoutNotifierRewardMultiplierUpdated(\n uint32 movingFundsTimeoutNotifierRewardMultiplier\n );\n\n event MovedFundsSweepTxMaxTotalFeeUpdateStarted(\n uint64 newMovedFundsSweepTxMaxTotalFee,\n uint256 timestamp\n );\n event MovedFundsSweepTxMaxTotalFeeUpdated(\n uint64 movedFundsSweepTxMaxTotalFee\n );\n\n event MovedFundsSweepTimeoutUpdateStarted(\n uint32 newMovedFundsSweepTimeout,\n uint256 timestamp\n );\n event MovedFundsSweepTimeoutUpdated(uint32 movedFundsSweepTimeout);\n\n event MovedFundsSweepTimeoutSlashingAmountUpdateStarted(\n uint96 newMovedFundsSweepTimeoutSlashingAmount,\n uint256 timestamp\n );\n event MovedFundsSweepTimeoutSlashingAmountUpdated(\n uint96 movedFundsSweepTimeoutSlashingAmount\n );\n\n event MovedFundsSweepTimeoutNotifierRewardMultiplierUpdateStarted(\n uint32 newMovedFundsSweepTimeoutNotifierRewardMultiplier,\n uint256 timestamp\n );\n event MovedFundsSweepTimeoutNotifierRewardMultiplierUpdated(\n uint32 movedFundsSweepTimeoutNotifierRewardMultiplier\n );\n\n event WalletCreationPeriodUpdateStarted(\n uint32 newWalletCreationPeriod,\n uint256 timestamp\n );\n event WalletCreationPeriodUpdated(uint32 walletCreationPeriod);\n\n event WalletCreationMinBtcBalanceUpdateStarted(\n uint64 newWalletCreationMinBtcBalance,\n uint256 timestamp\n );\n event WalletCreationMinBtcBalanceUpdated(\n uint64 walletCreationMinBtcBalance\n );\n\n event WalletCreationMaxBtcBalanceUpdateStarted(\n uint64 newWalletCreationMaxBtcBalance,\n uint256 timestamp\n );\n event WalletCreationMaxBtcBalanceUpdated(\n uint64 walletCreationMaxBtcBalance\n );\n\n event WalletClosureMinBtcBalanceUpdateStarted(\n uint64 newWalletClosureMinBtcBalance,\n uint256 timestamp\n );\n event WalletClosureMinBtcBalanceUpdated(uint64 walletClosureMinBtcBalance);\n\n event WalletMaxAgeUpdateStarted(uint32 newWalletMaxAge, uint256 timestamp);\n event WalletMaxAgeUpdated(uint32 walletMaxAge);\n\n event WalletMaxBtcTransferUpdateStarted(\n uint64 newWalletMaxBtcTransfer,\n uint256 timestamp\n );\n event WalletMaxBtcTransferUpdated(uint64 walletMaxBtcTransfer);\n\n event WalletClosingPeriodUpdateStarted(\n uint32 newWalletClosingPeriod,\n uint256 timestamp\n );\n event WalletClosingPeriodUpdated(uint32 walletClosingPeriod);\n\n event FraudChallengeDepositAmountUpdateStarted(\n uint96 newFraudChallengeDepositAmount,\n uint256 timestamp\n );\n event FraudChallengeDepositAmountUpdated(\n uint96 fraudChallengeDepositAmount\n );\n\n event FraudChallengeDefeatTimeoutUpdateStarted(\n uint32 newFraudChallengeDefeatTimeout,\n uint256 timestamp\n );\n event FraudChallengeDefeatTimeoutUpdated(\n uint32 fraudChallengeDefeatTimeout\n );\n\n event FraudSlashingAmountUpdateStarted(\n uint96 newFraudSlashingAmount,\n uint256 timestamp\n );\n event FraudSlashingAmountUpdated(uint96 fraudSlashingAmount);\n\n event FraudNotifierRewardMultiplierUpdateStarted(\n uint32 newFraudNotifierRewardMultiplier,\n uint256 timestamp\n );\n event FraudNotifierRewardMultiplierUpdated(\n uint32 fraudNotifierRewardMultiplier\n );\n\n /// @notice Reverts if called before the governance delay elapses.\n /// @param changeInitiatedTimestamp Timestamp indicating the beginning\n /// of the change.\n modifier onlyAfterGovernanceDelay(\n uint256 changeInitiatedTimestamp,\n uint256 governanceDelay\n ) {\n /* solhint-disable not-rely-on-time */\n require(changeInitiatedTimestamp > 0, \"Change not initiated\");\n require(\n block.timestamp - changeInitiatedTimestamp >= governanceDelay,\n \"Governance delay has not elapsed\"\n );\n _;\n /* solhint-enable not-rely-on-time */\n }\n\n // --- Deposit\n\n /// @notice Begins the deposit dust threshold amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newDepositDustThreshold New deposit dust threshold amount.\n function beginDepositDustThresholdUpdate(\n DepositData storage self,\n uint64 _newDepositDustThreshold\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newDepositDustThreshold = _newDepositDustThreshold;\n self.depositDustThresholdChangeInitiated = block.timestamp;\n emit DepositDustThresholdUpdateStarted(\n _newDepositDustThreshold,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the deposit dust threshold amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeDepositDustThresholdUpdate(\n DepositData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.depositDustThresholdChangeInitiated,\n governanceDelay\n )\n {\n emit DepositDustThresholdUpdated(self.newDepositDustThreshold);\n\n self.newDepositDustThreshold = 0;\n self.depositDustThresholdChangeInitiated = 0;\n }\n\n function getNewDepositDustThreshold(DepositData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newDepositDustThreshold;\n }\n\n /// @notice Begins the deposit treasury fee divisor amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newDepositTreasuryFeeDivisor New deposit treasury fee divisor amount.\n function beginDepositTreasuryFeeDivisorUpdate(\n DepositData storage self,\n uint64 _newDepositTreasuryFeeDivisor\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newDepositTreasuryFeeDivisor = _newDepositTreasuryFeeDivisor;\n self.depositTreasuryFeeDivisorChangeInitiated = block.timestamp;\n emit DepositTreasuryFeeDivisorUpdateStarted(\n _newDepositTreasuryFeeDivisor,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the deposit treasury fee divisor amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeDepositTreasuryFeeDivisorUpdate(\n DepositData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.depositTreasuryFeeDivisorChangeInitiated,\n governanceDelay\n )\n {\n emit DepositTreasuryFeeDivisorUpdated(\n self.newDepositTreasuryFeeDivisor\n );\n\n self.newDepositTreasuryFeeDivisor = 0;\n self.depositTreasuryFeeDivisorChangeInitiated = 0;\n }\n\n function getNewDepositTreasuryFeeDivisor(DepositData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newDepositTreasuryFeeDivisor;\n }\n\n /// @notice Begins the deposit tx max fee amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newDepositTxMaxFee New deposit tx max fee amount.\n function beginDepositTxMaxFeeUpdate(\n DepositData storage self,\n uint64 _newDepositTxMaxFee\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newDepositTxMaxFee = _newDepositTxMaxFee;\n self.depositTxMaxFeeChangeInitiated = block.timestamp;\n emit DepositTxMaxFeeUpdateStarted(_newDepositTxMaxFee, block.timestamp);\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the deposit tx max fee amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeDepositTxMaxFeeUpdate(\n DepositData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.depositTxMaxFeeChangeInitiated,\n governanceDelay\n )\n {\n emit DepositTxMaxFeeUpdated(self.newDepositTxMaxFee);\n\n self.newDepositTxMaxFee = 0;\n self.depositTxMaxFeeChangeInitiated = 0;\n }\n\n function getNewDepositTxMaxFee(DepositData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newDepositTxMaxFee;\n }\n\n // --- Redemption\n\n /// @notice Begins the redemption dust threshold amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionDustThreshold New redemption dust threshold amount.\n function beginRedemptionDustThresholdUpdate(\n RedemptionData storage self,\n uint64 _newRedemptionDustThreshold\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newRedemptionDustThreshold = _newRedemptionDustThreshold;\n self.redemptionDustThresholdChangeInitiated = block.timestamp;\n emit RedemptionDustThresholdUpdateStarted(\n _newRedemptionDustThreshold,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the redemption dust threshold amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionDustThresholdUpdate(\n RedemptionData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.redemptionDustThresholdChangeInitiated,\n governanceDelay\n )\n {\n emit RedemptionDustThresholdUpdated(self.newRedemptionDustThreshold);\n\n self.newRedemptionDustThreshold = 0;\n self.redemptionDustThresholdChangeInitiated = 0;\n }\n\n function getNewRedemptionDustThreshold(RedemptionData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newRedemptionDustThreshold;\n }\n\n /// @notice Begins the redemption treasury fee divisor amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionTreasuryFeeDivisor New redemption treasury fee divisor\n /// amount.\n function beginRedemptionTreasuryFeeDivisorUpdate(\n RedemptionData storage self,\n uint64 _newRedemptionTreasuryFeeDivisor\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newRedemptionTreasuryFeeDivisor = _newRedemptionTreasuryFeeDivisor;\n self.redemptionTreasuryFeeDivisorChangeInitiated = block.timestamp;\n emit RedemptionTreasuryFeeDivisorUpdateStarted(\n _newRedemptionTreasuryFeeDivisor,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the redemption treasury fee divisor amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionTreasuryFeeDivisorUpdate(\n RedemptionData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.redemptionTreasuryFeeDivisorChangeInitiated,\n governanceDelay\n )\n {\n emit RedemptionTreasuryFeeDivisorUpdated(\n self.newRedemptionTreasuryFeeDivisor\n );\n\n self.newRedemptionTreasuryFeeDivisor = 0;\n self.redemptionTreasuryFeeDivisorChangeInitiated = 0;\n }\n\n function getNewRedemptionTreasuryFeeDivisor(RedemptionData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newRedemptionTreasuryFeeDivisor;\n }\n\n /// @notice Begins the redemption tx max fee amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionTxMaxFee New redemption tx max fee amount.\n function beginRedemptionTxMaxFeeUpdate(\n RedemptionData storage self,\n uint64 _newRedemptionTxMaxFee\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newRedemptionTxMaxFee = _newRedemptionTxMaxFee;\n self.redemptionTxMaxFeeChangeInitiated = block.timestamp;\n emit RedemptionTxMaxFeeUpdateStarted(\n _newRedemptionTxMaxFee,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the redemption tx max fee amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionTxMaxFeeUpdate(\n RedemptionData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.redemptionTxMaxFeeChangeInitiated,\n governanceDelay\n )\n {\n emit RedemptionTxMaxFeeUpdated(self.newRedemptionTxMaxFee);\n\n self.newRedemptionTxMaxFee = 0;\n self.redemptionTxMaxFeeChangeInitiated = 0;\n }\n\n function getNewRedemptionTxMaxFee(RedemptionData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newRedemptionTxMaxFee;\n }\n\n /// @notice Begins the redemption timeout amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionTimeout New redemption timeout amount.\n function beginRedemptionTimeoutUpdate(\n RedemptionData storage self,\n uint32 _newRedemptionTimeout\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newRedemptionTimeout = _newRedemptionTimeout;\n self.redemptionTimeoutChangeInitiated = block.timestamp;\n emit RedemptionTimeoutUpdateStarted(\n _newRedemptionTimeout,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the redemption timeout amount update\n /// process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionTimeoutUpdate(\n RedemptionData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.redemptionTimeoutChangeInitiated,\n governanceDelay\n )\n {\n emit RedemptionTimeoutUpdated(self.newRedemptionTimeout);\n\n self.newRedemptionTimeout = 0;\n self.redemptionTimeoutChangeInitiated = 0;\n }\n\n function getNewRedemptionTimeout(RedemptionData storage self)\n internal\n view\n returns (uint32)\n {\n return self.newRedemptionTimeout;\n }\n\n /// @notice Begins the redemption timeout slashing amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionTimeoutSlashingAmount New redemption timeout slashing\n /// amount.\n function beginRedemptionTimeoutSlashingAmountUpdate(\n RedemptionData storage self,\n uint96 _newRedemptionTimeoutSlashingAmount\n ) external {\n /* solhint-disable not-rely-on-time */\n self\n .newRedemptionTimeoutSlashingAmount = _newRedemptionTimeoutSlashingAmount;\n self.redemptionTimeoutSlashingAmountChangeInitiated = block.timestamp;\n emit RedemptionTimeoutSlashingAmountUpdateStarted(\n _newRedemptionTimeoutSlashingAmount,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the redemption timeout slashing amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionTimeoutSlashingAmountUpdate(\n RedemptionData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.redemptionTimeoutSlashingAmountChangeInitiated,\n governanceDelay\n )\n {\n emit RedemptionTimeoutSlashingAmountUpdated(\n self.newRedemptionTimeoutSlashingAmount\n );\n\n self.newRedemptionTimeoutSlashingAmount = 0;\n self.redemptionTimeoutSlashingAmountChangeInitiated = 0;\n }\n\n function getNewRedemptionTimeoutSlashingAmount(RedemptionData storage self)\n internal\n view\n returns (uint96)\n {\n return self.newRedemptionTimeoutSlashingAmount;\n }\n\n /// @notice Begins the redemption timeout notifier reward multiplier amount\n /// update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newRedemptionTimeoutNotifierRewardMultiplier New redemption\n /// timeout notifier reward multiplier amount.\n function beginRedemptionTimeoutNotifierRewardMultiplierUpdate(\n RedemptionData storage self,\n uint32 _newRedemptionTimeoutNotifierRewardMultiplier\n ) internal {\n /* solhint-disable not-rely-on-time */\n self\n .newRedemptionTimeoutNotifierRewardMultiplier = _newRedemptionTimeoutNotifierRewardMultiplier;\n self.redemptionTimeoutNotifierRewardMultiplierChangeInitiated = block\n .timestamp;\n emit RedemptionTimeoutNotifierRewardMultiplierUpdateStarted(\n _newRedemptionTimeoutNotifierRewardMultiplier,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the redemption timeout notifier reward multiplier amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeRedemptionTimeoutNotifierRewardMultiplierUpdate(\n RedemptionData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.redemptionTimeoutNotifierRewardMultiplierChangeInitiated,\n governanceDelay\n )\n {\n emit RedemptionTimeoutNotifierRewardMultiplierUpdated(\n self.newRedemptionTimeoutNotifierRewardMultiplier\n );\n\n self.newRedemptionTimeoutNotifierRewardMultiplier = 0;\n self.redemptionTimeoutNotifierRewardMultiplierChangeInitiated = 0;\n }\n\n function getNewRedemptionTimeoutNotifierRewardMultiplier(\n RedemptionData storage self\n ) internal view returns (uint32) {\n return self.newRedemptionTimeoutNotifierRewardMultiplier;\n }\n\n // --- Moving funds\n\n /// @notice Begins the moving funds tx max total fee amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsTxMaxTotalFee New moving funds tx max total fee amount.\n function beginMovingFundsTxMaxTotalFeeUpdate(\n MovingFundsData storage self,\n uint64 _newMovingFundsTxMaxTotalFee\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newMovingFundsTxMaxTotalFee = _newMovingFundsTxMaxTotalFee;\n self.movingFundsTxMaxTotalFeeChangeInitiated = block.timestamp;\n emit MovingFundsTxMaxTotalFeeUpdateStarted(\n _newMovingFundsTxMaxTotalFee,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the moving funds tx max total fee amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsTxMaxTotalFeeUpdate(\n MovingFundsData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.movingFundsTxMaxTotalFeeChangeInitiated,\n governanceDelay\n )\n {\n emit MovingFundsTxMaxTotalFeeUpdated(self.newMovingFundsTxMaxTotalFee);\n\n self.newMovingFundsTxMaxTotalFee = 0;\n self.movingFundsTxMaxTotalFeeChangeInitiated = 0;\n }\n\n function getNewMovingFundsTxMaxTotalFee(MovingFundsData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newMovingFundsTxMaxTotalFee;\n }\n\n /// @notice Begins the moving funds dust threshold amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsDustThreshold New moving funds dust threshold amount.\n function beginMovingFundsDustThresholdUpdate(\n MovingFundsData storage self,\n uint64 _newMovingFundsDustThreshold\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newMovingFundsDustThreshold = _newMovingFundsDustThreshold;\n self.movingFundsDustThresholdChangeInitiated = block.timestamp;\n emit MovingFundsDustThresholdUpdateStarted(\n _newMovingFundsDustThreshold,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the moving funds dust threshold amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsDustThresholdUpdate(\n MovingFundsData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.movingFundsDustThresholdChangeInitiated,\n governanceDelay\n )\n {\n emit MovingFundsDustThresholdUpdated(self.newMovingFundsDustThreshold);\n\n self.newMovingFundsDustThreshold = 0;\n self.movingFundsDustThresholdChangeInitiated = 0;\n }\n\n function getNewMovingFundsDustThreshold(MovingFundsData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newMovingFundsDustThreshold;\n }\n\n /// @notice Begins the moving funds timeout reset delay amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsTimeoutResetDelay New moving funds timeout reset\n /// delay amount.\n function beginMovingFundsTimeoutResetDelayUpdate(\n MovingFundsData storage self,\n uint32 _newMovingFundsTimeoutResetDelay\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newMovingFundsTimeoutResetDelay = _newMovingFundsTimeoutResetDelay;\n self.movingFundsTimeoutResetDelayChangeInitiated = block.timestamp;\n emit MovingFundsTimeoutResetDelayUpdateStarted(\n _newMovingFundsTimeoutResetDelay,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the moving funds timeout reset delay amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsTimeoutResetDelayUpdate(\n MovingFundsData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.movingFundsTimeoutResetDelayChangeInitiated,\n governanceDelay\n )\n {\n emit MovingFundsTimeoutResetDelayUpdated(\n self.newMovingFundsTimeoutResetDelay\n );\n\n self.newMovingFundsTimeoutResetDelay = 0;\n self.movingFundsTimeoutResetDelayChangeInitiated = 0;\n }\n\n function getNewMovingFundsTimeoutResetDelay(MovingFundsData storage self)\n internal\n view\n returns (uint32)\n {\n return self.newMovingFundsTimeoutResetDelay;\n }\n\n /// @notice Begins the moving funds timeout amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsTimeout New moving funds timeout amount.\n function beginMovingFundsTimeoutUpdate(\n MovingFundsData storage self,\n uint32 _newMovingFundsTimeout\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newMovingFundsTimeout = _newMovingFundsTimeout;\n self.movingFundsTimeoutChangeInitiated = block.timestamp;\n emit MovingFundsTimeoutUpdateStarted(\n _newMovingFundsTimeout,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the moving funds timeout amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsTimeoutUpdate(\n MovingFundsData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.movingFundsTimeoutChangeInitiated,\n governanceDelay\n )\n {\n emit MovingFundsTimeoutUpdated(self.newMovingFundsTimeout);\n\n self.newMovingFundsTimeout = 0;\n self.movingFundsTimeoutChangeInitiated = 0;\n }\n\n function getNewMovingFundsTimeout(MovingFundsData storage self)\n internal\n view\n returns (uint32)\n {\n return self.newMovingFundsTimeout;\n }\n\n /// @notice Begins the moving funds timeout slashing amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsTimeoutSlashingAmount New moving funds timeout slashing amount.\n function beginMovingFundsTimeoutSlashingAmountUpdate(\n MovingFundsData storage self,\n uint96 _newMovingFundsTimeoutSlashingAmount\n ) external {\n /* solhint-disable not-rely-on-time */\n self\n .newMovingFundsTimeoutSlashingAmount = _newMovingFundsTimeoutSlashingAmount;\n self.movingFundsTimeoutSlashingAmountChangeInitiated = block.timestamp;\n emit MovingFundsTimeoutSlashingAmountUpdateStarted(\n _newMovingFundsTimeoutSlashingAmount,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the moving funds timeout slashing amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsTimeoutSlashingAmountUpdate(\n MovingFundsData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.movingFundsTimeoutSlashingAmountChangeInitiated,\n governanceDelay\n )\n {\n emit MovingFundsTimeoutSlashingAmountUpdated(\n self.newMovingFundsTimeoutSlashingAmount\n );\n\n self.newMovingFundsTimeoutSlashingAmount = 0;\n self.movingFundsTimeoutSlashingAmountChangeInitiated = 0;\n }\n\n function getNewMovingFundsTimeoutSlashingAmount(\n MovingFundsData storage self\n ) external view returns (uint96) {\n return self.newMovingFundsTimeoutSlashingAmount;\n }\n\n /// @notice Begins the moving funds timeout notifier reward multiplier amount\n /// update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovingFundsTimeoutNotifierRewardMultiplier New moving funds\n /// timeout notifier reward multiplier amount.\n function beginMovingFundsTimeoutNotifierRewardMultiplierUpdate(\n MovingFundsData storage self,\n uint32 _newMovingFundsTimeoutNotifierRewardMultiplier\n ) external {\n /* solhint-disable not-rely-on-time */\n self\n .newMovingFundsTimeoutNotifierRewardMultiplier = _newMovingFundsTimeoutNotifierRewardMultiplier;\n self.movingFundsTimeoutNotifierRewardMultiplierChangeInitiated = block\n .timestamp;\n emit MovingFundsTimeoutNotifierRewardMultiplierUpdateStarted(\n _newMovingFundsTimeoutNotifierRewardMultiplier,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the moving funds timeout notifier reward multiplier\n /// amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovingFundsTimeoutNotifierRewardMultiplierUpdate(\n MovingFundsData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.movingFundsTimeoutNotifierRewardMultiplierChangeInitiated,\n governanceDelay\n )\n {\n emit MovingFundsTimeoutNotifierRewardMultiplierUpdated(\n self.newMovingFundsTimeoutNotifierRewardMultiplier\n );\n\n self.newMovingFundsTimeoutNotifierRewardMultiplier = 0;\n self.movingFundsTimeoutNotifierRewardMultiplierChangeInitiated = 0;\n }\n\n function getNewMovingFundsTimeoutNotifierRewardMultiplier(\n MovingFundsData storage self\n ) internal view returns (uint32) {\n return self.newMovingFundsTimeoutNotifierRewardMultiplier;\n }\n\n /// @notice Begins the moved funds sweep tx max total fee amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovedFundsSweepTxMaxTotalFee New moved funds sweep tx max total\n /// fee amount.\n function beginMovedFundsSweepTxMaxTotalFeeUpdate(\n MovingFundsData storage self,\n uint64 _newMovedFundsSweepTxMaxTotalFee\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newMovedFundsSweepTxMaxTotalFee = _newMovedFundsSweepTxMaxTotalFee;\n self.movedFundsSweepTxMaxTotalFeeChangeInitiated = block.timestamp;\n emit MovedFundsSweepTxMaxTotalFeeUpdateStarted(\n _newMovedFundsSweepTxMaxTotalFee,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the moved funds sweep tx max total fee amount update\n /// process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovedFundsSweepTxMaxTotalFeeUpdate(\n MovingFundsData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.movedFundsSweepTxMaxTotalFeeChangeInitiated,\n governanceDelay\n )\n {\n emit MovedFundsSweepTxMaxTotalFeeUpdated(\n self.newMovedFundsSweepTxMaxTotalFee\n );\n\n self.newMovedFundsSweepTxMaxTotalFee = 0;\n self.movedFundsSweepTxMaxTotalFeeChangeInitiated = 0;\n }\n\n function getNewMovedFundsSweepTxMaxTotalFee(MovingFundsData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newMovedFundsSweepTxMaxTotalFee;\n }\n\n /// @notice Begins the moved funds sweep timeout amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovedFundsSweepTimeout New moved funds sweep timeout amount.\n function beginMovedFundsSweepTimeoutUpdate(\n MovingFundsData storage self,\n uint32 _newMovedFundsSweepTimeout\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newMovedFundsSweepTimeout = _newMovedFundsSweepTimeout;\n self.movedFundsSweepTimeoutChangeInitiated = block.timestamp;\n emit MovedFundsSweepTimeoutUpdateStarted(\n _newMovedFundsSweepTimeout,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the moved funds sweep timeout amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovedFundsSweepTimeoutUpdate(\n MovingFundsData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.movedFundsSweepTimeoutChangeInitiated,\n governanceDelay\n )\n {\n emit MovedFundsSweepTimeoutUpdated(self.newMovedFundsSweepTimeout);\n\n self.newMovedFundsSweepTimeout = 0;\n self.movedFundsSweepTimeoutChangeInitiated = 0;\n }\n\n function getNewMovedFundsSweepTimeout(MovingFundsData storage self)\n internal\n view\n returns (uint32)\n {\n return self.newMovedFundsSweepTimeout;\n }\n\n /// @notice Begins the moved funds sweep timeout slashing amount update\n /// process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovedFundsSweepTimeoutSlashingAmount New moved funds sweep\n /// timeout slashing amount.\n function beginMovedFundsSweepTimeoutSlashingAmountUpdate(\n MovingFundsData storage self,\n uint96 _newMovedFundsSweepTimeoutSlashingAmount\n ) external {\n /* solhint-disable not-rely-on-time */\n self\n .newMovedFundsSweepTimeoutSlashingAmount = _newMovedFundsSweepTimeoutSlashingAmount;\n self.movedFundsSweepTimeoutSlashingAmountChangeInitiated = block\n .timestamp;\n emit MovedFundsSweepTimeoutSlashingAmountUpdateStarted(\n _newMovedFundsSweepTimeoutSlashingAmount,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the moved funds sweep timeout slashing amount\n /// update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovedFundsSweepTimeoutSlashingAmountUpdate(\n MovingFundsData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.movedFundsSweepTimeoutSlashingAmountChangeInitiated,\n governanceDelay\n )\n {\n emit MovedFundsSweepTimeoutSlashingAmountUpdated(\n self.newMovedFundsSweepTimeoutSlashingAmount\n );\n\n self.newMovedFundsSweepTimeoutSlashingAmount = 0;\n self.movedFundsSweepTimeoutSlashingAmountChangeInitiated = 0;\n }\n\n function getNewMovedFundsSweepTimeoutSlashingAmount(\n MovingFundsData storage self\n ) internal view returns (uint96) {\n return self.newMovedFundsSweepTimeoutSlashingAmount;\n }\n\n /// @notice Begins the moved funds sweep timeout notifier reward multiplier\n /// amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newMovedFundsSweepTimeoutNotifierRewardMultiplier New moved funds\n /// sweep timeout notifier reward multiplier amount.\n function beginMovedFundsSweepTimeoutNotifierRewardMultiplierUpdate(\n MovingFundsData storage self,\n uint32 _newMovedFundsSweepTimeoutNotifierRewardMultiplier\n ) external {\n /* solhint-disable not-rely-on-time */\n self\n .newMovedFundsSweepTimeoutNotifierRewardMultiplier = _newMovedFundsSweepTimeoutNotifierRewardMultiplier;\n self\n .movedFundsSweepTimeoutNotifierRewardMultiplierChangeInitiated = block\n .timestamp;\n emit MovedFundsSweepTimeoutNotifierRewardMultiplierUpdateStarted(\n _newMovedFundsSweepTimeoutNotifierRewardMultiplier,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the moved funds sweep timeout notifier reward multiplier\n /// amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeMovedFundsSweepTimeoutNotifierRewardMultiplierUpdate(\n MovingFundsData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.movedFundsSweepTimeoutNotifierRewardMultiplierChangeInitiated,\n governanceDelay\n )\n {\n emit MovedFundsSweepTimeoutNotifierRewardMultiplierUpdated(\n self.newMovedFundsSweepTimeoutNotifierRewardMultiplier\n );\n\n self.newMovedFundsSweepTimeoutNotifierRewardMultiplier = 0;\n self.movedFundsSweepTimeoutNotifierRewardMultiplierChangeInitiated = 0;\n }\n\n function getNewMovedFundsSweepTimeoutNotifierRewardMultiplier(\n MovingFundsData storage self\n ) internal view returns (uint32) {\n return self.newMovedFundsSweepTimeoutNotifierRewardMultiplier;\n }\n\n // --- Wallet params\n\n /// @notice Begins the wallet creation period amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletCreationPeriod New wallet creation period amount.\n function beginWalletCreationPeriodUpdate(\n WalletData storage self,\n uint32 _newWalletCreationPeriod\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newWalletCreationPeriod = _newWalletCreationPeriod;\n self.walletCreationPeriodChangeInitiated = block.timestamp;\n emit WalletCreationPeriodUpdateStarted(\n _newWalletCreationPeriod,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the wallet creation period amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletCreationPeriodUpdate(\n WalletData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.walletCreationPeriodChangeInitiated,\n governanceDelay\n )\n {\n emit WalletCreationPeriodUpdated(self.newWalletCreationPeriod);\n\n self.newWalletCreationPeriod = 0;\n self.walletCreationPeriodChangeInitiated = 0;\n }\n\n function getNewWalletCreationPeriod(WalletData storage self)\n external\n view\n returns (uint32)\n {\n return self.newWalletCreationPeriod;\n }\n\n /// @notice Begins the wallet creation min btc balance amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletCreationMinBtcBalance New wallet creation min btc balance\n /// amount.\n function beginWalletCreationMinBtcBalanceUpdate(\n WalletData storage self,\n uint64 _newWalletCreationMinBtcBalance\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newWalletCreationMinBtcBalance = _newWalletCreationMinBtcBalance;\n self.walletCreationMinBtcBalanceChangeInitiated = block.timestamp;\n emit WalletCreationMinBtcBalanceUpdateStarted(\n _newWalletCreationMinBtcBalance,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the wallet creation min btc balance amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletCreationMinBtcBalanceUpdate(\n WalletData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.walletCreationMinBtcBalanceChangeInitiated,\n governanceDelay\n )\n {\n emit WalletCreationMinBtcBalanceUpdated(\n self.newWalletCreationMinBtcBalance\n );\n\n self.newWalletCreationMinBtcBalance = 0;\n self.walletCreationMinBtcBalanceChangeInitiated = 0;\n }\n\n function getNewWalletCreationMinBtcBalance(WalletData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newWalletCreationMinBtcBalance;\n }\n\n /// @notice Begins the wallet creation max btc balance amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletCreationMaxBtcBalance New wallet creation max btc balance\n /// amount.\n function beginWalletCreationMaxBtcBalanceUpdate(\n WalletData storage self,\n uint64 _newWalletCreationMaxBtcBalance\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newWalletCreationMaxBtcBalance = _newWalletCreationMaxBtcBalance;\n self.walletCreationMaxBtcBalanceChangeInitiated = block.timestamp;\n emit WalletCreationMaxBtcBalanceUpdateStarted(\n _newWalletCreationMaxBtcBalance,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the wallet creation max btc balance amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletCreationMaxBtcBalanceUpdate(\n WalletData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.walletCreationMaxBtcBalanceChangeInitiated,\n governanceDelay\n )\n {\n emit WalletCreationMaxBtcBalanceUpdated(\n self.newWalletCreationMaxBtcBalance\n );\n\n self.newWalletCreationMaxBtcBalance = 0;\n self.walletCreationMaxBtcBalanceChangeInitiated = 0;\n }\n\n function getNewWalletCreationMaxBtcBalance(WalletData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newWalletCreationMaxBtcBalance;\n }\n\n /// @notice Begins the wallet closure min btc balance amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletClosureMinBtcBalance New wallet closure min btc balance amount.\n function beginWalletClosureMinBtcBalanceUpdate(\n WalletData storage self,\n uint64 _newWalletClosureMinBtcBalance\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newWalletClosureMinBtcBalance = _newWalletClosureMinBtcBalance;\n self.walletClosureMinBtcBalanceChangeInitiated = block.timestamp;\n emit WalletClosureMinBtcBalanceUpdateStarted(\n _newWalletClosureMinBtcBalance,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the wallet closure min btc balance amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletClosureMinBtcBalanceUpdate(\n WalletData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.walletClosureMinBtcBalanceChangeInitiated,\n governanceDelay\n )\n {\n emit WalletClosureMinBtcBalanceUpdated(\n self.newWalletClosureMinBtcBalance\n );\n\n self.newWalletClosureMinBtcBalance = 0;\n self.walletClosureMinBtcBalanceChangeInitiated = 0;\n }\n\n function getNewWalletClosureMinBtcBalance(WalletData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newWalletClosureMinBtcBalance;\n }\n\n /// @notice Begins the wallet max age amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletMaxAge New wallet max age amount.\n function beginWalletMaxAgeUpdate(\n WalletData storage self,\n uint32 _newWalletMaxAge\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newWalletMaxAge = _newWalletMaxAge;\n self.walletMaxAgeChangeInitiated = block.timestamp;\n emit WalletMaxAgeUpdateStarted(_newWalletMaxAge, block.timestamp);\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the wallet max age amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletMaxAgeUpdate(\n WalletData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.walletMaxAgeChangeInitiated,\n governanceDelay\n )\n {\n emit WalletMaxAgeUpdated(self.newWalletMaxAge);\n\n self.newWalletMaxAge = 0;\n self.walletMaxAgeChangeInitiated = 0;\n }\n\n function getNewWalletMaxAge(WalletData storage self)\n internal\n view\n returns (uint32)\n {\n return self.newWalletMaxAge;\n }\n\n /// @notice Begins the wallet max btc transfer amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletMaxBtcTransfer New wallet max btc transfer amount.\n function beginWalletMaxBtcTransferUpdate(\n WalletData storage self,\n uint64 _newWalletMaxBtcTransfer\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newWalletMaxBtcTransfer = _newWalletMaxBtcTransfer;\n self.walletMaxBtcTransferChangeInitiated = block.timestamp;\n emit WalletMaxBtcTransferUpdateStarted(\n _newWalletMaxBtcTransfer,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the wallet max btc transfer amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletMaxBtcTransferUpdate(\n WalletData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.walletMaxBtcTransferChangeInitiated,\n governanceDelay\n )\n {\n emit WalletMaxBtcTransferUpdated(self.newWalletMaxBtcTransfer);\n\n self.newWalletMaxBtcTransfer = 0;\n self.walletMaxBtcTransferChangeInitiated = 0;\n }\n\n function getNewWalletMaxBtcTransfer(WalletData storage self)\n internal\n view\n returns (uint64)\n {\n return self.newWalletMaxBtcTransfer;\n }\n\n /// @notice Begins the wallet closing period amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newWalletClosingPeriod New wallet closing period amount.\n function beginWalletClosingPeriodUpdate(\n WalletData storage self,\n uint32 _newWalletClosingPeriod\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newWalletClosingPeriod = _newWalletClosingPeriod;\n self.walletClosingPeriodChangeInitiated = block.timestamp;\n emit WalletClosingPeriodUpdateStarted(\n _newWalletClosingPeriod,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the wallet closing period amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeWalletClosingPeriodUpdate(\n WalletData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.walletClosingPeriodChangeInitiated,\n governanceDelay\n )\n {\n emit WalletClosingPeriodUpdated(self.newWalletClosingPeriod);\n\n self.newWalletClosingPeriod = 0;\n self.walletClosingPeriodChangeInitiated = 0;\n }\n\n function getNewWalletClosingPeriod(WalletData storage self)\n internal\n view\n returns (uint32)\n {\n return self.newWalletClosingPeriod;\n }\n\n // --- Fraud\n\n /// @notice Begins the fraud challenge deposit amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newFraudChallengeDepositAmount New fraud challenge deposit amount.\n function beginFraudChallengeDepositAmountUpdate(\n FraudData storage self,\n uint96 _newFraudChallengeDepositAmount\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newFraudChallengeDepositAmount = _newFraudChallengeDepositAmount;\n self.fraudChallengeDepositAmountChangeInitiated = block.timestamp;\n emit FraudChallengeDepositAmountUpdateStarted(\n _newFraudChallengeDepositAmount,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the fraud challenge deposit amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeFraudChallengeDepositAmountUpdate(\n FraudData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.fraudChallengeDepositAmountChangeInitiated,\n governanceDelay\n )\n {\n emit FraudChallengeDepositAmountUpdated(\n self.newFraudChallengeDepositAmount\n );\n\n self.newFraudChallengeDepositAmount = 0;\n self.fraudChallengeDepositAmountChangeInitiated = 0;\n }\n\n function getNewFraudChallengeDepositAmount(FraudData storage self)\n internal\n view\n returns (uint96)\n {\n return self.newFraudChallengeDepositAmount;\n }\n\n /// @notice Begins the fraud challenge defeat timeout amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newFraudChallengeDefeatTimeout New fraud challenge defeat timeout\n /// amount.\n function beginFraudChallengeDefeatTimeoutUpdate(\n FraudData storage self,\n uint32 _newFraudChallengeDefeatTimeout\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newFraudChallengeDefeatTimeout = _newFraudChallengeDefeatTimeout;\n self.fraudChallengeDefeatTimeoutChangeInitiated = block.timestamp;\n emit FraudChallengeDefeatTimeoutUpdateStarted(\n _newFraudChallengeDefeatTimeout,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the fraud challenge defeat timeout amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeFraudChallengeDefeatTimeoutUpdate(\n FraudData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.fraudChallengeDefeatTimeoutChangeInitiated,\n governanceDelay\n )\n {\n emit FraudChallengeDefeatTimeoutUpdated(\n self.newFraudChallengeDefeatTimeout\n );\n\n self.newFraudChallengeDefeatTimeout = 0;\n self.fraudChallengeDefeatTimeoutChangeInitiated = 0;\n }\n\n function getNewFraudChallengeDefeatTimeout(FraudData storage self)\n internal\n view\n returns (uint32)\n {\n return self.newFraudChallengeDefeatTimeout;\n }\n\n /// @notice Begins the fraud slashing amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newFraudSlashingAmount New fraud slashing amount.\n function beginFraudSlashingAmountUpdate(\n FraudData storage self,\n uint96 _newFraudSlashingAmount\n ) external {\n /* solhint-disable not-rely-on-time */\n self.newFraudSlashingAmount = _newFraudSlashingAmount;\n self.fraudSlashingAmountChangeInitiated = block.timestamp;\n emit FraudSlashingAmountUpdateStarted(\n _newFraudSlashingAmount,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the fraud slashing amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeFraudSlashingAmountUpdate(\n FraudData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.fraudSlashingAmountChangeInitiated,\n governanceDelay\n )\n {\n emit FraudSlashingAmountUpdated(self.newFraudSlashingAmount);\n\n self.newFraudSlashingAmount = 0;\n self.fraudSlashingAmountChangeInitiated = 0;\n }\n\n function getNewFraudSlashingAmount(FraudData storage self)\n internal\n view\n returns (uint96)\n {\n return self.newFraudSlashingAmount;\n }\n\n /// @notice Begins the fraud notifier reward multiplier amount update process.\n /// @dev Can be called only by the contract owner.\n /// @param _newFraudNotifierRewardMultiplier New fraud notifier reward multiplier\n /// amount.\n function beginFraudNotifierRewardMultiplierUpdate(\n FraudData storage self,\n uint32 _newFraudNotifierRewardMultiplier\n ) external {\n /* solhint-disable not-rely-on-time */\n self\n .newFraudNotifierRewardMultiplier = _newFraudNotifierRewardMultiplier;\n self.fraudNotifierRewardMultiplierChangeInitiated = block.timestamp;\n emit FraudNotifierRewardMultiplierUpdateStarted(\n _newFraudNotifierRewardMultiplier,\n block.timestamp\n );\n /* solhint-enable not-rely-on-time */\n }\n\n /// @notice Finalizes the fraud notifier reward multiplier amount update process.\n /// @dev Can be called only by the contract owner, after the governance\n /// delay elapses.\n function finalizeFraudNotifierRewardMultiplierUpdate(\n FraudData storage self,\n uint256 governanceDelay\n )\n external\n onlyAfterGovernanceDelay(\n self.fraudNotifierRewardMultiplierChangeInitiated,\n governanceDelay\n )\n {\n emit FraudNotifierRewardMultiplierUpdated(\n self.newFraudNotifierRewardMultiplier\n );\n\n self.newFraudNotifierRewardMultiplier = 0;\n self.fraudNotifierRewardMultiplierChangeInitiated = 0;\n }\n\n function getNewFraudNotifierRewardMultiplier(FraudData storage self)\n internal\n view\n returns (uint32)\n {\n return self.newFraudNotifierRewardMultiplier;\n }\n}\n"
|
|
258
264
|
},
|
|
259
|
-
"
|
|
260
|
-
"content": "// SPDX-License-Identifier: MIT\n
|
|
265
|
+
"contracts/test/HeartbeatStub.sol": {
|
|
266
|
+
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\n\nimport \"../bridge/Heartbeat.sol\";\n\n/// @dev This is a contract implemented to test Heartbeat library directly.\ncontract HeartbeatStub {\n function isValidHeartbeatMessage(bytes calldata message)\n public\n pure\n returns (bool)\n {\n return Heartbeat.isValidHeartbeatMessage(message);\n }\n}\n"
|
|
267
|
+
},
|
|
268
|
+
"contracts/test/TestEcdsaLib.sol": {
|
|
269
|
+
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\n\nimport \"../bridge/EcdsaLib.sol\";\n\n// TODO: Rename to EcdsLibStub\n/// @dev This is a contract implemented to test EcdsaLib library directly.\ncontract TestEcdsaLib {\n function compressPublicKey(bytes32 x, bytes32 y)\n public\n pure\n returns (bytes memory)\n {\n return EcdsaLib.compressPublicKey(x, y);\n }\n}\n"
|
|
261
270
|
},
|
|
262
271
|
"contracts/vault/DonationVault.sol": {
|
|
263
272
|
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\nimport \"./IVault.sol\";\nimport \"../bank/Bank.sol\";\n\n/// @title BTC donation vault\n/// @notice Vault that allows making BTC donations to the system. Upon deposit,\n/// this vault does not increase depositors' balances and always\n/// decreases its own balance in the same transaction. The vault also\n/// allows making donations using existing Bank balances.\n///\n/// BEWARE: ALL BTC DEPOSITS TARGETING THIS VAULT ARE NOT REDEEMABLE\n/// AND THERE IS NO WAY TO RESTORE THE DONATED BALANCE.\n/// USE THIS VAULT ONLY WHEN YOU REALLY KNOW WHAT YOU ARE DOING!\ncontract DonationVault is IVault {\n Bank public bank;\n\n event DonationReceived(address donor, uint256 donatedAmount);\n\n modifier onlyBank() {\n require(msg.sender == address(bank), \"Caller is not the Bank\");\n _;\n }\n\n constructor(Bank _bank) {\n require(\n address(_bank) != address(0),\n \"Bank can not be the zero address\"\n );\n\n bank = _bank;\n }\n\n /// @notice Transfers the given `amount` of the Bank balance from the\n /// caller to the Donation Vault and immediately decreases the\n /// vault's balance in the Bank by the transferred `amount`.\n /// @param amount Amount of the Bank balance to donate.\n /// @dev Requirements:\n /// - The caller's balance in the Bank must be greater than or equal\n /// to the `amount`,\n /// - Donation Vault must have an allowance for caller's balance in\n /// the Bank for at least `amount`.\n function donate(uint256 amount) external {\n address donor = msg.sender;\n\n require(\n bank.balanceOf(donor) >= amount,\n \"Amount exceeds balance in the bank\"\n );\n\n emit DonationReceived(donor, amount);\n\n bank.transferBalanceFrom(donor, address(this), amount);\n bank.decreaseBalance(amount);\n }\n\n /// @notice Transfers the given `amount` of the Bank balance from the\n /// `owner` to the Donation Vault and immediately decreases the\n /// vault's balance in the Bank by the transferred `amount`.\n /// @param owner Address of the Bank balance owner who approved their\n /// balance to be used by the vault.\n /// @param amount The amount of the Bank balance approved by the owner\n /// to be used by the vault.\n /// @dev Requirements:\n /// - Can only be called by the Bank via `approveBalanceAndCall`,\n /// - The `owner` balance in the Bank must be greater than or equal\n /// to the `amount`.\n function receiveBalanceApproval(\n address owner,\n uint256 amount,\n bytes memory\n ) external override onlyBank {\n require(\n bank.balanceOf(owner) >= amount,\n \"Amount exceeds balance in the bank\"\n );\n\n emit DonationReceived(owner, amount);\n\n bank.transferBalanceFrom(owner, address(this), amount);\n bank.decreaseBalance(amount);\n }\n\n /// @notice Ignores the deposited amounts and does not increase depositors'\n /// individual balances. The vault decreases its own tBTC balance\n /// in the Bank by the total deposited amount.\n /// @param depositors Addresses of depositors whose deposits have been swept.\n /// @param depositedAmounts Amounts deposited by individual depositors and\n /// swept.\n /// @dev Requirements:\n /// - Can only be called by the Bank after the Bridge swept deposits\n /// and Bank increased balance for the vault,\n /// - The `depositors` array must not be empty,\n /// - The `depositors` array length must be equal to the\n /// `depositedAmounts` array length.\n function receiveBalanceIncrease(\n address[] calldata depositors,\n uint256[] calldata depositedAmounts\n ) external override onlyBank {\n require(depositors.length != 0, \"No depositors specified\");\n\n uint256 totalAmount = 0;\n for (uint256 i = 0; i < depositors.length; i++) {\n totalAmount += depositedAmounts[i];\n emit DonationReceived(depositors[i], depositedAmounts[i]);\n }\n\n bank.decreaseBalance(totalAmount);\n }\n}\n"
|