Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion solidity/contracts/GovernanceUtils.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-3.0-only

pragma solidity 0.8.17;
pragma solidity ^0.8.17;

library GovernanceUtils {
/// @notice Reverts if the governance delay has not passed since
Expand Down
2 changes: 1 addition & 1 deletion solidity/contracts/Timelock.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.17;
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/governance/TimelockController.sol";

Expand Down
2 changes: 1 addition & 1 deletion solidity/contracts/bank/Bank.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// ▐████▌ ▐████▌
// ▐████▌ ▐████▌

pragma solidity 0.8.17;
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/access/Ownable.sol";

Expand Down
2 changes: 1 addition & 1 deletion solidity/contracts/bank/IReceiveBalanceApproval.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// ▐████▌ ▐████▌
// ▐████▌ ▐████▌

pragma solidity 0.8.17;
pragma solidity ^0.8.17;

/// @title IReceiveBalanceApproval
/// @notice `IReceiveBalanceApproval` is an interface for a smart contract
Expand Down
2 changes: 1 addition & 1 deletion solidity/contracts/bridge/BitcoinTx.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// ▐████▌ ▐████▌
// ▐████▌ ▐████▌

pragma solidity 0.8.17;
pragma solidity ^0.8.17;

import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";
import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
Expand Down
83 changes: 82 additions & 1 deletion solidity/contracts/bridge/Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// ▐████▌ ▐████▌
// ▐████▌ ▐████▌

pragma solidity 0.8.17;
pragma solidity ^0.8.17;

import "@keep-network/random-beacon/contracts/Governable.sol";
import "@keep-network/random-beacon/contracts/ReimbursementPool.sol";
Expand All @@ -32,6 +32,7 @@ import "./EcdsaLib.sol";
import "./Wallets.sol";
import "./Fraud.sol";
import "./MovingFunds.sol";
import "./ReservedDeposit.sol";

import "../bank/IReceiveBalanceApproval.sol";
import "../bank/Bank.sol";
Expand Down Expand Up @@ -70,6 +71,7 @@ contract Bridge is
using MovingFunds for BridgeState.Storage;
using Wallets for BridgeState.Storage;
using Fraud for BridgeState.Storage;
using ReservedDeposit for BridgeState.Storage;

BridgeState.Storage internal self;

Expand Down Expand Up @@ -464,6 +466,52 @@ contract Bridge is
self.submitDepositSweepProof(sweepTx, sweepProof, mainUtxo, vault);
}

/// @notice Reveals a deposit with UTXO reservation for tax-efficient custody.
/// Allows depositors to reserve a specific Bitcoin UTXO and later redeem
/// the exact same BTC, maintaining custody continuity for tax purposes.
/// The depositor receives tBTC minus a storage fee (0.1% annually,
/// minimum 0.01 BTC per year).
/// @param fundingTx Bitcoin funding transaction data.
/// @param reveal Deposit reveal data including wallet and refund information.
/// @param reservationDays Number of days to reserve the UTXO (1-1460 days).
/// @param btcRedemptionAddress Pre-committed Bitcoin address (20 or 32 bytes)
/// where the BTC will be sent upon redemption.
/// @dev Storage fee is split: 90% to treasury immediately, 10% reserved for
/// liquidation incentive. Maintains perfect 1:1 BTC:tBTC backing.
function revealReservedDeposit(
BitcoinTx.Info calldata fundingTx,
Deposit.DepositRevealInfo calldata reveal,
uint32 reservationDays,
bytes calldata btcRedemptionAddress
) external {
self.revealReservedDeposit(
fundingTx,
reveal,
msg.sender,
reservationDays,
btcRedemptionAddress
);
}

/// @notice Redeems a reserved deposit, returning the depositor's tBTC to the
/// Bridge and queuing a Bitcoin transaction to send the exact same
/// UTXO back to the pre-committed redemption address.
/// @param utxoHash Hash of the reserved UTXO to redeem.
/// @dev Can only be called by the original depositor before reservation expiry.
/// Burns the depositor's tBTC and sends liquidation bonus to treasury.
function redeemReservedDeposit(bytes32 utxoHash) external {
self.redeemReservedDeposit(utxoHash, msg.sender);
}

/// @notice Liquidates an expired reservation, releasing the UTXO back to the
/// general pool and rewarding the liquidator with a bonus.
/// @param utxoHash Hash of the expired reservation to liquidate.
/// @dev Liquidator receives 10% of the original storage fee as incentive.
/// Can be called by anyone after reservation expiry.
function liquidateExpiredReservation(bytes32 utxoHash) external {
self.liquidateExpiredReservation(utxoHash, msg.sender);
}

/// @notice Requests redemption of the given amount from the specified
/// wallet to the redeemer Bitcoin output script. Handles the
/// simplest case in which the redeemer's balance is decreased in
Expand Down Expand Up @@ -1636,6 +1684,39 @@ contract Bridge is
return self.liveWalletsCount;
}

/// @notice Gets details about a reserved deposit.
/// @param utxoHash The hash of the reserved UTXO.
/// @return Reserved deposit details.
function reservedDeposits(bytes32 utxoHash)
external
view
returns (BridgeState.ReservedDeposit memory)
{
return self.reservedDeposits[utxoHash];
}

