@keep-network/tbtc-v2 0.1.1-dev.43 → 0.1.1-dev.44

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 (75) hide show
  1. package/artifacts/Bank.json +742 -0
  2. package/artifacts/Bridge.json +2914 -0
  3. package/artifacts/Deposit.json +117 -0
  4. package/artifacts/EcdsaDkgValidator.json +532 -0
  5. package/artifacts/EcdsaInactivity.json +156 -0
  6. package/artifacts/Fraud.json +153 -0
  7. package/artifacts/KeepRegistry.json +99 -0
  8. package/artifacts/KeepStake.json +286 -0
  9. package/artifacts/KeepToken.json +711 -0
  10. package/artifacts/KeepTokenStaking.json +483 -0
  11. package/artifacts/MovingFunds.json +137 -0
  12. package/artifacts/NuCypherStakingEscrow.json +256 -0
  13. package/artifacts/NuCypherToken.json +711 -0
  14. package/artifacts/RandomBeaconStub.json +141 -0
  15. package/artifacts/Redemption.json +161 -0
  16. package/artifacts/ReimbursementPool.json +509 -0
  17. package/artifacts/Relay.json +123 -0
  18. package/artifacts/SortitionPool.json +944 -0
  19. package/artifacts/Sweep.json +76 -0
  20. package/artifacts/T.json +1148 -0
  21. package/artifacts/TBTC.json +21 -21
  22. package/artifacts/TBTCToken.json +21 -21
  23. package/artifacts/TokenStaking.json +2288 -0
  24. package/artifacts/TokenholderGovernor.json +1795 -0
  25. package/artifacts/TokenholderTimelock.json +1058 -0
  26. package/artifacts/VendingMachine.json +24 -24
  27. package/artifacts/VendingMachineKeep.json +400 -0
  28. package/artifacts/VendingMachineNuCypher.json +400 -0
  29. package/artifacts/WalletRegistry.json +2709 -0
  30. package/artifacts/WalletRegistryGovernance.json +2364 -0
  31. package/artifacts/Wallets.json +186 -0
  32. package/artifacts/solcInputs/{f2c15d3cf1bd9566483f595c5ed30ccc.json → bbe44823ec28554a9429cce5cafee035.json} +19 -19
  33. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
  34. package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
  35. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
  36. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +2 -2
  37. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
  38. package/build/contracts/bridge/Bridge.sol/Bridge.json +409 -31
  39. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +1 -1
  40. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +114 -2
  41. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +1 -1
  42. package/build/contracts/bridge/Deposit.sol/Deposit.json +2 -2
  43. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
  44. package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +1 -1
  45. package/build/contracts/bridge/Fraud.sol/Fraud.json +5 -5
  46. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +1 -1
  47. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +1 -1
  48. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +40 -2
  49. package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +1 -1
  50. package/build/contracts/bridge/Redemption.sol/OutboundTx.json +2 -2
  51. package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +1 -1
  52. package/build/contracts/bridge/Redemption.sol/Redemption.json +2 -2
  53. package/build/contracts/bridge/Sweep.sol/Sweep.dbg.json +1 -1
  54. package/build/contracts/bridge/Sweep.sol/Sweep.json +2 -2
  55. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  56. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
  57. package/build/contracts/bridge/Wallets.sol/Wallets.json +21 -2
  58. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  59. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  60. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  61. package/contracts/bridge/Bridge.sol +276 -18
  62. package/contracts/bridge/BridgeState.sol +238 -5
  63. package/contracts/bridge/Deposit.sol +2 -2
  64. package/contracts/bridge/Fraud.sol +47 -17
  65. package/contracts/bridge/MovingFunds.sol +181 -1
  66. package/contracts/bridge/Redemption.sol +7 -12
  67. package/contracts/bridge/Sweep.sol +0 -3
  68. package/contracts/bridge/Wallets.sol +53 -35
  69. package/deploy/00_resolve_relay.ts +28 -0
  70. package/deploy/04_deploy_bank.ts +25 -0
  71. package/deploy/05_deploy_bridge.ts +60 -0
  72. package/deploy/06_bank_update_bridge.ts +19 -0
  73. package/deploy/07_transfer_ownership.ts +17 -0
  74. package/export.json +14797 -459
  75. package/package.json +2 -2
