Skip to content

Commit

Permalink
Update test contracts for upgrades
Browse files Browse the repository at this point in the history
Reflect changes from V1 contracts in V2 contracts defined for upgrades
testing.
  • Loading branch information
nkuba committed Apr 3, 2024
1 parent 66572c1 commit b14974e
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 425 deletions.
275 changes: 6 additions & 269 deletions core/contracts/test/upgrades/AcreBitcoinDepositorV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,65 +25,24 @@ contract AcreBitcoinDepositorV2 is
enum StakeRequestState {
Unknown,
Initialized,
Finalized,
Queued,
FinalizedFromQueue,
CancelledFromQueue
}

struct StakeRequest {
// State of the stake request.
StakeRequestState state;
// The address to which the stBTC shares will be minted. Stored only when
// request is queued.
address staker;
// tBTC token amount to stake after deducting tBTC minting fees and the
// Depositor fee. Stored only when request is queued.
uint88 queuedAmount;
Finalized
}

/// @notice Mapping of stake requests.
/// @dev The key is a deposit key identifying the deposit.
mapping(uint256 => StakeRequest) public stakeRequests;
mapping(uint256 => StakeRequestState) public stakeRequests;

/// @notice tBTC Token contract.
// TODO: Remove slither disable when introducing upgradeability.
// slither-disable-next-line immutable-states
IERC20 public tbtcToken;

/// @notice stBTC contract.
// TODO: Remove slither disable when introducing upgradeability.
// slither-disable-next-line immutable-states
stBTC public stbtc;

/// @notice Minimum amount of a single stake request (in tBTC token precision).
/// @dev This parameter should be set to a value exceeding the minimum deposit
/// amount supported by tBTC Bridge.
uint256 public minStakeAmount;

/// @notice Maximum amount of a single stake request (in tBTC token precision).
/// @dev The staking flow in the dApp is asynchronous and there is a short period
/// of time between a deposit funding transaction is made on Bitcoin chain
/// and revealed to this contract. This limit is used to gain better control
/// on the stakes queue, and reduce a risk of concurrent stake requests
/// made in the dApp being blocked by another big deposit.
uint256 public maxSingleStakeAmount;

/// @notice Maximum total assets soft limit (in tBTC token precision).
/// @dev stBTC contract defines a maximum total assets limit held by the protocol
/// that new deposits cannot exceed (hard cap). Due to the asynchronous
/// manner of Bitcoin deposits process we introduce a soft limit (soft cap)
/// set to a value lower than the hard cap to let the dApp initialize
/// Bitcoin deposits only up to the soft cap limit.
uint256 public maxTotalAssetsSoftLimit;

/// @notice Total balance of pending stake requests (in tBTC token precision).
/// @dev stBTC contract introduces limits for total deposits amount. Due to
/// asynchronous manner of the staking flow, this contract needs to track
/// balance of pending stake requests to ensure new stake request are
/// not initialized if they won't be able to finalize.
uint256 public queuedStakesBalance;

/// @notice Divisor used to compute the depositor fee taken from each deposit
/// and transferred to the treasury upon stake request finalization.
/// @dev That fee is computed as follows:
Expand Down Expand Up @@ -134,57 +93,14 @@ contract AcreBitcoinDepositorV2 is
uint256 stakedAmount
);

/// @notice Emitted when a stake request is queued.
/// @param depositKey Deposit key identifying the deposit.
/// @param caller Address that finalized the stake request.
/// @param queuedAmount Amount of queued tBTC tokens.
event StakeRequestQueued(
uint256 indexed depositKey,
address indexed caller,
uint256 queuedAmount
);

/// @notice Emitted when a stake request is finalized from the queue.
/// @dev Deposit details can be fetched from {{ ERC4626.Deposit }}
/// event emitted in the same transaction.
/// @param depositKey Deposit key identifying the deposit.
/// @param caller Address that finalized the stake request.
/// @param stakedAmount Amount of staked tBTC tokens.
event StakeRequestFinalizedFromQueue(
uint256 indexed depositKey,
address indexed caller,
uint256 stakedAmount
);

/// @notice Emitted when a queued stake request is cancelled.
/// @param depositKey Deposit key identifying the deposit.
/// @param staker Address of the staker.
/// @param amountCancelled Amount of queued tBTC tokens that got cancelled.
event StakeRequestCancelledFromQueue(
uint256 indexed depositKey,
address indexed staker,
uint256 amountCancelled
);

/// @notice Emitted when a minimum single stake amount is updated.
/// @param minStakeAmount New value of the minimum single stake
/// amount (in tBTC token precision).
event MinStakeAmountUpdated(uint256 minStakeAmount);

/// @notice Emitted when a maximum single stake amount is updated.
/// @param maxSingleStakeAmount New value of the maximum single stake
/// amount (in tBTC token precision).
event MaxSingleStakeAmountUpdated(uint256 maxSingleStakeAmount);

/// @notice Emitted when a maximum total assets soft limit is updated.
/// @param maxTotalAssetsSoftLimit New value of the maximum total assets
/// soft limit (in tBTC token precision).
event MaxTotalAssetsSoftLimitUpdated(uint256 maxTotalAssetsSoftLimit);

