@keep-network/tbtc-v2 1.6.0 → 1.7.0-dev.0

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 (143) hide show
  1. package/artifacts/.chainId +1 -1
  2. package/artifacts/BLS.json +223 -0
  3. package/artifacts/Bank.json +50 -40
  4. package/artifacts/BeaconAuthorization.json +273 -0
  5. package/artifacts/BeaconDkg.json +241 -0
  6. package/artifacts/BeaconDkgValidator.json +501 -0
  7. package/artifacts/BeaconInactivity.json +151 -0
  8. package/artifacts/BeaconSortitionPool.json +1187 -0
  9. package/artifacts/Bridge.json +449 -32
  10. package/artifacts/BridgeGovernance.json +22 -22
  11. package/artifacts/BridgeGovernanceParameters.json +10 -10
  12. package/artifacts/Deposit.json +12 -12
  13. package/artifacts/DepositSweep.json +12 -12
  14. package/artifacts/DonationVault.json +17 -17
  15. package/artifacts/EcdsaDkgValidator.json +517 -0
  16. package/artifacts/EcdsaInactivity.json +156 -0
  17. package/artifacts/EcdsaSortitionPool.json +1187 -0
  18. package/artifacts/Fraud.json +12 -12
  19. package/artifacts/LightRelay.json +38 -38
  20. package/artifacts/LightRelayMaintainerProxy.json +31 -31
  21. package/artifacts/MaintainerProxy.json +43 -43
  22. package/artifacts/MovingFunds.json +12 -12
  23. package/artifacts/NuCypherToken.json +711 -0
  24. package/artifacts/RandomBeacon.json +3271 -0
  25. package/artifacts/RandomBeaconChaosnet.json +252 -0
  26. package/artifacts/RandomBeaconGovernance.json +3499 -0
  27. package/artifacts/Redemption.json +12 -12
  28. package/artifacts/RedemptionWatchtower.json +39 -39
  29. package/artifacts/ReimbursementPool.json +509 -0
  30. package/artifacts/T.json +1148 -0
  31. package/artifacts/TBTC.json +37 -36
  32. package/artifacts/TBTCToken.json +738 -0
  33. package/artifacts/TBTCVault.json +47 -47
  34. package/artifacts/TokenStaking.json +2117 -0
  35. package/artifacts/TokenholderGovernor.json +1795 -0
  36. package/artifacts/TokenholderTimelock.json +1058 -0
  37. package/artifacts/VendingMachine.json +35 -34
  38. package/artifacts/VendingMachineNuCypher.json +400 -0
  39. package/artifacts/VendingMachineV2.json +21 -21
  40. package/artifacts/VendingMachineV3.json +21 -21
  41. package/artifacts/WalletProposalValidator.json +12 -12
  42. package/artifacts/WalletRegistry.json +1962 -0
  43. package/artifacts/WalletRegistryGovernance.json +2863 -0
  44. package/artifacts/Wallets.json +12 -12
  45. package/artifacts/solcInputs/{be146ce112252bdda3a03de3614fbbf1.json → c151efc9521638a56d4195bcc684d7ff.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/IRedemptionWatchtower.dbg.json +1 -1
  61. package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +1 -1
  62. package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +1 -1
  63. package/build/contracts/bridge/RedemptionWatchtower.sol/RedemptionWatchtower.dbg.json +1 -1
  64. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  65. package/build/contracts/bridge/VendingMachineV2.sol/VendingMachineV2.dbg.json +1 -1
  66. package/build/contracts/bridge/VendingMachineV3.sol/VendingMachineV3.dbg.json +1 -1
  67. package/build/contracts/bridge/WalletProposalValidator.sol/WalletProposalValidator.dbg.json +1 -1
  68. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
  69. package/build/contracts/integrator/AbstractTBTCDepositor.sol/AbstractTBTCDepositor.dbg.json +1 -1
  70. package/build/contracts/integrator/IBridge.sol/IBridge.dbg.json +1 -1
  71. package/build/contracts/integrator/IBridge.sol/IBridgeTypes.dbg.json +1 -1
  72. package/build/contracts/integrator/ITBTCVault.sol/ITBTCVault.dbg.json +1 -1
  73. package/build/contracts/l2/L2TBTC.sol/L2TBTC.dbg.json +1 -1
  74. package/build/contracts/l2/L2WormholeGateway.sol/IWormholeTokenBridge.dbg.json +1 -1
  75. package/build/contracts/l2/L2WormholeGateway.sol/L2WormholeGateway.dbg.json +1 -1
  76. package/build/contracts/maintainer/MaintainerProxy.sol/MaintainerProxy.dbg.json +1 -1
  77. package/build/contracts/relay/LightRelay.sol/ILightRelay.dbg.json +1 -1
  78. package/build/contracts/relay/LightRelay.sol/LightRelay.dbg.json +1 -1
  79. package/build/contracts/relay/LightRelay.sol/RelayUtils.dbg.json +1 -1
  80. package/build/contracts/relay/LightRelayMaintainerProxy.sol/LightRelayMaintainerProxy.dbg.json +1 -1
  81. package/build/contracts/test/BankStub.sol/BankStub.dbg.json +1 -1
  82. package/build/contracts/test/BridgeStub.sol/BridgeStub.dbg.json +1 -1
  83. package/build/contracts/test/HeartbeatStub.sol/HeartbeatStub.dbg.json +1 -1
  84. package/build/contracts/test/LightRelayStub.sol/LightRelayStub.dbg.json +1 -1
  85. package/build/contracts/test/ReceiveApprovalStub.sol/ReceiveApprovalStub.dbg.json +1 -1
  86. package/build/contracts/test/SepoliaLightRelay.sol/SepoliaLightRelay.dbg.json +1 -1
  87. package/build/contracts/test/SystemTestRelay.sol/SystemTestRelay.dbg.json +1 -1
  88. package/build/contracts/test/TestBitcoinTx.sol/TestBitcoinTx.dbg.json +1 -1
  89. package/build/contracts/test/TestERC20.sol/TestERC20.dbg.json +1 -1
  90. package/build/contracts/test/TestERC721.sol/TestERC721.dbg.json +1 -1
  91. package/build/contracts/test/TestEcdsaLib.sol/TestEcdsaLib.dbg.json +1 -1
  92. package/build/contracts/test/TestTBTCDepositor.sol/MockBridge.dbg.json +1 -1
  93. package/build/contracts/test/TestTBTCDepositor.sol/MockTBTCVault.dbg.json +1 -1
  94. package/build/contracts/test/TestTBTCDepositor.sol/TestTBTCDepositor.dbg.json +1 -1
  95. package/build/contracts/test/WormholeBridgeStub.sol/WormholeBridgeStub.dbg.json +1 -1
  96. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  97. package/build/contracts/vault/DonationVault.sol/DonationVault.dbg.json +1 -1
  98. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  99. package/build/contracts/vault/TBTCOptimisticMinting.sol/TBTCOptimisticMinting.dbg.json +1 -1
  100. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  101. package/export/artifacts/@keep-network/ecdsa/contracts/WalletRegistry.sol/WalletRegistry.json +13 -13
  102. package/export/artifacts/contracts/bank/Bank.sol/Bank.json +58 -58
  103. package/export/artifacts/contracts/bridge/Bridge.sol/Bridge.json +318 -318
  104. package/export/artifacts/contracts/bridge/RedemptionWatchtower.sol/RedemptionWatchtower.json +103 -103
  105. package/export/artifacts/contracts/bridge/VendingMachine.sol/VendingMachine.json +71 -71
  106. package/export/artifacts/contracts/bridge/VendingMachineV2.sol/VendingMachineV2.json +24 -24
  107. package/export/artifacts/contracts/bridge/VendingMachineV3.sol/VendingMachineV3.json +26 -26
  108. package/export/artifacts/contracts/bridge/WalletProposalValidator.sol/WalletProposalValidator.json +67 -67
  109. package/export/artifacts/contracts/l2/L2TBTC.sol/L2TBTC.json +40 -40
  110. package/export/artifacts/contracts/l2/L2WormholeGateway.sol/L2WormholeGateway.json +47 -47
  111. package/export/artifacts/contracts/maintainer/MaintainerProxy.sol/MaintainerProxy.json +118 -118
  112. package/export/artifacts/contracts/relay/LightRelay.sol/LightRelay.json +57 -57
  113. package/export/artifacts/contracts/relay/LightRelayMaintainerProxy.sol/LightRelayMaintainerProxy.json +31 -31
  114. package/export/artifacts/contracts/test/BankStub.sol/BankStub.json +60 -60
  115. package/export/artifacts/contracts/test/BridgeStub.sol/BridgeStub.json +356 -356
  116. package/export/artifacts/contracts/test/HeartbeatStub.sol/HeartbeatStub.json +4 -4
  117. package/export/artifacts/contracts/test/LightRelayStub.sol/LightRelayStub.json +59 -59
  118. package/export/artifacts/contracts/test/ReceiveApprovalStub.sol/ReceiveApprovalStub.json +7 -7
  119. package/export/artifacts/contracts/test/SepoliaLightRelay.sol/SepoliaLightRelay.json +59 -59
  120. package/export/artifacts/contracts/test/SystemTestRelay.sol/SystemTestRelay.json +14 -14
  121. package/export/artifacts/contracts/test/TestBitcoinTx.sol/TestBitcoinTx.json +128 -128
  122. package/export/artifacts/contracts/test/TestERC20.sol/TestERC20.json +6 -6
  123. package/export/artifacts/contracts/test/TestERC721.sol/TestERC721.json +8 -8
  124. package/export/artifacts/contracts/test/TestEcdsaLib.sol/TestEcdsaLib.json +4 -4
  125. package/export/artifacts/contracts/test/TestTBTCDepositor.sol/MockBridge.json +34 -34
  126. package/export/artifacts/contracts/test/TestTBTCDepositor.sol/MockTBTCVault.json +18 -18
  127. package/export/artifacts/contracts/test/TestTBTCDepositor.sol/TestTBTCDepositor.json +48 -48
  128. package/export/artifacts/contracts/test/WormholeBridgeStub.sol/WormholeBridgeStub.json +37 -37
  129. package/export/artifacts/contracts/token/TBTC.sol/TBTC.json +2 -2
  130. package/export/artifacts/contracts/vault/DonationVault.sol/DonationVault.json +19 -19
  131. package/export/artifacts/contracts/vault/TBTCVault.sol/TBTCVault.json +143 -143
  132. package/export/typechain/factories/EcdsaAuthorization__factory.js +1 -1
  133. package/export/typechain/factories/IStaking__factory.js +24 -116
  134. package/export/typechain/factories/WalletRegistry__factory.js +1 -1
  135. package/package.json +5 -5
  136. package/artifacts/solcInputs/7c1f8f1dbded4c787d87a982d1fc8349.json +0 -410
  137. package/artifacts/solcInputs/7cc3eda3cb3ff2522d18b5e7b31ea228.json +0 -102
  138. package/artifacts/solcInputs/802132f7da69a8a4226cb9424480847b.json +0 -218
  139. package/artifacts/solcInputs/887fad6b16575ba42183543c324eeb0e.json +0 -335
  140. package/artifacts/solcInputs/b0025f1f7efe4824592ac0c9793776c3.json +0 -392
  141. package/artifacts/solcInputs/b7c5f3550cc22e16e6b6ea9582ccbee3.json +0 -341
  142. package/artifacts/solcInputs/d71d4b4434e6669852eaf643ebd2a7bc.json +0 -209
  143. package/artifacts/solcInputs/fccb130292c8c7cc958ab4fa31a3e180.json +0 -187
@@ -38,7 +38,7 @@
38
38
  "content": "// SPDX-License-Identifier: GPL-3.0-only\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity 0.8.17;\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 Performs preliminary validation of a new group public key.\n /// The group public key must be unique and have 64 bytes in length.\n /// If the validation fails, the function reverts. This function\n /// must be called first for a public key of a wallet added with\n /// `addWallet` function.\n /// @param publicKey Uncompressed public key of a new wallet.\n function validatePublicKey(Data storage self, bytes calldata publicKey)\n internal\n view\n {\n require(publicKey.length == 64, \"Invalid length of the public key\");\n\n bytes32 walletID = keccak256(publicKey);\n require(\n self.registry[walletID].publicKeyX == bytes32(0),\n \"Wallet with the given public key already exists\"\n );\n\n bytes32 publicKeyX = bytes32(publicKey[:32]);\n require(publicKeyX != bytes32(0), \"Wallet public key must be non-zero\");\n }\n\n /// @notice Registers a new wallet. This function does not validate\n /// parameters. The code calling this function must call\n /// `validatePublicKey` first.\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 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"
39
39
  },
