@keep-network/tbtc-v2 1.0.2-dev.0 → 1.0.2-dev.1

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.
Files changed (80) hide show
  1. package/artifacts/BLS.json +1 -1
  2. package/artifacts/Bank.json +3 -3
  3. package/artifacts/BeaconAuthorization.json +1 -1
  4. package/artifacts/BeaconDkg.json +1 -1
  5. package/artifacts/BeaconDkgValidator.json +1 -1
  6. package/artifacts/BeaconInactivity.json +1 -1
  7. package/artifacts/BeaconSortitionPool.json +3 -3
  8. package/artifacts/Bridge.json +5 -5
  9. package/artifacts/BridgeGovernance.json +2 -2
  10. package/artifacts/BridgeGovernanceParameters.json +2 -2
  11. package/artifacts/Deposit.json +2 -2
  12. package/artifacts/DepositSweep.json +2 -2
  13. package/artifacts/DonationVault.json +2 -2
  14. package/artifacts/EcdsaDkgValidator.json +1 -1
  15. package/artifacts/EcdsaInactivity.json +1 -1
  16. package/artifacts/EcdsaSortitionPool.json +3 -3
  17. package/artifacts/Fraud.json +2 -2
  18. package/artifacts/KeepRegistry.json +1 -1
  19. package/artifacts/KeepStake.json +2 -2
  20. package/artifacts/KeepToken.json +2 -2
  21. package/artifacts/KeepTokenStaking.json +1 -1
  22. package/artifacts/LightRelay.json +3 -3
  23. package/artifacts/MaintainerProxy.json +3 -3
  24. package/artifacts/MovingFunds.json +2 -2
  25. package/artifacts/NuCypherStakingEscrow.json +1 -1
  26. package/artifacts/NuCypherToken.json +2 -2
  27. package/artifacts/RandomBeacon.json +2 -2
  28. package/artifacts/RandomBeaconChaosnet.json +2 -2
  29. package/artifacts/RandomBeaconGovernance.json +2 -2
  30. package/artifacts/Redemption.json +2 -2
  31. package/artifacts/ReimbursementPool.json +2 -2
  32. package/artifacts/T.json +2 -2
  33. package/artifacts/TBTC.json +3 -3
  34. package/artifacts/TBTCToken.json +3 -3
  35. package/artifacts/TBTCVault.json +93 -38
  36. package/artifacts/TokenStaking.json +1 -1
  37. package/artifacts/TokenholderGovernor.json +9 -9
  38. package/artifacts/TokenholderTimelock.json +8 -8
  39. package/artifacts/VendingMachine.json +3 -3
  40. package/artifacts/VendingMachineKeep.json +1 -1
  41. package/artifacts/VendingMachineNuCypher.json +1 -1
  42. package/artifacts/WalletRegistry.json +5 -5
  43. package/artifacts/WalletRegistryGovernance.json +2 -2
  44. package/artifacts/Wallets.json +2 -2
  45. package/artifacts/solcInputs/{02bce6ad4d51639a29a8a0ab743e5d10.json → bc3dee495878ca981ed1f35ee0b9d4da.json} +2 -2
  46. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
  47. package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
  48. package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.dbg.json +1 -1
  49. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
  50. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
  51. package/build/contracts/bridge/BridgeGovernanceParameters.sol/BridgeGovernanceParameters.dbg.json +1 -1
  52. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +1 -1
  53. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +1 -1
  54. package/build/contracts/bridge/DepositSweep.sol/DepositSweep.dbg.json +1 -1
  55. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
  56. package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +1 -1
  57. package/build/contracts/bridge/Heartbeat.sol/Heartbeat.dbg.json +1 -1
  58. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +1 -1
  59. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +1 -1
  60. package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +1 -1
  61. package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +1 -1
  62. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  63. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
  64. package/build/contracts/maintainer/MaintainerProxy.sol/MaintainerProxy.dbg.json +1 -1
  65. package/build/contracts/relay/LightRelay.sol/ILightRelay.dbg.json +1 -1
  66. package/build/contracts/relay/LightRelay.sol/LightRelay.dbg.json +1 -1
  67. package/build/contracts/relay/LightRelay.sol/RelayUtils.dbg.json +1 -1
  68. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  69. package/build/contracts/vault/DonationVault.sol/DonationVault.dbg.json +1 -1
  70. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  71. package/build/contracts/vault/TBTCOptimisticMinting.sol/TBTCOptimisticMinting.dbg.json +1 -1
  72. package/build/contracts/vault/TBTCOptimisticMinting.sol/TBTCOptimisticMinting.json +13 -0
  73. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  74. package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +46 -4
  75. package/contracts/vault/TBTCOptimisticMinting.sol +9 -5
  76. package/contracts/vault/TBTCVault.sol +87 -31
  77. package/export/artifacts/contracts/vault/TBTCVault.sol/TBTCVault.json +3109 -2548
  78. package/export/typechain/factories/TBTCOptimisticMinting__factory.js +13 -0
  79. package/export/typechain/factories/TBTCVault__factory.js +45 -3
  80. package/package.json +1 -1
@@ -299,10 +299,10 @@
299
299
  "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity 0.8.17;\n\nimport \"../bank/IReceiveBalanceApproval.sol\";\n\n/// @title Bank Vault interface\n/// @notice `IVault` is an interface for a smart contract consuming Bank\n/// balances of other contracts or externally owned accounts (EOA).\ninterface IVault is IReceiveBalanceApproval {\n /// @notice Called by the Bank in `increaseBalanceAndCall` function after\n /// increasing the balance in the Bank for the vault. It happens in\n /// the same transaction in which deposits were swept by the Bridge.\n /// This allows the depositor to route their deposit revealed to the\n /// Bridge to the particular smart contract (vault) in the same\n /// transaction in which the deposit is revealed. This way, the\n /// depositor does not have to execute additional transaction after\n /// the deposit gets swept by the Bridge to approve and transfer\n /// their balance to the vault.\n /// @param depositors Addresses of depositors whose deposits have been swept.\n /// @param depositedAmounts Amounts deposited by individual depositors and\n /// swept.\n /// @dev The implementation must ensure this function can only be called\n /// by the Bank. The Bank guarantees that the vault's balance was\n /// increased by the sum of all deposited amounts before this function\n /// is called, in the same transaction.\n function receiveBalanceIncrease(\n address[] calldata depositors,\n uint256[] calldata depositedAmounts\n ) external;\n}\n"
300
300
  },
