@keep-network/tbtc-v2 0.1.1-dev.1 → 0.1.1-dev.5
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.
- package/artifacts/TBTC.json +3 -3
- package/artifacts/TBTCToken.json +3 -3
- package/artifacts/VendingMachine.json +10 -10
- package/artifacts/solcInputs/{0c46d22cee2363c42c8bb0664dc1be66.json → d71966212a658480bad5748ad85b1396.json} +29 -17
- package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
- package/build/contracts/bank/Bank.sol/Bank.dbg.json +4 -0
- package/build/contracts/bank/Bank.sol/Bank.json +519 -0
- package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +4 -0
- package/build/contracts/bridge/Bridge.sol/Bridge.json +176 -0
- package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
- package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
- package/build/contracts/vault/IVault.sol/IVault.dbg.json +4 -0
- package/build/contracts/vault/IVault.sol/IVault.json +29 -0
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +4 -0
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +163 -0
- package/contracts/bank/Bank.sol +374 -0
- package/contracts/bridge/Bridge.sol +176 -0
- package/contracts/vault/IVault.sol +38 -0
- package/contracts/vault/TBTCVault.sol +128 -0
- package/package.json +3 -3
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
// ██████████████ ▐████▌ ██████████████
|
|
4
|
+
// ██████████████ ▐████▌ ██████████████
|
|
5
|
+
// ▐████▌ ▐████▌
|
|
6
|
+
// ▐████▌ ▐████▌
|
|
7
|
+
// ██████████████ ▐████▌ ██████████████
|
|
8
|
+
// ██████████████ ▐████▌ ██████████████
|
|
9
|
+
// ▐████▌ ▐████▌
|
|
10
|
+
// ▐████▌ ▐████▌
|
|
11
|
+
// ▐████▌ ▐████▌
|
|
12
|
+
// ▐████▌ ▐████▌
|
|
13
|
+
// ▐████▌ ▐████▌
|
|
14
|
+
// ▐████▌ ▐████▌
|
|
15
|
+
|
|
16
|
+
pragma solidity 0.8.4;
|
|
17
|
+
|
|
18
|
+
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
19
|
+
|
|
20
|
+
import "../vault/IVault.sol";
|
|
21
|
+
|
|
22
|
+
/// @title Bitcoin Bank
|
|
23
|
+
/// @notice Bank is a central component tracking Bitcoin balances. Balances can
|
|
24
|
+
/// be transferred between holders and holders can approve their
|
|
25
|
+
/// balances to be spent by others. Balances in the Bank are updated for
|
|
26
|
+
/// depositors who deposit their Bitcoin into the Bridge and only the
|
|
27
|
+
/// Bridge can increase balances.
|
|
28
|
+
/// @dev Bank is a governable contract and the Governance can upgrade the Bridge
|
|
29
|
+
/// address.
|
|
30
|
+
contract Bank is Ownable {
|
|
31
|
+
address public bridge;
|
|
32
|
+
|
|
33
|
+
/// @notice The balance of a given account in the Bank. Zero by default.
|
|
34
|
+
mapping(address => uint256) public balanceOf;
|
|
35
|
+
|
|
36
|
+
/// @notice The remaining amount of balance a spender will be
|
|
37
|
+
/// allowed to transfer on behalf of an owner using
|
|
38
|
+
/// `transferBalanceFrom`. Zero by default.
|
|
39
|
+
mapping(address => mapping(address => uint256)) public allowance;
|
|
40
|
+
|
|
41
|
+
/// @notice Returns the current nonce for EIP2612 permission for the
|
|
42
|
+
/// provided balance owner for a replay protection. Used to
|
|
43
|
+
/// construct EIP2612 signature provided to `permit` function.
|
|
44
|
+
mapping(address => uint256) public nonce;
|
|
45
|
+
|
|
46
|
+
uint256 public immutable cachedChainId;
|
|
47
|
+
bytes32 public immutable cachedDomainSeparator;
|
|
48
|
+
|
|
49
|
+
/// @notice Returns EIP2612 Permit message hash. Used to construct EIP2612
|
|
50
|
+
/// signature provided to `permit` function.
|
|
51
|
+
bytes32 public constant PERMIT_TYPEHASH =
|
|
52
|
+
keccak256(
|
|
53
|
+
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
event BalanceTransferred(
|
|
57
|
+
address indexed from,
|
|
58
|
+
address indexed to,
|
|
59
|
+
uint256 amount
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
event BalanceApproved(
|
|
63
|
+
address indexed owner,
|
|
64
|
+
address indexed spender,
|
|
65
|
+
uint256 amount
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
event BalanceIncreased(address indexed owner, uint256 amount);
|
|
69
|
+
|
|
70
|
+
event BalanceDecreased(address indexed owner, uint256 amount);
|
|
71
|
+
|
|
72
|
+
event BridgeUpdated(address newBridge);
|
|
73
|
+
|
|
74
|
+
modifier onlyBridge() {
|
|
75
|
+
require(msg.sender == address(bridge), "Caller is not the bridge");
|
|
76
|
+
_;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
constructor() {
|
|
80
|
+
cachedChainId = block.chainid;
|
|
81
|
+
cachedDomainSeparator = buildDomainSeparator();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// @notice Allows the Governance to upgrade the Bridge address.
|
|
85
|
+
/// @dev The function does not implement any governance delay and does not
|
|
86
|
+
/// check the status of the Bridge. The Governance implementation needs
|
|
87
|
+
/// to ensure all requirements for the upgrade are satisfied before
|
|
88
|
+
/// executing this function.
|
|
89
|
+
function updateBridge(address _bridge) external onlyOwner {
|
|
90
|
+
require(_bridge != address(0), "Bridge address must not be 0x0");
|
|
91
|
+
bridge = _bridge;
|
|
92
|
+
emit BridgeUpdated(_bridge);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/// @notice Moves the given `amount` of balance from the caller to
|
|
96
|
+
/// `recipient`.
|
|
97
|
+
/// @dev Requirements:
|
|
98
|
+
/// - `recipient` cannot be the zero address,
|
|
99
|
+
/// - the caller must have a balance of at least `amount`.
|
|
100
|
+
function transferBalance(address recipient, uint256 amount) external {
|
|
101
|
+
_transferBalance(msg.sender, recipient, amount);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// @notice Sets `amount` as the allowance of `spender` over the caller's
|
|
105
|
+
/// balance.
|
|
106
|
+
/// @dev If the `amount` is set to `type(uint256).max` then
|
|
107
|
+
/// `transferBalanceFrom` will not reduce an allowance.
|
|
108
|
+
/// Beware that changing an allowance with this function brings the
|
|
109
|
+
/// risk that someone may use both the old and the new allowance by
|
|
110
|
+
/// unfortunate transaction ordering. Please use
|
|
111
|
+
/// `increaseBalanceAllowance` and `decreaseBalanceAllowance` to
|
|
112
|
+
/// eliminate the risk.
|
|
113
|
+
function approveBalance(address spender, uint256 amount) external {
|
|
114
|
+
_approveBalance(msg.sender, spender, amount);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// @notice Atomically increases the balance allowance granted to `spender`
|
|
118
|
+
/// by the caller by the given `addedValue`.
|
|
119
|
+
function increaseBalanceAllowance(address spender, uint256 addedValue)
|
|
120
|
+
external
|
|
121
|
+
{
|
|
122
|
+
_approveBalance(
|
|
123
|
+
msg.sender,
|
|
124
|
+
spender,
|
|
125
|
+
allowance[msg.sender][spender] + addedValue
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// @notice Atomically decreases the balance allowance granted to `spender`
|
|
130
|
+
/// by the caller by the given `subtractedValue`.
|
|
131
|
+
function decreaseBalanceAllowance(address spender, uint256 subtractedValue)
|
|
132
|
+
external
|
|
133
|
+
{
|
|
134
|
+
uint256 currentAllowance = allowance[msg.sender][spender];
|
|
135
|
+
require(
|
|
136
|
+
currentAllowance >= subtractedValue,
|
|
137
|
+
"Can not decrease balance allowance below zero"
|
|
138
|
+
);
|
|
139
|
+
unchecked {
|
|
140
|
+
_approveBalance(
|
|
141
|
+
msg.sender,
|
|
142
|
+
spender,
|
|
143
|
+
currentAllowance - subtractedValue
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// @notice Moves `amount` of balance from `spender` to `recipient` using the
|
|
149
|
+
/// allowance mechanism. `amount` is then deducted from the caller's
|
|
150
|
+
/// allowance unless the allowance was made for `type(uint256).max`.
|
|
151
|
+
/// @dev Requirements:
|
|
152
|
+
/// - `recipient` cannot be the zero address,
|
|
153
|
+
/// - `spender` must have a balance of at least `amount`,
|
|
154
|
+
/// - the caller must have allowance for `spender`'s balance of at
|
|
155
|
+
/// least `amount`.
|
|
156
|
+
function transferBalanceFrom(
|
|
157
|
+
address spender,
|
|
158
|
+
address recipient,
|
|
159
|
+
uint256 amount
|
|
160
|
+
) external {
|
|
161
|
+
uint256 currentAllowance = allowance[spender][msg.sender];
|
|
162
|
+
if (currentAllowance != type(uint256).max) {
|
|
163
|
+
require(
|
|
164
|
+
currentAllowance >= amount,
|
|
165
|
+
"Transfer amount exceeds allowance"
|
|
166
|
+
);
|
|
167
|
+
unchecked {
|
|
168
|
+
_approveBalance(spender, msg.sender, currentAllowance - amount);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
_transferBalance(spender, recipient, amount);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// @notice EIP2612 approval made with secp256k1 signature.
|
|
175
|
+
/// Users can authorize a transfer of their balance with a signature
|
|
176
|
+
/// conforming EIP712 standard, rather than an on-chain transaction
|
|
177
|
+
/// from their address. Anyone can submit this signature on the
|
|
178
|
+
/// user's behalf by calling the permit function, paying gas fees,
|
|
179
|
+
/// and possibly performing other actions in the same transaction.
|
|
180
|
+
/// @dev The deadline argument can be set to `type(uint256).max to create
|
|
181
|
+
/// permits that effectively never expire. If the `amount` is set
|
|
182
|
+
/// to `type(uint256).max` then `transferBalanceFrom` will not
|
|
183
|
+
/// reduce an allowance. Beware that changing an allowance with this
|
|
184
|
+
/// function brings the risk that someone may use both the old and the
|
|
185
|
+
/// new allowance by unfortunate transaction ordering. Please use
|
|
186
|
+
/// `increaseBalanceAllowance` and `decreaseBalanceAllowance` to
|
|
187
|
+
/// eliminate the risk.
|
|
188
|
+
function permit(
|
|
189
|
+
address owner,
|
|
190
|
+
address spender,
|
|
191
|
+
uint256 amount,
|
|
192
|
+
uint256 deadline,
|
|
193
|
+
uint8 v,
|
|
194
|
+
bytes32 r,
|
|
195
|
+
bytes32 s
|
|
196
|
+
) external {
|
|
197
|
+
/* solhint-disable-next-line not-rely-on-time */
|
|
198
|
+
require(deadline >= block.timestamp, "Permission expired");
|
|
199
|
+
|
|
200
|
+
// Validate `s` and `v` values for a malleability concern described in EIP2.
|
|
201
|
+
// Only signatures with `s` value in the lower half of the secp256k1
|
|
202
|
+
// curve's order and `v` value of 27 or 28 are considered valid.
|
|
203
|
+
require(
|
|
204
|
+
uint256(s) <=
|
|
205
|
+
0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
|
|
206
|
+
"Invalid signature 's' value"
|
|
207
|
+
);
|
|
208
|
+
require(v == 27 || v == 28, "Invalid signature 'v' value");
|
|
209
|
+
|
|
210
|
+
bytes32 digest =
|
|
211
|
+
keccak256(
|
|
212
|
+
abi.encodePacked(
|
|
213
|
+
"\x19\x01",
|
|
214
|
+
DOMAIN_SEPARATOR(),
|
|
215
|
+
keccak256(
|
|
216
|
+
abi.encode(
|
|
217
|
+
PERMIT_TYPEHASH,
|
|
218
|
+
owner,
|
|
219
|
+
spender,
|
|
220
|
+
amount,
|
|
221
|
+
nonce[owner]++,
|
|
222
|
+
deadline
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
address recoveredAddress = ecrecover(digest, v, r, s);
|
|
228
|
+
require(
|
|
229
|
+
recoveredAddress != address(0) && recoveredAddress == owner,
|
|
230
|
+
"Invalid signature"
|
|
231
|
+
);
|
|
232
|
+
_approveBalance(owner, spender, amount);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/// @notice Increases balances of the provided `recipients` by the provided
|
|
236
|
+
/// `amounts`. Can only be called by the Bridge.
|
|
237
|
+
/// @dev Requirements:
|
|
238
|
+
/// - length of `recipients` and `amounts` must be the same.
|
|
239
|
+
function increaseBalances(
|
|
240
|
+
address[] calldata recipients,
|
|
241
|
+
uint256[] calldata amounts
|
|
242
|
+
) external onlyBridge {
|
|
243
|
+
require(
|
|
244
|
+
recipients.length == amounts.length,
|
|
245
|
+
"Arrays must have the same length"
|
|
246
|
+
);
|
|
247
|
+
for (uint256 i = 0; i < recipients.length; i++) {
|
|
248
|
+
_increaseBalance(recipients[i], amounts[i]);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/// @notice Increases balance of the provided `recipient` by the provided
|
|
253
|
+
/// `amount`. Can only be called by the Bridge.
|
|
254
|
+
function increaseBalance(address recipient, uint256 amount)
|
|
255
|
+
external
|
|
256
|
+
onlyBridge
|
|
257
|
+
{
|
|
258
|
+
_increaseBalance(recipient, amount);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/// @notice Increases the given smart contract `vault`'s balance and
|
|
262
|
+
/// notifies the `vault` contract. Called by the Bridge after
|
|
263
|
+
/// the deposits routed by depositors to that `vault` have been
|
|
264
|
+
/// swept by the Bridge. This way, the depositor does not have to
|
|
265
|
+
/// issue a separate transaction to the `vault` contract.
|
|
266
|
+
/// Can be called only by the Bridge.
|
|
267
|
+
/// @dev Requirements:
|
|
268
|
+
/// - `vault` must implement `IVault` interface,
|
|
269
|
+
/// - length of `depositors` and `depositedAmounts` must be the same.
|
|
270
|
+
/// @param vault Address of `IVault` recipient contract
|
|
271
|
+
/// @param depositors Addresses of depositors whose deposits have been swept
|
|
272
|
+
/// @param depositedAmounts Amounts deposited by individual depositors and
|
|
273
|
+
/// swept. The `vault`'s balance in the Bank will be increased by the
|
|
274
|
+
/// sum of all elements in this array.
|
|
275
|
+
function increaseBalanceAndCall(
|
|
276
|
+
address vault,
|
|
277
|
+
address[] calldata depositors,
|
|
278
|
+
uint256[] calldata depositedAmounts
|
|
279
|
+
) external onlyBridge {
|
|
280
|
+
require(
|
|
281
|
+
depositors.length == depositedAmounts.length,
|
|
282
|
+
"Arrays must have the same length"
|
|
283
|
+
);
|
|
284
|
+
uint256 totalAmount = 0;
|
|
285
|
+
for (uint256 i = 0; i < depositedAmounts.length; i++) {
|
|
286
|
+
totalAmount += depositedAmounts[i];
|
|
287
|
+
}
|
|
288
|
+
_increaseBalance(vault, totalAmount);
|
|
289
|
+
IVault(vault).onBalanceIncreased(depositors, depositedAmounts);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/// @notice Decreases caller's balance by the provided `amount`. There is no
|
|
293
|
+
/// way to restore the balance so do not call this function unless
|
|
294
|
+
/// you really know what you are doing!
|
|
295
|
+
function decreaseBalance(uint256 amount) external {
|
|
296
|
+
balanceOf[msg.sender] -= amount;
|
|
297
|
+
emit BalanceDecreased(msg.sender, amount);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/// @notice Returns hash of EIP712 Domain struct with `TBTC Bank` as
|
|
301
|
+
/// a signing domain and Bank contract as a verifying contract.
|
|
302
|
+
/// Used to construct EIP2612 signature provided to `permit`
|
|
303
|
+
/// function.
|
|
304
|
+
/* solhint-disable-next-line func-name-mixedcase */
|
|
305
|
+
function DOMAIN_SEPARATOR() public view returns (bytes32) {
|
|
306
|
+
// As explained in EIP-2612, if the DOMAIN_SEPARATOR contains the
|
|
307
|
+
// chainId and is defined at contract deployment instead of
|
|
308
|
+
// reconstructed for every signature, there is a risk of possible replay
|
|
309
|
+
// attacks between chains in the event of a future chain split.
|
|
310
|
+
// To address this issue, we check the cached chain ID against the
|
|
311
|
+
// current one and in case they are different, we build domain separator
|
|
312
|
+
// from scratch.
|
|
313
|
+
if (block.chainid == cachedChainId) {
|
|
314
|
+
return cachedDomainSeparator;
|
|
315
|
+
} else {
|
|
316
|
+
return buildDomainSeparator();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function _increaseBalance(address recipient, uint256 amount) internal {
|
|
321
|
+
require(
|
|
322
|
+
recipient != address(this),
|
|
323
|
+
"Can not increase balance for Bank"
|
|
324
|
+
);
|
|
325
|
+
balanceOf[recipient] += amount;
|
|
326
|
+
emit BalanceIncreased(recipient, amount);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function _transferBalance(
|
|
330
|
+
address spender,
|
|
331
|
+
address recipient,
|
|
332
|
+
uint256 amount
|
|
333
|
+
) private {
|
|
334
|
+
require(
|
|
335
|
+
recipient != address(0),
|
|
336
|
+
"Can not transfer to the zero address"
|
|
337
|
+
);
|
|
338
|
+
require(
|
|
339
|
+
recipient != address(this),
|
|
340
|
+
"Can not transfer to the Bank address"
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
uint256 spenderBalance = balanceOf[spender];
|
|
344
|
+
require(spenderBalance >= amount, "Transfer amount exceeds balance");
|
|
345
|
+
unchecked {balanceOf[spender] = spenderBalance - amount;}
|
|
346
|
+
balanceOf[recipient] += amount;
|
|
347
|
+
emit BalanceTransferred(spender, recipient, amount);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function _approveBalance(
|
|
351
|
+
address owner,
|
|
352
|
+
address spender,
|
|
353
|
+
uint256 amount
|
|
354
|
+
) private {
|
|
355
|
+
require(spender != address(0), "Can not approve to the zero address");
|
|
356
|
+
allowance[owner][spender] = amount;
|
|
357
|
+
emit BalanceApproved(owner, spender, amount);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function buildDomainSeparator() private view returns (bytes32) {
|
|
361
|
+
return
|
|
362
|
+
keccak256(
|
|
363
|
+
abi.encode(
|
|
364
|
+
keccak256(
|
|
365
|
+
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
|
|
366
|
+
),
|
|
367
|
+
keccak256(bytes("TBTC Bank")),
|
|
368
|
+
keccak256(bytes("1")),
|
|
369
|
+
block.chainid,
|
|
370
|
+
address(this)
|
|
371
|
+
)
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
// ██████████████ ▐████▌ ██████████████
|
|
4
|
+
// ██████████████ ▐████▌ ██████████████
|
|
5
|
+
// ▐████▌ ▐████▌
|
|
6
|
+
// ▐████▌ ▐████▌
|
|
7
|
+
// ██████████████ ▐████▌ ██████████████
|
|
8
|
+
// ██████████████ ▐████▌ ██████████████
|
|
9
|
+
// ▐████▌ ▐████▌
|
|
10
|
+
// ▐████▌ ▐████▌
|
|
11
|
+
// ▐████▌ ▐████▌
|
|
12
|
+
// ▐████▌ ▐████▌
|
|
13
|
+
// ▐████▌ ▐████▌
|
|
14
|
+
// ▐████▌ ▐████▌
|
|
15
|
+
|
|
16
|
+
pragma solidity 0.8.4;
|
|
17
|
+
|
|
18
|
+
/// @title BTC Bridge
|
|
19
|
+
/// @notice Bridge manages BTC deposit and redemption and is increasing and
|
|
20
|
+
/// decreasing balances in the Bank as a result of BTC deposit and
|
|
21
|
+
/// redemption operations.
|
|
22
|
+
///
|
|
23
|
+
/// Depositors send BTC funds to the most-recently-created-wallet of the
|
|
24
|
+
/// bridge using pay-to-script-hash (P2SH) which contains hashed
|
|
25
|
+
/// information about the depositor’s minting Ethereum address. Then,
|
|
26
|
+
/// the depositor reveals their desired Ethereum minting address to the
|
|
27
|
+
/// Ethereum chain. The Bridge listens for these sorts of messages and
|
|
28
|
+
/// when it gets one, it checks the Bitcoin network to make sure the
|
|
29
|
+
/// funds line up. If they do, the off-chain wallet may decide to pick
|
|
30
|
+
/// this transaction for sweeping, and when the sweep operation is
|
|
31
|
+
/// confirmed on the Bitcoin network, the wallet informs the Bridge
|
|
32
|
+
/// about the sweep increasing appropriate balances in the Bank.
|
|
33
|
+
/// @dev Bridge is an upgradeable component of the Bank.
|
|
34
|
+
contract Bridge {
|
|
35
|
+
struct DepositInfo {
|
|
36
|
+
uint64 amount;
|
|
37
|
+
address vault;
|
|
38
|
+
uint32 revealedAt;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// @notice Collection of all unswept deposits indexed by
|
|
42
|
+
/// keccak256(fundingTxHash | fundingOutputIndex | depositorAddress).
|
|
43
|
+
/// This mapping may contain valid and invalid deposits and the
|
|
44
|
+
/// wallet is responsible for validating them before attempting to
|
|
45
|
+
/// execute a sweep.
|
|
46
|
+
mapping(uint256 => DepositInfo) public unswept;
|
|
47
|
+
|
|
48
|
+
event DepositRevealed(
|
|
49
|
+
uint256 depositId,
|
|
50
|
+
bytes32 fundingTxHash,
|
|
51
|
+
uint8 fundingOutputIndex,
|
|
52
|
+
address depositor,
|
|
53
|
+
uint64 blindingFactor,
|
|
54
|
+
bytes refundPubKey,
|
|
55
|
+
uint64 amount,
|
|
56
|
+
address vault
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
/// @notice Used by the depositor to reveal information about their P2SH
|
|
60
|
+
/// Bitcoin deposit to the Bridge on Ethereum chain. The off-chain
|
|
61
|
+
/// wallet listens for revealed deposit events and may decide to
|
|
62
|
+
/// include the revealed deposit in the next executed sweep.
|
|
63
|
+
/// Information about the Bitcoin deposit can be revealed before or
|
|
64
|
+
/// after the Bitcoin transaction with P2SH deposit is mined on the
|
|
65
|
+
/// Bitcoin chain.
|
|
66
|
+
/// @param fundingTxHash The BTC transaction hash containing BTC P2SH
|
|
67
|
+
/// deposit funding transaction
|
|
68
|
+
/// @param fundingOutputIndex The index of the transaction output in the
|
|
69
|
+
/// funding TX with P2SH deposit, max 256
|
|
70
|
+
/// @param blindingFactor The blinding factor used in the BTC P2SH deposit,
|
|
71
|
+
/// max 2^64
|
|
72
|
+
/// @param refundPubKey The refund pub key used in the BTC P2SH deposit
|
|
73
|
+
/// @param amount The amount locked in the BTC P2SH deposit
|
|
74
|
+
/// @param vault Bank vault to which the swept deposit should be routed
|
|
75
|
+
/// @dev Requirements:
|
|
76
|
+
/// - `msg.sender` must be the Ethereum address used in the P2SH BTC deposit,
|
|
77
|
+
/// - `blindingFactor` must be the blinding factor used in the P2SH BTC deposit,
|
|
78
|
+
/// - `refundPubKey` must be the refund pub key used in the P2SH BTC deposit,
|
|
79
|
+
/// - `amount` must be the same as locked in the P2SH BTC deposit,
|
|
80
|
+
/// - BTC deposit for the given `fundingTxHash`, `fundingOutputIndex`
|
|
81
|
+
/// can be revealed by `msg.sender` only one time.
|
|
82
|
+
///
|
|
83
|
+
/// If any of these requirements is not met, the wallet _must_ refuse
|
|
84
|
+
/// to sweep the deposit and the depositor has to wait until the
|
|
85
|
+
/// deposit script unlocks to receive their BTC back.
|
|
86
|
+
function revealDeposit(
|
|
87
|
+
bytes32 fundingTxHash,
|
|
88
|
+
uint8 fundingOutputIndex,
|
|
89
|
+
uint64 blindingFactor,
|
|
90
|
+
bytes calldata refundPubKey,
|
|
91
|
+
uint64 amount,
|
|
92
|
+
address vault
|
|
93
|
+
) external {
|
|
94
|
+
uint256 depositId =
|
|
95
|
+
uint256(
|
|
96
|
+
keccak256(
|
|
97
|
+
abi.encode(fundingTxHash, fundingOutputIndex, msg.sender)
|
|
98
|
+
)
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
DepositInfo storage deposit = unswept[depositId];
|
|
102
|
+
require(deposit.revealedAt == 0, "Deposit already revealed");
|
|
103
|
+
|
|
104
|
+
deposit.amount = amount;
|
|
105
|
+
deposit.vault = vault;
|
|
106
|
+
/* solhint-disable-next-line not-rely-on-time */
|
|
107
|
+
deposit.revealedAt = uint32(block.timestamp);
|
|
108
|
+
|
|
109
|
+
emit DepositRevealed(
|
|
110
|
+
depositId,
|
|
111
|
+
fundingTxHash,
|
|
112
|
+
fundingOutputIndex,
|
|
113
|
+
msg.sender,
|
|
114
|
+
blindingFactor,
|
|
115
|
+
refundPubKey,
|
|
116
|
+
amount,
|
|
117
|
+
vault
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// @notice Used by the wallet to prove the BTC deposit sweep transaction
|
|
122
|
+
/// and to update Bank balances accordingly. Sweep is only accepted
|
|
123
|
+
/// if it satisfies SPV proof.
|
|
124
|
+
///
|
|
125
|
+
/// The function is performing Bank balance updates by first
|
|
126
|
+
/// computing the Bitcoin fee for the sweep transaction. The fee is
|
|
127
|
+
/// divided evenly between all swept deposits. Each depositor
|
|
128
|
+
/// receives a balance in the bank equal to the amount they have
|
|
129
|
+
/// declared during the reveal transaction, minus their fee share.
|
|
130
|
+
///
|
|
131
|
+
/// It is possible to prove the given sweep only one time.
|
|
132
|
+
/// @param txVersion Transaction version number (4-byte LE)
|
|
133
|
+
/// @param txInputVector All transaction inputs prepended by the number of
|
|
134
|
+
/// inputs encoded as a VarInt, max 0xFC(252) inputs
|
|
135
|
+
/// @param txOutput Single sweep transaction output
|
|
136
|
+
/// @param txLocktime Final 4 bytes of the transaction
|
|
137
|
+
/// @param merkleProof The merkle proof of transaction inclusion in a block
|
|
138
|
+
/// @param txIndexInBlock Transaction index in the block (0-indexed)
|
|
139
|
+
/// @param bitcoinHeaders Single bytestring of 80-byte bitcoin headers,
|
|
140
|
+
/// lowest height first
|
|
141
|
+
function sweep(
|
|
142
|
+
bytes4 txVersion,
|
|
143
|
+
bytes memory txInputVector,
|
|
144
|
+
bytes memory txOutput,
|
|
145
|
+
bytes4 txLocktime,
|
|
146
|
+
bytes memory merkleProof,
|
|
147
|
+
uint256 txIndexInBlock,
|
|
148
|
+
bytes memory bitcoinHeaders
|
|
149
|
+
) external {
|
|
150
|
+
// TODO We need to read `fundingTxHash`, `fundingOutputIndex` and
|
|
151
|
+
// P2SH script depositor address from `txInputVector`.
|
|
152
|
+
// We then hash them to obtain deposit identifier and read
|
|
153
|
+
// DepositInfo. From DepositInfo we know what amount was declared
|
|
154
|
+
// by the depositor in their reveal transaction and we use that
|
|
155
|
+
// amount to update their Bank balance, minus fee.
|
|
156
|
+
//
|
|
157
|
+
// TODO We need to validate if the sum in the output minus the
|
|
158
|
+
// amount from the previous wallet balance input minus fees is
|
|
159
|
+
// equal to the amount by which Bank balances were increased.
|
|
160
|
+
//
|
|
161
|
+
// TODO We need to validate txOutput to see if the balance was not
|
|
162
|
+
// transferred away from the wallet before increasing balances in
|
|
163
|
+
// the bank.
|
|
164
|
+
//
|
|
165
|
+
// TODO Delete deposit from unswept mapping or mark it as swept
|
|
166
|
+
// depending on the gas costs. Alternativly, do not allow to
|
|
167
|
+
// use the same TX input vector twice. Sweep should be provable
|
|
168
|
+
// only one time.
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// TODO It is possible a malicious wallet can sweep deposits that can not
|
|
172
|
+
// be later proved on Ethereum. For example, a deposit with
|
|
173
|
+
// an incorrect amount revealed. We need to provide a function for honest
|
|
174
|
+
// depositors, next to sweep, to prove their swept balances on Ethereum
|
|
175
|
+
// selectively, based on deposits they have earlier received.
|
|
176
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
// ██████████████ ▐████▌ ██████████████
|
|
4
|
+
// ██████████████ ▐████▌ ██████████████
|
|
5
|
+
// ▐████▌ ▐████▌
|
|
6
|
+
// ▐████▌ ▐████▌
|
|
7
|
+
// ██████████████ ▐████▌ ██████████████
|
|
8
|
+
// ██████████████ ▐████▌ ██████████████
|
|
9
|
+
// ▐████▌ ▐████▌
|
|
10
|
+
// ▐████▌ ▐████▌
|
|
11
|
+
// ▐████▌ ▐████▌
|
|
12
|
+
// ▐████▌ ▐████▌
|
|
13
|
+
// ▐████▌ ▐████▌
|
|
14
|
+
// ▐████▌ ▐████▌
|
|
15
|
+
|
|
16
|
+
pragma solidity 0.8.4;
|
|
17
|
+
|
|
18
|
+
/// @title Bank Vault interface
|
|
19
|
+
/// @notice `IVault` is an interface for a smart contract consuming Bank
|
|
20
|
+
/// balances allowing the smart contract to receive Bank balances right
|
|
21
|
+
/// after sweeping the deposit by the Bridge. This method allows the
|
|
22
|
+
/// depositor to route their deposit revealed to the Bridge to the
|
|
23
|
+
/// particular smart contract in the same transaction the deposit is
|
|
24
|
+
/// revealed. This way, the depositor does not have to execute
|
|
25
|
+
/// additional transaction after the deposit gets swept by the Bridge.
|
|
26
|
+
interface IVault {
|
|
27
|
+
/// @notice Called by the Bank in `increaseBalanceAndCall` function after
|
|
28
|
+
/// increasing the balance in the Bank for the vault.
|
|
29
|
+
/// @param depositors Addresses of depositors whose deposits have been swept
|
|
30
|
+
/// @param depositedAmounts Amounts deposited by individual depositors and
|
|
31
|
+
/// swept
|
|
32
|
+
/// @dev The implementation must ensure this function can only be called
|
|
33
|
+
/// by the Bank.
|
|
34
|
+
function onBalanceIncreased(
|
|
35
|
+
address[] calldata depositors,
|
|
36
|
+
uint256[] calldata depositedAmounts
|
|
37
|
+
) external;
|
|
38
|
+
}
|