@keep-network/tbtc-v2 0.1.1-dev.31 → 0.1.1-dev.34

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 (28) hide show
  1. package/artifacts/TBTC.json +3 -3
  2. package/artifacts/TBTCToken.json +3 -3
  3. package/artifacts/VendingMachine.json +10 -10
  4. package/artifacts/solcInputs/{d9d0873fb324e55f80390065c7fe0dff.json → a67a2c11233ce411fb55a9f369ced323.json} +9 -3
  5. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
  6. package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
  7. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
  8. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
  9. package/build/contracts/bridge/Bridge.sol/Bridge.json +234 -82
  10. package/build/contracts/bridge/Bridge.sol/IRelay.dbg.json +1 -1
  11. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
  12. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +10 -0
  13. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +4 -0
  14. package/build/contracts/bridge/Deposit.sol/Deposit.json +72 -0
  15. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
  16. package/build/contracts/bridge/Frauds.sol/Frauds.dbg.json +1 -1
  17. package/build/contracts/bridge/Frauds.sol/Frauds.json +2 -2
  18. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  19. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
  20. package/build/contracts/bridge/Wallets.sol/Wallets.json +2 -2
  21. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  22. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  23. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  24. package/contracts/bridge/Bridge.sol +439 -278
  25. package/contracts/bridge/BridgeState.sol +54 -0
  26. package/contracts/bridge/Deposit.sol +247 -0
  27. package/contracts/bridge/Wallets.sol +89 -19
  28. package/package.json +1 -1