301
301
  "contracts/vault/TBTCOptimisticMinting.sol": {
302
- "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity 0.8.17;\n\nimport \"../bridge/Bridge.sol\";\nimport \"../bridge/Deposit.sol\";\nimport \"../GovernanceUtils.sol\";\n\n/// @title TBTC Optimistic Minting\n/// @notice The Optimistic Minting mechanism allows to mint TBTC before\n/// `TBTCVault` receives the Bank balance. There are two permissioned\n/// sets in the system: Minters and Guardians, both set up in 1-of-n\n/// mode. Minters observe the revealed deposits and request minting TBTC.\n/// Any single Minter can perform this action. There is an\n/// `optimisticMintingDelay` between the time of the request from\n/// a Minter to the time TBTC is minted. During the time of the delay,\n/// any Guardian can cancel the minting.\n/// @dev This functionality is a part of `TBTCVault`. It is implemented in\n/// a separate abstract contract to achieve better separation of concerns\n/// and easier-to-follow code.\nabstract contract TBTCOptimisticMinting is Ownable {\n // Represents optimistic minting request for the given deposit revealed\n // to the Bridge.\n struct OptimisticMintingRequest {\n // UNIX timestamp at which the optimistic minting was requested.\n uint64 requestedAt;\n // UNIX timestamp at which the optimistic minting was finalized.\n // 0 if not yet finalized.\n uint64 finalizedAt;\n }\n\n /// @notice The time delay that needs to pass between initializing and\n /// finalizing the upgrade of governable parameters.\n uint256 public constant GOVERNANCE_DELAY = 24 hours;\n\n Bridge public immutable bridge;\n\n /// @notice Indicates if the optimistic minting has been paused. Only the\n /// Governance can pause optimistic minting. Note that the pause of\n /// the optimistic minting does not stop the standard minting flow\n /// where wallets sweep deposits.\n bool public isOptimisticMintingPaused;\n\n /// @notice Divisor used to compute the treasury fee taken from each\n /// optimistically minted deposit and transferred to the treasury\n /// upon finalization of the optimistic mint. This fee is computed\n /// as follows: `fee = amount / optimisticMintingFeeDivisor`.\n /// For example, if the fee needs to be 2%, the\n /// `optimisticMintingFeeDivisor` should be set to `50` because\n /// `1/50 = 0.02 = 2%`.\n /// The optimistic minting fee does not replace the deposit treasury\n /// fee cut by the Bridge. The optimistic fee is a percentage AFTER\n /// the treasury fee is cut:\n /// `optimisticMintingFee = (depositAmount - treasuryFee) / optimisticMintingFeeDivisor`\n uint32 public optimisticMintingFeeDivisor = 500; // 1/500 = 0.002 = 0.2%\n\n /// @notice The time that needs to pass between the moment the optimistic\n /// minting is requested and the moment optimistic minting is\n /// finalized with minting TBTC.\n uint32 public optimisticMintingDelay = 3 hours;\n\n /// @notice Indicates if the given address is a Minter. Only Minters can\n /// request optimistic minting.\n mapping(address => bool) public isMinter;\n\n /// @notice List of all Minters.\n /// @dev May be used to establish an order in which the Minters should\n /// request for an optimistic minting.\n address[] public minters;\n\n /// @notice Indicates if the given address is a Guardian. Only Guardians can\n /// cancel requested optimistic minting.\n mapping(address => bool) public isGuardian;\n\n /// @notice Collection of all revealed deposits for which the optimistic\n /// minting was requested. Indexed by a deposit key computed as\n /// `keccak256(fundingTxHash | fundingOutputIndex)`.\n mapping(uint256 => OptimisticMintingRequest)\n public optimisticMintingRequests;\n\n /// @notice Optimistic minting debt value per depositor's address. The debt\n /// represents the total value of all depositor's deposits revealed\n /// to the Bridge that has not been yet swept and led to the\n /// optimistic minting of TBTC. When `TBTCVault` sweeps a deposit,\n /// the debt is fully or partially paid off, no matter if that\n /// particular swept deposit was used for the optimistic minting or\n /// not.\n mapping(address => uint256) public optimisticMintingDebt;\n\n /// @notice New optimistic minting fee divisor value. Set only when the\n /// parameter update process is pending. Once the update gets\n // finalized, this will be the value of the divisor.\n uint32 public newOptimisticMintingFeeDivisor;\n /// @notice The timestamp at which the update of the optimistic minting fee\n /// divisor started. Zero if update is not in progress.\n uint256 public optimisticMintingFeeUpdateInitiatedTimestamp;\n\n /// @notice New optimistic minting delay value. Set only when the parameter\n /// update process is pending. Once the update gets finalized, this\n // will be the value of the delay.\n uint32 public newOptimisticMintingDelay;\n /// @notice The timestamp at which the update of the optimistic minting\n /// delay started. Zero if update is not in progress.\n uint256 public optimisticMintingDelayUpdateInitiatedTimestamp;\n\n event OptimisticMintingRequested(\n address indexed minter,\n uint256 indexed depositKey,\n address indexed depositor,\n uint256 amount,\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex\n );\n event OptimisticMintingFinalized(\n address indexed minter,\n uint256 indexed depositKey,\n address indexed depositor,\n uint256 optimisticMintingDebt\n );\n event OptimisticMintingCancelled(\n address indexed guardian,\n uint256 indexed depositKey\n );\n event OptimisticMintingDebtRepaid(\n address indexed depositor,\n uint256 optimisticMintingDebt\n );\n event MinterAdded(address indexed minter);\n event MinterRemoved(address indexed minter);\n event GuardianAdded(address indexed guardian);\n event GuardianRemoved(address indexed guardian);\n event OptimisticMintingPaused();\n event OptimisticMintingUnpaused();\n\n event OptimisticMintingFeeUpdateStarted(\n uint32 newOptimisticMintingFeeDivisor\n );\n event OptimisticMintingFeeUpdated(uint32 newOptimisticMintingFeeDivisor);\n\n event OptimisticMintingDelayUpdateStarted(uint32 newOptimisticMintingDelay);\n event OptimisticMintingDelayUpdated(uint32 newOptimisticMintingDelay);\n\n modifier onlyMinter() {\n require(isMinter[msg.sender], \"Caller is not a minter\");\n _;\n }\n\n modifier onlyGuardian() {\n require(isGuardian[msg.sender], \"Caller is not a guardian\");\n _;\n }\n\n modifier onlyOwnerOrGuardian() {\n require(\n owner() == msg.sender || isGuardian[msg.sender],\n \"Caller is not the owner or guardian\"\n );\n _;\n }\n\n modifier whenOptimisticMintingNotPaused() {\n require(!isOptimisticMintingPaused, \"Optimistic minting paused\");\n _;\n }\n\n modifier onlyAfterGovernanceDelay(uint256 updateInitiatedTimestamp) {\n GovernanceUtils.onlyAfterGovernanceDelay(\n updateInitiatedTimestamp,\n GOVERNANCE_DELAY\n );\n _;\n }\n\n constructor(Bridge _bridge) {\n require(\n address(_bridge) != address(0),\n \"Bridge can not be the zero address\"\n );\n\n bridge = _bridge;\n }\n\n /// @dev Mints the given amount of TBTC to the given depositor's address.\n /// Implemented by TBTCVault.\n function _mint(address minter, uint256 amount) internal virtual;\n\n /// @notice Allows to fetch a list of all Minters.\n function getMinters() external view returns (address[] memory) {\n return minters;\n }\n\n /// @notice Allows a Minter to request for an optimistic minting of TBTC.\n /// The following conditions must be met:\n /// - There is no optimistic minting request for the deposit,\n /// finalized or not.\n /// - The deposit with the given Bitcoin funding transaction hash\n /// and output index has been revealed to the Bridge.\n /// - The deposit has not been swept yet.\n /// - The deposit is targeted into the TBTCVault.\n /// - The optimistic minting is not paused.\n /// After calling this function, the Minter has to wait for\n /// `optimisticMintingDelay` before finalizing the mint with a call\n /// to finalizeOptimisticMint.\n /// @dev The deposit done on the Bitcoin side must be revealed early enough\n /// to the Bridge on Ethereum to pass the Bridge's validation. The\n /// validation passes successfully only if the deposit reveal is done\n /// respectively earlier than the moment when the deposit refund\n /// locktime is reached, i.e. the deposit becomes refundable. It may\n /// happen that the wallet does not sweep a revealed deposit and one of\n /// the Minters requests an optimistic mint for that deposit just\n /// before the locktime is reached. Guardians must cancel optimistic\n /// minting for this deposit because the wallet will not be able to\n /// sweep it. The on-chain optimistic minting code does not perform any\n /// validation for gas efficiency: it would have to perform the same\n /// validation as `validateDepositRefundLocktime` and expect the entire\n /// `DepositRevealInfo` to be passed to assemble the expected script\n /// hash on-chain. Guardians must validate if the deposit happened on\n /// Bitcoin, that the script hash has the expected format, and that the\n /// wallet is an active one so they can also validate the time left for\n /// the refund.\n function requestOptimisticMint(\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex\n ) external onlyMinter whenOptimisticMintingNotPaused {\n uint256 depositKey = calculateDepositKey(\n fundingTxHash,\n fundingOutputIndex\n );\n\n OptimisticMintingRequest storage request = optimisticMintingRequests[\n depositKey\n ];\n require(\n request.requestedAt == 0,\n \"Optimistic minting already requested for the deposit\"\n );\n\n Deposit.DepositRequest memory deposit = bridge.deposits(depositKey);\n\n require(deposit.revealedAt != 0, \"The deposit has not been revealed\");\n require(deposit.sweptAt == 0, \"The deposit is already swept\");\n require(deposit.vault == address(this), \"Unexpected vault address\");\n\n /* solhint-disable-next-line not-rely-on-time */\n request.requestedAt = uint64(block.timestamp);\n\n emit OptimisticMintingRequested(\n msg.sender,\n depositKey,\n deposit.depositor,\n deposit.amount,\n fundingTxHash,\n fundingOutputIndex\n );\n }\n\n /// @notice Allows a Minter to finalize previously requested optimistic\n /// minting. The following conditions must be met:\n /// - The optimistic minting has been requested for the given\n /// deposit.\n /// - The deposit has not been swept yet.\n /// - At least `optimisticMintingDelay` passed since the optimistic\n /// minting was requested for the given deposit.\n /// - The optimistic minting has not been finalized earlier for the\n /// given deposit.\n /// - The optimistic minting request for the given deposit has not\n /// been canceled by a Guardian.\n /// - The optimistic minting is not paused.\n /// This function mints TBTC and increases `optimisticMintingDebt`\n /// for the given depositor. The optimistic minting request is\n /// marked as finalized.\n function finalizeOptimisticMint(\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex\n ) external onlyMinter whenOptimisticMintingNotPaused {\n uint256 depositKey = calculateDepositKey(\n fundingTxHash,\n fundingOutputIndex\n );\n\n OptimisticMintingRequest storage request = optimisticMintingRequests[\n depositKey\n ];\n require(\n request.requestedAt != 0,\n \"Optimistic minting not requested for the deposit\"\n );\n require(\n request.finalizedAt == 0,\n \"Optimistic minting already finalized for the deposit\"\n );\n\n require(\n /* solhint-disable-next-line not-rely-on-time */\n block.timestamp > request.requestedAt + optimisticMintingDelay,\n \"Optimistic minting delay has not passed yet\"\n );\n\n Deposit.DepositRequest memory deposit = bridge.deposits(depositKey);\n require(deposit.sweptAt == 0, \"The deposit is already swept\");\n\n // Bridge, when sweeping, cuts a deposit treasury fee and splits\n // Bitcoin miner fee for the sweep transaction evenly between the\n // depositors in the sweep.\n //\n // When tokens are optimistically minted, we do not know what the\n // Bitcoin miner fee for the sweep transaction will look like.\n // The Bitcoin miner fee is ignored. When sweeping, the miner fee is\n // subtracted so the optimisticMintingDebt may stay non-zero after the\n // deposit is swept.\n //\n // This imbalance is supposed to be solved by a donation to the Bridge.\n uint256 amountToMint = deposit.amount - deposit.treasuryFee;\n\n // The Optimistic Minting mechanism may additionally cut a fee from the\n // amount that is left after deducting the Bridge deposit treasury fee.\n // Think of this fee as an extra payment for faster processing of\n // deposits. One does not need to use the Optimistic Minting mechanism\n // and they may wait for the Bridge to sweep their deposit if they do\n // not want to pay the Optimistic Minting fee.\n uint256 optimisticMintFee = optimisticMintingFeeDivisor > 0\n ? amountToMint / optimisticMintingFeeDivisor\n : 0;\n\n // Both the optimistic minting fee and the share that goes to the\n // depositor are optimistically minted. All TBTC that is optimistically\n // minted should be added to the optimistic minting debt. When the\n // deposit is swept, it is paying off both the depositor's share and the\n // treasury's share (optimistic minting fee).\n uint256 newDebt = optimisticMintingDebt[deposit.depositor] +\n amountToMint;\n optimisticMintingDebt[deposit.depositor] = newDebt;\n\n _mint(deposit.depositor, amountToMint - optimisticMintFee);\n if (optimisticMintFee > 0) {\n _mint(bridge.treasury(), optimisticMintFee);\n }\n\n /* solhint-disable-next-line not-rely-on-time */\n request.finalizedAt = uint64(block.timestamp);\n\n emit OptimisticMintingFinalized(\n msg.sender,\n depositKey,\n deposit.depositor,\n newDebt\n );\n }\n\n /// @notice Allows a Guardian to cancel optimistic minting request. The\n /// following conditions must be met:\n /// - The optimistic minting request for the given deposit exists.\n /// - The optimistic minting request for the given deposit has not\n /// been finalized yet.\n /// Optimistic minting request is removed. It is possible to request\n /// optimistic minting again for the same deposit later.\n /// @dev Guardians must validate the following conditions for every deposit\n /// for which the optimistic minting was requested:\n /// - The deposit happened on Bitcoin side and it has enough\n /// confirmations.\n /// - The optimistic minting has been requested early enough so that\n /// the wallet has enough time to sweep the deposit.\n /// - The wallet is an active one and it does perform sweeps or it will\n /// perform sweeps once the sweeps are activated.\n function cancelOptimisticMint(\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex\n ) external onlyGuardian {\n uint256 depositKey = calculateDepositKey(\n fundingTxHash,\n fundingOutputIndex\n );\n\n OptimisticMintingRequest storage request = optimisticMintingRequests[\n depositKey\n ];\n require(\n request.requestedAt != 0,\n \"Optimistic minting not requested for the deposit\"\n );\n require(\n request.finalizedAt == 0,\n \"Optimistic minting already finalized for the deposit\"\n );\n\n // Delete it. It allows to request optimistic minting for the given\n // deposit again. Useful in case of an errant Guardian.\n delete optimisticMintingRequests[depositKey];\n\n emit OptimisticMintingCancelled(msg.sender, depositKey);\n }\n\n /// @notice Adds the address to the Minter list.\n function addMinter(address minter) external onlyOwner {\n require(!isMinter[minter], \"This address is already a minter\");\n isMinter[minter] = true;\n minters.push(minter);\n emit MinterAdded(minter);\n }\n\n /// @notice Removes the address from the Minter list.\n function removeMinter(address minter) external onlyOwnerOrGuardian {\n require(isMinter[minter], \"This address is not a minter\");\n delete isMinter[minter];\n\n // We do not expect too many Minters so a simple loop is safe.\n for (uint256 i = 0; i < minters.length; i++) {\n if (minters[i] == minter) {\n minters[i] = minters[minters.length - 1];\n // slither-disable-next-line costly-loop\n minters.pop();\n break;\n }\n }\n\n emit MinterRemoved(minter);\n }\n\n /// @notice Adds the address to the Guardian set.\n function addGuardian(address guardian) external onlyOwner {\n require(!isGuardian[guardian], \"This address is already a guardian\");\n isGuardian[guardian] = true;\n emit GuardianAdded(guardian);\n }\n\n /// @notice Removes the address from the Guardian set.\n function removeGuardian(address guardian) external onlyOwner {\n require(isGuardian[guardian], \"This address is not a guardian\");\n delete isGuardian[guardian];\n emit GuardianRemoved(guardian);\n }\n\n /// @notice Pauses the optimistic minting. Note that the pause of the\n /// optimistic minting does not stop the standard minting flow\n /// where wallets sweep deposits.\n function pauseOptimisticMinting() external onlyOwner {\n require(\n !isOptimisticMintingPaused,\n \"Optimistic minting already paused\"\n );\n isOptimisticMintingPaused = true;\n emit OptimisticMintingPaused();\n }\n\n /// @notice Unpauses the optimistic minting.\n function unpauseOptimisticMinting() external onlyOwner {\n require(isOptimisticMintingPaused, \"Optimistic minting is not paused\");\n isOptimisticMintingPaused = false;\n emit OptimisticMintingUnpaused();\n }\n\n /// @notice Begins the process of updating optimistic minting fee.\n /// The fee is computed as follows:\n /// `fee = amount / optimisticMintingFeeDivisor`.\n /// For example, if the fee needs to be 2% of each deposit,\n /// the `optimisticMintingFeeDivisor` should be set to `50` because\n /// `1/50 = 0.02 = 2%`.\n /// @dev See the documentation for optimisticMintingFeeDivisor.\n function beginOptimisticMintingFeeUpdate(\n uint32 _newOptimisticMintingFeeDivisor\n ) external onlyOwner {\n /* solhint-disable-next-line not-rely-on-time */\n optimisticMintingFeeUpdateInitiatedTimestamp = block.timestamp;\n newOptimisticMintingFeeDivisor = _newOptimisticMintingFeeDivisor;\n emit OptimisticMintingFeeUpdateStarted(_newOptimisticMintingFeeDivisor);\n }\n\n /// @notice Finalizes the update process of the optimistic minting fee.\n function finalizeOptimisticMintingFeeUpdate()\n external\n onlyOwner\n onlyAfterGovernanceDelay(optimisticMintingFeeUpdateInitiatedTimestamp)\n {\n optimisticMintingFeeDivisor = newOptimisticMintingFeeDivisor;\n emit OptimisticMintingFeeUpdated(newOptimisticMintingFeeDivisor);\n\n newOptimisticMintingFeeDivisor = 0;\n optimisticMintingFeeUpdateInitiatedTimestamp = 0;\n }\n\n /// @notice Begins the process of updating optimistic minting delay.\n function beginOptimisticMintingDelayUpdate(\n uint32 _newOptimisticMintingDelay\n ) external onlyOwner {\n /* solhint-disable-next-line not-rely-on-time */\n optimisticMintingDelayUpdateInitiatedTimestamp = block.timestamp;\n newOptimisticMintingDelay = _newOptimisticMintingDelay;\n emit OptimisticMintingDelayUpdateStarted(_newOptimisticMintingDelay);\n }\n\n /// @notice Finalizes the update process of the optimistic minting delay.\n function finalizeOptimisticMintingDelayUpdate()\n external\n onlyOwner\n onlyAfterGovernanceDelay(optimisticMintingDelayUpdateInitiatedTimestamp)\n {\n optimisticMintingDelay = newOptimisticMintingDelay;\n emit OptimisticMintingDelayUpdated(newOptimisticMintingDelay);\n\n newOptimisticMintingDelay = 0;\n optimisticMintingDelayUpdateInitiatedTimestamp = 0;\n }\n\n /// @notice Calculates deposit key the same way as the Bridge contract.\n /// The deposit key is computed as\n /// `keccak256(fundingTxHash | fundingOutputIndex)`.\n function calculateDepositKey(\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex\n ) public pure returns (uint256) {\n return\n uint256(\n keccak256(abi.encodePacked(fundingTxHash, fundingOutputIndex))\n );\n }\n\n /// @notice Used by `TBTCVault.receiveBalanceIncrease` to repay the optimistic\n /// minting debt before TBTC is minted. When optimistic minting is\n /// finalized, debt equal to the value of the deposit being\n /// a subject of the optimistic minting is incurred. When `TBTCVault`\n /// sweeps a deposit, the debt is fully or partially paid off, no\n /// matter if that particular deposit was used for the optimistic\n /// minting or not.\n /// @dev See `TBTCVault.receiveBalanceIncrease`\n /// @param depositor The depositor whose balance increase is received.\n /// @param amount The balance increase amount for the depositor received.\n /// @return The TBTC amount that should be minted after paying off the\n /// optimistic minting debt.\n function repayOptimisticMintingDebt(address depositor, uint256 amount)\n internal\n returns (uint256)\n {\n uint256 debt = optimisticMintingDebt[depositor];\n if (debt == 0) {\n return amount;\n }\n\n if (amount > debt) {\n optimisticMintingDebt[depositor] = 0;\n emit OptimisticMintingDebtRepaid(depositor, 0);\n return amount - debt;\n } else {\n optimisticMintingDebt[depositor] = debt - amount;\n emit OptimisticMintingDebtRepaid(depositor, debt - amount);\n return 0;\n }\n }\n}\n"
302
+ "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity 0.8.17;\n\nimport \"../bridge/Bridge.sol\";\nimport \"../bridge/Deposit.sol\";\nimport \"../GovernanceUtils.sol\";\n\n/// @title TBTC Optimistic Minting\n/// @notice The Optimistic Minting mechanism allows to mint TBTC before\n/// `TBTCVault` receives the Bank balance. There are two permissioned\n/// sets in the system: Minters and Guardians, both set up in 1-of-n\n/// mode. Minters observe the revealed deposits and request minting TBTC.\n/// Any single Minter can perform this action. There is an\n/// `optimisticMintingDelay` between the time of the request from\n/// a Minter to the time TBTC is minted. During the time of the delay,\n/// any Guardian can cancel the minting.\n/// @dev This functionality is a part of `TBTCVault`. It is implemented in\n/// a separate abstract contract to achieve better separation of concerns\n/// and easier-to-follow code.\nabstract contract TBTCOptimisticMinting is Ownable {\n // Represents optimistic minting request for the given deposit revealed\n // to the Bridge.\n struct OptimisticMintingRequest {\n // UNIX timestamp at which the optimistic minting was requested.\n uint64 requestedAt;\n // UNIX timestamp at which the optimistic minting was finalized.\n // 0 if not yet finalized.\n uint64 finalizedAt;\n }\n\n /// @notice The time delay that needs to pass between initializing and\n /// finalizing the upgrade of governable parameters.\n uint256 public constant GOVERNANCE_DELAY = 24 hours;\n\n /// @notice Multiplier to convert satoshi to TBTC token units.\n uint256 public constant SATOSHI_MULTIPLIER = 10**10;\n\n Bridge public immutable bridge;\n\n /// @notice Indicates if the optimistic minting has been paused. Only the\n /// Governance can pause optimistic minting. Note that the pause of\n /// the optimistic minting does not stop the standard minting flow\n /// where wallets sweep deposits.\n bool public isOptimisticMintingPaused;\n\n /// @notice Divisor used to compute the treasury fee taken from each\n /// optimistically minted deposit and transferred to the treasury\n /// upon finalization of the optimistic mint. This fee is computed\n /// as follows: `fee = amount / optimisticMintingFeeDivisor`.\n /// For example, if the fee needs to be 2%, the\n /// `optimisticMintingFeeDivisor` should be set to `50` because\n /// `1/50 = 0.02 = 2%`.\n /// The optimistic minting fee does not replace the deposit treasury\n /// fee cut by the Bridge. The optimistic fee is a percentage AFTER\n /// the treasury fee is cut:\n /// `optimisticMintingFee = (depositAmount - treasuryFee) / optimisticMintingFeeDivisor`\n uint32 public optimisticMintingFeeDivisor = 500; // 1/500 = 0.002 = 0.2%\n\n /// @notice The time that needs to pass between the moment the optimistic\n /// minting is requested and the moment optimistic minting is\n /// finalized with minting TBTC.\n uint32 public optimisticMintingDelay = 3 hours;\n\n /// @notice Indicates if the given address is a Minter. Only Minters can\n /// request optimistic minting.\n mapping(address => bool) public isMinter;\n\n /// @notice List of all Minters.\n /// @dev May be used to establish an order in which the Minters should\n /// request for an optimistic minting.\n address[] public minters;\n\n /// @notice Indicates if the given address is a Guardian. Only Guardians can\n /// cancel requested optimistic minting.\n mapping(address => bool) public isGuardian;\n\n /// @notice Collection of all revealed deposits for which the optimistic\n /// minting was requested. Indexed by a deposit key computed as\n /// `keccak256(fundingTxHash | fundingOutputIndex)`.\n mapping(uint256 => OptimisticMintingRequest)\n public optimisticMintingRequests;\n\n /// @notice Optimistic minting debt value per depositor's address. The debt\n /// represents the total value of all depositor's deposits revealed\n /// to the Bridge that has not been yet swept and led to the\n /// optimistic minting of TBTC. When `TBTCVault` sweeps a deposit,\n /// the debt is fully or partially paid off, no matter if that\n /// particular swept deposit was used for the optimistic minting or\n /// not. The values are in 1e18 Ethereum precision.\n mapping(address => uint256) public optimisticMintingDebt;\n\n /// @notice New optimistic minting fee divisor value. Set only when the\n /// parameter update process is pending. Once the update gets\n // finalized, this will be the value of the divisor.\n uint32 public newOptimisticMintingFeeDivisor;\n /// @notice The timestamp at which the update of the optimistic minting fee\n /// divisor started. Zero if update is not in progress.\n uint256 public optimisticMintingFeeUpdateInitiatedTimestamp;\n\n /// @notice New optimistic minting delay value. Set only when the parameter\n /// update process is pending. Once the update gets finalized, this\n // will be the value of the delay.\n uint32 public newOptimisticMintingDelay;\n /// @notice The timestamp at which the update of the optimistic minting\n /// delay started. Zero if update is not in progress.\n uint256 public optimisticMintingDelayUpdateInitiatedTimestamp;\n\n event OptimisticMintingRequested(\n address indexed minter,\n uint256 indexed depositKey,\n address indexed depositor,\n uint256 amount, // amount in 1e18 Ethereum precision\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex\n );\n event OptimisticMintingFinalized(\n address indexed minter,\n uint256 indexed depositKey,\n address indexed depositor,\n uint256 optimisticMintingDebt\n );\n event OptimisticMintingCancelled(\n address indexed guardian,\n uint256 indexed depositKey\n );\n event OptimisticMintingDebtRepaid(\n address indexed depositor,\n uint256 optimisticMintingDebt\n );\n event MinterAdded(address indexed minter);\n event MinterRemoved(address indexed minter);\n event GuardianAdded(address indexed guardian);\n event GuardianRemoved(address indexed guardian);\n event OptimisticMintingPaused();\n event OptimisticMintingUnpaused();\n\n event OptimisticMintingFeeUpdateStarted(\n uint32 newOptimisticMintingFeeDivisor\n );\n event OptimisticMintingFeeUpdated(uint32 newOptimisticMintingFeeDivisor);\n\n event OptimisticMintingDelayUpdateStarted(uint32 newOptimisticMintingDelay);\n event OptimisticMintingDelayUpdated(uint32 newOptimisticMintingDelay);\n\n modifier onlyMinter() {\n require(isMinter[msg.sender], \"Caller is not a minter\");\n _;\n }\n\n modifier onlyGuardian() {\n require(isGuardian[msg.sender], \"Caller is not a guardian\");\n _;\n }\n\n modifier onlyOwnerOrGuardian() {\n require(\n owner() == msg.sender || isGuardian[msg.sender],\n \"Caller is not the owner or guardian\"\n );\n _;\n }\n\n modifier whenOptimisticMintingNotPaused() {\n require(!isOptimisticMintingPaused, \"Optimistic minting paused\");\n _;\n }\n\n modifier onlyAfterGovernanceDelay(uint256 updateInitiatedTimestamp) {\n GovernanceUtils.onlyAfterGovernanceDelay(\n updateInitiatedTimestamp,\n GOVERNANCE_DELAY\n );\n _;\n }\n\n constructor(Bridge _bridge) {\n require(\n address(_bridge) != address(0),\n \"Bridge can not be the zero address\"\n );\n\n bridge = _bridge;\n }\n\n /// @dev Mints the given amount of TBTC to the given depositor's address.\n /// Implemented by TBTCVault.\n function _mint(address minter, uint256 amount) internal virtual;\n\n /// @notice Allows to fetch a list of all Minters.\n function getMinters() external view returns (address[] memory) {\n return minters;\n }\n\n /// @notice Allows a Minter to request for an optimistic minting of TBTC.\n /// The following conditions must be met:\n /// - There is no optimistic minting request for the deposit,\n /// finalized or not.\n /// - The deposit with the given Bitcoin funding transaction hash\n /// and output index has been revealed to the Bridge.\n /// - The deposit has not been swept yet.\n /// - The deposit is targeted into the TBTCVault.\n /// - The optimistic minting is not paused.\n /// After calling this function, the Minter has to wait for\n /// `optimisticMintingDelay` before finalizing the mint with a call\n /// to finalizeOptimisticMint.\n /// @dev The deposit done on the Bitcoin side must be revealed early enough\n /// to the Bridge on Ethereum to pass the Bridge's validation. The\n /// validation passes successfully only if the deposit reveal is done\n /// respectively earlier than the moment when the deposit refund\n /// locktime is reached, i.e. the deposit becomes refundable. It may\n /// happen that the wallet does not sweep a revealed deposit and one of\n /// the Minters requests an optimistic mint for that deposit just\n /// before the locktime is reached. Guardians must cancel optimistic\n /// minting for this deposit because the wallet will not be able to\n /// sweep it. The on-chain optimistic minting code does not perform any\n /// validation for gas efficiency: it would have to perform the same\n /// validation as `validateDepositRefundLocktime` and expect the entire\n /// `DepositRevealInfo` to be passed to assemble the expected script\n /// hash on-chain. Guardians must validate if the deposit happened on\n /// Bitcoin, that the script hash has the expected format, and that the\n /// wallet is an active one so they can also validate the time left for\n /// the refund.\n function requestOptimisticMint(\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex\n ) external onlyMinter whenOptimisticMintingNotPaused {\n uint256 depositKey = calculateDepositKey(\n fundingTxHash,\n fundingOutputIndex\n );\n\n OptimisticMintingRequest storage request = optimisticMintingRequests[\n depositKey\n ];\n require(\n request.requestedAt == 0,\n \"Optimistic minting already requested for the deposit\"\n );\n\n Deposit.DepositRequest memory deposit = bridge.deposits(depositKey);\n\n require(deposit.revealedAt != 0, \"The deposit has not been revealed\");\n require(deposit.sweptAt == 0, \"The deposit is already swept\");\n require(deposit.vault == address(this), \"Unexpected vault address\");\n\n /* solhint-disable-next-line not-rely-on-time */\n request.requestedAt = uint64(block.timestamp);\n\n emit OptimisticMintingRequested(\n msg.sender,\n depositKey,\n deposit.depositor,\n deposit.amount * SATOSHI_MULTIPLIER,\n fundingTxHash,\n fundingOutputIndex\n );\n }\n\n /// @notice Allows a Minter to finalize previously requested optimistic\n /// minting. The following conditions must be met:\n /// - The optimistic minting has been requested for the given\n /// deposit.\n /// - The deposit has not been swept yet.\n /// - At least `optimisticMintingDelay` passed since the optimistic\n /// minting was requested for the given deposit.\n /// - The optimistic minting has not been finalized earlier for the\n /// given deposit.\n /// - The optimistic minting request for the given deposit has not\n /// been canceled by a Guardian.\n /// - The optimistic minting is not paused.\n /// This function mints TBTC and increases `optimisticMintingDebt`\n /// for the given depositor. The optimistic minting request is\n /// marked as finalized.\n function finalizeOptimisticMint(\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex\n ) external onlyMinter whenOptimisticMintingNotPaused {\n uint256 depositKey = calculateDepositKey(\n fundingTxHash,\n fundingOutputIndex\n );\n\n OptimisticMintingRequest storage request = optimisticMintingRequests[\n depositKey\n ];\n require(\n request.requestedAt != 0,\n \"Optimistic minting not requested for the deposit\"\n );\n require(\n request.finalizedAt == 0,\n \"Optimistic minting already finalized for the deposit\"\n );\n\n require(\n /* solhint-disable-next-line not-rely-on-time */\n block.timestamp > request.requestedAt + optimisticMintingDelay,\n \"Optimistic minting delay has not passed yet\"\n );\n\n Deposit.DepositRequest memory deposit = bridge.deposits(depositKey);\n require(deposit.sweptAt == 0, \"The deposit is already swept\");\n\n // Bridge, when sweeping, cuts a deposit treasury fee and splits\n // Bitcoin miner fee for the sweep transaction evenly between the\n // depositors in the sweep.\n //\n // When tokens are optimistically minted, we do not know what the\n // Bitcoin miner fee for the sweep transaction will look like.\n // The Bitcoin miner fee is ignored. When sweeping, the miner fee is\n // subtracted so the optimisticMintingDebt may stay non-zero after the\n // deposit is swept.\n //\n // This imbalance is supposed to be solved by a donation to the Bridge.\n uint256 amountToMint = (deposit.amount - deposit.treasuryFee) *\n SATOSHI_MULTIPLIER;\n\n // The Optimistic Minting mechanism may additionally cut a fee from the\n // amount that is left after deducting the Bridge deposit treasury fee.\n // Think of this fee as an extra payment for faster processing of\n // deposits. One does not need to use the Optimistic Minting mechanism\n // and they may wait for the Bridge to sweep their deposit if they do\n // not want to pay the Optimistic Minting fee.\n uint256 optimisticMintFee = optimisticMintingFeeDivisor > 0\n ? (amountToMint / optimisticMintingFeeDivisor)\n : 0;\n\n // Both the optimistic minting fee and the share that goes to the\n // depositor are optimistically minted. All TBTC that is optimistically\n // minted should be added to the optimistic minting debt. When the\n // deposit is swept, it is paying off both the depositor's share and the\n // treasury's share (optimistic minting fee).\n uint256 newDebt = optimisticMintingDebt[deposit.depositor] +\n amountToMint;\n optimisticMintingDebt[deposit.depositor] = newDebt;\n\n _mint(deposit.depositor, amountToMint - optimisticMintFee);\n if (optimisticMintFee > 0) {\n _mint(bridge.treasury(), optimisticMintFee);\n }\n\n /* solhint-disable-next-line not-rely-on-time */\n request.finalizedAt = uint64(block.timestamp);\n\n emit OptimisticMintingFinalized(\n msg.sender,\n depositKey,\n deposit.depositor,\n newDebt\n );\n }\n\n /// @notice Allows a Guardian to cancel optimistic minting request. The\n /// following conditions must be met:\n /// - The optimistic minting request for the given deposit exists.\n /// - The optimistic minting request for the given deposit has not\n /// been finalized yet.\n /// Optimistic minting request is removed. It is possible to request\n /// optimistic minting again for the same deposit later.\n /// @dev Guardians must validate the following conditions for every deposit\n /// for which the optimistic minting was requested:\n /// - The deposit happened on Bitcoin side and it has enough\n /// confirmations.\n /// - The optimistic minting has been requested early enough so that\n /// the wallet has enough time to sweep the deposit.\n /// - The wallet is an active one and it does perform sweeps or it will\n /// perform sweeps once the sweeps are activated.\n function cancelOptimisticMint(\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex\n ) external onlyGuardian {\n uint256 depositKey = calculateDepositKey(\n fundingTxHash,\n fundingOutputIndex\n );\n\n OptimisticMintingRequest storage request = optimisticMintingRequests[\n depositKey\n ];\n require(\n request.requestedAt != 0,\n \"Optimistic minting not requested for the deposit\"\n );\n require(\n request.finalizedAt == 0,\n \"Optimistic minting already finalized for the deposit\"\n );\n\n // Delete it. It allows to request optimistic minting for the given\n // deposit again. Useful in case of an errant Guardian.\n delete optimisticMintingRequests[depositKey];\n\n emit OptimisticMintingCancelled(msg.sender, depositKey);\n }\n\n /// @notice Adds the address to the Minter list.\n function addMinter(address minter) external onlyOwner {\n require(!isMinter[minter], \"This address is already a minter\");\n isMinter[minter] = true;\n minters.push(minter);\n emit MinterAdded(minter);\n }\n\n /// @notice Removes the address from the Minter list.\n function removeMinter(address minter) external onlyOwnerOrGuardian {\n require(isMinter[minter], \"This address is not a minter\");\n delete isMinter[minter];\n\n // We do not expect too many Minters so a simple loop is safe.\n for (uint256 i = 0; i < minters.length; i++) {\n if (minters[i] == minter) {\n minters[i] = minters[minters.length - 1];\n // slither-disable-next-line costly-loop\n minters.pop();\n break;\n }\n }\n\n emit MinterRemoved(minter);\n }\n\n /// @notice Adds the address to the Guardian set.\n function addGuardian(address guardian) external onlyOwner {\n require(!isGuardian[guardian], \"This address is already a guardian\");\n isGuardian[guardian] = true;\n emit GuardianAdded(guardian);\n }\n\n /// @notice Removes the address from the Guardian set.\n function removeGuardian(address guardian) external onlyOwner {\n require(isGuardian[guardian], \"This address is not a guardian\");\n delete isGuardian[guardian];\n emit GuardianRemoved(guardian);\n }\n\n /// @notice Pauses the optimistic minting. Note that the pause of the\n /// optimistic minting does not stop the standard minting flow\n /// where wallets sweep deposits.\n function pauseOptimisticMinting() external onlyOwner {\n require(\n !isOptimisticMintingPaused,\n \"Optimistic minting already paused\"\n );\n isOptimisticMintingPaused = true;\n emit OptimisticMintingPaused();\n }\n\n /// @notice Unpauses the optimistic minting.\n function unpauseOptimisticMinting() external onlyOwner {\n require(isOptimisticMintingPaused, \"Optimistic minting is not paused\");\n isOptimisticMintingPaused = false;\n emit OptimisticMintingUnpaused();\n }\n\n /// @notice Begins the process of updating optimistic minting fee.\n /// The fee is computed as follows:\n /// `fee = amount / optimisticMintingFeeDivisor`.\n /// For example, if the fee needs to be 2% of each deposit,\n /// the `optimisticMintingFeeDivisor` should be set to `50` because\n /// `1/50 = 0.02 = 2%`.\n /// @dev See the documentation for optimisticMintingFeeDivisor.\n function beginOptimisticMintingFeeUpdate(\n uint32 _newOptimisticMintingFeeDivisor\n ) external onlyOwner {\n /* solhint-disable-next-line not-rely-on-time */\n optimisticMintingFeeUpdateInitiatedTimestamp = block.timestamp;\n newOptimisticMintingFeeDivisor = _newOptimisticMintingFeeDivisor;\n emit OptimisticMintingFeeUpdateStarted(_newOptimisticMintingFeeDivisor);\n }\n\n /// @notice Finalizes the update process of the optimistic minting fee.\n function finalizeOptimisticMintingFeeUpdate()\n external\n onlyOwner\n onlyAfterGovernanceDelay(optimisticMintingFeeUpdateInitiatedTimestamp)\n {\n optimisticMintingFeeDivisor = newOptimisticMintingFeeDivisor;\n emit OptimisticMintingFeeUpdated(newOptimisticMintingFeeDivisor);\n\n newOptimisticMintingFeeDivisor = 0;\n optimisticMintingFeeUpdateInitiatedTimestamp = 0;\n }\n\n /// @notice Begins the process of updating optimistic minting delay.\n function beginOptimisticMintingDelayUpdate(\n uint32 _newOptimisticMintingDelay\n ) external onlyOwner {\n /* solhint-disable-next-line not-rely-on-time */\n optimisticMintingDelayUpdateInitiatedTimestamp = block.timestamp;\n newOptimisticMintingDelay = _newOptimisticMintingDelay;\n emit OptimisticMintingDelayUpdateStarted(_newOptimisticMintingDelay);\n }\n\n /// @notice Finalizes the update process of the optimistic minting delay.\n function finalizeOptimisticMintingDelayUpdate()\n external\n onlyOwner\n onlyAfterGovernanceDelay(optimisticMintingDelayUpdateInitiatedTimestamp)\n {\n optimisticMintingDelay = newOptimisticMintingDelay;\n emit OptimisticMintingDelayUpdated(newOptimisticMintingDelay);\n\n newOptimisticMintingDelay = 0;\n optimisticMintingDelayUpdateInitiatedTimestamp = 0;\n }\n\n /// @notice Calculates deposit key the same way as the Bridge contract.\n /// The deposit key is computed as\n /// `keccak256(fundingTxHash | fundingOutputIndex)`.\n function calculateDepositKey(\n bytes32 fundingTxHash,\n uint32 fundingOutputIndex\n ) public pure returns (uint256) {\n return\n uint256(\n keccak256(abi.encodePacked(fundingTxHash, fundingOutputIndex))\n );\n }\n\n /// @notice Used by `TBTCVault.receiveBalanceIncrease` to repay the optimistic\n /// minting debt before TBTC is minted. When optimistic minting is\n /// finalized, debt equal to the value of the deposit being\n /// a subject of the optimistic minting is incurred. When `TBTCVault`\n /// sweeps a deposit, the debt is fully or partially paid off, no\n /// matter if that particular deposit was used for the optimistic\n /// minting or not.\n /// @dev See `TBTCVault.receiveBalanceIncrease`\n /// @param depositor The depositor whose balance increase is received.\n /// @param amount The balance increase amount for the depositor received.\n /// @return The TBTC amount that should be minted after paying off the\n /// optimistic minting debt.\n function repayOptimisticMintingDebt(address depositor, uint256 amount)\n internal\n returns (uint256)\n {\n uint256 debt = optimisticMintingDebt[depositor];\n if (debt == 0) {\n return amount;\n }\n\n if (amount > debt) {\n optimisticMintingDebt[depositor] = 0;\n emit OptimisticMintingDebtRepaid(depositor, 0);\n return amount - debt;\n } else {\n optimisticMintingDebt[depositor] = debt - amount;\n emit OptimisticMintingDebtRepaid(depositor, debt - amount);\n return 0;\n }\n }\n}\n"
303
303
  },