/// @notice Emitted when a depositor fee divisor is updated.
/// @param depositorFeeDivisor New value of the depositor fee divisor.
event DepositorFeeDivisorUpdated(uint64 depositorFeeDivisor);

// TEST: New event;
event NewEvent();

Expand All @@ -204,10 +120,6 @@ contract AcreBitcoinDepositorV2 is
StakeRequestState expectedState
);

/// @dev Attempted to initialize a stake request with a deposit amount
/// exceeding the maximum limit for a single stake amount.
error ExceededMaxSingleStake(uint256 amount, uint256 max);

/// @dev Attempted to finalize bridging with depositor's contract tBTC balance
/// lower than the calculated bridged tBTC amount. This error means
/// that Governance should top-up the tBTC reserve for bridging fees
Expand All @@ -217,31 +129,12 @@ contract AcreBitcoinDepositorV2 is
uint256 currentBalance
);

/// @dev Attempted to notify a bridging completion, while it was already
/// notified.
error BridgingCompletionAlreadyNotified();

/// @dev Attempted to finalize a stake request, while bridging completion has
/// not been notified yet.
error BridgingNotCompleted();

/// @dev Calculated depositor fee exceeds the amount of minted tBTC tokens.
error DepositorFeeExceedsBridgedAmount(
uint256 depositorFee,
uint256 bridgedAmount
);

/// @dev Attempted to call bridging finalization for a stake request for
/// which the function was already called.
error BridgingFinalizationAlreadyCalled();

/// @dev Attempted to finalize or cancel a stake request that was not added
/// to the queue, or was already finalized or cancelled.
error StakeRequestNotQueued();

/// @dev Attempted to call function by an account that is not the staker.
error CallerNotStaker();

/// @dev Attempted to set minimum stake amount to a value lower than the
/// tBTC Bridge deposit dust threshold.
error MinStakeAmountLowerThanBridgeMinDeposit(
Expand Down Expand Up @@ -292,7 +185,7 @@ contract AcreBitcoinDepositorV2 is
// We don't check if the request was already initialized, as this check
// is enforced in `_initializeDeposit` when calling the
// `Bridge.revealDepositWithExtraData` function.
(uint256 depositKey, uint256 initialDepositAmount) = _initializeDeposit(
(uint256 depositKey, ) = _initializeDeposit(
fundingTx,
reveal,
encodeExtraData(staker, referral)
Expand All @@ -304,12 +197,6 @@ contract AcreBitcoinDepositorV2 is
StakeRequestState.Initialized
);

if (initialDepositAmount > maxSingleStakeAmount)
revert ExceededMaxSingleStake(
initialDepositAmount,
maxSingleStakeAmount
);

emit StakeRequestInitialized(depositKey, msg.sender, staker);
}

Expand All @@ -318,9 +205,6 @@ contract AcreBitcoinDepositorV2 is
/// It stakes the tBTC from the given deposit into stBTC, emitting the
/// stBTC shares to the staker specified in the deposit extra data
/// and using the referral provided in the extra data.
/// @dev In case depositing in stBTC vault fails (e.g. because of the
/// maximum deposit limit being reached), the `queueStake` function
/// should be called to add the stake request to the staking queue.
/// @param depositKey Deposit key identifying the deposit.
function finalizeStake(uint256 depositKey) external {
transitionStakeRequestState(
Expand All @@ -339,104 +223,6 @@ contract AcreBitcoinDepositorV2 is
stbtc.deposit(amountToStake, staker);
}

/// @notice This function should be called for previously initialized stake
/// request, after tBTC bridging process was finalized, in case the
/// `finalizeStake` failed due to stBTC vault deposit limit
/// being reached.
/// @dev It queues the stake request, until the stBTC vault is ready to
/// accept the deposit. The request must be finalized with `finalizeQueuedStake`
/// after the limit is increased or other user withdraws their funds
/// from the stBTC contract to make place for another deposit.
/// The staker has a possibility to submit `cancelQueuedStake` that
/// will withdraw the minted tBTC token and abort staking process.
/// @param depositKey Deposit key identifying the deposit.
function queueStake(uint256 depositKey) external {
transitionStakeRequestState(
depositKey,
StakeRequestState.Initialized,
StakeRequestState.Queued
);

StakeRequest storage request = stakeRequests[depositKey];

uint256 amountToQueue;
(amountToQueue, request.staker) = finalizeBridging(depositKey);

request.queuedAmount = SafeCast.toUint88(amountToQueue);

// Increase pending stakes balance.
queuedStakesBalance += amountToQueue;

emit StakeRequestQueued(depositKey, msg.sender, amountToQueue);
}

/// @notice This function should be called for previously queued stake
/// request, when stBTC vault is able to accept a deposit.
/// @param depositKey Deposit key identifying the deposit.
function finalizeQueuedStake(uint256 depositKey) external {
transitionStakeRequestState(
depositKey,
StakeRequestState.Queued,
StakeRequestState.FinalizedFromQueue
);

StakeRequest storage request = stakeRequests[depositKey];

if (request.queuedAmount == 0) revert StakeRequestNotQueued();

uint256 amountToStake = request.queuedAmount;
delete (request.queuedAmount);

// Decrease pending stakes balance.
queuedStakesBalance -= amountToStake;

emit StakeRequestFinalizedFromQueue(
depositKey,
msg.sender,
amountToStake
);

// Deposit tBTC in stBTC.
tbtcToken.safeIncreaseAllowance(address(stbtc), amountToStake);
// slither-disable-next-line unused-return
stbtc.deposit(amountToStake, request.staker);
}

/// @notice Cancel queued stake.
/// The function can be called by the staker to recover tBTC that cannot
/// be finalized to stake in stBTC contract due to a deposit limit being
/// reached.
/// @dev This function can be called only after the stake request was added
/// to queue.
/// @dev Only staker provided in the extra data of the stake request can
/// call this function.
/// @param depositKey Deposit key identifying the deposit.
function cancelQueuedStake(uint256 depositKey) external {
transitionStakeRequestState(
depositKey,
StakeRequestState.Queued,
StakeRequestState.CancelledFromQueue
);

StakeRequest storage request = stakeRequests[depositKey];

uint256 amount = request.queuedAmount;
if (amount == 0) revert StakeRequestNotQueued();

address staker = request.staker;
// Check if caller is the staker.
if (msg.sender != staker) revert CallerNotStaker();

delete (request.queuedAmount);

emit StakeRequestCancelledFromQueue(depositKey, staker, amount);

// Decrease pending stakes balance.
queuedStakesBalance -= amount;

tbtcToken.safeTransfer(staker, amount);
}

/// @notice Updates the minimum stake amount.
/// @dev It requires that the new value is greater or equal to the tBTC Bridge
/// deposit dust threshold, to ensure deposit will be able to be bridged.
Expand All @@ -456,30 +242,9 @@ contract AcreBitcoinDepositorV2 is
minStakeAmount = newMinStakeAmount;

emit MinStakeAmountUpdated(newMinStakeAmount);
}

/// @notice Updates the maximum single stake amount.
/// @param newMaxSingleStakeAmount New maximum single stake amount (in tBTC
/// precision).
function updateMaxSingleStakeAmount(
uint256 newMaxSingleStakeAmount
) external onlyOwner {
maxSingleStakeAmount = newMaxSingleStakeAmount;

// TEST: Emit newly added event.
emit NewEvent();
emit MaxSingleStakeAmountUpdated(newMaxSingleStakeAmount);
}

/// @notice Updates the maximum total assets soft limit.
/// @param newMaxTotalAssetsSoftLimit New maximum total assets soft limit
/// (in tBTC precision).
function updateMaxTotalAssetsSoftLimit(
uint256 newMaxTotalAssetsSoftLimit
) external onlyOwner {
maxTotalAssetsSoftLimit = newMaxTotalAssetsSoftLimit;

emit MaxTotalAssetsSoftLimitUpdated(newMaxTotalAssetsSoftLimit);
}

/// @notice Updates the depositor fee divisor.
Expand All @@ -502,34 +267,6 @@ contract AcreBitcoinDepositorV2 is
return minStakeAmount;
}