40
40
  "@keep-network/ecdsa/contracts/WalletRegistry.sol": {
41
- "content": "// SPDX-License-Identifier: GPL-3.0-only\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity 0.8.17;\n\nimport \"./api/IWalletRegistry.sol\";\nimport \"./api/IWalletOwner.sol\";\nimport \"./libraries/Wallets.sol\";\nimport {EcdsaAuthorization as Authorization} from \"./libraries/EcdsaAuthorization.sol\";\nimport {EcdsaDkg as DKG} from \"./libraries/EcdsaDkg.sol\";\nimport {EcdsaInactivity as Inactivity} from \"./libraries/EcdsaInactivity.sol\";\nimport {EcdsaDkgValidator as DKGValidator} from \"./EcdsaDkgValidator.sol\";\n\nimport \"@keep-network/sortition-pools/contracts/SortitionPool.sol\";\nimport \"@keep-network/random-beacon/contracts/api/IRandomBeacon.sol\";\nimport \"@keep-network/random-beacon/contracts/api/IRandomBeaconConsumer.sol\";\nimport \"@keep-network/random-beacon/contracts/Reimbursable.sol\";\nimport \"@keep-network/random-beacon/contracts/ReimbursementPool.sol\";\nimport \"@keep-network/random-beacon/contracts/Governable.sol\";\n\nimport \"@threshold-network/solidity-contracts/contracts/staking/IApplication.sol\";\nimport \"@threshold-network/solidity-contracts/contracts/staking/IStaking.sol\";\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\n\ncontract WalletRegistry is\n IWalletRegistry,\n IRandomBeaconConsumer,\n IApplication,\n Governable,\n Reimbursable,\n Initializable\n{\n using Authorization for Authorization.Data;\n using DKG for DKG.Data;\n using Wallets for Wallets.Data;\n\n // Libraries data storages\n Authorization.Data internal authorization;\n DKG.Data internal dkg;\n Wallets.Data internal wallets;\n\n /// @notice Slashing amount for submitting a malicious DKG result. Every\n /// DKG result submitted can be challenged for the time of\n /// `dkg.resultChallengePeriodLength`. If the DKG result submitted\n /// is challenged and proven to be malicious, the operator who\n /// submitted the malicious result is slashed for\n /// `_maliciousDkgResultSlashingAmount`.\n uint96 internal _maliciousDkgResultSlashingAmount;\n\n /// @notice Percentage of the staking contract malicious behavior\n /// notification reward which will be transferred to the notifier\n /// reporting about a malicious DKG result. Notifiers are rewarded\n /// from a notifiers treasury pool. For example, if\n /// notification reward is 1000 and the value of the multiplier is\n /// 5, the notifier will receive: 5% of 1000 = 50 per each\n /// operator affected.\n uint256 internal _maliciousDkgResultNotificationRewardMultiplier;\n\n /// @notice Duration of the sortition pool rewards ban imposed on operators\n /// who missed their turn for DKG result submission or who failed\n /// a heartbeat.\n uint256 internal _sortitionPoolRewardsBanDuration;\n\n /// @notice Calculated max gas cost for submitting a DKG result. This will\n /// be refunded as part of the DKG approval process. It is in the\n /// submitter's interest to not skip his priority turn on the approval,\n /// otherwise the refund of the DKG submission will be refunded to\n /// another group member that will call the DKG approve function.\n uint256 internal _dkgResultSubmissionGas;\n\n /// @notice Gas that is meant to balance the DKG result approval's overall\n /// cost. It can be updated by the governance based on the current\n /// market conditions.\n uint256 internal _dkgResultApprovalGasOffset;\n\n /// @notice Gas that is meant to balance the notification of an operator\n /// inactivity. It can be updated by the governance based on the\n /// current market conditions.\n uint256 internal _notifyOperatorInactivityGasOffset;\n\n /// @notice Gas that is meant to balance the notification of a seed for DKG\n /// delivery timeout. It can be updated by the governance based on the\n /// current market conditions.\n uint256 internal _notifySeedTimeoutGasOffset;\n\n /// @notice Gas that is meant to balance the notification of a DKG protocol\n /// execution timeout. It can be updated by the governance based on the\n /// current market conditions.\n /// @dev The value is subtracted for the refundable gas calculation, as the\n /// DKG timeout notification transaction recovers some gas when cleaning\n /// up the storage.\n uint256 internal _notifyDkgTimeoutNegativeGasOffset;\n\n /// @notice Stores current operator inactivity claim nonce for the given\n /// wallet signing group. Each claim is made with a unique nonce\n /// which protects against claim replay.\n mapping(bytes32 => uint256) public inactivityClaimNonce; // walletID -> nonce\n\n // Address that is set as owner of all wallets. Only this address can request\n // new wallets creation and manage their state.\n IWalletOwner public walletOwner;\n\n // External dependencies\n\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n SortitionPool public immutable sortitionPool;\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n IStaking public immutable staking;\n IRandomBeacon public randomBeacon;\n\n // Events\n event DkgStarted(uint256 indexed seed);\n\n event DkgResultSubmitted(\n bytes32 indexed resultHash,\n uint256 indexed seed,\n DKG.Result result\n );\n\n event DkgTimedOut();\n\n event DkgResultApproved(\n bytes32 indexed resultHash,\n address indexed approver\n );\n\n event DkgResultChallenged(\n bytes32 indexed resultHash,\n address indexed challenger,\n string reason\n );\n\n event DkgStateLocked();\n\n event DkgSeedTimedOut();\n\n event WalletCreated(\n bytes32 indexed walletID,\n bytes32 indexed dkgResultHash\n );\n\n event WalletClosed(bytes32 indexed walletID);\n\n event DkgMaliciousResultSlashed(\n bytes32 indexed resultHash,\n uint256 slashingAmount,\n address maliciousSubmitter\n );\n\n event DkgMaliciousResultSlashingFailed(\n bytes32 indexed resultHash,\n uint256 slashingAmount,\n address maliciousSubmitter\n );\n\n event AuthorizationParametersUpdated(\n uint96 minimumAuthorization,\n uint64 authorizationDecreaseDelay,\n uint64 authorizationDecreaseChangePeriod\n );\n\n event RewardParametersUpdated(\n uint256 maliciousDkgResultNotificationRewardMultiplier,\n uint256 sortitionPoolRewardsBanDuration\n );\n\n event SlashingParametersUpdated(uint256 maliciousDkgResultSlashingAmount);\n\n event DkgParametersUpdated(\n uint256 seedTimeout,\n uint256 resultChallengePeriodLength,\n uint256 resultChallengeExtraGas,\n uint256 resultSubmissionTimeout,\n uint256 resultSubmitterPrecedencePeriodLength\n );\n\n event GasParametersUpdated(\n uint256 dkgResultSubmissionGas,\n uint256 dkgResultApprovalGasOffset,\n uint256 notifyOperatorInactivityGasOffset,\n uint256 notifySeedTimeoutGasOffset,\n uint256 notifyDkgTimeoutNegativeGasOffset\n );\n\n event RandomBeaconUpgraded(address randomBeacon);\n\n event WalletOwnerUpdated(address walletOwner);\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 event InactivityClaimed(\n bytes32 indexed walletID,\n uint256 nonce,\n address notifier\n );\n\n modifier onlyStakingContract() {\n require(\n msg.sender == address(staking),\n \"Caller is not the staking contract\"\n );\n _;\n }\n\n /// @notice Reverts if called not by the Wallet Owner.\n modifier onlyWalletOwner() {\n require(\n msg.sender == address(walletOwner),\n \"Caller is not the Wallet Owner\"\n );\n _;\n }\n\n modifier onlyReimbursableAdmin() override {\n require(governance == msg.sender, \"Caller is not the governance\");\n _;\n }\n\n /// @dev Used to initialize immutable variables only, use `initialize` function\n /// for upgradable contract initialization on deployment.\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor(SortitionPool _sortitionPool, IStaking _staking) {\n sortitionPool = _sortitionPool;\n staking = _staking;\n\n _disableInitializers();\n }\n\n /// @dev Initializes upgradable contract on deployment.\n function initialize(\n DKGValidator _ecdsaDkgValidator,\n IRandomBeacon _randomBeacon,\n ReimbursementPool _reimbursementPool\n ) external initializer {\n randomBeacon = _randomBeacon;\n reimbursementPool = _reimbursementPool;\n\n _transferGovernance(msg.sender);\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.\n //\n\n // Minimum authorization is 40k T.\n //\n // Authorization decrease delay is 45 days.\n //\n // Authorization decrease change period is 45 days. It means pending\n // authorization decrease can be overwritten all the time.\n authorization.setMinimumAuthorization(40_000e18);\n authorization.setAuthorizationDecreaseDelay(3_888_000);\n authorization.setAuthorizationDecreaseChangePeriod(3_888_000);\n\n // Malicious DKG result slashing amount is set initially to 1% of the\n // minimum authorization (400 T). This values needs to be increased\n // significantly once the system is fully launched.\n //\n // Notifier of a malicious DKG result receives 100% of the notifier\n // reward from the staking contract.\n //\n // Inactive operators are set as ineligible for rewards for 2 weeks.\n _maliciousDkgResultSlashingAmount = 400e18;\n _maliciousDkgResultNotificationRewardMultiplier = 100;\n _sortitionPoolRewardsBanDuration = 2 weeks;\n\n // DKG seed timeout is set to 48h assuming 15s block time. The same\n // value is used by the Random Beacon as a relay entry hard timeout.\n //\n // DKG result challenge period length is set to 48h as well, assuming\n // 15s block time.\n //\n // DKG result submission timeout covers:\n // - 20 blocks required to confirm the DkgStarted event off-chain\n // - 5 retries of the off-chain protocol that takes 211 blocks at most\n // - 15 blocks to submit the result for each of the 100 members\n // That gives: 20 + (5 * 211) + (15 * 100) = 2575\n //\n //\n // The original DKG result submitter has 20 blocks to approve it before\n // anyone else can do that.\n //\n // With these parameters, the happy path takes no more than 104 hours.\n // In practice, it should take about 48 hours (just the challenge time).\n dkg.init(sortitionPool, _ecdsaDkgValidator);\n dkg.setSeedTimeout(11_520);\n dkg.setResultChallengePeriodLength(11_520);\n dkg.setResultChallengeExtraGas(50_000);\n dkg.setResultSubmissionTimeout(2575);\n dkg.setSubmitterPrecedencePeriodLength(20);\n\n // Gas parameters were adjusted based on Ethereum state in April 2022.\n // If the cost of EVM opcodes change over time, these parameters will\n // have to be updated.\n _dkgResultSubmissionGas = 290_000;\n _dkgResultApprovalGasOffset = 72_000;\n _notifyOperatorInactivityGasOffset = 93_000;\n _notifySeedTimeoutGasOffset = 7_250;\n _notifyDkgTimeoutNegativeGasOffset = 2_300;\n }\n\n /// @notice Withdraws application rewards for the given staking provider.\n /// Rewards are withdrawn to the staking provider's beneficiary\n /// address set in the staking contract. Reverts if staking provider\n /// has not registered the operator address.\n /// @dev Emits `RewardsWithdrawn` event.\n function withdrawRewards(address stakingProvider) external {\n address operator = stakingProviderToOperator(stakingProvider);\n require(operator != address(0), \"Unknown operator\");\n (, address beneficiary, ) = staking.rolesOf(stakingProvider);\n uint96 amount = sortitionPool.withdrawRewards(operator, beneficiary);\n // slither-disable-next-line reentrancy-events\n emit RewardsWithdrawn(stakingProvider, amount);\n }\n\n /// @notice Withdraws rewards belonging to operators marked as ineligible\n /// for sortition pool rewards.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract.\n /// @param recipient Recipient of withdrawn rewards.\n function withdrawIneligibleRewards(address recipient)\n external\n onlyGovernance\n {\n sortitionPool.withdrawIneligible(recipient);\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(address operator) external {\n authorization.registerOperator(operator);\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() external {\n authorization.joinSortitionPool(staking, sortitionPool);\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(address operator) external {\n authorization.updateOperatorStatus(staking, sortitionPool, operator);\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 Can only be called by T staking contract.\n function authorizationIncreased(\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) external onlyStakingContract {\n authorization.authorizationIncreased(\n stakingProvider,\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 /// If the operator is not known (`registerOperator` was not called)\n /// it lets to `approveAuthorizationDecrease` immediatelly. 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.\n ///\n /// @dev Can only be called by T staking contract.\n function authorizationDecreaseRequested(\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) external onlyStakingContract {\n authorization.authorizationDecreaseRequested(\n stakingProvider,\n fromAmount,\n toAmount\n );\n }\n\n /// @notice Approves the previously registered authorization decrease\n /// request. Reverts if authorization decrease delay has not passed\n /// yet or if the authorization decrease was not requested for the\n /// given staking provider.\n function approveAuthorizationDecrease(address stakingProvider) external {\n authorization.approveAuthorizationDecrease(staking, 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 function involuntaryAuthorizationDecrease(\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) external onlyStakingContract {\n authorization.involuntaryAuthorizationDecrease(\n staking,\n sortitionPool,\n stakingProvider,\n fromAmount,\n toAmount\n );\n }\n\n /// @notice Updates address of the Random Beacon.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param _randomBeacon Random Beacon address.\n function upgradeRandomBeacon(IRandomBeacon _randomBeacon)\n external\n onlyGovernance\n {\n randomBeacon = _randomBeacon;\n emit RandomBeaconUpgraded(address(_randomBeacon));\n }\n\n /// @notice Updates the wallet owner.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters. The wallet owner has to implement `IWalletOwner`\n /// interface.\n /// @param _walletOwner New wallet owner address.\n function updateWalletOwner(IWalletOwner _walletOwner)\n external\n onlyGovernance\n {\n walletOwner = _walletOwner;\n emit WalletOwnerUpdated(address(_walletOwner));\n }\n\n /// @notice Updates the values of authorization parameters.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param _minimumAuthorization New minimum authorization amount.\n /// @param _authorizationDecreaseDelay New authorization decrease delay in\n /// seconds.\n /// @param _authorizationDecreaseChangePeriod New authorization decrease\n /// change period in seconds.\n function updateAuthorizationParameters(\n uint96 _minimumAuthorization,\n uint64 _authorizationDecreaseDelay,\n uint64 _authorizationDecreaseChangePeriod\n ) external onlyGovernance {\n authorization.setMinimumAuthorization(_minimumAuthorization);\n authorization.setAuthorizationDecreaseDelay(\n _authorizationDecreaseDelay\n );\n authorization.setAuthorizationDecreaseChangePeriod(\n _authorizationDecreaseChangePeriod\n );\n\n emit AuthorizationParametersUpdated(\n _minimumAuthorization,\n _authorizationDecreaseDelay,\n _authorizationDecreaseChangePeriod\n );\n }\n\n /// @notice Updates the values of DKG parameters.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param _seedTimeout New seed timeout.\n /// @param _resultChallengePeriodLength New DKG result challenge period\n /// length.\n /// @param _resultChallengeExtraGas New extra gas value required to be left\n /// at the end of the DKG result challenge transaction.\n /// @param _resultSubmissionTimeout New DKG result submission timeout.\n /// @param _submitterPrecedencePeriodLength New submitter precedence period\n /// length.\n function updateDkgParameters(\n uint256 _seedTimeout,\n uint256 _resultChallengePeriodLength,\n uint256 _resultChallengeExtraGas,\n uint256 _resultSubmissionTimeout,\n uint256 _submitterPrecedencePeriodLength\n ) external onlyGovernance {\n dkg.setSeedTimeout(_seedTimeout);\n dkg.setResultChallengePeriodLength(_resultChallengePeriodLength);\n dkg.setResultChallengeExtraGas(_resultChallengeExtraGas);\n dkg.setResultSubmissionTimeout(_resultSubmissionTimeout);\n dkg.setSubmitterPrecedencePeriodLength(\n _submitterPrecedencePeriodLength\n );\n\n // slither-disable-next-line reentrancy-events\n emit DkgParametersUpdated(\n _seedTimeout,\n _resultChallengePeriodLength,\n _resultChallengeExtraGas,\n _resultSubmissionTimeout,\n _submitterPrecedencePeriodLength\n );\n }\n\n /// @notice Updates the values of reward parameters.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param maliciousDkgResultNotificationRewardMultiplier New value of the\n /// DKG malicious result notification reward multiplier.\n /// @param sortitionPoolRewardsBanDuration New sortition pool rewards\n /// ban duration in seconds.\n function updateRewardParameters(\n uint256 maliciousDkgResultNotificationRewardMultiplier,\n uint256 sortitionPoolRewardsBanDuration\n ) external onlyGovernance {\n _maliciousDkgResultNotificationRewardMultiplier = maliciousDkgResultNotificationRewardMultiplier;\n _sortitionPoolRewardsBanDuration = sortitionPoolRewardsBanDuration;\n emit RewardParametersUpdated(\n maliciousDkgResultNotificationRewardMultiplier,\n sortitionPoolRewardsBanDuration\n );\n }\n\n /// @notice Updates the values of slashing parameters.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param maliciousDkgResultSlashingAmount New malicious DKG result\n /// slashing amount.\n function updateSlashingParameters(uint96 maliciousDkgResultSlashingAmount)\n external\n onlyGovernance\n {\n _maliciousDkgResultSlashingAmount = maliciousDkgResultSlashingAmount;\n emit SlashingParametersUpdated(maliciousDkgResultSlashingAmount);\n }\n\n /// @notice Updates the values of gas-related parameters.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param dkgResultSubmissionGas New DKG result submission gas.\n /// @param dkgResultApprovalGasOffset New DKG result approval gas offset.\n /// @param notifyOperatorInactivityGasOffset New operator inactivity\n /// notification gas offset.\n /// @param notifySeedTimeoutGasOffset New seed for DKG delivery timeout\n /// notification gas offset.\n /// @param notifyDkgTimeoutNegativeGasOffset New DKG timeout notification gas\n /// offset.\n function updateGasParameters(\n uint256 dkgResultSubmissionGas,\n uint256 dkgResultApprovalGasOffset,\n uint256 notifyOperatorInactivityGasOffset,\n uint256 notifySeedTimeoutGasOffset,\n uint256 notifyDkgTimeoutNegativeGasOffset\n ) external onlyGovernance {\n _dkgResultSubmissionGas = dkgResultSubmissionGas;\n _dkgResultApprovalGasOffset = dkgResultApprovalGasOffset;\n _notifyOperatorInactivityGasOffset = notifyOperatorInactivityGasOffset;\n _notifySeedTimeoutGasOffset = notifySeedTimeoutGasOffset;\n _notifyDkgTimeoutNegativeGasOffset = notifyDkgTimeoutNegativeGasOffset;\n\n emit GasParametersUpdated(\n dkgResultSubmissionGas,\n dkgResultApprovalGasOffset,\n notifyOperatorInactivityGasOffset,\n _notifySeedTimeoutGasOffset,\n _notifyDkgTimeoutNegativeGasOffset\n );\n }\n\n /// @notice Requests a new wallet creation.\n /// @dev Can be called only by the owner of wallets.\n /// It locks the DKG and request a new relay entry. It expects\n /// that the DKG process will be started once a new relay entry\n /// gets generated.\n function requestNewWallet() external onlyWalletOwner {\n dkg.lockState();\n\n randomBeacon.requestRelayEntry(this);\n }\n\n /// @notice Closes an existing wallet. Reverts if wallet with the given ID\n /// does not exist or if it has already been closed.\n /// @param walletID ID of the wallet.\n /// @dev Only a Wallet Owner can call this function.\n function closeWallet(bytes32 walletID) external onlyWalletOwner {\n wallets.deleteWallet(walletID);\n emit WalletClosed(walletID);\n }\n\n /// @notice A callback that is executed once a new relay entry gets\n /// generated. It starts the DKG process.\n /// @dev Can be called only by the random beacon contract.\n /// @param relayEntry Relay entry.\n function __beaconCallback(uint256 relayEntry, uint256) external {\n require(\n msg.sender == address(randomBeacon),\n \"Caller is not the Random Beacon\"\n );\n\n dkg.start(relayEntry);\n }\n\n /// @notice Submits result of DKG protocol.\n /// The DKG result consists of result submitting member index,\n /// calculated group public key, bytes array of misbehaved members,\n /// concatenation of signatures from group members, indices of members\n /// corresponding to each signature and the list of group members.\n /// The result is registered optimistically and waits for an approval.\n /// The result can be challenged when it is believed to be incorrect.\n /// The challenge verifies the registered result i.a. it checks if members\n /// list corresponds to the expected set of members determined\n /// by the sortition pool.\n /// @dev The message to be signed by each member is keccak256 hash of the\n /// chain ID, calculated group public key, misbehaved members indices\n /// and DKG start block. The calculated hash should be prefixed with\n /// `\\x19Ethereum signed message:\\n` before signing, so the message to\n /// sign is:\n /// `\\x19Ethereum signed message:\\n${keccak256(chainID,groupPubKey,misbehavedIndices,startBlock)}`\n /// @param dkgResult DKG result.\n function submitDkgResult(DKG.Result calldata dkgResult) external {\n wallets.validatePublicKey(dkgResult.groupPubKey);\n dkg.submitResult(dkgResult);\n }\n\n /// @notice Approves DKG result. Can be called when the challenge period for\n /// the submitted result is finished. Considers the submitted result\n /// as valid, bans misbehaved group members from the sortition pool\n /// rewards, and completes the group creation by activating the\n /// candidate group. For the first `resultSubmissionTimeout` blocks\n /// after the end of the challenge period can be called only by the\n /// DKG result submitter. After that time, can be called by anyone.\n /// A new wallet based on the DKG result details.\n /// @param dkgResult Result to approve. Must match the submitted result\n /// stored during `submitDkgResult`.\n function approveDkgResult(DKG.Result calldata dkgResult) external {\n uint256 gasStart = gasleft();\n uint32[] memory misbehavedMembers = dkg.approveResult(dkgResult);\n\n (bytes32 walletID, bytes32 publicKeyX, bytes32 publicKeyY) = wallets\n .addWallet(dkgResult.membersHash, dkgResult.groupPubKey);\n\n emit WalletCreated(walletID, keccak256(abi.encode(dkgResult)));\n\n if (misbehavedMembers.length > 0) {\n sortitionPool.setRewardIneligibility(\n misbehavedMembers,\n // solhint-disable-next-line not-rely-on-time\n block.timestamp + _sortitionPoolRewardsBanDuration\n );\n }\n\n walletOwner.__ecdsaWalletCreatedCallback(\n walletID,\n publicKeyX,\n publicKeyY\n );\n\n dkg.complete();\n\n // Refund msg.sender's ETH for DKG result submission and result approval\n reimbursementPool.refund(\n _dkgResultSubmissionGas +\n (gasStart - gasleft()) +\n _dkgResultApprovalGasOffset,\n msg.sender\n );\n }\n\n /// @notice Notifies about seed for DKG delivery timeout. It is expected\n /// that a seed is delivered by the Random Beacon as a relay entry in a\n /// callback function.\n function notifySeedTimeout() external {\n uint256 gasStart = gasleft();\n\n dkg.notifySeedTimeout();\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + _notifySeedTimeoutGasOffset,\n msg.sender\n );\n }\n\n /// @notice Notifies about DKG timeout.\n function notifyDkgTimeout() external {\n uint256 gasStart = gasleft();\n\n dkg.notifyDkgTimeout();\n\n // Note that the offset is subtracted as it is expected that the cleanup\n // performed on DKG timeout notification removes data from the storage\n // which is recovering gas for the transaction.\n reimbursementPool.refund(\n (gasStart - gasleft()) - _notifyDkgTimeoutNegativeGasOffset,\n msg.sender\n );\n }\n\n /// @notice Challenges DKG result. If the submitted result is proved to be\n /// invalid it reverts the DKG back to the result submission phase.\n /// @param dkgResult Result to challenge. Must match the submitted result\n /// stored during `submitDkgResult`.\n /// @dev Due to EIP-150 1/64 of the gas is not forwarded to the call, and\n /// will be kept to execute the remaining operations in the function\n /// after the call inside the try-catch. To eliminate a class of\n /// attacks related to the gas limit manipulation, this function\n /// requires an extra amount of gas to be left at the end of the\n /// execution.\n function challengeDkgResult(DKG.Result calldata dkgResult) external {\n // solhint-disable-next-line avoid-tx-origin\n require(msg.sender == tx.origin, \"Not EOA\");\n\n (\n bytes32 maliciousDkgResultHash,\n uint32 maliciousDkgResultSubmitterId\n ) = dkg.challengeResult(dkgResult);\n\n address maliciousDkgResultSubmitterAddress = sortitionPool\n .getIDOperator(maliciousDkgResultSubmitterId);\n\n address[] memory operatorWrapper = new address[](1);\n operatorWrapper[0] = operatorToStakingProvider(\n maliciousDkgResultSubmitterAddress\n );\n\n try\n staking.seize(\n _maliciousDkgResultSlashingAmount,\n _maliciousDkgResultNotificationRewardMultiplier,\n msg.sender,\n operatorWrapper\n )\n {\n // slither-disable-next-line reentrancy-events\n emit DkgMaliciousResultSlashed(\n maliciousDkgResultHash,\n _maliciousDkgResultSlashingAmount,\n maliciousDkgResultSubmitterAddress\n );\n } catch {\n // Should never happen but we want to ensure a non-critical path\n // failure from an external contract does not stop the challenge\n // to complete.\n emit DkgMaliciousResultSlashingFailed(\n maliciousDkgResultHash,\n _maliciousDkgResultSlashingAmount,\n maliciousDkgResultSubmitterAddress\n );\n }\n\n // Due to EIP-150, 1/64 of the gas is not forwarded to the call, and\n // will be kept to execute the remaining operations in the function\n // after the call inside the try-catch.\n //\n // To ensure there is no way for the caller to manipulate gas limit in\n // such a way that the call inside try-catch fails with out-of-gas and\n // the rest of the function is executed with the remaining 1/64 of gas,\n // we require an extra gas amount to be left at the end of the call to\n // `challengeDkgResult`.\n dkg.requireChallengeExtraGas();\n }\n\n /// @notice Notifies about operators who are inactive. Using this function,\n /// a majority of the wallet signing group can decide about\n /// punishing specific group members who constantly fail doing their\n /// job. If the provided claim is proved to be valid and signed by\n /// sufficient number of group members, operators of members deemed\n /// as inactive are banned from sortition pool rewards for the\n /// duration specified by `sortitionPoolRewardsBanDuration` parameter.\n /// The function allows to signal about single operators being\n /// inactive as well as to signal wallet-wide heartbeat failures\n /// that are propagated to the wallet owner who should begin the\n /// procedure of moving responsibilities to another wallet given\n /// that the wallet who failed the heartbeat may soon be not able to\n /// function and provide new signatures.\n /// The sender of the claim must be one of the claim signers. This\n /// function can be called only for registered wallets\n /// @param claim Operator inactivity claim.\n /// @param nonce Current inactivity claim nonce for the given wallet signing\n /// group. Must be the same as the stored one.\n /// @param groupMembers Identifiers of the wallet signing group members.\n function notifyOperatorInactivity(\n Inactivity.Claim calldata claim,\n uint256 nonce,\n uint32[] calldata groupMembers\n ) external {\n uint256 gasStart = gasleft();\n\n bytes32 walletID = claim.walletID;\n\n require(nonce == inactivityClaimNonce[walletID], \"Invalid nonce\");\n\n (bytes32 pubKeyX, bytes32 pubKeyY) = wallets\n .getWalletPublicKeyCoordinates(walletID);\n bytes32 memberIdsHash = wallets.getWalletMembersIdsHash(walletID);\n\n require(\n memberIdsHash == keccak256(abi.encode(groupMembers)),\n \"Invalid group members\"\n );\n\n uint32[] memory ineligibleOperators = Inactivity.verifyClaim(\n sortitionPool,\n claim,\n bytes.concat(pubKeyX, pubKeyY),\n nonce,\n groupMembers\n );\n\n inactivityClaimNonce[walletID]++;\n\n emit InactivityClaimed(walletID, nonce, msg.sender);\n\n sortitionPool.setRewardIneligibility(\n ineligibleOperators,\n // solhint-disable-next-line not-rely-on-time\n block.timestamp + _sortitionPoolRewardsBanDuration\n );\n\n if (claim.heartbeatFailed) {\n walletOwner.__ecdsaWalletHeartbeatFailedCallback(\n walletID,\n pubKeyX,\n pubKeyY\n );\n }\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + _notifyOperatorInactivityGasOffset,\n msg.sender\n );\n }\n\n /// @notice Allows the wallet owner to add all signing group members of the\n /// wallet with the given ID to the slashing queue of the staking .\n /// contract. The notifier will receive reward per each group member\n /// from the staking contract notifiers treasury. The reward is\n /// scaled by the `rewardMultiplier` provided as a parameter.\n /// @param amount Amount of tokens to seize from each signing group member.\n /// @param rewardMultiplier Fraction of the staking contract notifiers\n /// reward the notifier should receive; should be between [0, 100].\n /// @param notifier Address of the misbehavior notifier.\n /// @param walletID ID of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @dev Requirements:\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.\n /// - `rewardMultiplier` must be between [0, 100].\n /// - This function does revert if staking contract call reverts.\n /// The calling code needs to handle the potential revert.\n function seize(\n uint96 amount,\n uint256 rewardMultiplier,\n address notifier,\n bytes32 walletID,\n uint32[] calldata walletMembersIDs\n ) external onlyWalletOwner {\n bytes32 memberIdsHash = wallets.getWalletMembersIdsHash(walletID);\n require(\n memberIdsHash == keccak256(abi.encode(walletMembersIDs)),\n \"Invalid wallet members identifiers\"\n );\n\n address[] memory groupMembersAddresses = sortitionPool.getIDOperators(\n walletMembersIDs\n );\n address[] memory stakingProvidersAddresses = new address[](\n walletMembersIDs.length\n );\n for (uint256 i = 0; i < groupMembersAddresses.length; i++) {\n stakingProvidersAddresses[i] = operatorToStakingProvider(\n groupMembersAddresses[i]\n );\n }\n\n staking.seize(\n amount,\n rewardMultiplier,\n notifier,\n stakingProvidersAddresses\n );\n }\n\n /// @notice Checks if DKG result is valid for the current DKG.\n /// @param result DKG result.\n /// @return True if the result is valid. If the result is invalid it returns\n /// false and an error message.\n function isDkgResultValid(DKG.Result calldata result)\n external\n view\n returns (bool, string memory)\n {\n return dkg.isResultValid(result);\n }\n\n /// @notice Check current wallet creation state.\n function getWalletCreationState() external view returns (DKG.State) {\n return dkg.currentState();\n }\n\n /// @notice Checks whether the given operator is a member of the given\n /// wallet signing group.\n /// @param walletID ID of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @param operator Address of the checked operator.\n /// @param walletMemberIndex Position of the operator in the wallet signing\n /// group members list.\n /// @return True - if the operator is a member of the given wallet signing\n /// group. False - otherwise.\n /// @dev Requirements:\n /// - The `operator` parameter must be an actual sortition pool operator.\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.\n /// - The `walletMemberIndex` must be in range [1, walletMembersIDs.length]\n function isWalletMember(\n bytes32 walletID,\n uint32[] calldata walletMembersIDs,\n address operator,\n uint256 walletMemberIndex\n ) external view returns (bool) {\n uint32 operatorID = sortitionPool.getOperatorID(operator);\n\n require(operatorID != 0, \"Not a sortition pool operator\");\n\n bytes32 memberIdsHash = wallets.getWalletMembersIdsHash(walletID);\n\n require(\n memberIdsHash == keccak256(abi.encode(walletMembersIDs)),\n \"Invalid wallet members identifiers\"\n );\n\n require(\n 1 <= walletMemberIndex &&\n walletMemberIndex <= walletMembersIDs.length,\n \"Wallet member index is out of range\"\n );\n\n return walletMembersIDs[walletMemberIndex - 1] == operatorID;\n }\n\n /// @notice Checks if awaiting seed timed out.\n /// @return True if awaiting seed timed out, false otherwise.\n function hasSeedTimedOut() external view returns (bool) {\n return dkg.hasSeedTimedOut();\n }\n\n /// @notice Checks if DKG timed out. The DKG timeout period includes time required\n /// for off-chain protocol execution and time for the result publication\n /// for all group members. After this time result cannot be submitted\n /// and DKG can be notified about the timeout.\n /// @return True if DKG timed out, false otherwise.\n function hasDkgTimedOut() external view returns (bool) {\n return dkg.hasDkgTimedOut();\n }\n\n function getWallet(bytes32 walletID)\n external\n view\n returns (Wallets.Wallet memory)\n {\n return wallets.registry[walletID];\n }\n\n /// @notice Gets public key of a wallet with a 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 /// @param walletID ID of the wallet.\n /// @return Uncompressed public key of the wallet.\n function getWalletPublicKey(bytes32 walletID)\n external\n view\n returns (bytes memory)\n {\n return wallets.getWalletPublicKey(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 wallet is registered, false otherwise.\n function isWalletRegistered(bytes32 walletID) external view returns (bool) {\n return wallets.isWalletRegistered(walletID);\n }\n\n /// @notice The minimum authorization amount required so that operator can\n /// participate in ECDSA Wallet operations.\n function minimumAuthorization() external view returns (uint96) {\n return authorization.parameters.minimumAuthorization;\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 sortition pool.\n /// If the authorized stake minus the pending authorization decrease\n /// is below the minimum authorization, eligible stake is 0.\n function eligibleStake(address stakingProvider)\n external\n view\n returns (uint96)\n {\n return authorization.eligibleStake(staking, stakingProvider);\n }\n\n /// @notice Returns the amount of rewards available for withdrawal for the\n /// given staking provider. Reverts if staking provider has not\n /// registered the operator address.\n function availableRewards(address stakingProvider)\n external\n view\n returns (uint96)\n {\n address operator = stakingProviderToOperator(stakingProvider);\n require(operator != address(0), \"Unknown operator\");\n return sortitionPool.getAvailableRewards(operator);\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(address stakingProvider)\n external\n view\n returns (uint96)\n {\n return authorization.pendingAuthorizationDecrease(stakingProvider);\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(address stakingProvider)\n external\n view\n returns (uint64)\n {\n return\n authorization.remainingAuthorizationDecreaseDelay(stakingProvider);\n }\n\n /// @notice Returns operator registered for the given staking provider.\n function stakingProviderToOperator(address stakingProvider)\n public\n view\n returns (address)\n {\n return authorization.stakingProviderToOperator[stakingProvider];\n }\n\n /// @notice Returns staking provider of the given operator.\n function operatorToStakingProvider(address operator)\n public\n view\n returns (address)\n {\n return authorization.operatorToStakingProvider[operator];\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(address operator) external view returns (bool) {\n return\n authorization.isOperatorUpToDate(staking, sortitionPool, operator);\n }\n\n /// @notice Returns true if the given operator is in the sortition pool.\n /// Otherwise, returns false.\n function isOperatorInPool(address operator) external view returns (bool) {\n return sortitionPool.isOperatorInPool(operator);\n }\n\n /// @notice Selects a new group of operators. Can only be called when DKG\n /// is in progress and the pool is locked.\n /// At least one operator has to be registered in the pool,\n /// otherwise the function fails reverting the transaction.\n /// @return IDs of selected group members.\n function selectGroup() external view returns (uint32[] memory) {\n return sortitionPool.selectGroup(DKG.groupSize, bytes32(dkg.seed));\n }\n\n /// @notice Retrieves dkg parameters that were set in DKG library.\n function dkgParameters() external view returns (DKG.Parameters memory) {\n return dkg.parameters;\n }\n\n /// @notice Returns authorization-related parameters.\n /// @dev The minimum authorization is also returned by `minimumAuthorization()`\n /// function, as a requirement of `IApplication` interface.\n /// @return minimumAuthorization The minimum authorization amount required\n /// so that operator can participate in the random beacon. This\n /// amount is required to execute slashing for providing a malicious\n /// DKG result or when a relay entry times out.\n /// @return authorizationDecreaseDelay Delay in seconds that needs to pass\n /// between the time authorization decrease is requested and the\n /// time that request gets approved. Protects against free-riders\n /// earning rewards and not being active in the network.\n /// @return authorizationDecreaseChangePeriod Authorization decrease change\n /// period in seconds. It is the time, before authorization decrease\n /// delay end, during which the pending authorization decrease\n /// request can be overwritten.\n /// If set to 0, pending authorization decrease request can not be\n /// overwritten until the entire `authorizationDecreaseDelay` ends.\n /// If set to value equal `authorizationDecreaseDelay`, request can\n /// always be overwritten.\n function authorizationParameters()\n external\n view\n returns (\n uint96 minimumAuthorization,\n uint64 authorizationDecreaseDelay,\n uint64 authorizationDecreaseChangePeriod\n )\n {\n return (\n authorization.parameters.minimumAuthorization,\n authorization.parameters.authorizationDecreaseDelay,\n authorization.parameters.authorizationDecreaseChangePeriod\n );\n }\n\n /// @notice Retrieves reward-related parameters.\n /// @return maliciousDkgResultNotificationRewardMultiplier Percentage of the\n /// staking contract malicious behavior notification reward which\n /// will be transferred to the notifier reporting about a malicious\n /// DKG result. Notifiers are rewarded from a notifiers treasury\n /// pool. For example, if notification reward is 1000 and the value\n /// of the multiplier is 5, the notifier will receive:\n /// 5% of 1000 = 50 per each operator affected.\n /// @return sortitionPoolRewardsBanDuration Duration of the sortition pool\n /// rewards ban imposed on operators who missed their turn for DKG\n /// result submission or who failed a heartbeat.\n function rewardParameters()\n external\n view\n returns (\n uint256 maliciousDkgResultNotificationRewardMultiplier,\n uint256 sortitionPoolRewardsBanDuration\n )\n {\n return (\n _maliciousDkgResultNotificationRewardMultiplier,\n _sortitionPoolRewardsBanDuration\n );\n }\n\n /// @notice Retrieves slashing-related parameters.\n /// @return maliciousDkgResultSlashingAmount Slashing amount for submitting\n /// a malicious DKG result. Every DKG result submitted can be\n /// challenged for the time of `dkg.resultChallengePeriodLength`.\n /// If the DKG result submitted is challenged and proven to be\n /// malicious, the operator who submitted the malicious result is\n /// slashed for `_maliciousDkgResultSlashingAmount`.\n function slashingParameters()\n external\n view\n returns (uint96 maliciousDkgResultSlashingAmount)\n {\n return _maliciousDkgResultSlashingAmount;\n }\n\n /// @notice Retrieves gas-related parameters.\n /// @return dkgResultSubmissionGas Calculated max gas cost for submitting\n /// a DKG result. This will be refunded as part of the DKG approval\n /// process. It is in the submitter's interest to not skip his\n /// priority turn on the approval, otherwise the refund of the DKG\n /// submission will be refunded to another group member that will\n /// call the DKG approve function.\n /// @return dkgResultApprovalGasOffset Gas that is meant to balance the DKG\n /// result approval's overall cost. It can be updated by the\n /// governance based on the current market conditions.\n /// @return notifyOperatorInactivityGasOffset Gas that is meant to balance\n /// the notification of an operator inactivity. It can be updated by\n /// the governance based on the current market conditions.\n /// @return notifySeedTimeoutGasOffset Gas that is meant to balance the\n /// notification of a seed for DKG delivery timeout. It can be updated\n /// by the governance based on the current market conditions.\n /// @return notifyDkgTimeoutNegativeGasOffset Gas that is meant to balance\n /// the notification of a DKG protocol execution timeout. It can be\n /// updated by the governance based on the current market conditions.\n function gasParameters()\n external\n view\n returns (\n uint256 dkgResultSubmissionGas,\n uint256 dkgResultApprovalGasOffset,\n uint256 notifyOperatorInactivityGasOffset,\n uint256 notifySeedTimeoutGasOffset,\n uint256 notifyDkgTimeoutNegativeGasOffset\n )\n {\n return (\n _dkgResultSubmissionGas,\n _dkgResultApprovalGasOffset,\n _notifyOperatorInactivityGasOffset,\n _notifySeedTimeoutGasOffset,\n _notifyDkgTimeoutNegativeGasOffset\n );\n }\n}\n"
41
+ "content": "// SPDX-License-Identifier: GPL-3.0-only\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity 0.8.17;\n\nimport \"./api/IWalletRegistry.sol\";\nimport \"./api/IWalletOwner.sol\";\nimport \"./libraries/Wallets.sol\";\nimport {EcdsaAuthorization as Authorization} from \"./libraries/EcdsaAuthorization.sol\";\nimport {EcdsaDkg as DKG} from \"./libraries/EcdsaDkg.sol\";\nimport {EcdsaInactivity as Inactivity} from \"./libraries/EcdsaInactivity.sol\";\nimport {EcdsaDkgValidator as DKGValidator} from \"./EcdsaDkgValidator.sol\";\n\nimport \"@keep-network/sortition-pools/contracts/SortitionPool.sol\";\nimport \"@keep-network/random-beacon/contracts/api/IRandomBeacon.sol\";\nimport \"@keep-network/random-beacon/contracts/api/IRandomBeaconConsumer.sol\";\nimport \"@keep-network/random-beacon/contracts/Reimbursable.sol\";\nimport \"@keep-network/random-beacon/contracts/ReimbursementPool.sol\";\nimport \"@keep-network/random-beacon/contracts/Governable.sol\";\n\nimport \"@threshold-network/solidity-contracts/contracts/staking/IApplication.sol\";\nimport \"@threshold-network/solidity-contracts/contracts/staking/IStaking.sol\";\n\nimport \"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol\";\n\ncontract WalletRegistry is\n IWalletRegistry,\n IRandomBeaconConsumer,\n IApplication,\n Governable,\n Reimbursable,\n Initializable\n{\n using Authorization for Authorization.Data;\n using DKG for DKG.Data;\n using Wallets for Wallets.Data;\n\n // Libraries data storages\n Authorization.Data internal authorization;\n DKG.Data internal dkg;\n Wallets.Data internal wallets;\n\n /// @notice Slashing amount for submitting a malicious DKG result. Every\n /// DKG result submitted can be challenged for the time of\n /// `dkg.resultChallengePeriodLength`. If the DKG result submitted\n /// is challenged and proven to be malicious, the operator who\n /// submitted the malicious result is slashed for\n /// `_maliciousDkgResultSlashingAmount`.\n uint96 internal _maliciousDkgResultSlashingAmount;\n\n /// @notice Percentage of the staking contract malicious behavior\n /// notification reward which will be transferred to the notifier\n /// reporting about a malicious DKG result. Notifiers are rewarded\n /// from a notifiers treasury pool. For example, if\n /// notification reward is 1000 and the value of the multiplier is\n /// 5, the notifier will receive: 5% of 1000 = 50 per each\n /// operator affected.\n uint256 internal _maliciousDkgResultNotificationRewardMultiplier;\n\n /// @notice Duration of the sortition pool rewards ban imposed on operators\n /// who missed their turn for DKG result submission or who failed\n /// a heartbeat.\n uint256 internal _sortitionPoolRewardsBanDuration;\n\n /// @notice Calculated max gas cost for submitting a DKG result. This will\n /// be refunded as part of the DKG approval process. It is in the\n /// submitter's interest to not skip his priority turn on the approval,\n /// otherwise the refund of the DKG submission will be refunded to\n /// another group member that will call the DKG approve function.\n uint256 internal _dkgResultSubmissionGas;\n\n /// @notice Gas that is meant to balance the DKG result approval's overall\n /// cost. It can be updated by the governance based on the current\n /// market conditions.\n uint256 internal _dkgResultApprovalGasOffset;\n\n /// @notice Gas that is meant to balance the notification of an operator\n /// inactivity. It can be updated by the governance based on the\n /// current market conditions.\n uint256 internal _notifyOperatorInactivityGasOffset;\n\n /// @notice Gas that is meant to balance the notification of a seed for DKG\n /// delivery timeout. It can be updated by the governance based on the\n /// current market conditions.\n uint256 internal _notifySeedTimeoutGasOffset;\n\n /// @notice Gas that is meant to balance the notification of a DKG protocol\n /// execution timeout. It can be updated by the governance based on the\n /// current market conditions.\n /// @dev The value is subtracted for the refundable gas calculation, as the\n /// DKG timeout notification transaction recovers some gas when cleaning\n /// up the storage.\n uint256 internal _notifyDkgTimeoutNegativeGasOffset;\n\n /// @notice Stores current operator inactivity claim nonce for the given\n /// wallet signing group. Each claim is made with a unique nonce\n /// which protects against claim replay.\n mapping(bytes32 => uint256) public inactivityClaimNonce; // walletID -> nonce\n\n // Address that is set as owner of all wallets. Only this address can request\n // new wallets creation and manage their state.\n IWalletOwner public walletOwner;\n\n // External dependencies\n\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n SortitionPool public immutable sortitionPool;\n /// @custom:oz-upgrades-unsafe-allow state-variable-immutable\n IStaking public immutable staking;\n IRandomBeacon public randomBeacon;\n\n // Events\n event DkgStarted(uint256 indexed seed);\n\n event DkgResultSubmitted(\n bytes32 indexed resultHash,\n uint256 indexed seed,\n DKG.Result result\n );\n\n event DkgTimedOut();\n\n event DkgResultApproved(\n bytes32 indexed resultHash,\n address indexed approver\n );\n\n event DkgResultChallenged(\n bytes32 indexed resultHash,\n address indexed challenger,\n string reason\n );\n\n event DkgStateLocked();\n\n event DkgSeedTimedOut();\n\n event WalletCreated(\n bytes32 indexed walletID,\n bytes32 indexed dkgResultHash\n );\n\n event WalletClosed(bytes32 indexed walletID);\n\n event DkgMaliciousResultSlashed(\n bytes32 indexed resultHash,\n uint256 slashingAmount,\n address maliciousSubmitter\n );\n\n event DkgMaliciousResultSlashingFailed(\n bytes32 indexed resultHash,\n uint256 slashingAmount,\n address maliciousSubmitter\n );\n\n event AuthorizationParametersUpdated(\n uint96 minimumAuthorization,\n uint64 authorizationDecreaseDelay,\n uint64 authorizationDecreaseChangePeriod\n );\n\n event RewardParametersUpdated(\n uint256 maliciousDkgResultNotificationRewardMultiplier,\n uint256 sortitionPoolRewardsBanDuration\n );\n\n event SlashingParametersUpdated(uint256 maliciousDkgResultSlashingAmount);\n\n event DkgParametersUpdated(\n uint256 seedTimeout,\n uint256 resultChallengePeriodLength,\n uint256 resultChallengeExtraGas,\n uint256 resultSubmissionTimeout,\n uint256 resultSubmitterPrecedencePeriodLength\n );\n\n event GasParametersUpdated(\n uint256 dkgResultSubmissionGas,\n uint256 dkgResultApprovalGasOffset,\n uint256 notifyOperatorInactivityGasOffset,\n uint256 notifySeedTimeoutGasOffset,\n uint256 notifyDkgTimeoutNegativeGasOffset\n );\n\n event RandomBeaconUpgraded(address randomBeacon);\n\n event WalletOwnerUpdated(address walletOwner);\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 event InactivityClaimed(\n bytes32 indexed walletID,\n uint256 nonce,\n address notifier\n );\n\n modifier onlyStakingContract() {\n require(\n msg.sender == address(staking),\n \"Caller is not the staking contract\"\n );\n _;\n }\n\n /// @notice Reverts if called not by the Wallet Owner.\n modifier onlyWalletOwner() {\n require(\n msg.sender == address(walletOwner),\n \"Caller is not the Wallet Owner\"\n );\n _;\n }\n\n modifier onlyReimbursableAdmin() override {\n require(governance == msg.sender, \"Caller is not the governance\");\n _;\n }\n\n /// @dev Used to initialize immutable variables only, use `initialize` function\n /// for upgradable contract initialization on deployment.\n /// @custom:oz-upgrades-unsafe-allow constructor\n constructor(SortitionPool _sortitionPool, IStaking _staking) {\n sortitionPool = _sortitionPool;\n staking = _staking;\n\n _disableInitializers();\n }\n\n /// @dev Initializes upgradable contract on deployment.\n function initialize(\n DKGValidator _ecdsaDkgValidator,\n IRandomBeacon _randomBeacon,\n ReimbursementPool _reimbursementPool\n ) external initializer {\n randomBeacon = _randomBeacon;\n reimbursementPool = _reimbursementPool;\n\n _transferGovernance(msg.sender);\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.\n //\n\n // Minimum authorization is 40k T.\n //\n // Authorization decrease delay is 45 days.\n //\n // Authorization decrease change period is 45 days. It means pending\n // authorization decrease can be overwritten all the time.\n authorization.setMinimumAuthorization(40_000e18);\n authorization.setAuthorizationDecreaseDelay(3_888_000);\n authorization.setAuthorizationDecreaseChangePeriod(3_888_000);\n\n // Malicious DKG result slashing amount is set initially to 1% of the\n // minimum authorization (400 T). This values needs to be increased\n // significantly once the system is fully launched.\n //\n // Notifier of a malicious DKG result receives 100% of the notifier\n // reward from the staking contract.\n //\n // Inactive operators are set as ineligible for rewards for 2 weeks.\n _maliciousDkgResultSlashingAmount = 400e18;\n _maliciousDkgResultNotificationRewardMultiplier = 100;\n _sortitionPoolRewardsBanDuration = 2 weeks;\n\n // DKG seed timeout is set to 48h assuming 15s block time. The same\n // value is used by the Random Beacon as a relay entry hard timeout.\n //\n // DKG result challenge period length is set to 48h as well, assuming\n // 15s block time.\n //\n // DKG result submission timeout covers:\n // - 20 blocks required to confirm the DkgStarted event off-chain\n // - 1 attempt of the off-chain protocol that takes 216 blocks at most\n // - 3 blocks to submit the result for each of the 100 members\n // That gives: 20 + (1 * 216) + (3 * 100) = 536\n //\n //\n // The original DKG result submitter has 20 blocks to approve it before\n // anyone else can do that.\n //\n // With these parameters, the happy path takes no more than 104 hours.\n // In practice, it should take about 48 hours (just the challenge time).\n dkg.init(sortitionPool, _ecdsaDkgValidator);\n dkg.setSeedTimeout(11_520);\n dkg.setResultChallengePeriodLength(11_520);\n dkg.setResultChallengeExtraGas(50_000);\n dkg.setResultSubmissionTimeout(536);\n dkg.setSubmitterPrecedencePeriodLength(20);\n\n // Gas parameters were adjusted based on Ethereum state in April 2022.\n // If the cost of EVM opcodes change over time, these parameters will\n // have to be updated.\n _dkgResultSubmissionGas = 290_000;\n _dkgResultApprovalGasOffset = 72_000;\n _notifyOperatorInactivityGasOffset = 93_000;\n _notifySeedTimeoutGasOffset = 7_250;\n _notifyDkgTimeoutNegativeGasOffset = 2_300;\n }\n\n /// @notice Withdraws application rewards for the given staking provider.\n /// Rewards are withdrawn to the staking provider's beneficiary\n /// address set in the staking contract. Reverts if staking provider\n /// has not registered the operator address.\n /// @dev Emits `RewardsWithdrawn` event.\n function withdrawRewards(address stakingProvider) external {\n address operator = stakingProviderToOperator(stakingProvider);\n require(operator != address(0), \"Unknown operator\");\n (, address beneficiary, ) = staking.rolesOf(stakingProvider);\n uint96 amount = sortitionPool.withdrawRewards(operator, beneficiary);\n // slither-disable-next-line reentrancy-events\n emit RewardsWithdrawn(stakingProvider, amount);\n }\n\n /// @notice Withdraws rewards belonging to operators marked as ineligible\n /// for sortition pool rewards.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract.\n /// @param recipient Recipient of withdrawn rewards.\n function withdrawIneligibleRewards(address recipient)\n external\n onlyGovernance\n {\n sortitionPool.withdrawIneligible(recipient);\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(address operator) external {\n authorization.registerOperator(operator);\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() external {\n authorization.joinSortitionPool(staking, sortitionPool);\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(address operator) external {\n authorization.updateOperatorStatus(staking, sortitionPool, operator);\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 Can only be called by T staking contract.\n function authorizationIncreased(\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) external onlyStakingContract {\n authorization.authorizationIncreased(\n stakingProvider,\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 /// If the operator is not known (`registerOperator` was not called)\n /// it lets to `approveAuthorizationDecrease` immediatelly. 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.\n ///\n /// @dev Can only be called by T staking contract.\n function authorizationDecreaseRequested(\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) external onlyStakingContract {\n authorization.authorizationDecreaseRequested(\n stakingProvider,\n fromAmount,\n toAmount\n );\n }\n\n /// @notice Approves the previously registered authorization decrease\n /// request. Reverts if authorization decrease delay has not passed\n /// yet or if the authorization decrease was not requested for the\n /// given staking provider.\n function approveAuthorizationDecrease(address stakingProvider) external {\n authorization.approveAuthorizationDecrease(staking, 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 function involuntaryAuthorizationDecrease(\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) external onlyStakingContract {\n authorization.involuntaryAuthorizationDecrease(\n staking,\n sortitionPool,\n stakingProvider,\n fromAmount,\n toAmount\n );\n }\n\n /// @notice Updates address of the Random Beacon.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param _randomBeacon Random Beacon address.\n function upgradeRandomBeacon(IRandomBeacon _randomBeacon)\n external\n onlyGovernance\n {\n randomBeacon = _randomBeacon;\n emit RandomBeaconUpgraded(address(_randomBeacon));\n }\n\n /// @notice Updates the wallet owner.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters. The wallet owner has to implement `IWalletOwner`\n /// interface.\n /// @param _walletOwner New wallet owner address.\n function updateWalletOwner(IWalletOwner _walletOwner)\n external\n onlyGovernance\n {\n walletOwner = _walletOwner;\n emit WalletOwnerUpdated(address(_walletOwner));\n }\n\n /// @notice Updates the values of authorization parameters.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param _minimumAuthorization New minimum authorization amount.\n /// @param _authorizationDecreaseDelay New authorization decrease delay in\n /// seconds.\n /// @param _authorizationDecreaseChangePeriod New authorization decrease\n /// change period in seconds.\n function updateAuthorizationParameters(\n uint96 _minimumAuthorization,\n uint64 _authorizationDecreaseDelay,\n uint64 _authorizationDecreaseChangePeriod\n ) external onlyGovernance {\n authorization.setMinimumAuthorization(_minimumAuthorization);\n authorization.setAuthorizationDecreaseDelay(\n _authorizationDecreaseDelay\n );\n authorization.setAuthorizationDecreaseChangePeriod(\n _authorizationDecreaseChangePeriod\n );\n\n emit AuthorizationParametersUpdated(\n _minimumAuthorization,\n _authorizationDecreaseDelay,\n _authorizationDecreaseChangePeriod\n );\n }\n\n /// @notice Updates the values of DKG parameters.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param _seedTimeout New seed timeout.\n /// @param _resultChallengePeriodLength New DKG result challenge period\n /// length.\n /// @param _resultChallengeExtraGas New extra gas value required to be left\n /// at the end of the DKG result challenge transaction.\n /// @param _resultSubmissionTimeout New DKG result submission timeout.\n /// @param _submitterPrecedencePeriodLength New submitter precedence period\n /// length.\n function updateDkgParameters(\n uint256 _seedTimeout,\n uint256 _resultChallengePeriodLength,\n uint256 _resultChallengeExtraGas,\n uint256 _resultSubmissionTimeout,\n uint256 _submitterPrecedencePeriodLength\n ) external onlyGovernance {\n dkg.setSeedTimeout(_seedTimeout);\n dkg.setResultChallengePeriodLength(_resultChallengePeriodLength);\n dkg.setResultChallengeExtraGas(_resultChallengeExtraGas);\n dkg.setResultSubmissionTimeout(_resultSubmissionTimeout);\n dkg.setSubmitterPrecedencePeriodLength(\n _submitterPrecedencePeriodLength\n );\n\n // slither-disable-next-line reentrancy-events\n emit DkgParametersUpdated(\n _seedTimeout,\n _resultChallengePeriodLength,\n _resultChallengeExtraGas,\n _resultSubmissionTimeout,\n _submitterPrecedencePeriodLength\n );\n }\n\n /// @notice Updates the values of reward parameters.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param maliciousDkgResultNotificationRewardMultiplier New value of the\n /// DKG malicious result notification reward multiplier.\n /// @param sortitionPoolRewardsBanDuration New sortition pool rewards\n /// ban duration in seconds.\n function updateRewardParameters(\n uint256 maliciousDkgResultNotificationRewardMultiplier,\n uint256 sortitionPoolRewardsBanDuration\n ) external onlyGovernance {\n _maliciousDkgResultNotificationRewardMultiplier = maliciousDkgResultNotificationRewardMultiplier;\n _sortitionPoolRewardsBanDuration = sortitionPoolRewardsBanDuration;\n emit RewardParametersUpdated(\n maliciousDkgResultNotificationRewardMultiplier,\n sortitionPoolRewardsBanDuration\n );\n }\n\n /// @notice Updates the values of slashing parameters.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param maliciousDkgResultSlashingAmount New malicious DKG result\n /// slashing amount.\n function updateSlashingParameters(uint96 maliciousDkgResultSlashingAmount)\n external\n onlyGovernance\n {\n _maliciousDkgResultSlashingAmount = maliciousDkgResultSlashingAmount;\n emit SlashingParametersUpdated(maliciousDkgResultSlashingAmount);\n }\n\n /// @notice Updates the values of gas-related parameters.\n /// @dev Can be called only by the contract guvnor, which should be the\n /// wallet registry governance contract. The caller is responsible for\n /// validating parameters.\n /// @param dkgResultSubmissionGas New DKG result submission gas.\n /// @param dkgResultApprovalGasOffset New DKG result approval gas offset.\n /// @param notifyOperatorInactivityGasOffset New operator inactivity\n /// notification gas offset.\n /// @param notifySeedTimeoutGasOffset New seed for DKG delivery timeout\n /// notification gas offset.\n /// @param notifyDkgTimeoutNegativeGasOffset New DKG timeout notification gas\n /// offset.\n function updateGasParameters(\n uint256 dkgResultSubmissionGas,\n uint256 dkgResultApprovalGasOffset,\n uint256 notifyOperatorInactivityGasOffset,\n uint256 notifySeedTimeoutGasOffset,\n uint256 notifyDkgTimeoutNegativeGasOffset\n ) external onlyGovernance {\n _dkgResultSubmissionGas = dkgResultSubmissionGas;\n _dkgResultApprovalGasOffset = dkgResultApprovalGasOffset;\n _notifyOperatorInactivityGasOffset = notifyOperatorInactivityGasOffset;\n _notifySeedTimeoutGasOffset = notifySeedTimeoutGasOffset;\n _notifyDkgTimeoutNegativeGasOffset = notifyDkgTimeoutNegativeGasOffset;\n\n emit GasParametersUpdated(\n dkgResultSubmissionGas,\n dkgResultApprovalGasOffset,\n notifyOperatorInactivityGasOffset,\n _notifySeedTimeoutGasOffset,\n _notifyDkgTimeoutNegativeGasOffset\n );\n }\n\n /// @notice Requests a new wallet creation.\n /// @dev Can be called only by the owner of wallets.\n /// It locks the DKG and request a new relay entry. It expects\n /// that the DKG process will be started once a new relay entry\n /// gets generated.\n function requestNewWallet() external onlyWalletOwner {\n dkg.lockState();\n\n randomBeacon.requestRelayEntry(this);\n }\n\n /// @notice Closes an existing wallet. Reverts if wallet with the given ID\n /// does not exist or if it has already been closed.\n /// @param walletID ID of the wallet.\n /// @dev Only a Wallet Owner can call this function.\n function closeWallet(bytes32 walletID) external onlyWalletOwner {\n wallets.deleteWallet(walletID);\n emit WalletClosed(walletID);\n }\n\n /// @notice A callback that is executed once a new relay entry gets\n /// generated. It starts the DKG process.\n /// @dev Can be called only by the random beacon contract.\n /// @param relayEntry Relay entry.\n function __beaconCallback(uint256 relayEntry, uint256) external {\n require(\n msg.sender == address(randomBeacon),\n \"Caller is not the Random Beacon\"\n );\n\n dkg.start(relayEntry);\n }\n\n /// @notice Submits result of DKG protocol.\n /// The DKG result consists of result submitting member index,\n /// calculated group public key, bytes array of misbehaved members,\n /// concatenation of signatures from group members, indices of members\n /// corresponding to each signature and the list of group members.\n /// The result is registered optimistically and waits for an approval.\n /// The result can be challenged when it is believed to be incorrect.\n /// The challenge verifies the registered result i.a. it checks if members\n /// list corresponds to the expected set of members determined\n /// by the sortition pool.\n /// @dev The message to be signed by each member is keccak256 hash of the\n /// chain ID, calculated group public key, misbehaved members indices\n /// and DKG start block. The calculated hash should be prefixed with\n /// `\\x19Ethereum signed message:\\n` before signing, so the message to\n /// sign is:\n /// `\\x19Ethereum signed message:\\n${keccak256(chainID,groupPubKey,misbehavedIndices,startBlock)}`\n /// @param dkgResult DKG result.\n function submitDkgResult(DKG.Result calldata dkgResult) external {\n wallets.validatePublicKey(dkgResult.groupPubKey);\n dkg.submitResult(dkgResult);\n }\n\n /// @notice Approves DKG result. Can be called when the challenge period for\n /// the submitted result is finished. Considers the submitted result\n /// as valid, bans misbehaved group members from the sortition pool\n /// rewards, and completes the group creation by activating the\n /// candidate group. For the first `resultSubmissionTimeout` blocks\n /// after the end of the challenge period can be called only by the\n /// DKG result submitter. After that time, can be called by anyone.\n /// A new wallet based on the DKG result details.\n /// @param dkgResult Result to approve. Must match the submitted result\n /// stored during `submitDkgResult`.\n function approveDkgResult(DKG.Result calldata dkgResult) external {\n uint256 gasStart = gasleft();\n uint32[] memory misbehavedMembers = dkg.approveResult(dkgResult);\n\n (bytes32 walletID, bytes32 publicKeyX, bytes32 publicKeyY) = wallets\n .addWallet(dkgResult.membersHash, dkgResult.groupPubKey);\n\n emit WalletCreated(walletID, keccak256(abi.encode(dkgResult)));\n\n if (misbehavedMembers.length > 0) {\n sortitionPool.setRewardIneligibility(\n misbehavedMembers,\n // solhint-disable-next-line not-rely-on-time\n block.timestamp + _sortitionPoolRewardsBanDuration\n );\n }\n\n walletOwner.__ecdsaWalletCreatedCallback(\n walletID,\n publicKeyX,\n publicKeyY\n );\n\n dkg.complete();\n\n // Refund msg.sender's ETH for DKG result submission and result approval\n reimbursementPool.refund(\n _dkgResultSubmissionGas +\n (gasStart - gasleft()) +\n _dkgResultApprovalGasOffset,\n msg.sender\n );\n }\n\n /// @notice Notifies about seed for DKG delivery timeout. It is expected\n /// that a seed is delivered by the Random Beacon as a relay entry in a\n /// callback function.\n function notifySeedTimeout() external {\n uint256 gasStart = gasleft();\n\n dkg.notifySeedTimeout();\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + _notifySeedTimeoutGasOffset,\n msg.sender\n );\n }\n\n /// @notice Notifies about DKG timeout.\n function notifyDkgTimeout() external {\n uint256 gasStart = gasleft();\n\n dkg.notifyDkgTimeout();\n\n // Note that the offset is subtracted as it is expected that the cleanup\n // performed on DKG timeout notification removes data from the storage\n // which is recovering gas for the transaction.\n reimbursementPool.refund(\n (gasStart - gasleft()) - _notifyDkgTimeoutNegativeGasOffset,\n msg.sender\n );\n }\n\n /// @notice Challenges DKG result. If the submitted result is proved to be\n /// invalid it reverts the DKG back to the result submission phase.\n /// @param dkgResult Result to challenge. Must match the submitted result\n /// stored during `submitDkgResult`.\n /// @dev Due to EIP-150 1/64 of the gas is not forwarded to the call, and\n /// will be kept to execute the remaining operations in the function\n /// after the call inside the try-catch. To eliminate a class of\n /// attacks related to the gas limit manipulation, this function\n /// requires an extra amount of gas to be left at the end of the\n /// execution.\n function challengeDkgResult(DKG.Result calldata dkgResult) external {\n // solhint-disable-next-line avoid-tx-origin\n require(msg.sender == tx.origin, \"Not EOA\");\n\n (\n bytes32 maliciousDkgResultHash,\n uint32 maliciousDkgResultSubmitterId\n ) = dkg.challengeResult(dkgResult);\n\n address maliciousDkgResultSubmitterAddress = sortitionPool\n .getIDOperator(maliciousDkgResultSubmitterId);\n\n address[] memory operatorWrapper = new address[](1);\n operatorWrapper[0] = operatorToStakingProvider(\n maliciousDkgResultSubmitterAddress\n );\n\n try\n staking.seize(\n _maliciousDkgResultSlashingAmount,\n _maliciousDkgResultNotificationRewardMultiplier,\n msg.sender,\n operatorWrapper\n )\n {\n // slither-disable-next-line reentrancy-events\n emit DkgMaliciousResultSlashed(\n maliciousDkgResultHash,\n _maliciousDkgResultSlashingAmount,\n maliciousDkgResultSubmitterAddress\n );\n } catch {\n // Should never happen but we want to ensure a non-critical path\n // failure from an external contract does not stop the challenge\n // to complete.\n emit DkgMaliciousResultSlashingFailed(\n maliciousDkgResultHash,\n _maliciousDkgResultSlashingAmount,\n maliciousDkgResultSubmitterAddress\n );\n }\n\n // Due to EIP-150, 1/64 of the gas is not forwarded to the call, and\n // will be kept to execute the remaining operations in the function\n // after the call inside the try-catch.\n //\n // To ensure there is no way for the caller to manipulate gas limit in\n // such a way that the call inside try-catch fails with out-of-gas and\n // the rest of the function is executed with the remaining 1/64 of gas,\n // we require an extra gas amount to be left at the end of the call to\n // `challengeDkgResult`.\n dkg.requireChallengeExtraGas();\n }\n\n /// @notice Notifies about operators who are inactive. Using this function,\n /// a majority of the wallet signing group can decide about\n /// punishing specific group members who constantly fail doing their\n /// job. If the provided claim is proved to be valid and signed by\n /// sufficient number of group members, operators of members deemed\n /// as inactive are banned from sortition pool rewards for the\n /// duration specified by `sortitionPoolRewardsBanDuration` parameter.\n /// The function allows to signal about single operators being\n /// inactive as well as to signal wallet-wide heartbeat failures\n /// that are propagated to the wallet owner who should begin the\n /// procedure of moving responsibilities to another wallet given\n /// that the wallet who failed the heartbeat may soon be not able to\n /// function and provide new signatures.\n /// The sender of the claim must be one of the claim signers. This\n /// function can be called only for registered wallets\n /// @param claim Operator inactivity claim.\n /// @param nonce Current inactivity claim nonce for the given wallet signing\n /// group. Must be the same as the stored one.\n /// @param groupMembers Identifiers of the wallet signing group members.\n function notifyOperatorInactivity(\n Inactivity.Claim calldata claim,\n uint256 nonce,\n uint32[] calldata groupMembers\n ) external {\n uint256 gasStart = gasleft();\n\n bytes32 walletID = claim.walletID;\n\n require(nonce == inactivityClaimNonce[walletID], \"Invalid nonce\");\n\n (bytes32 pubKeyX, bytes32 pubKeyY) = wallets\n .getWalletPublicKeyCoordinates(walletID);\n bytes32 memberIdsHash = wallets.getWalletMembersIdsHash(walletID);\n\n require(\n memberIdsHash == keccak256(abi.encode(groupMembers)),\n \"Invalid group members\"\n );\n\n uint32[] memory ineligibleOperators = Inactivity.verifyClaim(\n sortitionPool,\n claim,\n bytes.concat(pubKeyX, pubKeyY),\n nonce,\n groupMembers\n );\n\n inactivityClaimNonce[walletID]++;\n\n emit InactivityClaimed(walletID, nonce, msg.sender);\n\n sortitionPool.setRewardIneligibility(\n ineligibleOperators,\n // solhint-disable-next-line not-rely-on-time\n block.timestamp + _sortitionPoolRewardsBanDuration\n );\n\n if (claim.heartbeatFailed) {\n walletOwner.__ecdsaWalletHeartbeatFailedCallback(\n walletID,\n pubKeyX,\n pubKeyY\n );\n }\n\n reimbursementPool.refund(\n (gasStart - gasleft()) + _notifyOperatorInactivityGasOffset,\n msg.sender\n );\n }\n\n /// @notice Allows the wallet owner to add all signing group members of the\n /// wallet with the given ID to the slashing queue of the staking .\n /// contract. The notifier will receive reward per each group member\n /// from the staking contract notifiers treasury. The reward is\n /// scaled by the `rewardMultiplier` provided as a parameter.\n /// @param amount Amount of tokens to seize from each signing group member.\n /// @param rewardMultiplier Fraction of the staking contract notifiers\n /// reward the notifier should receive; should be between [0, 100].\n /// @param notifier Address of the misbehavior notifier.\n /// @param walletID ID of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @dev Requirements:\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.\n /// - `rewardMultiplier` must be between [0, 100].\n /// - This function does revert if staking contract call reverts.\n /// The calling code needs to handle the potential revert.\n function seize(\n uint96 amount,\n uint256 rewardMultiplier,\n address notifier,\n bytes32 walletID,\n uint32[] calldata walletMembersIDs\n ) external onlyWalletOwner {\n bytes32 memberIdsHash = wallets.getWalletMembersIdsHash(walletID);\n require(\n memberIdsHash == keccak256(abi.encode(walletMembersIDs)),\n \"Invalid wallet members identifiers\"\n );\n\n address[] memory groupMembersAddresses = sortitionPool.getIDOperators(\n walletMembersIDs\n );\n address[] memory stakingProvidersAddresses = new address[](\n walletMembersIDs.length\n );\n for (uint256 i = 0; i < groupMembersAddresses.length; i++) {\n stakingProvidersAddresses[i] = operatorToStakingProvider(\n groupMembersAddresses[i]\n );\n }\n\n staking.seize(\n amount,\n rewardMultiplier,\n notifier,\n stakingProvidersAddresses\n );\n }\n\n /// @notice Checks if DKG result is valid for the current DKG.\n /// @param result DKG result.\n /// @return True if the result is valid. If the result is invalid it returns\n /// false and an error message.\n function isDkgResultValid(DKG.Result calldata result)\n external\n view\n returns (bool, string memory)\n {\n return dkg.isResultValid(result);\n }\n\n /// @notice Check current wallet creation state.\n function getWalletCreationState() external view returns (DKG.State) {\n return dkg.currentState();\n }\n\n /// @notice Checks whether the given operator is a member of the given\n /// wallet signing group.\n /// @param walletID ID of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @param operator Address of the checked operator.\n /// @param walletMemberIndex Position of the operator in the wallet signing\n /// group members list.\n /// @return True - if the operator is a member of the given wallet signing\n /// group. False - otherwise.\n /// @dev Requirements:\n /// - The `operator` parameter must be an actual sortition pool operator.\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.\n /// - The `walletMemberIndex` must be in range [1, walletMembersIDs.length]\n function isWalletMember(\n bytes32 walletID,\n uint32[] calldata walletMembersIDs,\n address operator,\n uint256 walletMemberIndex\n ) external view returns (bool) {\n uint32 operatorID = sortitionPool.getOperatorID(operator);\n\n require(operatorID != 0, \"Not a sortition pool operator\");\n\n bytes32 memberIdsHash = wallets.getWalletMembersIdsHash(walletID);\n\n require(\n memberIdsHash == keccak256(abi.encode(walletMembersIDs)),\n \"Invalid wallet members identifiers\"\n );\n\n require(\n 1 <= walletMemberIndex &&\n walletMemberIndex <= walletMembersIDs.length,\n \"Wallet member index is out of range\"\n );\n\n return walletMembersIDs[walletMemberIndex - 1] == operatorID;\n }\n\n /// @notice Checks if awaiting seed timed out.\n /// @return True if awaiting seed timed out, false otherwise.\n function hasSeedTimedOut() external view returns (bool) {\n return dkg.hasSeedTimedOut();\n }\n\n /// @notice Checks if DKG timed out. The DKG timeout period includes time required\n /// for off-chain protocol execution and time for the result publication\n /// for all group members. After this time result cannot be submitted\n /// and DKG can be notified about the timeout.\n /// @return True if DKG timed out, false otherwise.\n function hasDkgTimedOut() external view returns (bool) {\n return dkg.hasDkgTimedOut();\n }\n\n function getWallet(bytes32 walletID)\n external\n view\n returns (Wallets.Wallet memory)\n {\n return wallets.registry[walletID];\n }\n\n /// @notice Gets public key of a wallet with a 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 /// @param walletID ID of the wallet.\n /// @return Uncompressed public key of the wallet.\n function getWalletPublicKey(bytes32 walletID)\n external\n view\n returns (bytes memory)\n {\n return wallets.getWalletPublicKey(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 wallet is registered, false otherwise.\n function isWalletRegistered(bytes32 walletID) external view returns (bool) {\n return wallets.isWalletRegistered(walletID);\n }\n\n /// @notice The minimum authorization amount required so that operator can\n /// participate in ECDSA Wallet operations.\n function minimumAuthorization() external view returns (uint96) {\n return authorization.parameters.minimumAuthorization;\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 sortition pool.\n /// If the authorized stake minus the pending authorization decrease\n /// is below the minimum authorization, eligible stake is 0.\n function eligibleStake(address stakingProvider)\n external\n view\n returns (uint96)\n {\n return authorization.eligibleStake(staking, stakingProvider);\n }\n\n /// @notice Returns the amount of rewards available for withdrawal for the\n /// given staking provider. Reverts if staking provider has not\n /// registered the operator address.\n function availableRewards(address stakingProvider)\n external\n view\n returns (uint96)\n {\n address operator = stakingProviderToOperator(stakingProvider);\n require(operator != address(0), \"Unknown operator\");\n return sortitionPool.getAvailableRewards(operator);\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(address stakingProvider)\n external\n view\n returns (uint96)\n {\n return authorization.pendingAuthorizationDecrease(stakingProvider);\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(address stakingProvider)\n external\n view\n returns (uint64)\n {\n return\n authorization.remainingAuthorizationDecreaseDelay(stakingProvider);\n }\n\n /// @notice Returns operator registered for the given staking provider.\n function stakingProviderToOperator(address stakingProvider)\n public\n view\n returns (address)\n {\n return authorization.stakingProviderToOperator[stakingProvider];\n }\n\n /// @notice Returns staking provider of the given operator.\n function operatorToStakingProvider(address operator)\n public\n view\n returns (address)\n {\n return authorization.operatorToStakingProvider[operator];\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(address operator) external view returns (bool) {\n return\n authorization.isOperatorUpToDate(staking, sortitionPool, operator);\n }\n\n /// @notice Returns true if the given operator is in the sortition pool.\n /// Otherwise, returns false.\n function isOperatorInPool(address operator) external view returns (bool) {\n return sortitionPool.isOperatorInPool(operator);\n }\n\n /// @notice Selects a new group of operators. Can only be called when DKG\n /// is in progress and the pool is locked.\n /// At least one operator has to be registered in the pool,\n /// otherwise the function fails reverting the transaction.\n /// @return IDs of selected group members.\n function selectGroup() external view returns (uint32[] memory) {\n return sortitionPool.selectGroup(DKG.groupSize, bytes32(dkg.seed));\n }\n\n /// @notice Retrieves dkg parameters that were set in DKG library.\n function dkgParameters() external view returns (DKG.Parameters memory) {\n return dkg.parameters;\n }\n\n /// @notice Returns authorization-related parameters.\n /// @dev The minimum authorization is also returned by `minimumAuthorization()`\n /// function, as a requirement of `IApplication` interface.\n /// @return minimumAuthorization The minimum authorization amount required\n /// so that operator can participate in the random beacon. This\n /// amount is required to execute slashing for providing a malicious\n /// DKG result or when a relay entry times out.\n /// @return authorizationDecreaseDelay Delay in seconds that needs to pass\n /// between the time authorization decrease is requested and the\n /// time that request gets approved. Protects against free-riders\n /// earning rewards and not being active in the network.\n /// @return authorizationDecreaseChangePeriod Authorization decrease change\n /// period in seconds. It is the time, before authorization decrease\n /// delay end, during which the pending authorization decrease\n /// request can be overwritten.\n /// If set to 0, pending authorization decrease request can not be\n /// overwritten until the entire `authorizationDecreaseDelay` ends.\n /// If set to value equal `authorizationDecreaseDelay`, request can\n /// always be overwritten.\n function authorizationParameters()\n external\n view\n returns (\n uint96 minimumAuthorization,\n uint64 authorizationDecreaseDelay,\n uint64 authorizationDecreaseChangePeriod\n )\n {\n return (\n authorization.parameters.minimumAuthorization,\n authorization.parameters.authorizationDecreaseDelay,\n authorization.parameters.authorizationDecreaseChangePeriod\n );\n }\n\n /// @notice Retrieves reward-related parameters.\n /// @return maliciousDkgResultNotificationRewardMultiplier Percentage of the\n /// staking contract malicious behavior notification reward which\n /// will be transferred to the notifier reporting about a malicious\n /// DKG result. Notifiers are rewarded from a notifiers treasury\n /// pool. For example, if notification reward is 1000 and the value\n /// of the multiplier is 5, the notifier will receive:\n /// 5% of 1000 = 50 per each operator affected.\n /// @return sortitionPoolRewardsBanDuration Duration of the sortition pool\n /// rewards ban imposed on operators who missed their turn for DKG\n /// result submission or who failed a heartbeat.\n function rewardParameters()\n external\n view\n returns (\n uint256 maliciousDkgResultNotificationRewardMultiplier,\n uint256 sortitionPoolRewardsBanDuration\n )\n {\n return (\n _maliciousDkgResultNotificationRewardMultiplier,\n _sortitionPoolRewardsBanDuration\n );\n }\n\n /// @notice Retrieves slashing-related parameters.\n /// @return maliciousDkgResultSlashingAmount Slashing amount for submitting\n /// a malicious DKG result. Every DKG result submitted can be\n /// challenged for the time of `dkg.resultChallengePeriodLength`.\n /// If the DKG result submitted is challenged and proven to be\n /// malicious, the operator who submitted the malicious result is\n /// slashed for `_maliciousDkgResultSlashingAmount`.\n function slashingParameters()\n external\n view\n returns (uint96 maliciousDkgResultSlashingAmount)\n {\n return _maliciousDkgResultSlashingAmount;\n }\n\n /// @notice Retrieves gas-related parameters.\n /// @return dkgResultSubmissionGas Calculated max gas cost for submitting\n /// a DKG result. This will be refunded as part of the DKG approval\n /// process. It is in the submitter's interest to not skip his\n /// priority turn on the approval, otherwise the refund of the DKG\n /// submission will be refunded to another group member that will\n /// call the DKG approve function.\n /// @return dkgResultApprovalGasOffset Gas that is meant to balance the DKG\n /// result approval's overall cost. It can be updated by the\n /// governance based on the current market conditions.\n /// @return notifyOperatorInactivityGasOffset Gas that is meant to balance\n /// the notification of an operator inactivity. It can be updated by\n /// the governance based on the current market conditions.\n /// @return notifySeedTimeoutGasOffset Gas that is meant to balance the\n /// notification of a seed for DKG delivery timeout. It can be updated\n /// by the governance based on the current market conditions.\n /// @return notifyDkgTimeoutNegativeGasOffset Gas that is meant to balance\n /// the notification of a DKG protocol execution timeout. It can be\n /// updated by the governance based on the current market conditions.\n function gasParameters()\n external\n view\n returns (\n uint256 dkgResultSubmissionGas,\n uint256 dkgResultApprovalGasOffset,\n uint256 notifyOperatorInactivityGasOffset,\n uint256 notifySeedTimeoutGasOffset,\n uint256 notifyDkgTimeoutNegativeGasOffset\n )\n {\n return (\n _dkgResultSubmissionGas,\n _dkgResultApprovalGasOffset,\n _notifyOperatorInactivityGasOffset,\n _notifySeedTimeoutGasOffset,\n _notifyDkgTimeoutNegativeGasOffset\n );\n }\n}\n"
42
42
  },