@@ -21,6 +21,7 @@ import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
21
21
  import "./BitcoinTx.sol";
22
22
  import "./BridgeState.sol";
23
23
  import "./Redemption.sol";
24
+ import "./Wallets.sol";
24
25
 
25
26
  /// @title Moving Bridge wallet funds
26
27
  /// @notice The library handles the logic for moving Bitcoin between Bridge
@@ -38,11 +39,155 @@ library MovingFunds {
38
39
  using BTCUtils for bytes;
39
40
  using BytesLib for bytes;
40
41
 
42
+ event MovingFundsCommitmentSubmitted(
43
+ bytes20 walletPubKeyHash,
44
+ bytes20[] targetWallets,
45
+ address submitter
46
+ );
47
+
41
48
  event MovingFundsCompleted(
42
49
  bytes20 walletPubKeyHash,
43
50
  bytes32 movingFundsTxHash
44
51
  );
45
52
 
53
+ event MovingFundsTimedOut(bytes20 walletPubKeyHash);
54
+
55
+ /// @notice Submits the moving funds target wallets commitment.
56
+ /// Once all requirements are met, that function registers the
57
+ /// target wallets commitment and opens the way for moving funds
58
+ /// proof submission.
59
+ /// @param walletPubKeyHash 20-byte public key hash of the source wallet
60
+ /// @param walletMainUtxo Data of the source wallet's main UTXO, as
61
+ /// currently known on the Ethereum chain
62
+ /// @param walletMembersIDs Identifiers of the source wallet signing group
63
+ /// members
64
+ /// @param walletMemberIndex Position of the caller in the source wallet
65
+ /// signing group members list
66
+ /// @param targetWallets List of 20-byte public key hashes of the target
67
+ /// wallets that the source wallet commits to move the funds to
68
+ /// @dev Requirements:
69
+ /// - The source wallet must be in the MovingFunds state
70
+ /// - The source wallet must not have pending redemption requests
71
+ /// - The source wallet must not have submitted its commitment already
72
+ /// - The expression `keccak256(abi.encode(walletMembersIDs))` must
73
+ /// be exactly the same as the hash stored under `membersIdsHash`
74
+ /// for the given source wallet in the ECDSA registry. Those IDs are
75
+ /// not directly stored in the contract for gas efficiency purposes
76
+ /// but they can be read from appropriate `DkgResultSubmitted`
77
+ /// and `DkgResultApproved` events.
78
+ /// - The `walletMemberIndex` must be in range [1, walletMembersIDs.length]
79
+ /// - The caller must be the member of the source wallet signing group
80
+ /// at the position indicated by `walletMemberIndex` parameter
81
+ /// - The `walletMainUtxo` components must point to the recent main
82
+ /// UTXO of the source wallet, as currently known on the Ethereum
83
+ /// chain.
84
+ /// - Source wallet BTC balance must be greater than zero
85
+ /// - At least one Live wallet must exist in the system
86
+ /// - Submitted target wallets count must match the expected count
87
+ /// `N = min(liveWalletsCount, ceil(walletBtcBalance / walletMaxBtcTransfer))`
88
+ /// where `N > 0`
89
+ /// - Each target wallet must be not equal to the source wallet
90
+ /// - Each target wallet must follow the expected order i.e. all
91
+ /// target wallets 20-byte public key hashes represented as numbers
92
+ /// must form a strictly increasing sequence without duplicates.
93
+ /// - Each target wallet must be in Live state
94
+ function submitMovingFundsCommitment(
95
+ BridgeState.Storage storage self,
96
+ bytes20 walletPubKeyHash,
97
+ BitcoinTx.UTXO calldata walletMainUtxo,
98
+ uint32[] calldata walletMembersIDs,
99
+ uint256 walletMemberIndex,
100
+ bytes20[] calldata targetWallets
101
+ ) external {
102
+ Wallets.Wallet storage wallet = self.registeredWallets[
103
+ walletPubKeyHash
104
+ ];
105
+
106
+ require(
107
+ wallet.state == Wallets.WalletState.MovingFunds,
108
+ "Source wallet must be in MovingFunds state"
109
+ );
110
+
111
+ require(
112
+ wallet.pendingRedemptionsValue == 0,
113
+ "Source wallet must handle all pending redemptions first"
114
+ );
115
+
116
+ require(
117
+ wallet.movingFundsTargetWalletsCommitmentHash == bytes32(0),
118
+ "Target wallets commitment already submitted"
119
+ );
120
+
121
+ require(
122
+ self.ecdsaWalletRegistry.isWalletMember(
123
+ wallet.ecdsaWalletID,
124
+ walletMembersIDs,
125
+ msg.sender,
126
+ walletMemberIndex
127
+ ),
128
+ "Caller is not a member of the source wallet"
129
+ );
130
+
131
+ uint64 walletBtcBalance = self.getWalletBtcBalance(
132
+ walletPubKeyHash,
133
+ walletMainUtxo
134
+ );
135
+
136
+ require(walletBtcBalance > 0, "Wallet BTC balance is zero");
137
+
138
+ uint256 expectedTargetWalletsCount = Math.min(
139
+ self.liveWalletsCount,
140
+ Math.ceilDiv(walletBtcBalance, self.walletMaxBtcTransfer)
141
+ );
142
+
143
+ // This requirement fails only when `liveWalletsCount` is zero. In
144
+ // that case, the system cannot accept the commitment and must provide
145
+ // new wallets first.
146
+ //
147
+ // TODO: Expose separate function to reset the moving funds timeout
148
+ // if no Live wallets exist in the system.
149
+ require(expectedTargetWalletsCount > 0, "No target wallets available");
150
+
151
+ require(
152
+ targetWallets.length == expectedTargetWalletsCount,
153
+ "Submitted target wallets count is other than expected"
154
+ );
155
+
156
+ uint160 lastProcessedTargetWallet = 0;
157
+
158
+ for (uint256 i = 0; i < targetWallets.length; i++) {
159
+ bytes20 targetWallet = targetWallets[i];
160
+
161
+ require(
162
+ targetWallet != walletPubKeyHash,
163
+ "Submitted target wallet cannot be equal to the source wallet"
164
+ );
165
+
166
+ require(
167
+ uint160(targetWallet) > lastProcessedTargetWallet,
168
+ "Submitted target wallet breaks the expected order"
169
+ );
170
+
171
+ require(
172
+ self.registeredWallets[targetWallet].state ==
173
+ Wallets.WalletState.Live,
174
+ "Submitted target wallet must be in Live state"
175
+ );
176
+
177
+ lastProcessedTargetWallet = uint160(targetWallet);
178
+ }
179
+
180
+ wallet.movingFundsTargetWalletsCommitmentHash = keccak256(
181
+ abi.encodePacked(targetWallets)
182
+ );
183
+
184
+ emit MovingFundsCommitmentSubmitted(
185
+ walletPubKeyHash,
186
+ targetWallets,
187
+ msg.sender
188
+ );
189
+ }
190
+
46
191
  /// @notice Used by the wallet to prove the BTC moving funds transaction
47
192
  /// and to make the necessary state changes. Moving funds is only
48
193
  /// accepted if it satisfies SPV proof.
@@ -125,7 +270,7 @@ library MovingFunds {
125
270
  );
126
271
 
127
272
  self.notifyWalletFundsMoved(walletPubKeyHash, targetWalletsHash);
128
-
273
+ // slither-disable-next-line reentrancy-events
129
274
  emit MovingFundsCompleted(walletPubKeyHash, movingFundsTxHash);
130
275
  }