@@ -0,0 +1,54 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ // ██████████████ ▐████▌ ██████████████
4
+ // ██████████████ ▐████▌ ██████████████
5
+ // ▐████▌ ▐████▌
6
+ // ▐████▌ ▐████▌
7
+ // ██████████████ ▐████▌ ██████████████
8
+ // ██████████████ ▐████▌ ██████████████
9
+ // ▐████▌ ▐████▌
10
+ // ▐████▌ ▐████▌
11
+ // ▐████▌ ▐████▌
12
+ // ▐████▌ ▐████▌
13
+ // ▐████▌ ▐████▌
14
+ // ▐████▌ ▐████▌
15
+
16
+ pragma solidity ^0.8.9;
17
+
18
+ import "./Deposit.sol";
19
+
20
+ library BridgeState {
21
+ struct Storage {
22
+ /// TODO: Make it governable.
23
+ /// @notice The minimal amount that can be requested to deposit.
24
+ /// Value of this parameter must take into account the value of
25
+ /// `depositTreasuryFeeDivisor` and `depositTxMaxFee`
26
+ /// parameters in order to make requests that can incur the
27
+ /// treasury and transaction fee and still satisfy the depositor.
28
+ uint64 depositDustThreshold;
29
+ /// TODO: Make it governable.
30
+ /// @notice Divisor used to compute the treasury fee taken from each
31
+ /// deposit and transferred to the treasury upon sweep proof
32
+ /// submission. That fee is computed as follows:
33
+ /// `treasuryFee = depositedAmount / depositTreasuryFeeDivisor`
34
+ /// For example, if the treasury fee needs to be 2% of each deposit,
35
+ /// the `depositTreasuryFeeDivisor` should be set to `50`
36
+ /// because `1/50 = 0.02 = 2%`.
37
+ uint64 depositTreasuryFeeDivisor;
38
+ /// @notice Collection of all revealed deposits indexed by
39
+ /// keccak256(fundingTxHash | fundingOutputIndex).
40
+ /// The fundingTxHash is bytes32 (ordered as in Bitcoin internally)
41
+ /// and fundingOutputIndex an uint32. This mapping may contain valid
42
+ /// and invalid deposits and the wallet is responsible for
43
+ /// validating them before attempting to execute a sweep.
44
+ mapping(uint256 => Deposit.Request) deposits;
45
+ /// @notice Indicates if the vault with the given address is trusted or not.
46
+ /// Depositors can route their revealed deposits only to trusted
47
+ /// vaults and have trusted vaults notified about new deposits as
48
+ /// soon as these deposits get swept. Vaults not trusted by the
49
+ /// Bridge can still be used by Bank balance owners on their own
50
+ /// responsibility - anyone can approve their Bank balance to any
51
+ /// address.
52
+ mapping(address => bool) isVaultTrusted;
53
+ }
54
+ }
@@ -0,0 +1,247 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ // ██████████████ ▐████▌ ██████████████
4
+ // ██████████████ ▐████▌ ██████████████
5
+ // ▐████▌ ▐████▌
6
+ // ▐████▌ ▐████▌
7
+ // ██████████████ ▐████▌ ██████████████
8
+ // ██████████████ ▐████▌ ██████████████
9
+ // ▐████▌ ▐████▌
10
+ // ▐████▌ ▐████▌
11
+ // ▐████▌ ▐████▌
12
+ // ▐████▌ ▐████▌
13
+ // ▐████▌ ▐████▌
14
+ // ▐████▌ ▐████▌
15
+
16
+ pragma solidity ^0.8.9;
17
+
18
+ import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";
19
+ import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
20
+
21
+ import "./BitcoinTx.sol";
22
+ import "./BridgeState.sol";
23
+ import "./Wallets.sol";
24
+
25
+ library Deposit {
26
+ using Wallets for Wallets.Data;
27
+
28
+ using BTCUtils for bytes;
29
+ using BytesLib for bytes;
30
+
31
+ /// @notice Represents data which must be revealed by the depositor during
32
+ /// deposit reveal.
33
+ struct RevealInfo {
34
+ // Index of the funding output belonging to the funding transaction.
35
+ uint32 fundingOutputIndex;
36
+ // Ethereum depositor address.
37
+ address depositor;
38
+ // The blinding factor as 8 bytes. Byte endianness doesn't matter
39
+ // as this factor is not interpreted as uint.
40
+ bytes8 blindingFactor;
41
+ // The compressed Bitcoin public key (33 bytes and 02 or 03 prefix)
42
+ // of the deposit's wallet hashed in the HASH160 Bitcoin opcode style.
43
+ bytes20 walletPubKeyHash;
44
+ // The compressed Bitcoin public key (33 bytes and 02 or 03 prefix)
45
+ // that can be used to make the deposit refund after the refund
46
+ // locktime passes. Hashed in the HASH160 Bitcoin opcode style.
47
+ bytes20 refundPubKeyHash;
48
+ // The refund locktime (4-byte LE). Interpreted according to locktime
49
+ // parsing rules described in:
50
+ // https://developer.bitcoin.org/devguide/transactions.html#locktime-and-sequence-number
51
+ // and used with OP_CHECKLOCKTIMEVERIFY opcode as described in:
52
+ // https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki
53
+ bytes4 refundLocktime;
54
+ // Address of the Bank vault to which the deposit is routed to.
55
+ // Optional, can be 0x0. The vault must be trusted by the Bridge.
56
+ address vault;
57
+ }
58
+
59
+ /// @notice Represents tBTC deposit request data.
60
+ struct Request {
61
+ // Ethereum depositor address.
62
+ address depositor;
63
+ // Deposit amount in satoshi.
64
+ uint64 amount;
65
+ // UNIX timestamp the deposit was revealed at.
66
+ uint32 revealedAt;
67
+ // Address of the Bank vault the deposit is routed to.
68
+ // Optional, can be 0x0.
69
+ address vault;
70
+ // Treasury TBTC fee in satoshi at the moment of deposit reveal.
71
+ uint64 treasuryFee;
72
+ // UNIX timestamp the deposit was swept at. Note this is not the
73
+ // time when the deposit was swept on the Bitcoin chain but actually
74
+ // the time when the sweep proof was delivered to the Ethereum chain.
75
+ uint32 sweptAt;
76
+ }
77
+
78
+ event DepositRevealed(
79
+ bytes32 fundingTxHash,
80
+ uint32 fundingOutputIndex,
81
+ address depositor,
82
+ uint64 amount,
83
+ bytes8 blindingFactor,
84
+ bytes20 walletPubKeyHash,
85
+ bytes20 refundPubKeyHash,
86
+ bytes4 refundLocktime,
87
+ address vault
88
+ );
89
+
90
+ /// @notice Used by the depositor to reveal information about their P2(W)SH
91
+ /// Bitcoin deposit to the Bridge on Ethereum chain. The off-chain
92
+ /// wallet listens for revealed deposit events and may decide to
93
+ /// include the revealed deposit in the next executed sweep.
94
+ /// Information about the Bitcoin deposit can be revealed before or
95
+ /// after the Bitcoin transaction with P2(W)SH deposit is mined on
96
+ /// the Bitcoin chain. Worth noting, the gas cost of this function
97
+ /// scales with the number of P2(W)SH transaction inputs and
98
+ /// outputs. The deposit may be routed to one of the trusted vaults.
99
+ /// When a deposit is routed to a vault, vault gets notified when
100
+ /// the deposit gets swept and it may execute the appropriate action.
101
+ /// @param fundingTx Bitcoin funding transaction data, see `BitcoinTx.Info`
102
+ /// @param reveal Deposit reveal data, see `RevealInfo struct
103
+ /// @dev Requirements:
104
+ /// - `reveal.walletPubKeyHash` must identify a `Live` wallet
105
+ /// - `reveal.vault` must be 0x0 or point to a trusted vault
106
+ /// - `reveal.fundingOutputIndex` must point to the actual P2(W)SH
107
+ /// output of the BTC deposit transaction
108
+ /// - `reveal.depositor` must be the Ethereum address used in the
109
+ /// P2(W)SH BTC deposit transaction,
110
+ /// - `reveal.blindingFactor` must be the blinding factor used in the
111
+ /// P2(W)SH BTC deposit transaction,
112
+ /// - `reveal.walletPubKeyHash` must be the wallet pub key hash used in
113
+ /// the P2(W)SH BTC deposit transaction,
114
+ /// - `reveal.refundPubKeyHash` must be the refund pub key hash used in
115
+ /// the P2(W)SH BTC deposit transaction,
116
+ /// - `reveal.refundLocktime` must be the refund locktime used in the
117
+ /// P2(W)SH BTC deposit transaction,
118
+ /// - BTC deposit for the given `fundingTxHash`, `fundingOutputIndex`
119
+ /// can be revealed only one time.
120
+ ///
121
+ /// If any of these requirements is not met, the wallet _must_ refuse
122
+ /// to sweep the deposit and the depositor has to wait until the
123
+ /// deposit script unlocks to receive their BTC back.
124
+ function revealDeposit(
125
+ BridgeState.Storage storage self,
126
+ Wallets.Data storage wallets,
127
+ BitcoinTx.Info calldata fundingTx,
128
+ RevealInfo calldata reveal
129
+ ) external {
130
+ require(
131
+ wallets.registeredWallets[reveal.walletPubKeyHash].state ==
132
+ Wallets.WalletState.Live,
133
+ "Wallet is not in Live state"
134
+ );
135
+
136
+ require(
137
+ reveal.vault == address(0) || self.isVaultTrusted[reveal.vault],
138
+ "Vault is not trusted"
139
+ );
140
+
141
+ // TODO: Should we enforce a specific locktime at contract level?
142
+
143
+ bytes memory expectedScript = abi.encodePacked(
144
+ hex"14", // Byte length of depositor Ethereum address.
145
+ reveal.depositor,
146
+ hex"75", // OP_DROP
147
+ hex"08", // Byte length of blinding factor value.
148
+ reveal.blindingFactor,
149
+ hex"75", // OP_DROP
150
+ hex"76", // OP_DUP
151
+ hex"a9", // OP_HASH160
152
+ hex"14", // Byte length of a compressed Bitcoin public key hash.
153
+ reveal.walletPubKeyHash,
154
+ hex"87", // OP_EQUAL
155
+ hex"63", // OP_IF
156
+ hex"ac", // OP_CHECKSIG
157
+ hex"67", // OP_ELSE
158
+ hex"76", // OP_DUP
159
+ hex"a9", // OP_HASH160
160
+ hex"14", // Byte length of a compressed Bitcoin public key hash.
161
+ reveal.refundPubKeyHash,
162
+ hex"88", // OP_EQUALVERIFY
163
+ hex"04", // Byte length of refund locktime value.
164
+ reveal.refundLocktime,
165
+ hex"b1", // OP_CHECKLOCKTIMEVERIFY
166
+ hex"75", // OP_DROP
167
+ hex"ac", // OP_CHECKSIG
168
+ hex"68" // OP_ENDIF
169
+ );
170
+
171
+ bytes memory fundingOutput = fundingTx
172
+ .outputVector
173
+ .extractOutputAtIndex(reveal.fundingOutputIndex);
174
+ bytes memory fundingOutputHash = fundingOutput.extractHash();
175
+
176
+ if (fundingOutputHash.length == 20) {
177
+ // A 20-byte output hash is used by P2SH. That hash is constructed
178
+ // by applying OP_HASH160 on the locking script. A 20-byte output
179
+ // hash is used as well by P2PKH and P2WPKH (OP_HASH160 on the
180
+ // public key). However, since we compare the actual output hash
181
+ // with an expected locking script hash, this check will succeed only
182
+ // for P2SH transaction type with expected script hash value. For
183
+ // P2PKH and P2WPKH, it will fail on the output hash comparison with
184
+ // the expected locking script hash.
185
+ require(
186
+ fundingOutputHash.slice20(0) == expectedScript.hash160View(),
187
+ "Wrong 20-byte script hash"
188
+ );
189
+ } else if (fundingOutputHash.length == 32) {
190
+ // A 32-byte output hash is used by P2WSH. That hash is constructed
191
+ // by applying OP_SHA256 on the locking script.
192
+ require(
193
+ fundingOutputHash.toBytes32() == sha256(expectedScript),
194
+ "Wrong 32-byte script hash"
195
+ );
196
+ } else {
197
+ revert("Wrong script hash length");
198
+ }
199
+
200
+ // Resulting TX hash is in native Bitcoin little-endian format.
201
+ bytes32 fundingTxHash = abi
202
+ .encodePacked(
203
+ fundingTx.version,
204
+ fundingTx.inputVector,
205
+ fundingTx.outputVector,
206
+ fundingTx.locktime
207
+ )
208
+ .hash256View();
209
+
210
+ Request storage deposit = self.deposits[
211
+ uint256(
212
+ keccak256(
213
+ abi.encodePacked(fundingTxHash, reveal.fundingOutputIndex)
214
+ )
215
+ )
216
+ ];
217
+ require(deposit.revealedAt == 0, "Deposit already revealed");
218
+
219
+ uint64 fundingOutputAmount = fundingOutput.extractValue();
220
+
221
+ require(
222
+ fundingOutputAmount >= self.depositDustThreshold,
223
+ "Deposit amount too small"
224
+ );
225
+
226
+ deposit.amount = fundingOutputAmount;
227
+ deposit.depositor = reveal.depositor;
228
+ /* solhint-disable-next-line not-rely-on-time */
229
+ deposit.revealedAt = uint32(block.timestamp);
230
+ deposit.vault = reveal.vault;
231
+ deposit.treasuryFee = self.depositTreasuryFeeDivisor > 0
232
+ ? fundingOutputAmount / self.depositTreasuryFeeDivisor
233
+ : 0;
234
+
235
+ emit DepositRevealed(
236
+ fundingTxHash,
237
+ reveal.fundingOutputIndex,
238
+ reveal.depositor,
239
+ fundingOutputAmount,
240
+ reveal.blindingFactor,
241
+ reveal.walletPubKeyHash,
242
+ reveal.refundPubKeyHash,
243
+ reveal.refundLocktime,
244
+ reveal.vault
245
+ );
246
+ }
247
+ }
@@ -92,9 +92,13 @@ library Wallets {
92
92
  uint32 createdAt;
93
93
  // UNIX timestamp indicating the moment the wallet was requested to
94
94
  // move their funds.
95
- uint32 moveFundsRequestedAt;
95
+ uint32 movingFundsRequestedAt;
96
96
  // Current state of the wallet.
97
97
  WalletState state;
98
+ // Moving funds target wallet commitment submitted by the wallet. It
99
+ // is built by applying the keccak256 hash over the list of 20-byte
100
+ // public key hashes of the target wallets.
101
+ bytes32 movingFundsTargetWalletsCommitmentHash;
98
102
  }