43
43
  "@keep-network/random-beacon/contracts/api/IRandomBeacon.sol": {
44
44
  "content": "// SPDX-License-Identifier: GPL-3.0-only\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity 0.8.17;\n\nimport \"./IRandomBeaconConsumer.sol\";\n\n/// @title Random Beacon interface\ninterface IRandomBeacon {\n /// @notice Creates a request to generate a new relay entry. Requires a\n /// request fee denominated in T token.\n /// @param callbackContract Beacon consumer callback contract.\n function requestRelayEntry(IRandomBeaconConsumer callbackContract) external;\n}\n"
@@ -242,7 +242,7 @@
242
242
  "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 Application interface for Threshold Network applications\n/// @notice Generic interface for an application. Application is an external\n/// smart contract or a set of smart contracts utilizing functionalities\n/// offered by Threshold Network. Applications authorized for the given\n/// staking provider are eligible to slash the stake delegated to that\n/// staking provider.\ninterface IApplication {\n /// @dev Event emitted by `withdrawRewards` function.\n event RewardsWithdrawn(address indexed stakingProvider, uint96 amount);\n\n /// @notice Withdraws application rewards for the given staking provider.\n /// Rewards are withdrawn to the staking provider's beneficiary\n /// address set in the staking contract.\n /// @dev Emits `RewardsWithdrawn` event.\n function withdrawRewards(address stakingProvider) external;\n\n /// @notice Used by T staking contract to inform the application that the\n /// authorized amount for the given staking provider increased.\n /// The application may do any necessary housekeeping. The\n /// application must revert the transaction in case the\n /// authorization is below the minimum required.\n function authorizationIncreased(\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) external;\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. The application should mark the authorization as\n /// pending decrease and respond to the staking contract with\n /// `approveAuthorizationDecrease` at its discretion. It may\n /// happen right away but it also may happen several months later.\n /// If there is already a pending authorization decrease request\n /// for the application, and the application does not agree for\n /// overwriting it, the function should revert.\n function authorizationDecreaseRequested(\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) external;\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. Lets the application to\n /// do any housekeeping neccessary. Called with 250k gas limit and\n /// does not revert the transaction if\n /// `involuntaryAuthorizationDecrease` call failed.\n function involuntaryAuthorizationDecrease(\n address stakingProvider,\n uint96 fromAmount,\n uint96 toAmount\n ) external;\n\n /// @notice Returns the amount of application rewards available for\n /// withdrawal for the given staking provider.\n function availableRewards(address stakingProvider)\n external\n view\n returns (uint96);\n\n /// @notice The minimum authorization amount required for the staking\n /// provider so that they can participate in the application.\n function minimumAuthorization() external view returns (uint96);\n}\n"
243
243
  },