304
304
  "contracts/vault/TBTCVault.sol": {
305
- "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\nimport \"./IVault.sol\";\nimport \"./TBTCOptimisticMinting.sol\";\nimport \"../bank/Bank.sol\";\nimport \"../token/TBTC.sol\";\n\n/// @title TBTC application vault\n/// @notice TBTC is a fully Bitcoin-backed ERC-20 token pegged to the price of\n/// Bitcoin. It facilitates Bitcoin holders to act on the Ethereum\n/// blockchain and access the decentralized finance (DeFi) ecosystem.\n/// TBTC Vault mints and unmints TBTC based on Bitcoin balances in the\n/// Bank.\n/// @dev TBTC Vault is the owner of TBTC token contract and is the only contract\n/// minting the token.\ncontract TBTCVault is IVault, Ownable, TBTCOptimisticMinting {\n using SafeERC20 for IERC20;\n\n Bank public immutable bank;\n TBTC public immutable tbtcToken;\n\n /// @notice The address of a new TBTC vault. Set only when the upgrade\n /// process is pending. Once the upgrade gets finalized, the new\n /// TBTC vault will become an owner of TBTC token.\n address public newVault;\n /// @notice The timestamp at which an upgrade to a new TBTC vault was\n /// initiated. Set only when the upgrade process is pending.\n uint256 public upgradeInitiatedTimestamp;\n\n event Minted(address indexed to, uint256 amount);\n event Unminted(address indexed from, uint256 amount);\n\n event UpgradeInitiated(address newVault, uint256 timestamp);\n event UpgradeFinalized(address newVault);\n\n modifier onlyBank() {\n require(msg.sender == address(bank), \"Caller is not the Bank\");\n _;\n }\n\n constructor(\n Bank _bank,\n TBTC _tbtcToken,\n Bridge _bridge\n ) TBTCOptimisticMinting(_bridge) {\n require(\n address(_bank) != address(0),\n \"Bank can not be the zero address\"\n );\n\n require(\n address(_tbtcToken) != address(0),\n \"TBTC token can not be the zero address\"\n );\n\n bank = _bank;\n tbtcToken = _tbtcToken;\n }\n\n /// @notice Transfers the given `amount` of the Bank balance from caller\n /// to TBTC Vault, and mints `amount` of TBTC to the caller.\n /// @dev TBTC Vault must have an allowance for caller's balance in the Bank\n /// for at least `amount`.\n /// @param amount Amount of TBTC to mint.\n function mint(uint256 amount) external {\n require(\n bank.balanceOf(msg.sender) >= amount,\n \"Amount exceeds balance in the bank\"\n );\n _mint(msg.sender, amount);\n bank.transferBalanceFrom(msg.sender, address(this), amount);\n }\n\n /// @notice Transfers the given `amount` of the Bank balance from the caller\n /// to TBTC Vault and mints `amount` of TBTC to the caller.\n /// @dev Can only be called by the Bank via `approveBalanceAndCall`.\n /// @param owner The owner who approved their Bank balance.\n /// @param amount Amount of TBTC to mint.\n function receiveBalanceApproval(\n address owner,\n uint256 amount,\n bytes calldata\n ) external override onlyBank {\n require(\n bank.balanceOf(owner) >= amount,\n \"Amount exceeds balance in the bank\"\n );\n _mint(owner, amount);\n bank.transferBalanceFrom(owner, address(this), amount);\n }\n\n /// @notice Mints the same amount of TBTC as the deposited amount for each\n /// depositor in the array. Can only be called by the Bank after the\n /// Bridge swept deposits and Bank increased balance for the\n /// vault.\n /// @dev Fails if `depositors` array is empty. Expects the length of\n /// `depositors` and `depositedAmounts` is the same.\n function receiveBalanceIncrease(\n address[] calldata depositors,\n uint256[] calldata depositedAmounts\n ) external override onlyBank {\n require(depositors.length != 0, \"No depositors specified\");\n for (uint256 i = 0; i < depositors.length; i++) {\n address depositor = depositors[i];\n uint256 amount = depositedAmounts[i];\n _mint(depositor, repayOptimisticMintingDebt(depositor, amount));\n }\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance and transfers\n /// `amount` back to the caller's balance in the Bank.\n /// @dev Caller must have at least `amount` of TBTC approved to\n /// TBTC Vault.\n /// @param amount Amount of TBTC to unmint.\n function unmint(uint256 amount) external {\n _unmint(msg.sender, amount);\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance and transfers\n /// `amount` of Bank balance to the Bridge requesting redemption\n /// based on the provided `redemptionData`.\n /// @dev Caller must have at least `amount` of TBTC approved to\n /// TBTC Vault.\n /// @param amount Amount of TBTC to unmint and request to redeem in Bridge.\n /// @param redemptionData Redemption data in a format expected from\n /// `redemptionData` parameter of Bridge's `receiveBalanceApproval`\n /// function.\n function unmintAndRedeem(uint256 amount, bytes calldata redemptionData)\n external\n {\n _unmintAndRedeem(msg.sender, amount, redemptionData);\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance. If `extraData`\n /// is empty, transfers `amount` back to the caller's balance in the\n /// Bank. If `extraData` is not empty, requests redemption in the\n /// Bridge using the `extraData` as a `redemptionData` parameter to\n /// Bridge's `receiveBalanceApproval` function.\n /// @dev This function is doing the same as `unmint` or `unmintAndRedeem`\n /// (depending on `extraData` parameter) but it allows to execute\n /// unminting without a separate approval transaction. The function can\n /// be called only via `approveAndCall` of TBTC token.\n /// @param from TBTC token holder executing unminting.\n /// @param amount Amount of TBTC to unmint.\n /// @param token TBTC token address.\n /// @param extraData Redemption data in a format expected from\n /// `redemptionData` parameter of Bridge's `receiveBalanceApproval`\n /// function. If empty, `receiveApproval` is not requesting a\n /// redemption of Bank balance but is instead performing just TBTC\n /// unminting to a Bank balance.\n function receiveApproval(\n address from,\n uint256 amount,\n address token,\n bytes calldata extraData\n ) external {\n require(token == address(tbtcToken), \"Token is not TBTC\");\n require(msg.sender == token, \"Only TBTC caller allowed\");\n if (extraData.length == 0) {\n _unmint(from, amount);\n } else {\n _unmintAndRedeem(from, amount, extraData);\n }\n }\n\n /// @notice Initiates vault upgrade process. The upgrade process needs to be\n /// finalized with a call to `finalizeUpgrade` function after the\n /// `UPGRADE_GOVERNANCE_DELAY` passes. Only the governance can\n /// initiate the upgrade.\n /// @param _newVault The new vault address.\n function initiateUpgrade(address _newVault) external onlyOwner {\n require(_newVault != address(0), \"New vault address cannot be zero\");\n /* solhint-disable-next-line not-rely-on-time */\n emit UpgradeInitiated(_newVault, block.timestamp);\n /* solhint-disable-next-line not-rely-on-time */\n upgradeInitiatedTimestamp = block.timestamp;\n newVault = _newVault;\n }\n\n /// @notice Allows the governance to finalize vault upgrade process. The\n /// upgrade process needs to be first initiated with a call to\n /// `initiateUpgrade` and the `GOVERNANCE_DELAY` needs to pass.\n /// Once the upgrade is finalized, the new vault becomes the owner\n /// of the TBTC token and receives the whole Bank balance of this\n /// vault.\n function finalizeUpgrade()\n external\n onlyOwner\n onlyAfterGovernanceDelay(upgradeInitiatedTimestamp)\n {\n emit UpgradeFinalized(newVault);\n // slither-disable-next-line reentrancy-no-eth\n tbtcToken.transferOwnership(newVault);\n bank.transferBalance(newVault, bank.balanceOf(address(this)));\n newVault = address(0);\n upgradeInitiatedTimestamp = 0;\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC20\n /// token sent mistakenly to the TBTC token contract address.\n /// @param token Address of the recovered ERC20 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param amount Recovered amount.\n function recoverERC20FromToken(\n IERC20 token,\n address recipient,\n uint256 amount\n ) external onlyOwner {\n tbtcToken.recoverERC20(token, recipient, amount);\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC721\n /// token sent mistakenly to the TBTC token contract address.\n /// @param token Address of the recovered ERC721 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param tokenId Identifier of the recovered token.\n /// @param data Additional data.\n function recoverERC721FromToken(\n IERC721 token,\n address recipient,\n uint256 tokenId,\n bytes calldata data\n ) external onlyOwner {\n tbtcToken.recoverERC721(token, recipient, tokenId, data);\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC20\n /// token sent - mistakenly or not - to the vault address. This\n /// function should be used to withdraw TBTC v1 tokens transferred\n /// to TBTCVault as a result of VendingMachine > TBTCVault upgrade.\n /// @param token Address of the recovered ERC20 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param amount Recovered amount.\n function recoverERC20(\n IERC20 token,\n address recipient,\n uint256 amount\n ) external onlyOwner {\n token.safeTransfer(recipient, amount);\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC721\n /// token sent mistakenly to the vault address.\n /// @param token Address of the recovered ERC721 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param tokenId Identifier of the recovered token.\n /// @param data Additional data.\n function recoverERC721(\n IERC721 token,\n address recipient,\n uint256 tokenId,\n bytes calldata data\n ) external onlyOwner {\n token.safeTransferFrom(address(this), recipient, tokenId, data);\n }\n\n // slither-disable-next-line calls-loop\n function _mint(address minter, uint256 amount) internal override {\n emit Minted(minter, amount);\n tbtcToken.mint(minter, amount);\n }\n\n function _unmint(address unminter, uint256 amount) internal {\n emit Unminted(unminter, amount);\n tbtcToken.burnFrom(unminter, amount);\n bank.transferBalance(unminter, amount);\n }\n\n function _unmintAndRedeem(\n address redeemer,\n uint256 amount,\n bytes calldata redemptionData\n ) internal {\n emit Unminted(redeemer, amount);\n tbtcToken.burnFrom(redeemer, amount);\n bank.approveBalanceAndCall(address(bridge), amount, redemptionData);\n }\n}\n"
305
+ "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\nimport \"./IVault.sol\";\nimport \"./TBTCOptimisticMinting.sol\";\nimport \"../bank/Bank.sol\";\nimport \"../token/TBTC.sol\";\n\n/// @title TBTC application vault\n/// @notice TBTC is a fully Bitcoin-backed ERC-20 token pegged to the price of\n/// Bitcoin. It facilitates Bitcoin holders to act on the Ethereum\n/// blockchain and access the decentralized finance (DeFi) ecosystem.\n/// TBTC Vault mints and unmints TBTC based on Bitcoin balances in the\n/// Bank.\n/// @dev TBTC Vault is the owner of TBTC token contract and is the only contract\n/// minting the token.\ncontract TBTCVault is IVault, Ownable, TBTCOptimisticMinting {\n using SafeERC20 for IERC20;\n\n Bank public immutable bank;\n TBTC public immutable tbtcToken;\n\n /// @notice The address of a new TBTC vault. Set only when the upgrade\n /// process is pending. Once the upgrade gets finalized, the new\n /// TBTC vault will become an owner of TBTC token.\n address public newVault;\n /// @notice The timestamp at which an upgrade to a new TBTC vault was\n /// initiated. Set only when the upgrade process is pending.\n uint256 public upgradeInitiatedTimestamp;\n\n event Minted(address indexed to, uint256 amount);\n event Unminted(address indexed from, uint256 amount);\n\n event UpgradeInitiated(address newVault, uint256 timestamp);\n event UpgradeFinalized(address newVault);\n\n modifier onlyBank() {\n require(msg.sender == address(bank), \"Caller is not the Bank\");\n _;\n }\n\n constructor(\n Bank _bank,\n TBTC _tbtcToken,\n Bridge _bridge\n ) TBTCOptimisticMinting(_bridge) {\n require(\n address(_bank) != address(0),\n \"Bank can not be the zero address\"\n );\n\n require(\n address(_tbtcToken) != address(0),\n \"TBTC token can not be the zero address\"\n );\n\n bank = _bank;\n tbtcToken = _tbtcToken;\n }\n\n /// @notice Mints the given `amount` of TBTC to the caller previously\n /// transferring `amount / SATOSHI_MULTIPLIER` of the Bank balance\n /// from caller to TBTC Vault. If `amount` is not divisible by\n /// SATOSHI_MULTIPLIER, the remainder is left on the caller's\n /// Bank balance.\n /// @dev TBTC Vault must have an allowance for caller's balance in the\n /// Bank for at least `amount / SATOSHI_MULTIPLIER`.\n /// @param amount Amount of TBTC to mint.\n function mint(uint256 amount) external {\n (uint256 convertibleAmount, , uint256 satoshis) = amountToSatoshis(\n amount\n );\n\n require(\n bank.balanceOf(msg.sender) >= satoshis,\n \"Amount exceeds balance in the bank\"\n );\n _mint(msg.sender, convertibleAmount);\n bank.transferBalanceFrom(msg.sender, address(this), satoshis);\n }\n\n /// @notice Transfers `satoshis` of the Bank balance from the caller\n /// to TBTC Vault and mints `satoshis * SATOSHI_MULTIPLIER` of TBTC\n /// to the caller.\n /// @dev Can only be called by the Bank via `approveBalanceAndCall`.\n /// @param owner The owner who approved their Bank balance.\n /// @param satoshis Amount of satoshis used to mint TBTC.\n function receiveBalanceApproval(\n address owner,\n uint256 satoshis,\n bytes calldata\n ) external override onlyBank {\n require(\n bank.balanceOf(owner) >= satoshis,\n \"Amount exceeds balance in the bank\"\n );\n _mint(owner, satoshis * SATOSHI_MULTIPLIER);\n bank.transferBalanceFrom(owner, address(this), satoshis);\n }\n\n /// @notice Mints the same amount of TBTC as the deposited satoshis amount\n /// multiplied by SATOSHI_MULTIPLIER for each depositor in the array.\n /// Can only be called by the Bank after the Bridge swept deposits\n /// and Bank increased balance for the vault.\n /// @dev Fails if `depositors` array is empty. Expects the length of\n /// `depositors` and `depositedSatoshiAmounts` is the same.\n function receiveBalanceIncrease(\n address[] calldata depositors,\n uint256[] calldata depositedSatoshiAmounts\n ) external override onlyBank {\n require(depositors.length != 0, \"No depositors specified\");\n for (uint256 i = 0; i < depositors.length; i++) {\n address depositor = depositors[i];\n uint256 satoshis = depositedSatoshiAmounts[i];\n _mint(\n depositor,\n repayOptimisticMintingDebt(\n depositor,\n satoshis * SATOSHI_MULTIPLIER\n )\n );\n }\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance and transfers\n /// `amount / SATOSHI_MULTIPLIER` back to the caller's balance in\n /// the Bank. If `amount` is not divisible by SATOSHI_MULTIPLIER,\n /// the remainder is left on the caller's account.\n /// @dev Caller must have at least `amount` of TBTC approved to\n /// TBTC Vault.\n /// @param amount Amount of TBTC to unmint.\n function unmint(uint256 amount) external {\n (uint256 convertibleAmount, , ) = amountToSatoshis(amount);\n\n _unmint(msg.sender, convertibleAmount);\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance and transfers\n /// `amount / SATOSHI_MULTIPLIER` of Bank balance to the Bridge\n /// requesting redemption based on the provided `redemptionData`.\n /// If `amount` is not divisible by SATOSHI_MULTIPLIER, the\n /// remainder is left on the caller's account.\n /// @dev Caller must have at least `amount` of TBTC approved to\n /// TBTC Vault.\n /// @param amount Amount of TBTC to unmint and request to redeem in Bridge.\n /// @param redemptionData Redemption data in a format expected from\n /// `redemptionData` parameter of Bridge's `receiveBalanceApproval`\n /// function.\n function unmintAndRedeem(uint256 amount, bytes calldata redemptionData)\n external\n {\n (uint256 convertibleAmount, , ) = amountToSatoshis(amount);\n\n _unmintAndRedeem(msg.sender, convertibleAmount, redemptionData);\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance. If `extraData`\n /// is empty, transfers `amount` back to the caller's balance in the\n /// Bank. If `extraData` is not empty, requests redemption in the\n /// Bridge using the `extraData` as a `redemptionData` parameter to\n /// Bridge's `receiveBalanceApproval` function.\n /// If `amount` is not divisible by SATOSHI_MULTIPLIER, the\n /// remainder is left on the caller's account. Note that it may\n /// left a token approval equal to the remainder.\n /// @dev This function is doing the same as `unmint` or `unmintAndRedeem`\n /// (depending on `extraData` parameter) but it allows to execute\n /// unminting without a separate approval transaction. The function can\n /// be called only via `approveAndCall` of TBTC token.\n /// @param from TBTC token holder executing unminting.\n /// @param amount Amount of TBTC to unmint.\n /// @param token TBTC token address.\n /// @param extraData Redemption data in a format expected from\n /// `redemptionData` parameter of Bridge's `receiveBalanceApproval`\n /// function. If empty, `receiveApproval` is not requesting a\n /// redemption of Bank balance but is instead performing just TBTC\n /// unminting to a Bank balance.\n function receiveApproval(\n address from,\n uint256 amount,\n address token,\n bytes calldata extraData\n ) external {\n require(token == address(tbtcToken), \"Token is not TBTC\");\n require(msg.sender == token, \"Only TBTC caller allowed\");\n (uint256 convertibleAmount, , ) = amountToSatoshis(amount);\n if (extraData.length == 0) {\n _unmint(from, convertibleAmount);\n } else {\n _unmintAndRedeem(from, convertibleAmount, extraData);\n }\n }\n\n /// @notice Initiates vault upgrade process. The upgrade process needs to be\n /// finalized with a call to `finalizeUpgrade` function after the\n /// `UPGRADE_GOVERNANCE_DELAY` passes. Only the governance can\n /// initiate the upgrade.\n /// @param _newVault The new vault address.\n function initiateUpgrade(address _newVault) external onlyOwner {\n require(_newVault != address(0), \"New vault address cannot be zero\");\n /* solhint-disable-next-line not-rely-on-time */\n emit UpgradeInitiated(_newVault, block.timestamp);\n /* solhint-disable-next-line not-rely-on-time */\n upgradeInitiatedTimestamp = block.timestamp;\n newVault = _newVault;\n }\n\n /// @notice Allows the governance to finalize vault upgrade process. The\n /// upgrade process needs to be first initiated with a call to\n /// `initiateUpgrade` and the `GOVERNANCE_DELAY` needs to pass.\n /// Once the upgrade is finalized, the new vault becomes the owner\n /// of the TBTC token and receives the whole Bank balance of this\n /// vault.\n function finalizeUpgrade()\n external\n onlyOwner\n onlyAfterGovernanceDelay(upgradeInitiatedTimestamp)\n {\n emit UpgradeFinalized(newVault);\n // slither-disable-next-line reentrancy-no-eth\n tbtcToken.transferOwnership(newVault);\n bank.transferBalance(newVault, bank.balanceOf(address(this)));\n newVault = address(0);\n upgradeInitiatedTimestamp = 0;\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC20\n /// token sent mistakenly to the TBTC token contract address.\n /// @param token Address of the recovered ERC20 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param amount Recovered amount.\n function recoverERC20FromToken(\n IERC20 token,\n address recipient,\n uint256 amount\n ) external onlyOwner {\n tbtcToken.recoverERC20(token, recipient, amount);\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC721\n /// token sent mistakenly to the TBTC token contract address.\n /// @param token Address of the recovered ERC721 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param tokenId Identifier of the recovered token.\n /// @param data Additional data.\n function recoverERC721FromToken(\n IERC721 token,\n address recipient,\n uint256 tokenId,\n bytes calldata data\n ) external onlyOwner {\n tbtcToken.recoverERC721(token, recipient, tokenId, data);\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC20\n /// token sent - mistakenly or not - to the vault address. This\n /// function should be used to withdraw TBTC v1 tokens transferred\n /// to TBTCVault as a result of VendingMachine > TBTCVault upgrade.\n /// @param token Address of the recovered ERC20 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param amount Recovered amount.\n function recoverERC20(\n IERC20 token,\n address recipient,\n uint256 amount\n ) external onlyOwner {\n token.safeTransfer(recipient, amount);\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC721\n /// token sent mistakenly to the vault address.\n /// @param token Address of the recovered ERC721 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param tokenId Identifier of the recovered token.\n /// @param data Additional data.\n function recoverERC721(\n IERC721 token,\n address recipient,\n uint256 tokenId,\n bytes calldata data\n ) external onlyOwner {\n token.safeTransferFrom(address(this), recipient, tokenId, data);\n }\n\n /// @notice Returns the amount of TBTC to be minted/unminted, the remainder,\n /// and the Bank balance to be transferred for the given mint/unmint.\n /// Note that if the `amount` is not divisible by SATOSHI_MULTIPLIER,\n /// the remainder is left on the caller's account when minting or\n /// unminting.\n /// @return convertibleAmount Amount of TBTC to be minted/unminted.\n /// @return remainder Not convertible remainder if amount is not divisible\n /// by SATOSHI_MULTIPLIER.\n /// @return satoshis Amount in satoshis - the Bank balance to be transferred\n /// for the given mint/unmint\n function amountToSatoshis(uint256 amount)\n public\n view\n returns (\n uint256 convertibleAmount,\n uint256 remainder,\n uint256 satoshis\n )\n {\n remainder = amount % SATOSHI_MULTIPLIER;\n convertibleAmount = amount - remainder;\n satoshis = convertibleAmount / SATOSHI_MULTIPLIER;\n }\n\n // slither-disable-next-line calls-loop\n function _mint(address minter, uint256 amount) internal override {\n emit Minted(minter, amount);\n tbtcToken.mint(minter, amount);\n }\n\n /// @dev `amount` MUST be divisible by SATOSHI_MULTIPLIER with no change.\n function _unmint(address unminter, uint256 amount) internal {\n emit Unminted(unminter, amount);\n tbtcToken.burnFrom(unminter, amount);\n bank.transferBalance(unminter, amount / SATOSHI_MULTIPLIER);\n }\n\n /// @dev `amount` MUST be divisible by SATOSHI_MULTIPLIER with no change.\n function _unmintAndRedeem(\n address redeemer,\n uint256 amount,\n bytes calldata redemptionData\n ) internal {\n emit Unminted(redeemer, amount);\n tbtcToken.burnFrom(redeemer, amount);\n bank.approveBalanceAndCall(\n address(bridge),\n amount / SATOSHI_MULTIPLIER,\n redemptionData\n );\n }\n}\n"
306
306
  }
307
307
  },
308
308
  "settings": {
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }
@@ -263,6 +263,19 @@
263
263
  "stateMutability": "view",
264
264
  "type": "function"
265
265
  },
266
+ {
267
+ "inputs": [],
268
+ "name": "SATOSHI_MULTIPLIER",
269
+ "outputs": [
270
+ {
271
+ "internalType": "uint256",
272
+ "name": "",
273
+ "type": "uint256"
274
+ }
275
+ ],
276
+ "stateMutability": "view",
277
+ "type": "function"
278
+ },
266
279
  {
267
280
  "inputs": [
268
281
  {
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/d926d1da251e520e63a062927473833a.json"
3
+ "buildInfo": "../../../build-info/b3e1d87ceb125dc6c3d855b0a198d278.json"
4
4
  }