@keep-network/tbtc-v2 1.6.0-dev.5 → 1.6.0-dev.7

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 (160) hide show
  1. package/artifacts/BLS.json +6 -6
  2. package/artifacts/Bank.json +17 -17
  3. package/artifacts/BeaconAuthorization.json +6 -6
  4. package/artifacts/BeaconDkg.json +6 -6
  5. package/artifacts/BeaconDkgValidator.json +9 -9
  6. package/artifacts/BeaconInactivity.json +6 -6
  7. package/artifacts/BeaconSortitionPool.json +16 -16
  8. package/artifacts/Bridge.json +32 -32
  9. package/artifacts/BridgeGovernance.json +14 -14
  10. package/artifacts/BridgeGovernanceParameters.json +7 -7
  11. package/artifacts/Deposit.json +7 -7
  12. package/artifacts/DepositSweep.json +7 -7
  13. package/artifacts/DonationVault.json +11 -11
  14. package/artifacts/EcdsaDkgValidator.json +7 -7
  15. package/artifacts/EcdsaInactivity.json +6 -6
  16. package/artifacts/EcdsaSortitionPool.json +16 -16
  17. package/artifacts/Fraud.json +7 -7
  18. package/artifacts/LightRelay.json +28 -28
  19. package/artifacts/LightRelayMaintainerProxy.json +24 -24
  20. package/artifacts/MaintainerProxy.json +37 -37
  21. package/artifacts/MovingFunds.json +7 -7
  22. package/artifacts/NuCypherToken.json +11 -11
  23. package/artifacts/RandomBeacon.json +23 -23
  24. package/artifacts/RandomBeaconChaosnet.json +11 -11
  25. package/artifacts/RandomBeaconGovernance.json +12 -12
  26. package/artifacts/Redemption.json +7 -7
  27. package/artifacts/ReimbursementPool.json +11 -11
  28. package/artifacts/T.json +11 -11
  29. package/artifacts/TBTC.json +19 -19
  30. package/artifacts/TBTCToken.json +19 -19
  31. package/artifacts/TBTCVault.json +36 -36
  32. package/artifacts/TokenStaking.json +156 -308
  33. package/artifacts/TokenholderGovernor.json +47 -47
  34. package/artifacts/TokenholderTimelock.json +36 -36
  35. package/artifacts/VendingMachine.json +22 -22
  36. package/artifacts/VendingMachineNuCypher.json +9 -9
  37. package/artifacts/VendingMachineV2.json +15 -15
  38. package/artifacts/VendingMachineV3.json +15 -15
  39. package/artifacts/WalletProposalValidator.json +437 -0
  40. package/artifacts/WalletRegistry.json +31 -31
  41. package/artifacts/WalletRegistryGovernance.json +56 -56
  42. package/artifacts/Wallets.json +7 -7
  43. package/artifacts/solcInputs/{d2d7e276da75d7184610fe11a4a103b7.json → b4e1c442421284b256fcad9f86102ecc.json} +4 -4
  44. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
  45. package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
  46. package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.dbg.json +1 -1
  47. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
  48. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
  49. package/build/contracts/bridge/BridgeGovernanceParameters.sol/BridgeGovernanceParameters.dbg.json +1 -1
  50. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +1 -1
  51. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +1 -1
  52. package/build/contracts/bridge/DepositSweep.sol/DepositSweep.dbg.json +1 -1
  53. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
  54. package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +1 -1
  55. package/build/contracts/bridge/Heartbeat.sol/Heartbeat.dbg.json +1 -1
  56. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +1 -1
  57. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +1 -1
  58. package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +1 -1
  59. package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +1 -1
  60. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  61. package/build/contracts/bridge/VendingMachineV2.sol/VendingMachineV2.dbg.json +1 -1
  62. package/build/contracts/bridge/VendingMachineV3.sol/VendingMachineV3.dbg.json +1 -1
  63. package/build/contracts/bridge/WalletProposalValidator.sol/WalletProposalValidator.dbg.json +4 -0
  64. package/build/contracts/bridge/WalletProposalValidator.sol/WalletProposalValidator.json +323 -0
  65. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
  66. package/build/contracts/l2/L2TBTC.sol/L2TBTC.dbg.json +1 -1
  67. package/build/contracts/l2/L2WormholeGateway.sol/IWormholeTokenBridge.dbg.json +1 -1
  68. package/build/contracts/l2/L2WormholeGateway.sol/L2WormholeGateway.dbg.json +1 -1
  69. package/build/contracts/maintainer/MaintainerProxy.sol/MaintainerProxy.dbg.json +1 -1
  70. package/build/contracts/relay/LightRelay.sol/ILightRelay.dbg.json +1 -1
  71. package/build/contracts/relay/LightRelay.sol/LightRelay.dbg.json +1 -1
  72. package/build/contracts/relay/LightRelay.sol/RelayUtils.dbg.json +1 -1
  73. package/build/contracts/relay/LightRelayMaintainerProxy.sol/LightRelayMaintainerProxy.dbg.json +1 -1
  74. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  75. package/build/contracts/vault/DonationVault.sol/DonationVault.dbg.json +1 -1
  76. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  77. package/build/contracts/vault/TBTCOptimisticMinting.sol/TBTCOptimisticMinting.dbg.json +1 -1
  78. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  79. package/contracts/bridge/{WalletCoordinator.sol → WalletProposalValidator.sol} +129 -447
  80. package/deploy/39_deploy_wallet_proposal_validator.ts +33 -0
  81. package/export/artifacts/@keep-network/ecdsa/contracts/EcdsaDkgValidator.sol/EcdsaDkgValidator.json +24 -24
  82. package/export/artifacts/@keep-network/ecdsa/contracts/WalletRegistry.sol/WalletRegistry.json +4999 -4814
  83. package/export/artifacts/@keep-network/ecdsa/contracts/libraries/EcdsaDkg.sol/EcdsaDkg.json +2 -2
  84. package/export/artifacts/@keep-network/ecdsa/contracts/libraries/EcdsaInactivity.sol/EcdsaInactivity.json +23 -23
  85. package/export/artifacts/@keep-network/random-beacon/contracts/ReimbursementPool.sol/ReimbursementPool.json +53 -53
  86. package/export/artifacts/@keep-network/sortition-pools/contracts/Chaosnet.sol/Chaosnet.json +21 -21
  87. package/export/artifacts/@keep-network/sortition-pools/contracts/Rewards.sol/Rewards.json +16 -16
  88. package/export/artifacts/@keep-network/sortition-pools/contracts/SortitionPool.sol/SortitionPool.json +206 -206
  89. package/export/artifacts/@keep-network/sortition-pools/contracts/SortitionTree.sol/SortitionTree.json +26 -26
  90. package/export/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json +46 -46
  91. package/export/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json +36 -36
  92. package/export/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json +78 -78
  93. package/export/artifacts/@openzeppelin/contracts/token/ERC721/ERC721.sol/ERC721.json +68 -68
  94. package/export/artifacts/@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol/ERC20Upgradeable.json +43 -43
  95. package/export/artifacts/@thesis/solidity-contracts/contracts/token/ERC20WithPermit.sol/ERC20WithPermit.json +79 -79
  96. package/export/artifacts/@thesis/solidity-contracts/contracts/token/MisfundRecovery.sol/MisfundRecovery.json +42 -42
  97. package/export/artifacts/contracts/bank/Bank.sol/Bank.json +77 -77
  98. package/export/artifacts/contracts/bridge/Bridge.sol/Bridge.json +320 -320
  99. package/export/artifacts/contracts/bridge/VendingMachine.sol/VendingMachine.json +108 -108
  100. package/export/artifacts/contracts/bridge/VendingMachineV2.sol/VendingMachineV2.json +63 -63
  101. package/export/artifacts/contracts/bridge/VendingMachineV3.sol/VendingMachineV3.json +65 -65
  102. package/export/artifacts/contracts/bridge/WalletProposalValidator.sol/WalletProposalValidator.json +25381 -0
  103. package/export/artifacts/contracts/l2/L2TBTC.sol/L2TBTC.json +194 -194
  104. package/export/artifacts/contracts/l2/L2WormholeGateway.sol/L2WormholeGateway.json +98 -98
  105. package/export/artifacts/contracts/maintainer/MaintainerProxy.sol/MaintainerProxy.json +149 -149
  106. package/export/artifacts/contracts/relay/LightRelay.sol/LightRelay.json +76 -76
  107. package/export/artifacts/contracts/relay/LightRelayMaintainerProxy.sol/LightRelayMaintainerProxy.json +62 -62
  108. package/export/artifacts/contracts/test/BankStub.sol/BankStub.json +79 -79
  109. package/export/artifacts/contracts/test/BridgeStub.sol/BridgeStub.json +358 -358
  110. package/export/artifacts/contracts/test/GoerliLightRelay.sol/GoerliLightRelay.json +78 -78
  111. package/export/artifacts/contracts/test/HeartbeatStub.sol/HeartbeatStub.json +4 -4
  112. package/export/artifacts/contracts/test/LightRelayStub.sol/LightRelayStub.json +78 -78
  113. package/export/artifacts/contracts/test/ReceiveApprovalStub.sol/ReceiveApprovalStub.json +7 -7
  114. package/export/artifacts/contracts/test/SepoliaLightRelay.sol/SepoliaLightRelay.json +78 -78
  115. package/export/artifacts/contracts/test/SystemTestRelay.sol/SystemTestRelay.json +14 -14
  116. package/export/artifacts/contracts/test/TestERC20.sol/TestERC20.json +85 -85
  117. package/export/artifacts/contracts/test/TestERC721.sol/TestERC721.json +78 -78
  118. package/export/artifacts/contracts/test/TestEcdsaLib.sol/TestEcdsaLib.json +4 -4
  119. package/export/artifacts/contracts/test/WormholeBridgeStub.sol/WormholeBridgeStub.json +37 -37
  120. package/export/artifacts/contracts/token/TBTC.sol/TBTC.json +104 -104
  121. package/export/artifacts/contracts/vault/DonationVault.sol/DonationVault.json +19 -19
  122. package/export/artifacts/contracts/vault/TBTCVault.sol/TBTCVault.json +184 -184
  123. package/export/deploy/39_deploy_wallet_proposal_validator.js +82 -0
  124. package/export/hardhat.config.js +0 -6
  125. package/export/typechain/factories/EcdsaAuthorization__factory.js +1 -1
  126. package/export/typechain/factories/IStaking__factory.js +24 -103
  127. package/export/typechain/factories/WalletProposalValidator__factory.js +402 -0
  128. package/export/typechain/factories/WalletRegistry__factory.js +1 -1
  129. package/export/typechain/index.js +3 -3
  130. package/package.json +2 -2
  131. package/artifacts/KeepRegistry.json +0 -99
  132. package/artifacts/KeepStake.json +0 -286
  133. package/artifacts/KeepToken.json +0 -711
  134. package/artifacts/KeepTokenStaking.json +0 -483
  135. package/artifacts/NuCypherStakingEscrow.json +0 -287
  136. package/artifacts/VendingMachineKeep.json +0 -400
  137. package/artifacts/WalletCoordinator.json +0 -1107
  138. package/build/contracts/bridge/WalletCoordinator.sol/WalletCoordinator.dbg.json +0 -4
  139. package/build/contracts/bridge/WalletCoordinator.sol/WalletCoordinator.json +0 -1042
  140. package/deploy/34_deploy_wallet_coordinator.ts +0 -43
  141. package/deploy/35_add_coordinator_address.ts +0 -20
  142. package/deploy/36_transfer_wallet_coordinator_ownership.ts +0 -19
  143. package/deploy/81_upgrade_wallet_coordinator_v2.ts +0 -99
  144. package/export/artifacts/contracts/bridge/WalletCoordinator.sol/WalletCoordinator.json +0 -33310
  145. package/export/deploy/34_deploy_wallet_coordinator.js +0 -115
  146. package/export/deploy/35_add_coordinator_address.js +0 -60
  147. package/export/deploy/36_transfer_wallet_coordinator_ownership.js +0 -60
  148. package/export/deploy/81_upgrade_wallet_coordinator_v2.js +0 -140
  149. package/export/typechain/factories/WalletCoordinator__factory.js +0 -1121
  150. /package/deploy/{37_deploy_light_relay_maintainer_proxy.ts → 34_deploy_light_relay_maintainer_proxy.ts} +0 -0
  151. /package/deploy/{38_authorize_maintainer_in_light_relay_maintainer_proxy.ts → 35_authorize_maintainer_in_light_relay_maintainer_proxy.ts} +0 -0
  152. /package/deploy/{39_transfer_light_relay_maintainer_proxy_ownership.ts → 36_transfer_light_relay_maintainer_proxy_ownership.ts} +0 -0
  153. /package/deploy/{40_authorize_light_relay_maintainer_proxy_in_reimbursement_pool.ts → 37_authorize_light_relay_maintainer_proxy_in_reimbursement_pool.ts} +0 -0
  154. /package/deploy/{41_authorize_light_relay_maintainer_proxy_in_light_relay.ts → 38_authorize_light_relay_maintainer_proxy_in_light_relay.ts} +0 -0
  155. /package/export/deploy/{37_deploy_light_relay_maintainer_proxy.js → 34_deploy_light_relay_maintainer_proxy.js} +0 -0
  156. /package/export/deploy/{38_authorize_maintainer_in_light_relay_maintainer_proxy.js → 35_authorize_maintainer_in_light_relay_maintainer_proxy.js} +0 -0
  157. /package/export/deploy/{39_transfer_light_relay_maintainer_proxy_ownership.js → 36_transfer_light_relay_maintainer_proxy_ownership.js} +0 -0
  158. /package/export/deploy/{40_authorize_light_relay_maintainer_proxy_in_reimbursement_pool.js → 37_authorize_light_relay_maintainer_proxy_in_reimbursement_pool.js} +0 -0
  159. /package/export/deploy/{41_authorize_light_relay_maintainer_proxy_in_light_relay.js → 38_authorize_light_relay_maintainer_proxy_in_light_relay.js} +0 -0
  160. /package/export/typechain/{WalletCoordinator.js → WalletProposalValidator.js} +0 -0