244
244
  "@threshold-network/solidity-contracts/contracts/staking/IStaking.sol": {
245
- "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 /// or their staking provider.\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 if the application agrees for that. If the\n /// application does not agree for overwriting, the function\n /// reverts.\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 /// @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"
245
+ "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 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 if the application agrees for that. If the\n /// application does not agree for overwriting, the function\n /// reverts.\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 /// If `autoIncrease` flag is true then the amount will be added for\n /// all authorized applications.\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 Toggle `autoIncrease` flag. If true then the complete amount\n /// in top-up will be added to already authorized applications.\n function toggleAutoAuthorizationIncrease(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 Sets to 0 the amount of T that is cached from the legacy\n /// NU staking contract. Reverts if there is at least one\n /// authorization higher than the sum of remaining legacy NU stake\n /// and native T stake for that staking provider or if the unstaked\n /// amount is higher than the cached legacy stake amount. If succeeded,\n /// the legacy NU stake can be partially or fully undelegated on\n /// the legacy NU staking contract. This function allows to unstake\n /// from NU staking contract while still being able to operate in\n /// T network and earning rewards based on the native T staked.\n /// Can be called only by the stake owner or the staking provider.\n function unstakeNu(address stakingProvider) 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 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 auto-increase flag.\n function getAutoIncreaseFlag(address stakingProvider)\n external\n view\n returns (bool);\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 worth\n /// 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\n /// * 10 T if NU stake type specified i.e.\n /// min = 40 T max - (10 T) = 30 T\n /// * 0 T if T stake type specified i.e.\n /// min = 40 T max = 40 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\n /// the staked amounts of the T 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"
246
246
  },