131
276
 
@@ -283,4 +428,39 @@ library MovingFunds {
283
428
 
284
429
  return (targetWalletsHash, outputsTotalValue);
285
430
  }
431
+
432
+ /// @notice Notifies about a timed out moving funds process. Terminates
433
+ /// the wallet and slashes signing group members as a result.
434
+ /// @param walletPubKeyHash 20-byte public key hash of the wallet
435
+ /// @dev Requirements:
436
+ /// - The wallet must be in the MovingFunds state
437
+ /// - The moving funds timeout must be actually exceeded
438
+ function notifyMovingFundsTimeout(
439
+ BridgeState.Storage storage self,
440
+ bytes20 walletPubKeyHash
441
+ ) external {
442
+ Wallets.Wallet storage wallet = self.registeredWallets[
443
+ walletPubKeyHash
444
+ ];
445
+
446
+ require(
447
+ wallet.state == Wallets.WalletState.MovingFunds,
448
+ "ECDSA wallet must be in MovingFunds state"
449
+ );
450
+
451
+ require(
452
+ /* solhint-disable-next-line not-rely-on-time */
453
+ block.timestamp >
454
+ wallet.movingFundsRequestedAt + self.movingFundsTimeout,
455
+ "Moving funds has not timed out yet"
456
+ );
457
+
458
+ self.terminateWallet(walletPubKeyHash);
459
+
460
+ // TODO: Perform slashing of wallet operators, reward the notifier
461
+ // using seized amount, and add unit tests for that.
462
+
463
+ // slither-disable-next-line reentrancy-events
464
+ emit MovingFundsTimedOut(walletPubKeyHash);
465
+ }
286
466
  }