/// @notice Gets details about a priority redemption request.
/// @param redemptionKey The key identifying the redemption request.
/// @return Priority redemption details.
function priorityRedemptions(bytes32 redemptionKey)
external
view
returns (BridgeState.PriorityRedemption memory)
{
return self.priorityRedemptions[redemptionKey];
}

/// @notice Gets all reservation UTXOs for a depositor.
/// @param depositor The address of the depositor.
/// @return Array of UTXO hashes for the depositor's reservations.
function getDepositorReservations(address depositor)
external
view
returns (bytes32[] memory)
{
return self.depositorReservations[depositor];
}

/// @notice Returns the fraud challenge identified by the given key built
/// as keccak256(walletPublicKey|sighash).
function fraudChallenges(uint256 challengeKey)
Expand Down
2 changes: 1 addition & 1 deletion solidity/contracts/bridge/BridgeGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// ▐████▌ ▐████▌
// ▐████▌ ▐████▌

pragma solidity 0.8.17;
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/access/Ownable.sol";
import "./BridgeGovernanceParameters.sol";
Expand Down
2 changes: 1 addition & 1 deletion solidity/contracts/bridge/BridgeGovernanceParameters.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// ▐████▌ ▐████▌
// ▐████▌ ▐████▌

pragma solidity 0.8.17;
pragma solidity ^0.8.17;

/// @title Bridge Governance library for storing updatable parameters.
library BridgeGovernanceParameters {
Expand Down
57 changes: 55 additions & 2 deletions solidity/contracts/bridge/BridgeState.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// ▐████▌ ▐████▌
// ▐████▌ ▐████▌

pragma solidity 0.8.17;
pragma solidity ^0.8.17;

import {IWalletRegistry as EcdsaWalletRegistry} from "@keep-network/ecdsa/contracts/api/IWalletRegistry.sol";
import "@keep-network/random-beacon/contracts/ReimbursementPool.sol";
Expand All @@ -28,6 +28,9 @@ import "./MovingFunds.sol";
import "../bank/Bank.sol";

library BridgeState {
bytes32 internal constant RESERVED_DEPOSIT_EXTRA_DATA =
keccak256("tbtc-reserved-deposit");

struct Storage {
// Address of the Bank the Bridge belongs to.
Bank bank;
Expand Down Expand Up @@ -320,14 +323,64 @@ library BridgeState {
// Address of the redemption watchtower. The redemption watchtower
// is responsible for vetoing redemption requests.
address redemptionWatchtower;

// Reserved Deposit Storage
mapping(bytes32 => ReservedDeposit) reservedDeposits;
mapping(address => bytes32[]) depositorReservations;
mapping(bytes32 => PriorityRedemption) priorityRedemptions;

// Reserved storage space in case we need to add more variables.
// The convention from OpenZeppelin suggests the storage space should
// add up to 50 slots. Here we want to have more slots as there are
// planned upgrades of the Bridge contract. If more entires are added to
// the struct in the upcoming versions we need to reduce the array size.
// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
// slither-disable-next-line unused-state
uint256[49] __gap;
// Reduced from 49 to 46 to account for 3 new storage slots used above
uint256[46] __gap;
}

/// @notice Represents a BTC UTXO reservation for tax-efficient custody
struct ReservedDeposit {
bytes32 utxoHash; // Hash of the UTXO
address depositor; // Original depositor address
uint64 btcAmount; // Amount of BTC deposited (satoshis)
uint64 tbtcMinted; // Amount of tBTC minted to user
uint64 treasuryFee; // Fee that went to treasury (90% of storage fee)
uint64 liquidationBonus; // Reserved for liquidator/treasury (10% of storage fee)
uint256 depositTimestamp; // When the deposit was made
uint256 expiryTimestamp; // When the reservation expires
bytes btcRedemptionAddress; // Pre-committed BTC redemption address
bytes20 walletPubKeyHash; // Wallet that holds this UTXO
uint32 fundingOutputIndex; // Output index in the funding transaction
bool isActive; // Whether reservation is still active
}

/// @notice Represents a priority redemption for reserved UTXOs
struct PriorityRedemption {
bytes32 utxoHash; // Hash of the UTXO to redeem
bytes20 walletPubKeyHash; // Wallet handling the redemption
address redeemer; // Address requesting redemption
bytes redeemerOutputScript; // Bitcoin script for redemption
uint64 requestedAmount; // Amount to redeem (satoshis)
uint64 treasuryFee; // Treasury fee (0 for reserved)
uint64 txMaxFee; // Max transaction fee
uint256 requestedAt; // When redemption was requested
bool isPriority; // Flag for priority processing
}

struct ReservationProcessingContext {
uint64 btcAmount;
uint64 totalFee;
uint64 liquidationBonus;
uint256 expiryTimestamp;
}

struct ReservedSweepResult {
address depositor;
uint256 mintAmount;
uint256 treasuryFee;
uint256 liquidationBonus;
}

event DepositParametersUpdated(
Expand Down
2 changes: 1 addition & 1 deletion solidity/contracts/bridge/Deposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// ▐████▌ ▐████▌
// ▐████▌ ▐████▌

pragma solidity 0.8.17;
pragma solidity ^0.8.17;

import {BTCUtils} from "@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol";
import {BytesLib} from "@keep-network/bitcoin-spv-sol/contracts/BytesLib.sol";
Expand Down
Loading