247
247
  "contracts/bank/Bank.sol": {
248
248
  "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 \"./IReceiveBalanceApproval.sol\";\nimport \"../vault/IVault.sol\";\n\n/// @title Bitcoin Bank\n/// @notice Bank is a central component tracking Bitcoin balances. Balances can\n/// be transferred between balance owners, and balance owners can\n/// approve their balances to be spent by others. Balances in the Bank\n/// are updated for depositors who deposited their Bitcoin into the\n/// Bridge and only the Bridge can increase balances.\n/// @dev Bank is a governable contract and the Governance can upgrade the Bridge\n/// address.\ncontract Bank is Ownable {\n address public bridge;\n\n /// @notice The balance of the given account in the Bank. Zero by default.\n mapping(address => uint256) public balanceOf;\n\n /// @notice The remaining amount of balance a spender will be\n /// allowed to transfer on behalf of an owner using\n /// `transferBalanceFrom`. Zero by default.\n mapping(address => mapping(address => uint256)) public allowance;\n\n /// @notice Returns the current nonce for an EIP2612 permission for the\n /// provided balance owner to protect against replay attacks. Used\n /// to construct an EIP2612 signature provided to the `permit`\n /// function.\n mapping(address => uint256) public nonces;\n\n uint256 public immutable cachedChainId;\n bytes32 public immutable cachedDomainSeparator;\n\n /// @notice Returns an EIP2612 Permit message hash. Used to construct\n /// an EIP2612 signature provided to the `permit` function.\n bytes32 public constant PERMIT_TYPEHASH =\n keccak256(\n \"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\"\n );\n\n event BalanceTransferred(\n address indexed from,\n address indexed to,\n uint256 amount\n );\n\n event BalanceApproved(\n address indexed owner,\n address indexed spender,\n uint256 amount\n );\n\n event BalanceIncreased(address indexed owner, uint256 amount);\n\n event BalanceDecreased(address indexed owner, uint256 amount);\n\n event BridgeUpdated(address newBridge);\n\n modifier onlyBridge() {\n require(msg.sender == address(bridge), \"Caller is not the bridge\");\n _;\n }\n\n constructor() {\n cachedChainId = block.chainid;\n cachedDomainSeparator = buildDomainSeparator();\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 /// Requirements:\n /// - The new Bridge address must not be zero.\n /// @param _bridge The new Bridge address.\n function updateBridge(address _bridge) external onlyOwner {\n require(_bridge != address(0), \"Bridge address must not be 0x0\");\n bridge = _bridge;\n emit BridgeUpdated(_bridge);\n }\n\n /// @notice Moves the given `amount` of balance from the caller to\n /// `recipient`.\n /// @dev Requirements:\n /// - `recipient` cannot be the zero address,\n /// - the caller must have a balance of at least `amount`.\n /// @param recipient The recipient of the balance.\n /// @param amount The amount of the balance transferred.\n function transferBalance(address recipient, uint256 amount) external {\n _transferBalance(msg.sender, recipient, amount);\n }\n\n /// @notice Sets `amount` as the allowance of `spender` over the caller's\n /// balance. Does not allow updating an existing allowance to\n /// a value that is non-zero to avoid someone using both the old and\n /// the new allowance by unfortunate transaction ordering. To update\n /// an allowance to a non-zero value please set it to zero first or\n /// use `increaseBalanceAllowance` or `decreaseBalanceAllowance` for\n /// an atomic update.\n /// @dev If the `amount` is set to `type(uint256).max`,\n /// `transferBalanceFrom` will not reduce an allowance.\n /// @param spender The address that will be allowed to spend the balance.\n /// @param amount The amount the spender is allowed to spend.\n function approveBalance(address spender, uint256 amount) external {\n require(\n amount == 0 || allowance[msg.sender][spender] == 0,\n \"Non-atomic allowance change not allowed\"\n );\n _approveBalance(msg.sender, spender, amount);\n }\n\n /// @notice Sets the `amount` as an allowance of a smart contract `spender`\n /// over the caller's balance and calls the `spender` via\n /// `receiveBalanceApproval`.\n /// @dev If the `amount` is set to `type(uint256).max`, the potential\n /// `transferBalanceFrom` executed in `receiveBalanceApproval` of\n /// `spender` will not reduce an allowance. Beware that changing an\n /// allowance with this function brings the risk that `spender` may use\n /// both the old and the new allowance by unfortunate transaction\n /// ordering. Please use `increaseBalanceAllowance` and\n /// `decreaseBalanceAllowance` to eliminate the risk.\n /// @param spender The smart contract that will be allowed to spend the\n /// balance.\n /// @param amount The amount the spender contract is allowed to spend.\n /// @param extraData Extra data passed to the `spender` contract via\n /// `receiveBalanceApproval` call.\n function approveBalanceAndCall(\n address spender,\n uint256 amount,\n bytes calldata extraData\n ) external {\n _approveBalance(msg.sender, spender, amount);\n IReceiveBalanceApproval(spender).receiveBalanceApproval(\n msg.sender,\n amount,\n extraData\n );\n }\n\n /// @notice Atomically increases the caller's balance allowance granted to\n /// `spender` by the given `addedValue`.\n /// @param spender The spender address for which the allowance is increased.\n /// @param addedValue The amount by which the allowance is increased.\n function increaseBalanceAllowance(address spender, uint256 addedValue)\n external\n {\n _approveBalance(\n msg.sender,\n spender,\n allowance[msg.sender][spender] + addedValue\n );\n }\n\n /// @notice Atomically decreases the caller's balance allowance granted to\n /// `spender` by the given `subtractedValue`.\n /// @dev Requirements:\n /// - `spender` must not be the zero address,\n /// - the current allowance for `spender` must not be lower than\n /// the `subtractedValue`.\n /// @param spender The spender address for which the allowance is decreased.\n /// @param subtractedValue The amount by which the allowance is decreased.\n function decreaseBalanceAllowance(address spender, uint256 subtractedValue)\n external\n {\n uint256 currentAllowance = allowance[msg.sender][spender];\n require(\n currentAllowance >= subtractedValue,\n \"Can not decrease balance allowance below zero\"\n );\n unchecked {\n _approveBalance(\n msg.sender,\n spender,\n currentAllowance - subtractedValue\n );\n }\n }\n\n /// @notice Moves `amount` of balance from `spender` to `recipient` using the\n /// allowance mechanism. `amount` is then deducted from the caller's\n /// allowance unless the allowance was made for `type(uint256).max`.\n /// @dev Requirements:\n /// - `recipient` cannot be the zero address,\n /// - `spender` must have a balance of at least `amount`,\n /// - the caller must have an allowance for `spender`'s balance of at\n /// least `amount`.\n /// @param spender The address from which the balance is transferred.\n /// @param recipient The address to which the balance is transferred.\n /// @param amount The amount of balance that is transferred.\n function transferBalanceFrom(\n address spender,\n address recipient,\n uint256 amount\n ) external {\n uint256 currentAllowance = allowance[spender][msg.sender];\n if (currentAllowance != type(uint256).max) {\n require(\n currentAllowance >= amount,\n \"Transfer amount exceeds allowance\"\n );\n unchecked {\n _approveBalance(spender, msg.sender, currentAllowance - amount);\n }\n }\n _transferBalance(spender, recipient, amount);\n }\n\n /// @notice An EIP2612 approval made with secp256k1 signature. Users can\n /// authorize a transfer of their balance with a signature\n /// conforming to the EIP712 standard, rather than an on-chain\n /// transaction from their address. Anyone can submit this signature\n /// on the user's behalf by calling the `permit` function, paying\n /// gas fees, and possibly performing other actions in the same\n /// transaction.\n /// @dev The deadline argument can be set to `type(uint256).max to create\n /// permits that effectively never expire. If the `amount` is set\n /// to `type(uint256).max` then `transferBalanceFrom` will not\n /// reduce an allowance. Beware that changing an allowance with this\n /// function brings the risk that someone may use both the old and the\n /// new allowance by unfortunate transaction ordering. Please use\n /// `increaseBalanceAllowance` and `decreaseBalanceAllowance` to\n /// eliminate the risk.\n /// @param owner The balance owner who signed the permission.\n /// @param spender The address that will be allowed to spend the balance.\n /// @param amount The amount the spender is allowed to spend.\n /// @param deadline The UNIX time until which the permit is valid.\n /// @param v V part of the permit signature.\n /// @param r R part of the permit signature.\n /// @param s S part of the permit signature.\n function permit(\n address owner,\n address spender,\n uint256 amount,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external {\n /* solhint-disable-next-line not-rely-on-time */\n require(deadline >= block.timestamp, \"Permission expired\");\n\n // Validate `s` and `v` values for a malleability concern described in EIP2.\n // Only signatures with `s` value in the lower half of the secp256k1\n // curve's order and `v` value of 27 or 28 are considered valid.\n require(\n uint256(s) <=\n 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,\n \"Invalid signature 's' value\"\n );\n require(v == 27 || v == 28, \"Invalid signature 'v' value\");\n\n bytes32 digest = keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR(),\n keccak256(\n abi.encode(\n PERMIT_TYPEHASH,\n owner,\n spender,\n amount,\n nonces[owner]++,\n deadline\n )\n )\n )\n );\n address recoveredAddress = ecrecover(digest, v, r, s);\n require(\n recoveredAddress != address(0) && recoveredAddress == owner,\n \"Invalid signature\"\n );\n _approveBalance(owner, spender, amount);\n }\n\n /// @notice Increases balances of the provided `recipients` by the provided\n /// `amounts`. Can only be called by the Bridge.\n /// @dev Requirements:\n /// - length of `recipients` and `amounts` must be the same,\n /// - none of `recipients` addresses must point to the Bank.\n /// @param recipients Balance increase recipients.\n /// @param amounts Amounts by which balances are increased.\n function increaseBalances(\n address[] calldata recipients,\n uint256[] calldata amounts\n ) external onlyBridge {\n require(\n recipients.length == amounts.length,\n \"Arrays must have the same length\"\n );\n for (uint256 i = 0; i < recipients.length; i++) {\n _increaseBalance(recipients[i], amounts[i]);\n }\n }\n\n /// @notice Increases balance of the provided `recipient` by the provided\n /// `amount`. Can only be called by the Bridge.\n /// @dev Requirements:\n /// - `recipient` address must not point to the Bank.\n /// @param recipient Balance increase recipient.\n /// @param amount Amount by which the balance is increased.\n function increaseBalance(address recipient, uint256 amount)\n external\n onlyBridge\n {\n _increaseBalance(recipient, amount);\n }\n\n /// @notice Increases the given smart contract `vault`'s balance and\n /// notifies the `vault` contract about it.\n /// Can be called only by the Bridge.\n /// @dev Requirements:\n /// - `vault` must implement `IVault` interface,\n /// - length of `recipients` and `amounts` must be the same.\n /// @param vault Address of `IVault` recipient contract.\n /// @param recipients Balance increase recipients.\n /// @param amounts Amounts by which balances are increased.\n function increaseBalanceAndCall(\n address vault,\n address[] calldata recipients,\n uint256[] calldata amounts\n ) external onlyBridge {\n require(\n recipients.length == amounts.length,\n \"Arrays must have the same length\"\n );\n uint256 totalAmount = 0;\n for (uint256 i = 0; i < amounts.length; i++) {\n totalAmount += amounts[i];\n }\n _increaseBalance(vault, totalAmount);\n IVault(vault).receiveBalanceIncrease(recipients, amounts);\n }\n\n /// @notice Decreases caller's balance by the provided `amount`. There is no\n /// way to restore the balance so do not call this function unless\n /// you really know what you are doing!\n /// @dev Requirements:\n /// - The caller must have a balance of at least `amount`.\n /// @param amount The amount by which the balance is decreased.\n function decreaseBalance(uint256 amount) external {\n balanceOf[msg.sender] -= amount;\n emit BalanceDecreased(msg.sender, amount);\n }\n\n /// @notice Returns hash of EIP712 Domain struct with `TBTC Bank` as\n /// a signing domain and Bank contract as a verifying contract.\n /// Used to construct an EIP2612 signature provided to the `permit`\n /// function.\n /* solhint-disable-next-line func-name-mixedcase */\n function DOMAIN_SEPARATOR() public view returns (bytes32) {\n // As explained in EIP-2612, if the DOMAIN_SEPARATOR contains the\n // chainId and is defined at contract deployment instead of\n // reconstructed for every signature, there is a risk of possible replay\n // attacks between chains in the event of a future chain split.\n // To address this issue, we check the cached chain ID against the\n // current one and in case they are different, we build domain separator\n // from scratch.\n if (block.chainid == cachedChainId) {\n return cachedDomainSeparator;\n } else {\n return buildDomainSeparator();\n }\n }\n\n function _increaseBalance(address recipient, uint256 amount) internal {\n require(\n recipient != address(this),\n \"Can not increase balance for Bank\"\n );\n balanceOf[recipient] += amount;\n emit BalanceIncreased(recipient, amount);\n }\n\n function _transferBalance(\n address spender,\n address recipient,\n uint256 amount\n ) private {\n require(\n recipient != address(0),\n \"Can not transfer to the zero address\"\n );\n require(\n recipient != address(this),\n \"Can not transfer to the Bank address\"\n );\n\n uint256 spenderBalance = balanceOf[spender];\n require(spenderBalance >= amount, \"Transfer amount exceeds balance\");\n unchecked {\n balanceOf[spender] = spenderBalance - amount;\n }\n balanceOf[recipient] += amount;\n emit BalanceTransferred(spender, recipient, amount);\n }\n\n function _approveBalance(\n address owner,\n address spender,\n uint256 amount\n ) private {\n require(spender != address(0), \"Can not approve to the zero address\");\n allowance[owner][spender] = amount;\n emit BalanceApproved(owner, spender, amount);\n }\n\n function buildDomainSeparator() private view returns (bytes32) {\n return\n keccak256(\n abi.encode(\n keccak256(\n \"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"\n ),\n keccak256(bytes(\"TBTC Bank\")),\n keccak256(bytes(\"1\")),\n block.chainid,\n address(this)\n )\n );\n }\n}\n"
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/b1d2a7556f526b76c5c3ec733ff56387.json"
3
+ "buildInfo": "../../../build-info/a3a46a4061ea66973be43b2f92dc2912.json"
4
4
  }