/// @notice Maximum stake amount (in tBTC token precision).
/// @dev It takes into consideration the maximum total assets soft limit (soft
/// cap), that is expected to be set below the stBTC maximum total assets
/// limit (hard cap).
/// @dev This function should be called before Bitcoin transaction funding
/// is made. The `initializeStakeRequest` function is not enforcing this
/// limit, not to block the reveal deposit operation of the concurrent
/// deposits made in the dApp in the short window between limit check,
/// submission of Bitcoin funding transaction and stake request
/// initialization.
/// @return Maximum allowed stake amount.
function maxStake() external view returns (uint256) {
uint256 currentTotalAssets = stbtc.totalAssets();

if (currentTotalAssets >= maxTotalAssetsSoftLimit) {
return 0;
}

uint256 availableLimit = maxTotalAssetsSoftLimit - currentTotalAssets;

if (queuedStakesBalance >= availableLimit) {
return 0;
}
availableLimit -= queuedStakesBalance;

return Math.min(availableLimit, maxSingleStakeAmount);
}

// TODO: Handle minimum deposit amount in tBTC Bridge vs stBTC.

/// @notice Encodes staker address and referral as extra data.
Expand Down Expand Up @@ -572,14 +309,14 @@ contract AcreBitcoinDepositorV2 is
StakeRequestState newState
) internal {
// Validate current stake request state.
if (stakeRequests[depositKey].state != expectedState)
if (stakeRequests[depositKey] != expectedState)
revert UnexpectedStakeRequestState(
stakeRequests[depositKey].state,
stakeRequests[depositKey],
expectedState
);

// Transition to a new state.
stakeRequests[depositKey].state = newState;
stakeRequests[depositKey] = newState;
}

/// @notice This function should be called for previously initialized stake
Expand Down
Loading

0 comments on commit b14974e

Please sign in to comment.