@@ -430,10 +430,6 @@ library Redemption {
430
430
  BitcoinTx.UTXO calldata mainUtxo,
431
431
  bytes20 walletPubKeyHash
432
432
  ) external {
433
- // TODO: Just as for `submitSweepProof`, fail early if the function
434
- // call gets frontrunned. See discussion:
435
- // https://github.com/keep-network/tbtc-v2/pull/106#discussion_r801745204
436
-
437
433
  // The actual transaction proof is performed here. After that point, we
438
434
  // can assume the transaction happened on Bitcoin chain and has
439
435
  // a sufficient number of confirmations as determined by
@@ -603,11 +599,10 @@ library Redemption {
603
599
  bytes20 walletPubKeyHash,
604
600
  RedemptionTxOutputsProcessingInfo memory processInfo
605
601
  ) internal returns (RedemptionTxOutputsInfo memory resultInfo) {
606
- // Helper variable that counts the number of processed redemption
607
- // outputs. Redemptions can be either pending or reported as timed out.
608
- // TODO: Revisit the approach with redemptions count according to
609
- // https://github.com/keep-network/tbtc-v2/pull/128#discussion_r808237765
610
- uint256 processedRedemptionsCount = 0;
602
+ // Helper flag indicating whether there was at least one redemption
603
+ // output present (redemption must be either pending or reported as
604
+ // timed out).
605
+ bool redemptionPresent = false;
611
606
 
612
607
  // Outputs processing loop.
613
608
  for (uint256 i = 0; i < processInfo.outputsCount; i++) {
@@ -654,7 +649,7 @@ library Redemption {
654
649
  );
655
650
  resultInfo.totalBurnableValue += burnableValue;
656
651
  resultInfo.totalTreasuryFee += treasuryFee;
657
- processedRedemptionsCount++;
652
+ redemptionPresent = true;
658
653
  }
659
654
 
660
655
  // Make the `outputStartingIndex` pointing to the next output by
@@ -666,7 +661,7 @@ library Redemption {
666
661
  // referring back to the wallet PKH and just burning main UTXO value
667
662
  // for transaction fees.
668
663
  require(
669
- processedRedemptionsCount > 0,
664
+ redemptionPresent,
670
665
  "Redemption transaction must process at least one redemption"
671
666
  );
672
667
  }
@@ -842,7 +837,7 @@ library Redemption {
842
837
  // Propagate timeout consequences to the wallet
843
838
  self.notifyWalletTimedOutRedemption(walletPubKeyHash);
844
839
  }
845
-
840
+ // slither-disable-next-line reentrancy-events
846
841
  emit RedemptionTimedOut(walletPubKeyHash, redeemerOutputScript);
847
842
 
848
843
  // Return the requested amount of tokens to the redeemer
@@ -109,9 +109,6 @@ library Sweep {
109
109
  BitcoinTx.Proof calldata sweepProof,
110
110
  BitcoinTx.UTXO calldata mainUtxo
111
111
  ) external {
112
- // TODO: Fail early if the function call gets frontrunned. See discussion:
113
- // https://github.com/keep-network/tbtc-v2/pull/106#discussion_r801745204
114
-
115
112
  // The actual transaction proof is performed here. After that point, we
116
113
  // can assume the transaction happened on Bitcoin chain and has
117
114
  // a sufficient number of confirmations as determined by
@@ -16,8 +16,8 @@
16
16
  pragma solidity ^0.8.9;
17
17
 
18
18
  import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";
19
- import {IWalletRegistry as EcdsaWalletRegistry} from "@keep-network/ecdsa/contracts/api/IWalletRegistry.sol";
20
19
  import {EcdsaDkg} from "@keep-network/ecdsa/contracts/libraries/EcdsaDkg.sol";
20
+ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
21
21
 
22
22
  import "./BitcoinTx.sol";
23
23
  import "./EcdsaLib.sol";
@@ -40,8 +40,15 @@ library Wallets {
40
40
  /// fulfill their pending redemption requests although new
41
41
  /// redemption requests and new deposit reveals are not accepted.
42
42
  MovingFunds,
43
- /// @dev The wallet moved or redeemed all their funds and cannot
44
- /// perform any action.
43
+ /// @dev The wallet moved or redeemed all their funds and is in the
44
+ /// closing period where they can be subject of fraud challenges
45
+ /// and must defend against them. This state is needed to protect
46
+ /// against deposit frauds on deposits revealed but not swept.
47
+ /// The closing period must be greater that the deposit refund
48
+ /// time plus some time margin.
49
+ Closing,
50
+ /// @dev The wallet finalized the closing period successfully and
51
+ /// cannot perform any action in the Bridge.
45
52
  Closed,
46
53
  /// @dev The wallet committed a fraud that was reported. The wallet is
47
54
  /// blocked and can not perform any actions in the Bridge.
@@ -68,6 +75,9 @@ library Wallets {
68
75
  // UNIX timestamp indicating the moment the wallet was requested to
69
76
  // move their funds.
70
77
  uint32 movingFundsRequestedAt;
78
+ // UNIX timestamp indicating the moment the wallet's closing period
79
+ // started.
80
+ uint32 closingStartedAt;
71
81
  // Current state of the wallet.
72
82
  WalletState state;
73
83
  // Moving funds target wallet commitment submitted by the wallet. It
@@ -88,6 +98,11 @@ library Wallets {
88
98
  bytes20 indexed walletPubKeyHash
89
99
  );
90
100
 
101
+ event WalletClosing(
102
+ bytes32 indexed ecdsaWalletID,
103
+ bytes20 indexed walletPubKeyHash
104
+ );
105
+
91
106
  event WalletClosed(
92
107
  bytes32 indexed ecdsaWalletID,
93
108
  bytes20 indexed walletPubKeyHash
@@ -237,6 +252,8 @@ library Wallets {
237
252
  // Set the freshly created wallet as the new active wallet.
238
253
  self.activeWalletPubKeyHash = walletPubKeyHash;
239
254
 
255
+ self.liveWalletsCount++;
256
+
240
257
  emit NewWalletRegistered(ecdsaWalletID, walletPubKeyHash);
241
258
  }
242
259
 
@@ -347,9 +364,8 @@ library Wallets {
347
364
  }
348
365
 
349
366
  /// @notice Requests a wallet to move their funds. If the wallet balance
350
- /// is zero, the wallet is closed immediately and the ECDSA
351
- /// registry is notified about this fact. If the move funds
352
- /// request refers to the current active wallet, such a wallet
367
+ /// is zero, the wallet closing begins immediately. If the move
368
+ /// funds request refers to the current active wallet, such a wallet
353
369
  /// is no longer considered active and the active wallet slot
354
370
  /// is unset allowing to trigger a new wallet creation immediately.
355
371
  /// @param walletPubKeyHash 20-byte public key hash of the wallet
@@ -363,8 +379,8 @@ library Wallets {
363
379
 
364
380
  if (wallet.mainUtxoHash == bytes32(0)) {
365
381
  // If the wallet has no main UTXO, that means its BTC balance
366
- // is zero and it should be closed immediately.
367
- closeWallet(self, walletPubKeyHash);
382
+ // is zero and the wallet closing should begin immediately.
383
+ beginWalletClosing(self, walletPubKeyHash);
368
384
  } else {
369
385
  // Otherwise, initialize the moving funds process.
370
386
  wallet.state = WalletState.MovingFunds;
@@ -380,51 +396,49 @@ library Wallets {
380
396
  // possible in order to get a new healthy active wallet.
381
397
  delete self.activeWalletPubKeyHash;
382
398
  }
399
+
400
+ self.liveWalletsCount--;
383
401
  }
384
402
 
385
- /// @notice Closes the given wallet and notifies the ECDSA registry
386
- /// about this fact.
403
+ /// @notice Begins the closing period of the given wallet.
387
404
  /// @param walletPubKeyHash 20-byte public key hash of the wallet
388
405
  /// @dev Requirements:
389
406
  /// - The caller must make sure that the wallet is in the
390
- /// Live or MovingFunds state.
391
- function closeWallet(
407
+ /// MovingFunds state
408
+ function beginWalletClosing(
392
409
  BridgeState.Storage storage self,
393
410
  bytes20 walletPubKeyHash
394
411
  ) internal {
395
412
  Wallet storage wallet = self.registeredWallets[walletPubKeyHash];
413
+ // Initialize the closing period.
414
+ wallet.state = WalletState.Closing;
415
+ /* solhint-disable-next-line not-rely-on-time */
416
+ wallet.closingStartedAt = uint32(block.timestamp);
396
417
 
397
- wallet.state = WalletState.Closed;
398
-
399
- emit WalletClosed(wallet.ecdsaWalletID, walletPubKeyHash);
400
-
401
- self.ecdsaWalletRegistry.closeWallet(wallet.ecdsaWalletID);
418
+ emit WalletClosing(wallet.ecdsaWalletID, walletPubKeyHash);
402
419
  }
403
420
 
404
- /// @notice Reports about a fraud committed by the given wallet. This
405
- /// function performs slashing and wallet termination in reaction
406
- /// to a proven fraud and it should only be called when the fraud
407
- /// was confirmed.
421
+ /// @notice Finalizes the closing period of the given wallet and notifies
422
+ /// the ECDSA registry about this fact.
408
423
  /// @param walletPubKeyHash 20-byte public key hash of the wallet
409
424
  /// @dev Requirements:
410
- /// - Wallet must be in Live or MovingFunds state
411
- function notifyWalletFraud(
425
+ /// - The caller must make sure that the wallet is in the Closing state
426
+ ///
427
+ /// TODO: Make this function callable from the Bridge contract if
428
+ /// `block.timestamp > wallet.closingStartedAt + self.walletClosingPeriod`.
429
+ ///
430
+ // slither-disable-next-line dead-code
431
+ function finalizeWalletClosing(
412
432
  BridgeState.Storage storage self,
413
433
  bytes20 walletPubKeyHash
414
434
  ) internal {
415
- WalletState walletState = self
416
- .registeredWallets[walletPubKeyHash]
417
- .state;
435
+ Wallet storage wallet = self.registeredWallets[walletPubKeyHash];
418
436
 
419
- require(
420
- walletState == WalletState.Live ||
421
- walletState == WalletState.MovingFunds,
422
- "ECDSA wallet must be in Live or MovingFunds state"
423
- );
437
+ wallet.state = WalletState.Closed;
424
438
 
425
- terminateWallet(self, walletPubKeyHash);
439
+ emit WalletClosed(wallet.ecdsaWalletID, walletPubKeyHash);
426
440
 
427
- // TODO: Perform slashing of wallet operators and add unit tests for that.
441
+ self.ecdsaWalletRegistry.closeWallet(wallet.ecdsaWalletID);
428
442
  }
429
443
 
430
444
  /// @notice Terminates the given wallet and notifies the ECDSA registry
@@ -435,13 +449,17 @@ library Wallets {
435
449
  /// @param walletPubKeyHash 20-byte public key hash of the wallet
436
450
  /// @dev Requirements:
437
451
  /// - The caller must make sure that the wallet is in the
438
- /// Live or MovingFunds state.
452
+ /// Live or MovingFunds or Closing state.
439
453
  function terminateWallet(
440
454
  BridgeState.Storage storage self,
441
455
  bytes20 walletPubKeyHash
442
456
  ) internal {
443
457
  Wallet storage wallet = self.registeredWallets[walletPubKeyHash];
444
458
 
459
+ if (wallet.state == WalletState.Live) {
460
+ self.liveWalletsCount--;
461
+ }
462
+
445
463
  wallet.state = WalletState.Terminated;
446
464
 
447
465
  emit WalletTerminated(wallet.ecdsaWalletID, walletPubKeyHash);
@@ -505,6 +523,6 @@ library Wallets {
505
523
  // If funds were moved, the wallet has no longer a main UTXO.
506
524
  delete wallet.mainUtxoHash;
507
525
 
508
- closeWallet(self, walletPubKeyHash);
526
+ beginWalletClosing(self, walletPubKeyHash);
509
527
  }
510
528
  }
@@ -0,0 +1,28 @@
1
+ import { HardhatRuntimeEnvironment } from "hardhat/types"
2
+ import { DeployFunction } from "hardhat-deploy/types"
3
+
4
+ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
5
+ const { getNamedAccounts, deployments, helpers } = hre
6
+ const { log } = deployments
7
+ const { deployer } = await getNamedAccounts()
8
+
9
+ const Relay = await deployments.getOrNull("Relay")
10
+
11
+ if (Relay && helpers.address.isValid(Relay.address)) {
12
+ log(`using external Relay at ${Relay.address}`)
13
+ } else if (hre.network.name !== "hardhat") {
14
+ throw new Error("deployed Relay contract not found")
15
+ } else {
16
+ log("deploying Relay stub")
17
+
18
+ await deployments.deploy("Relay", {
19
+ contract: "TestRelay",
20
+ from: deployer,
21
+ log: true,
22
+ })
23
+ }
24
+ }
25
+
26
+ export default func
27
+
28
+ func.tags = ["Relay"]
@@ -0,0 +1,25 @@
1
+ import { HardhatRuntimeEnvironment } from "hardhat/types"
2
+ import { DeployFunction } from "hardhat-deploy/types"
3
+
4
+ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
5
+ const { deployments, getNamedAccounts } = hre
6
+ const { deploy } = deployments
7
+ const { deployer } = await getNamedAccounts()
8
+
9
+ const Bank = await deploy("Bank", {
10
+ from: deployer,
11
+ args: [],
12
+ log: true,
13
+ })
14
+
15
+ if (hre.network.tags.tenderly) {
16
+ await hre.tenderly.verify({
17
+ name: "Bank",
18
+ address: Bank.address,
19
+ })
20
+ }
21
+ }
22
+
23
+ export default func
24
+
25
+ func.tags = ["Bank"]
@@ -0,0 +1,60 @@
1
+ import { HardhatRuntimeEnvironment } from "hardhat/types"
2
+ import { DeployFunction } from "hardhat-deploy/types"
3
+
4
+ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
5
+ const { deployments, getNamedAccounts } = hre
6
+ const { deploy } = deployments
7
+ const { deployer, treasury } = await getNamedAccounts()
8
+
9
+ const Bank = await deployments.get("Bank")
10
+ const Relay = await deployments.get("Relay")
11
+
12
+ // TODO: Test for mainnet deployment that when `WalletRegistry` is provided
13
+ // in `external/mainnet/` directory it gets resolved correctly, and the deployment
14
+ // script from `@keep-network/ecdsa` is not invoked once again.
15
+ const WalletRegistry = await deployments.get("WalletRegistry")
16
+
17
+ const txProofDifficultyFactor = 6
18
+
19
+ const Deposit = await deploy("Deposit", { from: deployer, log: true })
20
+ const Sweep = await deploy("Sweep", { from: deployer, log: true })
21
+ const Redemption = await deploy("Redemption", { from: deployer, log: true })
22
+ const Wallets = await deploy("Wallets", { from: deployer, log: true })
23
+ const Fraud = await deploy("Fraud", { from: deployer, log: true })
24
+ const MovingFunds = await deploy("MovingFunds", {
25
+ from: deployer,
26
+ log: true,
27
+ })
28
+
29
+ const Bridge = await deploy("Bridge", {
30
+ from: deployer,
31
+ args: [
32
+ Bank.address,
33
+ Relay.address,
34
+ treasury,
35
+ WalletRegistry.address,
36
+ txProofDifficultyFactor,
37
+ ],
38
+ libraries: {
39
+ Deposit: Deposit.address,
40
+ Sweep: Sweep.address,
41
+ Redemption: Redemption.address,
42
+ Wallets: Wallets.address,
43
+ Fraud: Fraud.address,
44
+ MovingFunds: MovingFunds.address,
45
+ },
46
+ log: true,
47
+ })
48
+
49
+ if (hre.network.tags.tenderly) {
50
+ await hre.tenderly.verify({
51
+ name: "Bridge",
52
+ address: Bridge.address,
53
+ })
54
+ }
55
+ }
56
+
57
+ export default func
58
+
59
+ func.tags = ["Bridge"]
60
+ func.dependencies = ["Bank", "Relay", "Treasury", "WalletRegistry"]
@@ -0,0 +1,19 @@
1
+ import { HardhatRuntimeEnvironment } from "hardhat/types"
2
+ import { DeployFunction } from "hardhat-deploy/types"
3
+
4
+ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
5
+ const { getNamedAccounts, deployments } = hre
6
+ const { execute, log } = deployments
7
+ const { deployer } = await getNamedAccounts()
8
+
9
+ const Bridge = await deployments.get("Bridge")
10
+
11
+ log("updating Bridge in Bank")
12
+
13
+ await execute("Bank", { from: deployer }, "updateBridge", Bridge.address)
14
+ }
15
+
16
+ export default func
17
+
18
+ func.tags = ["BankUpdateBridge"]
19
+ func.dependencies = ["Bank", "Bridge"]
@@ -0,0 +1,17 @@
1
+ import { HardhatRuntimeEnvironment } from "hardhat/types"
2
+ import { DeployFunction } from "hardhat-deploy/types"
3
+
4
+ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
5
+ const { getNamedAccounts, helpers } = hre
6
+ const { deployer, governance } = await getNamedAccounts()
7
+
8
+ await helpers.ownable.transferOwnership("Bank", governance, deployer)
9
+
10
+ await helpers.ownable.transferOwnership("Bridge", governance, deployer)
11
+ }
12
+
13
+ export default func
14
+
15
+ func.tags = ["TransferOwnership"]
16
+ func.dependencies = ["Bank", "Bridge"]
17
+ func.runAtTheEnd = true