@@ -17,10 +17,6 @@ pragma solidity 0.8.17;
17
17
 
18
18
  import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";
19
19
  import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
20
- import "@keep-network/random-beacon/contracts/Reimbursable.sol";
21
- import "@keep-network/random-beacon/contracts/ReimbursementPool.sol";
22
-
23
- import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
24
20
 
25
21
  import "./BitcoinTx.sol";
26
22
  import "./Bridge.sol";
@@ -28,50 +24,14 @@ import "./Deposit.sol";
28
24
  import "./Redemption.sol";
29
25
  import "./Wallets.sol";
30
26
 
31
- /// @title Wallet coordinator.
32
- /// @notice The wallet coordinator contract aims to facilitate the coordination
33
- /// of the off-chain wallet members during complex multi-chain wallet
34
- /// operations like deposit sweeping, redemptions, or moving funds.
35
- /// Such processes involve various moving parts and many steps that each
36
- /// individual wallet member must do. Given the distributed nature of
37
- /// the off-chain wallet software, full off-chain implementation is
38
- /// challenging and prone to errors, especially byzantine faults.
39
- /// This contract provides a single and trusted on-chain coordination
40
- /// point thus taking the riskiest part out of the off-chain software.
41
- /// The off-chain wallet members can focus on the core tasks and do not
42
- /// bother about electing a trusted coordinator or aligning internal
43
- /// states using complex consensus algorithms.
44
- contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
27
+ /// @title Wallet proposal validator.
28
+ /// @notice This contract exposes several view functions allowing to validate
29
+ /// specific wallet action proposals. This contract is non-upgradeable
30
+ /// and does not have any write functions.
31
+ contract WalletProposalValidator {
45
32
  using BTCUtils for bytes;
46
33
  using BytesLib for bytes;
47
34
 
48
- /// @notice Represents wallet action:
49
- enum WalletAction {
50
- /// @dev The wallet does not perform any action.
51
- Idle,
52
- /// @dev The wallet is executing heartbeat.
53
- Heartbeat,
54
- /// @dev The wallet is handling a deposit sweep action.
55
- DepositSweep,
56
- /// @dev The wallet is handling a redemption action.
57
- Redemption,
58
- /// @dev The wallet is handling a moving funds action.
59
- MovingFunds,
60
- /// @dev The wallet is handling a moved funds sweep action.
61
- MovedFundsSweep
62
- }
63
-
64
- /// @notice Holds information about a wallet time lock.
65
- struct WalletLock {
66
- /// @notice A UNIX timestamp defining the moment until which the wallet
67
- /// is locked and cannot receive new proposals. The value of 0
68
- /// means the wallet is not locked and can receive a proposal
69
- /// at any time.
70
- uint32 expiresAt;
71
- /// @notice The wallet action being the cause of the lock.
72
- WalletAction cause;
73
- }
74
-
75
35
  /// @notice Helper structure representing a deposit sweep proposal.
76
36
  struct DepositSweepProposal {
77
37
  // 20-byte public key hash of the target wallet.
@@ -134,54 +94,38 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
134
94
  uint256 redemptionTxFee;
135
95
  }
136
96
 
137
- /// @notice Mapping that holds addresses allowed to submit proposals and
138
- /// request heartbeats.
139
- mapping(address => bool) public isCoordinator;
97
+ /// @notice Helper structure representing a moving funds proposal.
98
+ struct MovingFundsProposal {
99
+ // 20-byte public key hash of the source wallet.
100
+ bytes20 walletPubKeyHash;
101
+ // List of 20-byte public key hashes of target wallets.
102
+ bytes20[] targetWallets;
103
+ // Proposed BTC fee for the entire transaction.
104
+ uint256 movingFundsTxFee;
105
+ }
140
106
 
141
- /// @notice Mapping that holds wallet time locks. The key is a 20-byte
142
- /// wallet public key hash.
143
- mapping(bytes20 => WalletLock) public walletLock;
107
+ /// @notice Helper structure representing a heartbeat proposal.
108
+ struct HeartbeatProposal {
109
+ // 20-byte public key hash of the target wallet.
110
+ bytes20 walletPubKeyHash;
111
+ // Message to be signed as part of the heartbeat.
112
+ bytes message;
113
+ }
144
114
 
145
115
  /// @notice Handle to the Bridge contract.
146
- Bridge public bridge;
147
-
148
- /// @notice Determines the wallet heartbeat request validity time. In other
149
- /// words, this is the worst-case time for a wallet heartbeat
150
- /// during which the wallet is busy and canot take other actions.
151
- /// This is also the duration of the time lock applied to the wallet
152
- /// once a new heartbeat request is submitted.
153
- ///
154
- /// For example, if a deposit sweep proposal was submitted at
155
- /// 2 pm and heartbeatRequestValidity is 1 hour, the next request or
156
- /// proposal (of any type) can be submitted after 3 pm.
157
- uint32 public heartbeatRequestValidity;
158
-
159
- /// @notice Gas that is meant to balance the heartbeat request overall cost.
160
- /// Can be updated by the owner based on the current conditions.
161
- uint32 public heartbeatRequestGasOffset;
162
-
163
- /// @notice Determines the deposit sweep proposal validity time. In other
164
- /// words, this is the worst-case time for a deposit sweep during
165
- /// which the wallet is busy and cannot take another actions. This
166
- /// is also the duration of the time lock applied to the wallet
167
- /// once a new deposit sweep proposal is submitted.
168
- ///
169
- /// For example, if a deposit sweep proposal was submitted at
170
- /// 2 pm and depositSweepProposalValidity is 4 hours, the next
171
- /// proposal (of any type) can be submitted after 6 pm.
172
- uint32 public depositSweepProposalValidity;
116
+ Bridge public immutable bridge;
173
117
 
174
118
  /// @notice The minimum time that must elapse since the deposit reveal
175
119
  /// before a deposit becomes eligible for a deposit sweep.
176
120
  ///
177
- /// For example, if a deposit was revealed at 9 am and depositMinAge
121
+ /// For example, if a deposit was revealed at 9 am and DEPOSIT_MIN_AGE
178
122
  /// is 2 hours, the deposit is eligible for sweep after 11 am.
179
123
  ///
180
124
  /// @dev Forcing deposit minimum age ensures block finality for Ethereum.
181
125
  /// In the happy path case, i.e. where the deposit is revealed immediately
182
126
  /// after being broadcast on the Bitcoin network, the minimum age
183
127
  /// check also ensures block finality for Bitcoin.
184
- uint32 public depositMinAge;
128
+ uint32 public constant DEPOSIT_MIN_AGE = 2 hours;
185
129
 
186
130
  /// @notice Each deposit can be technically swept until it reaches its
187
131
  /// refund timestamp after which it can be taken back by the depositor.
@@ -196,39 +140,23 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
196
140
  /// the deposit becomes refundable.
197
141
  ///
198
142
  /// For example, if a deposit becomes refundable after 8 pm and
199
- /// depositRefundSafetyMargin is 6 hours, the deposit is valid for
143
+ /// DEPOSIT_REFUND_SAFETY_MARGIN is 6 hours, the deposit is valid
200
144
  /// for a sweep only before 2 pm.
201
- uint32 public depositRefundSafetyMargin;
145
+ uint32 public constant DEPOSIT_REFUND_SAFETY_MARGIN = 24 hours;
202
146
 
203
147
  /// @notice The maximum count of deposits that can be swept within a
204
148
  /// single sweep.
205
- uint16 public depositSweepMaxSize;
206
-
207
- /// @notice Gas that is meant to balance the deposit sweep proposal
208
- /// submission overall cost. Can be updated by the owner based on
209
- /// the current conditions.
210
- uint32 public depositSweepProposalSubmissionGasOffset;
211
-
212
- /// @notice Determines the redemption proposal validity time. In other
213
- /// words, this is the worst-case time for a redemption during
214
- /// which the wallet is busy and cannot take another actions. This
215
- /// is also the duration of the time lock applied to the wallet
216
- /// once a new redemption proposal is submitted.
217
- ///
218
- /// For example, if a redemption proposal was submitted at
219
- /// 2 pm and redemptionProposalValidity is 2 hours, the next
220
- /// proposal (of any type) can be submitted after 4 pm.
221
- uint32 public redemptionProposalValidity;
149
+ uint16 public constant DEPOSIT_SWEEP_MAX_SIZE = 20;
222
150
 
223
151
  /// @notice The minimum time that must elapse since the redemption request
224
152
  /// creation before a request becomes eligible for a processing.
225
153
  ///
226
154
  /// For example, if a request was created at 9 am and
227
- /// redemptionRequestMinAge is 2 hours, the request is eligible for
228
- /// processing after 11 am.
155
+ /// REDEMPTION_REQUEST_MIN_AGE is 2 hours, the request is
156
+ /// eligible for processing after 11 am.
229
157
  ///
230
158
  /// @dev Forcing request minimum age ensures block finality for Ethereum.
231
- uint32 public redemptionRequestMinAge;
159
+ uint32 public constant REDEMPTION_REQUEST_MIN_AGE = 600; // 10 minutes or ~50 blocks.
232
160
 
233
161
  /// @notice Each redemption request can be technically handled until it
234
162
  /// reaches its timeout timestamp after which it can be reported
@@ -245,276 +173,16 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
245
173
  /// point after which the request can be reported as timed out.
246
174
  ///
247
175
  /// For example, if a request times out after 8 pm and
248
- /// redemptionRequestTimeoutSafetyMargin is 2 hours, the request is
249
- /// valid for processing only before 6 pm.
250
- uint32 public redemptionRequestTimeoutSafetyMargin;
176
+ /// REDEMPTION_REQUEST_TIMEOUT_SAFETY_MARGIN is 2 hours, the
177
+ /// request is valid for processing only before 6 pm.
178
+ uint32 public constant REDEMPTION_REQUEST_TIMEOUT_SAFETY_MARGIN = 2 hours;
251
179
 
252
180
  /// @notice The maximum count of redemption requests that can be processed
253
181
  /// within a single redemption.
254
- uint16 public redemptionMaxSize;
255
-
256
- /// @notice Gas that is meant to balance the redemption proposal
257
- /// submission overall cost. Can be updated by the owner based on
258
- /// the current conditions.
259
- uint32 public redemptionProposalSubmissionGasOffset;
260
-
261
- event CoordinatorAdded(address indexed coordinator);
262
-
263
- event CoordinatorRemoved(address indexed coordinator);
264
-
265
- event WalletManuallyUnlocked(bytes20 indexed walletPubKeyHash);
266
-
267
- event HeartbeatRequestParametersUpdated(
268
- uint32 heartbeatRequestValidity,
269
- uint32 heartbeatRequestGasOffset
270
- );
271
-
272
- event HeartbeatRequestSubmitted(
273
- bytes20 walletPubKeyHash,
274
- bytes message,
275
- address indexed coordinator
276
- );
277
-
278
- event DepositSweepProposalParametersUpdated(
279
- uint32 depositSweepProposalValidity,
280
- uint32 depositMinAge,
281
- uint32 depositRefundSafetyMargin,
282
- uint16 depositSweepMaxSize,
283
- uint32 depositSweepProposalSubmissionGasOffset
284
- );
285
-
286
- event DepositSweepProposalSubmitted(
287
- DepositSweepProposal proposal,
288
- address indexed coordinator
289
- );
290
-
291
- event RedemptionProposalParametersUpdated(
292
- uint32 redemptionProposalValidity,
293
- uint32 redemptionRequestMinAge,
294
- uint32 redemptionRequestTimeoutSafetyMargin,
295
- uint16 redemptionMaxSize,
296
- uint32 redemptionProposalSubmissionGasOffset
297
- );
298
-
299
- event RedemptionProposalSubmitted(
300
- RedemptionProposal proposal,
301
- address indexed coordinator
302
- );
303
-
304
- modifier onlyCoordinator() {
305
- require(isCoordinator[msg.sender], "Caller is not a coordinator");
306
- _;
307
- }
308
-
309
- modifier onlyAfterWalletLock(bytes20 walletPubKeyHash) {
310
- require(
311
- /* solhint-disable-next-line not-rely-on-time */
312
- block.timestamp > walletLock[walletPubKeyHash].expiresAt,
313
- "Wallet locked"
314
- );
315
- _;
316
- }
317
-
318
- modifier onlyReimbursableAdmin() override {
319
- require(owner() == msg.sender, "Caller is not the owner");
320
- _;
321
- }
322
-
323
- function initialize(Bridge _bridge) external initializer {
324
- __Ownable_init();
182
+ uint16 public constant REDEMPTION_MAX_SIZE = 20;
325
183
 
184
+ constructor(Bridge _bridge) {
326
185
  bridge = _bridge;
327
- // Pre-fetch addresses to save gas later.
328
- (, , , reimbursementPool) = _bridge.contractReferences();
329
-
330
- heartbeatRequestValidity = 1 hours;
331
- heartbeatRequestGasOffset = 10_000;
332
-
333
- depositSweepProposalValidity = 4 hours;
334
- depositMinAge = 2 hours;
335
- depositRefundSafetyMargin = 24 hours;
336
- depositSweepMaxSize = 5;
337
- depositSweepProposalSubmissionGasOffset = 20_000; // optimized for 10 inputs
338
-
339
- redemptionProposalValidity = 2 hours;
340
- redemptionRequestMinAge = 600; // 10 minutes or ~50 blocks.
341
- redemptionRequestTimeoutSafetyMargin = 2 hours;
342
- redemptionMaxSize = 20;
343
- redemptionProposalSubmissionGasOffset = 20_000;
344
- }
345
-
346
- /// @notice Adds the given address to the set of coordinator addresses.
347
- /// @param coordinator Address of the new coordinator.
348
- /// @dev Requirements:
349
- /// - The caller must be the owner,
350
- /// - The `coordinator` must not be an existing coordinator.
351
- function addCoordinator(address coordinator) external onlyOwner {
352
- require(
353
- !isCoordinator[coordinator],
354
- "This address is already a coordinator"
355
- );
356
- isCoordinator[coordinator] = true;
357
- emit CoordinatorAdded(coordinator);
358
- }
359
-
360
- /// @notice Removes the given address from the set of coordinator addresses.
361
- /// @param coordinator Address of the existing coordinator.
362
- /// @dev Requirements:
363
- /// - The caller must be the owner,
364
- /// - The `coordinator` must be an existing coordinator.
365
- function removeCoordinator(address coordinator) external onlyOwner {
366
- require(
367
- isCoordinator[coordinator],
368
- "This address is not a coordinator"
369
- );
370
- delete isCoordinator[coordinator];
371
- emit CoordinatorRemoved(coordinator);
372
- }
373
-
374
- /// @notice Allows to unlock the given wallet before their time lock expires.
375
- /// This function should be used in exceptional cases where
376
- /// something went wrong and there is a need to unlock the wallet
377
- /// without waiting.
378
- /// @param walletPubKeyHash 20-byte public key hash of the wallet
379
- /// @dev Requirements:
380
- /// - The caller must be the owner.
381
- function unlockWallet(bytes20 walletPubKeyHash) external onlyOwner {
382
- // Just in case, allow the owner to unlock the wallet earlier.
383
- walletLock[walletPubKeyHash] = WalletLock(0, WalletAction.Idle);
384
- emit WalletManuallyUnlocked(walletPubKeyHash);
385
- }
386
-
387
- /// @notice Updates parameters related to heartbeat request.
388
- /// @param _heartbeatRequestValidity The new value of `heartbeatRequestValidity`.
389
- /// @param _heartbeatRequestGasOffset The new value of `heartbeatRequestGasOffset`.
390
- /// @dev Requirements:
391
- /// - The caller must be the owner.
392
- function updateHeartbeatRequestParameters(
393
- uint32 _heartbeatRequestValidity,
394
- uint32 _heartbeatRequestGasOffset
395
- ) external onlyOwner {
396
- heartbeatRequestValidity = _heartbeatRequestValidity;
397
- heartbeatRequestGasOffset = _heartbeatRequestGasOffset;
398
- emit HeartbeatRequestParametersUpdated(
399
- _heartbeatRequestValidity,
400
- _heartbeatRequestGasOffset
401
- );
402
- }
403
-
404
- /// @notice Updates parameters related to deposit sweep proposal.
405
- /// @param _depositSweepProposalValidity The new value of `depositSweepProposalValidity`.
406
- /// @param _depositMinAge The new value of `depositMinAge`.
407
- /// @param _depositRefundSafetyMargin The new value of `depositRefundSafetyMargin`.
408
- /// @param _depositSweepMaxSize The new value of `depositSweepMaxSize`.
409
- /// @dev Requirements:
410
- /// - The caller must be the owner.
411
- function updateDepositSweepProposalParameters(
412
- uint32 _depositSweepProposalValidity,
413
- uint32 _depositMinAge,
414
- uint32 _depositRefundSafetyMargin,
415
- uint16 _depositSweepMaxSize,
416
- uint32 _depositSweepProposalSubmissionGasOffset
417
- ) external onlyOwner {
418
- depositSweepProposalValidity = _depositSweepProposalValidity;
419
- depositMinAge = _depositMinAge;
420
- depositRefundSafetyMargin = _depositRefundSafetyMargin;
421
- depositSweepMaxSize = _depositSweepMaxSize;
422
- depositSweepProposalSubmissionGasOffset = _depositSweepProposalSubmissionGasOffset;
423
-
424
- emit DepositSweepProposalParametersUpdated(
425
- _depositSweepProposalValidity,
426
- _depositMinAge,
427
- _depositRefundSafetyMargin,
428
- _depositSweepMaxSize,
429
- _depositSweepProposalSubmissionGasOffset
430
- );
431
- }
432
-
433
- /// @notice Submits a heartbeat request from the wallet. Locks the wallet
434
- /// for a specific time, equal to the request validity period.
435
- /// This function validates the proposed heartbeat messge to see
436
- /// if it matches the heartbeat format expected by the Bridge.
437
- /// @param walletPubKeyHash 20-byte public key hash of the wallet that is
438
- /// supposed to execute the heartbeat.
439
- /// @param message The proposed heartbeat message for the wallet to sign.
440
- /// @dev Requirements:
441
- /// - The caller is a coordinator,
442
- /// - The wallet is not time-locked,
443
- /// - The message to sign is a valid heartbeat message.
444
- function requestHeartbeat(bytes20 walletPubKeyHash, bytes calldata message)
445
- public
446
- onlyCoordinator
447
- onlyAfterWalletLock(walletPubKeyHash)
448
- {
449
- require(
450
- Heartbeat.isValidHeartbeatMessage(message),
451
- "Not a valid heartbeat message"
452
- );
453
-
454
- walletLock[walletPubKeyHash] = WalletLock(
455
- /* solhint-disable-next-line not-rely-on-time */
456
- uint32(block.timestamp) + heartbeatRequestValidity,
457
- WalletAction.Heartbeat
458
- );
459
-
460
- emit HeartbeatRequestSubmitted(walletPubKeyHash, message, msg.sender);
461
- }
462
-
463
- /// @notice Wraps `requestHeartbeat` call and reimburses the caller's
464
- /// transaction cost.
465
- /// @dev See `requestHeartbeat` function documentation.
466
- function requestHeartbeatWithReimbursement(
467
- bytes20 walletPubKeyHash,
468
- bytes calldata message
469
- ) external {
470
- uint256 gasStart = gasleft();
471
-
472
- requestHeartbeat(walletPubKeyHash, message);
473
-
474
- reimbursementPool.refund(
475
- (gasStart - gasleft()) + heartbeatRequestGasOffset,
476
- msg.sender
477
- );
478
- }
479
-
480
- /// @notice Submits a deposit sweep proposal. Locks the target wallet
481
- /// for a specific time, equal to the proposal validity period.
482
- /// This function does not store the proposal in the state but
483
- /// just emits an event that serves as a guiding light for wallet
484
- /// off-chain members. Wallet members are supposed to validate
485
- /// the proposal on their own, before taking any action.
486
- /// @param proposal The deposit sweep proposal
487
- /// @dev Requirements:
488
- /// - The caller is a coordinator,
489
- /// - The wallet is not time-locked.
490
- function submitDepositSweepProposal(DepositSweepProposal calldata proposal)
491
- public
492
- onlyCoordinator
493
- onlyAfterWalletLock(proposal.walletPubKeyHash)
494
- {
495
- walletLock[proposal.walletPubKeyHash] = WalletLock(
496
- /* solhint-disable-next-line not-rely-on-time */
497
- uint32(block.timestamp) + depositSweepProposalValidity,
498
- WalletAction.DepositSweep
499
- );
500
-
501
- emit DepositSweepProposalSubmitted(proposal, msg.sender);
502
- }
503
-
504
- /// @notice Wraps `submitDepositSweepProposal` call and reimburses the
505
- /// caller's transaction cost.
506
- /// @dev See `submitDepositSweepProposal` function documentation.
507
- function submitDepositSweepProposalWithReimbursement(
508
- DepositSweepProposal calldata proposal
509
- ) external {
510
- uint256 gasStart = gasleft();
511
-
512
- submitDepositSweepProposal(proposal);
513
-
514
- reimbursementPool.refund(
515
- (gasStart - gasleft()) + depositSweepProposalSubmissionGasOffset,
516
- msg.sender
517
- );
518
186
  }
519
187
 
520
188
  /// @notice View function encapsulating the main rules of a valid deposit
@@ -535,7 +203,7 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
535
203
  /// @dev Requirements:
536
204
  /// - The target wallet must be in the Live state,
537
205
  /// - The number of deposits included in the sweep must be in
538
- /// the range [1, `depositSweepMaxSize`],
206
+ /// the range [1, `DEPOSIT_SWEEP_MAX_SIZE`],
539
207
  /// - The length of `depositsExtraInfo` array must be equal to the
540
208
  /// length of `proposal.depositsKeys`, i.e. each deposit must
541
209
  /// have exactly one set of corresponding extra data,
@@ -543,7 +211,7 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
543
211
  /// - The proposed maximum per-deposit sweep tx fee must be lesser than
544
212
  /// or equal the maximum fee allowed by the Bridge (`Bridge.depositTxMaxFee`),
545
213
  /// - Each deposit must be revealed to the Bridge,
546
- /// - Each deposit must be old enough, i.e. at least `depositMinAge`
214
+ /// - Each deposit must be old enough, i.e. at least `DEPOSIT_MIN_AGE
547
215
  /// elapsed since their reveal time,
548
216
  /// - Each deposit must not be swept yet,
549
217
  /// - Each deposit must have valid extra data (see `validateDepositExtraInfo`),
@@ -568,7 +236,7 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
568
236
  require(proposal.depositsKeys.length > 0, "Sweep below the min size");
569
237
 
570
238
  require(
571
- proposal.depositsKeys.length <= depositSweepMaxSize,
239
+ proposal.depositsKeys.length <= DEPOSIT_SWEEP_MAX_SIZE,
572
240
  "Sweep exceeds the max size"
573
241
  );
574
242
 
@@ -607,7 +275,7 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
607
275
 
608
276
  require(
609
277
  /* solhint-disable-next-line not-rely-on-time */
610
- block.timestamp > depositRequest.revealedAt + depositMinAge,
278
+ block.timestamp > depositRequest.revealedAt + DEPOSIT_MIN_AGE,
611
279
  "Deposit min age not achieved yet"
612
280
  );
613
281
 
@@ -625,7 +293,7 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
625
293
  require(
626
294
  /* solhint-disable-next-line not-rely-on-time */
627
295
  block.timestamp <
628
- depositRefundableTimestamp - depositRefundSafetyMargin,
296
+ depositRefundableTimestamp - DEPOSIT_REFUND_SAFETY_MARGIN,
629
297
  "Deposit refund safety margin is not preserved"
630
298
  );
631
299
 
@@ -787,78 +455,6 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
787
455
  revert("Extra info funding output script does not match");
788
456
  }
789
457
 
790
- /// @notice Updates parameters related to redemption proposal.
791
- /// @param _redemptionProposalValidity The new value of `redemptionProposalValidity`.
792
- /// @param _redemptionRequestMinAge The new value of `redemptionRequestMinAge`.
793
- /// @param _redemptionRequestTimeoutSafetyMargin The new value of
794
- /// `redemptionRequestTimeoutSafetyMargin`.
795
- /// @param _redemptionMaxSize The new value of `redemptionMaxSize`.
796
- /// @param _redemptionProposalSubmissionGasOffset The new value of
797
- /// `redemptionProposalSubmissionGasOffset`.
798
- /// @dev Requirements:
799
- /// - The caller must be the owner.
800
- function updateRedemptionProposalParameters(
801
- uint32 _redemptionProposalValidity,
802
- uint32 _redemptionRequestMinAge,
803
- uint32 _redemptionRequestTimeoutSafetyMargin,
804
- uint16 _redemptionMaxSize,
805
- uint32 _redemptionProposalSubmissionGasOffset
806
- ) external onlyOwner {
807
- redemptionProposalValidity = _redemptionProposalValidity;
808
- redemptionRequestMinAge = _redemptionRequestMinAge;
809
- redemptionRequestTimeoutSafetyMargin = _redemptionRequestTimeoutSafetyMargin;
810
- redemptionMaxSize = _redemptionMaxSize;
811
- redemptionProposalSubmissionGasOffset = _redemptionProposalSubmissionGasOffset;
812
-
813
- emit RedemptionProposalParametersUpdated(
814
- _redemptionProposalValidity,
815
- _redemptionRequestMinAge,
816
- _redemptionRequestTimeoutSafetyMargin,
817
- _redemptionMaxSize,
818
- _redemptionProposalSubmissionGasOffset
819
- );
820
- }
821
-
822
- /// @notice Submits a redemption proposal. Locks the target wallet
823
- /// for a specific time, equal to the proposal validity period.
824
- /// This function does not store the proposal in the state but
825
- /// just emits an event that serves as a guiding light for wallet
826
- /// off-chain members. Wallet members are supposed to validate
827
- /// the proposal on their own, before taking any action.
828
- /// @param proposal The redemption proposal
829
- /// @dev Requirements:
830
- /// - The caller is a coordinator,
831
- /// - The wallet is not time-locked.
832
- function submitRedemptionProposal(RedemptionProposal calldata proposal)
833
- public
834
- onlyCoordinator
835
- onlyAfterWalletLock(proposal.walletPubKeyHash)
836
- {
837
- walletLock[proposal.walletPubKeyHash] = WalletLock(
838
- /* solhint-disable-next-line not-rely-on-time */
839
- uint32(block.timestamp) + redemptionProposalValidity,
840
- WalletAction.Redemption
841
- );
842
-
843
- emit RedemptionProposalSubmitted(proposal, msg.sender);
844
- }
845
-
846
- /// @notice Wraps `submitRedemptionProposal` call and reimburses the
847
- /// caller's transaction cost.
848
- /// @dev See `submitRedemptionProposal` function documentation.
849
- function submitRedemptionProposalWithReimbursement(
850
- RedemptionProposal calldata proposal
851
- ) external {
852
- uint256 gasStart = gasleft();
853
-
854
- submitRedemptionProposal(proposal);
855
-
856
- reimbursementPool.refund(
857
- (gasStart - gasleft()) + redemptionProposalSubmissionGasOffset,
858
- msg.sender
859
- );
860
- }
861
-
862
458
  /// @notice View function encapsulating the main rules of a valid redemption
863
459
  /// proposal. This function is meant to facilitate the off-chain
864
460
  /// validation of the incoming proposals. Thanks to it, most
@@ -897,7 +493,7 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
897
493
  require(requestsCount > 0, "Redemption below the min size");
898
494
 
899
495
  require(
900
- requestsCount <= redemptionMaxSize,
496
+ requestsCount <= REDEMPTION_MAX_SIZE,
901
497
  "Redemption exceeds the max size"
902
498
  );
903
499
 
@@ -960,7 +556,7 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
960
556
  require(
961
557
  /* solhint-disable-next-line not-rely-on-time */
962
558
  block.timestamp >
963
- redemptionRequest.requestedAt + redemptionRequestMinAge,
559
+ redemptionRequest.requestedAt + REDEMPTION_REQUEST_MIN_AGE,
964
560
  "Redemption request min age not achieved yet"
965
561
  );
966
562
 
@@ -971,7 +567,7 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
971
567
  require(
972
568
  /* solhint-disable-next-line not-rely-on-time */
973
569
  block.timestamp <
974
- requestTimeout - redemptionRequestTimeoutSafetyMargin,
570
+ requestTimeout - REDEMPTION_REQUEST_TIMEOUT_SAFETY_MARGIN,
975
571
  "Redemption request timeout safety margin is not preserved"
976
572
  );
977
573
 
@@ -1000,4 +596,90 @@ contract WalletCoordinator is OwnableUpgradeable, Reimbursable {
1000
596
 
1001
597
  return true;
1002
598
  }
599
+
600
+ /// @notice View function encapsulating the main rules of a valid moving
601
+ /// funds proposal. This function is meant to facilitate the
602
+ /// off-chain validation of the incoming proposals. Thanks to it,
603
+ /// most of the work can be done using a single readonly contract
604
+ /// call.
605
+ /// @param proposal The moving funds proposal to validate.
606
+ /// @return True if the proposal is valid. Reverts otherwise.
607
+ /// @dev Notice that this function is meant to be invoked after the moving
608
+ /// funds commitment has already been submitted. This function skips
609
+ /// some checks related to the moving funds procedure as they were
610
+ /// already checked on the commitment submission.
611
+ /// Requirements:
612
+ /// - The source wallet must be in the MovingFunds state,
613
+ /// - The target wallets commitment must be submitted,
614
+ /// - The target wallets commitment hash must match the target wallets
615
+ /// from the proposal,
616
+ /// - The proposed moving funds transaction fee must be greater than
617
+ /// zero,
618
+ /// - The proposed moving funds transaction fee must not exceed the
619
+ /// maximum total fee allowed for moving funds.
620
+ function validateMovingFundsProposal(MovingFundsProposal calldata proposal)
621
+ external
622
+ view
623
+ returns (bool)
624
+ {
625
+ Wallets.Wallet memory sourceWallet = bridge.wallets(
626
+ proposal.walletPubKeyHash
627
+ );
628
+
629
+ // Make sure the source wallet is in MovingFunds state.
630
+ require(
631
+ sourceWallet.state == Wallets.WalletState.MovingFunds,
632
+ "Source wallet is not in MovingFunds state"
633
+ );
634
+
635
+ // Make sure the moving funds commitment has been submitted and
636
+ // the commitment hash matches the target wallets from the proposal.
637
+ require(
638
+ sourceWallet.movingFundsTargetWalletsCommitmentHash != bytes32(0),
639
+ "Target wallets commitment is not submitted"
640
+ );
641
+
642
+ require(
643
+ sourceWallet.movingFundsTargetWalletsCommitmentHash ==
644
+ keccak256(abi.encodePacked(proposal.targetWallets)),
645
+ "Target wallets do not match target wallets commitment hash"
646
+ );
647
+
648
+ // Make sure the proposed fee is valid.
649
+ (uint64 movingFundsTxMaxTotalFee, , , , , , , , , , ) = bridge
650
+ .movingFundsParameters();
651
+
652
+ require(
653
+ proposal.movingFundsTxFee > 0,
654
+ "Proposed transaction fee cannot be zero"
655
+ );
656
+
657
+ require(
658
+ proposal.movingFundsTxFee <= movingFundsTxMaxTotalFee,
659
+ "Proposed transaction fee is too high"
660
+ );
661
+
662
+ return true;
663
+ }
664
+
665
+ /// @notice View function encapsulating the main rules of a valid heartbeat
666
+ /// proposal. This function is meant to facilitate the off-chain
667
+ /// validation of the incoming proposals. Thanks to it, most
668
+ /// of the work can be done using a single readonly contract call.
669
+ /// @param proposal The heartbeat proposal to validate.
670
+ /// @return True if the proposal is valid. Reverts otherwise.
671
+ /// @dev Requirements:
672
+ /// - The message to sign is a valid heartbeat message.
673
+ function validateHeartbeatProposal(HeartbeatProposal calldata proposal)
674
+ external
675
+ view
676
+ returns (bool)
677
+ {
678
+ require(
679
+ Heartbeat.isValidHeartbeatMessage(proposal.message),
680
+ "Not a valid heartbeat message"
681
+ );
682
+
683
+ return true;
684
+ }
1003
685
  }