99
103
 
100
104
  event WalletCreationPeriodUpdated(uint32 newCreationPeriod);
@@ -308,7 +312,7 @@ library Wallets {
308
312
 
309
313
  // Compress wallet's public key and calculate Bitcoin's hash160 of it.
310
314
  bytes20 walletPubKeyHash = bytes20(
311
- EcdsaLib.compressPublicKey(publicKeyX, publicKeyY).hash160()
315
+ EcdsaLib.compressPublicKey(publicKeyX, publicKeyY).hash160View()
312
316
  );
313
317
 
314
318
  Wallet storage wallet = self.registeredWallets[walletPubKeyHash];
@@ -346,7 +350,7 @@ library Wallets {
346
350
 
347
351
  // Compress wallet's public key and calculate Bitcoin's hash160 of it.
348
352
  bytes20 walletPubKeyHash = bytes20(
349
- EcdsaLib.compressPublicKey(publicKeyX, publicKeyY).hash160()
353
+ EcdsaLib.compressPublicKey(publicKeyX, publicKeyY).hash160View()
350
354
  );
351
355
 
352
356
  require(
@@ -448,16 +452,12 @@ library Wallets {
448
452
  if (wallet.mainUtxoHash == bytes32(0)) {
449
453
  // If the wallet has no main UTXO, that means its BTC balance
450
454
  // is zero and it should be closed immediately.
451
- wallet.state = WalletState.Closed;
452
-
453
- emit WalletClosed(wallet.ecdsaWalletID, walletPubKeyHash);
454
-
455
- self.registry.closeWallet(wallet.ecdsaWalletID);
455
+ closeWallet(self, walletPubKeyHash);
456
456
  } else {
457
457
  // Otherwise, initialize the moving funds process.
458
458
  wallet.state = WalletState.MovingFunds;
459
459
  /* solhint-disable-next-line not-rely-on-time */
460
- wallet.moveFundsRequestedAt = uint32(block.timestamp);
460
+ wallet.movingFundsRequestedAt = uint32(block.timestamp);
461
461
 
462
462
  emit WalletMovingFunds(wallet.ecdsaWalletID, walletPubKeyHash);
463
463
  }
@@ -470,9 +470,21 @@ library Wallets {
470
470
  }
471
471
  }
472
472
 
473
- // TODO: Implement functions that will be called upon moving funds process
474
- // end. Remember the moving funds process ends up with a successful
475
- // proof or a timeout.
473
+ /// @notice Closes the given wallet and notifies the ECDSA registry
474
+ /// about this fact.
475
+ /// @param walletPubKeyHash 20-byte public key hash of the wallet
476
+ /// @dev Requirements:
477
+ /// - The caller must make sure that the wallet is in the
478
+ /// Live or MovingFunds state.
479
+ function closeWallet(Data storage self, bytes20 walletPubKeyHash) internal {
480
+ Wallet storage wallet = self.registeredWallets[walletPubKeyHash];
481
+
482
+ wallet.state = WalletState.Closed;
483
+
484
+ emit WalletClosed(wallet.ecdsaWalletID, walletPubKeyHash);
485
+
486
+ self.registry.closeWallet(wallet.ecdsaWalletID);
487
+ }
476
488
 
477
489
  /// @notice Reports about a fraud committed by the given wallet. This
478
490
  /// function performs slashing and wallet termination in reaction
@@ -482,9 +494,19 @@ library Wallets {
482
494
  /// @dev Requirements:
483
495
  /// - Wallet must be in Live or MovingFunds state
484
496
  function notifyFraud(Data storage self, bytes20 walletPubKeyHash) external {
485
- // TODO: Perform slashing of wallet operators and add unit tests for that.
497
+ WalletState walletState = self
498
+ .registeredWallets[walletPubKeyHash]
499
+ .state;
500
+
501
+ require(
502
+ walletState == WalletState.Live ||
503
+ walletState == WalletState.MovingFunds,
504
+ "ECDSA wallet must be in Live or MovingFunds state"
505
+ );
486
506
 
487
507
  terminateWallet(self, walletPubKeyHash);
508
+
509
+ // TODO: Perform slashing of wallet operators and add unit tests for that.
488
510
  }
489
511
 
490
512
  /// @notice Terminates the given wallet and notifies the ECDSA registry
@@ -494,16 +516,12 @@ library Wallets {
494
516
  /// creation immediately.
495
517
  /// @param walletPubKeyHash 20-byte public key hash of the wallet
496
518
  /// @dev Requirements:
497
- /// - Wallet must be in Live or MovingFunds state
519
+ /// - The caller must make sure that the wallet is in the
520
+ /// Live or MovingFunds state.
498
521
  function terminateWallet(Data storage self, bytes20 walletPubKeyHash)
499
522
  internal
500
523
  {
501
524
  Wallet storage wallet = self.registeredWallets[walletPubKeyHash];
502
- require(
503
- wallet.state == WalletState.Live ||
504
- wallet.state == WalletState.MovingFunds,
505
- "ECDSA wallet must be in Live or MovingFunds state"
506
- );
507
525
 
508
526
  wallet.state = WalletState.Terminated;
509
527
 
@@ -518,4 +536,56 @@ library Wallets {
518
536
 
519
537
  self.registry.closeWallet(wallet.ecdsaWalletID);
520
538
  }
539
+
540
+ /// @notice Notifies that the wallet completed the moving funds process
541
+ /// successfully. Checks if the funds were moved to the expected
542
+ /// target wallets. Closes the source wallet if everything went
543
+ /// good and reverts otherwise.
544
+ /// @param walletPubKeyHash 20-byte public key hash of the wallet
545
+ /// @param targetWalletsHash 32-byte keccak256 hash over the list of
546
+ /// 20-byte public key hashes of the target wallets actually used
547
+ /// within the moving funds transactions.
548
+ /// @dev Requirements:
549
+ /// - The caller must make sure the moving funds transaction actually
550
+ /// happened on Bitcoin chain and fits the protocol requirements.
551
+ /// - The source wallet must be in the MovingFunds state
552
+ /// - The target wallets commitment must be submitted by the source
553
+ /// wallet.
554
+ /// - The actual target wallets used in the moving funds transaction
555
+ /// must be exactly the same as the target wallets commitment.
556
+ function notifyFundsMoved(
557
+ Data storage self,
558
+ bytes20 walletPubKeyHash,
559
+ bytes32 targetWalletsHash
560
+ ) external {
561
+ Wallet storage wallet = self.registeredWallets[walletPubKeyHash];
562
+ // Check that the wallet is in the MovingFunds state but don't check
563
+ // if the moving funds timeout is exceeded. That should give a
564
+ // possibility to move funds in case when timeout was hit but was
565
+ // not reported yet.
566
+ require(
567
+ wallet.state == WalletState.MovingFunds,
568
+ "ECDSA wallet must be in MovingFunds state"
569
+ );
570
+
571
+ bytes32 targetWalletsCommitmentHash = wallet
572
+ .movingFundsTargetWalletsCommitmentHash;
573
+
574
+ require(
575
+ targetWalletsCommitmentHash != bytes32(0),
576
+ "Target wallets commitment not submitted yet"
577
+ );
578
+
579
+ // Make sure that the target wallets where funds were moved to are
580
+ // exactly the same as the ones the source wallet committed to.
581
+ require(
582
+ targetWalletsCommitmentHash == targetWalletsHash,
583
+ "Target wallets don't correspond to the commitment"
584
+ );
585
+
586
+ // If funds were moved, the wallet has no longer a main UTXO.
587
+ delete wallet.mainUtxoHash;
588
+
589
+ closeWallet(self, walletPubKeyHash);
590
+ }
521
591
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keep-network/tbtc-v2",
3
- "version": "0.1.1-dev.31+main.749b9391b64c3a3fcc1eb2a1412a5f5620189e34",
3
+ "version": "0.1.1-dev.34+main.2216025329269acf92935ba67297c49576c45fae",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "artifacts/",