Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pausable contracts #311

Merged
merged 23 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4a817e9
Make the `stBTC` contract pausable
r-czajkowski Mar 11, 2024
b2cc6a0
Make the `AcreBitcoinDepositor` contract pausable
r-czajkowski Mar 11, 2024
ee9c231
Merge branch 'main' into pausable-contracts
r-czajkowski Mar 13, 2024
6db130a
Extract common ZeroAddress error to a seprate file
r-czajkowski Mar 13, 2024
32078eb
Create `AbstractPausable` pausable contract
r-czajkowski Mar 13, 2024
57776d4
Update `AcreBitcoinDepositor` contract
r-czajkowski Mar 13, 2024
5926881
Update `stBTC` contract
r-czajkowski Mar 13, 2024
6c79d51
Remove unnecessary imports
r-czajkowski Mar 13, 2024
7ee3d56
Remove pausing from `AcreBitcoinDepositor`
r-czajkowski Mar 18, 2024
8a3fc66
Rename variable
r-czajkowski Mar 18, 2024
42b61f4
Allow an owner to trigger the stop mechanism
r-czajkowski Mar 18, 2024
ef0ee41
Fix test case
r-czajkowski Mar 18, 2024
176aaa5
Make the `pauseAdmin` variable public
r-czajkowski Mar 18, 2024
eca8344
Fix variable names
r-czajkowski Mar 18, 2024
e94bbc1
Update docs in `AbstractPausable` contract
r-czajkowski Mar 18, 2024
735bc44
Rename error
r-czajkowski Mar 18, 2024
65cf6b4
Refactor access fns in `AbstractPausable` contract
r-czajkowski Mar 18, 2024
2bc3976
Move the init functions to the top
r-czajkowski Mar 18, 2024
4cf5520
Rename `AbstractPausbale` to `PausableOwnable`
r-czajkowski Mar 18, 2024
0824fab
Merge branch 'main' into pausable-contracts
r-czajkowski Mar 18, 2024
acd2bb8
Merge remote-tracking branch 'origin/main' into pausable-contracts
nkuba Apr 4, 2024
41a49cd
Move deployment script for pasue admin
nkuba Apr 4, 2024
349af5d
Add gap in PausableOwnable contract
nkuba Apr 4, 2024
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: 0 additions & 2 deletions core/contracts/AcreBitcoinDepositor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import "@keep-network/tbtc-v2/contracts/integrator/AbstractTBTCDepositor.sol";

import {stBTC} from "./stBTC.sol";

// TODO: Make Pausable

/// @title Acre Bitcoin Depositor contract.
/// @notice The contract integrates Acre staking with tBTC minting.
/// User who wants to stake BTC in Acre should submit a Bitcoin transaction
Expand Down
104 changes: 104 additions & 0 deletions core/contracts/PausableOwnable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.21;

import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";

import {ZeroAddress} from "./utils/Errors.sol";

/// @title PausableOwnable
/// @notice This abstract contract extracts a common part of the emergency stop
/// mechanism. The emergency stop mechanism can be triggered by an
/// authorized account. Only owner of the contract can update pause
/// admin address.
abstract contract PausableOwnable is
PausableUpgradeable,
Ownable2StepUpgradeable
{
/// @notice An authorized account that can trigger emergency stop mechanism.
address public pauseAdmin;

// Reserved storage space that allows adding more variables without affecting
// the storage layout of the child contracts. The convention from OpenZeppelin
// suggests the storage space should add up to 50 slots. If more variables are
// added in the upcoming versions one need to reduce the array size accordingly.
// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
// slither-disable-next-line unused-state
uint256[49] private __gap;

/// @notice Emitted when a pause admin address is updated.
/// @param newAccount New pause admin address.
/// @param oldAccount Old pause admin address.
event PauseAdminUpdated(address newAccount, address oldAccount);

/// @notice Reverts when an unauthorized account triggers the emergency stop
/// mechanism.
error PausableUnauthorizedAccount(address account);

/// @notice Reverts if called by any account other than the pause admin
/// or the contract owner.
modifier onlyPauseAdminOrOwner() {
address msgSender = _msgSender();

if (pauseAdmin != msgSender && owner() != msgSender) {
revert PausableUnauthorizedAccount(msgSender);
}
_;
}

/// @notice Initializes the contract. MUST BE CALLED from the child
/// contract initializer.
/// @param initialOwner Initial owner of the contract.
/// @param initialPauseAdmin Initial emergency stop account that can trigger
/// the emergency stop mechanism.
// solhint-disable-next-line func-name-mixedcase
function __PausableOwnable_init(
address initialOwner,
address initialPauseAdmin
) internal onlyInitializing {
__Pausable_init();
__Ownable2Step_init();
__Ownable_init(initialOwner);
__PausableOwnable_init_unchained(initialPauseAdmin);
}

// solhint-disable-next-line func-name-mixedcase
function __PausableOwnable_init_unchained(
address initialPauseAdmin
) internal onlyInitializing {
pauseAdmin = initialPauseAdmin;
}

/// @notice Enables an emergency stop mechanism.
/// @dev Requirements:
/// - The caller must be an authorized account to trigger pause.
/// - The contract must not be already paused.
// solhint-disable-next-line ordering
function pause() external onlyPauseAdminOrOwner {
_pause();
}

/// @notice Turns off the emergency stop mechanism.
/// @dev Requirements:
/// - The caller must be an authorized account to trigger unpause.
/// - The contract must be paused.
function unpause() external onlyPauseAdminOrOwner {
_unpause();
}

/// @notice Updates an authorized account that can trigger emergency stop
/// mechanism.
/// @dev Throws if called by any account other than the owner.
/// @param newPauseAdmin New account that can trigger emergency
/// stop mechanism.
function updatePauseAdmin(address newPauseAdmin) external onlyOwner {
// TODO: Introduce a parameters update process.
if (newPauseAdmin == address(0)) {
revert ZeroAddress();
}

emit PauseAdminUpdated(newPauseAdmin, pauseAdmin);

pauseAdmin = newPauseAdmin;
}
}
31 changes: 22 additions & 9 deletions core/contracts/stBTC.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
pragma solidity ^0.8.21;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";

import "./Dispatcher.sol";
import "./PausableOwnable.sol";
import "./lib/ERC4626Fees.sol";
import {ZeroAddress} from "./utils/Errors.sol";

/// @title stBTC
/// @notice This contract implements the ERC-4626 tokenized vault standard. By
Expand All @@ -18,7 +19,7 @@ import "./lib/ERC4626Fees.sol";
/// of yield-bearing vaults. This contract facilitates the minting and
/// burning of shares (stBTC), which are represented as standard ERC20
/// tokens, providing a seamless exchange with tBTC tokens.
contract stBTC is ERC4626Fees, Ownable2StepUpgradeable {
contract stBTC is ERC4626Fees, PausableOwnable {
using SafeERC20 for IERC20;

/// Dispatcher contract that routes tBTC from stBTC to a given vault and back.
Expand Down Expand Up @@ -66,9 +67,6 @@ contract stBTC is ERC4626Fees, Ownable2StepUpgradeable {
/// @param min Minimum amount to check 'amount' against.
error LessThanMinDeposit(uint256 amount, uint256 min);

/// Reverts if the address is zero.
error ZeroAddress();

/// Reverts if the address is disallowed.
error DisallowedAddress();

Expand All @@ -80,8 +78,7 @@ contract stBTC is ERC4626Fees, Ownable2StepUpgradeable {
function initialize(IERC20 asset, address _treasury) public initializer {
__ERC4626_init(asset);
__ERC20_init("Acre Staked Bitcoin", "stBTC");
__Ownable2Step_init();
__Ownable_init(msg.sender);
__PausableOwnable_init(msg.sender, msg.sender);

if (address(_treasury) == address(0)) {
revert ZeroAddress();
Expand Down Expand Up @@ -186,7 +183,7 @@ contract stBTC is ERC4626Fees, Ownable2StepUpgradeable {
function deposit(
uint256 assets,
address receiver
) public override returns (uint256) {
) public override whenNotPaused returns (uint256) {
if (assets < minimumDepositAmount) {
revert LessThanMinDeposit(assets, minimumDepositAmount);
}
Expand All @@ -211,12 +208,28 @@ contract stBTC is ERC4626Fees, Ownable2StepUpgradeable {
function mint(
uint256 shares,
address receiver
) public override returns (uint256 assets) {
) public override whenNotPaused returns (uint256 assets) {
if ((assets = super.mint(shares, receiver)) < minimumDepositAmount) {
revert LessThanMinDeposit(assets, minimumDepositAmount);
}
}

function withdraw(
uint256 assets,
address receiver,
address owner
) public override whenNotPaused returns (uint256) {
return super.withdraw(assets, receiver, owner);
}

function redeem(
uint256 shares,
address receiver,
address owner
) public override whenNotPaused returns (uint256) {
return super.redeem(shares, receiver, owner);
}

/// @notice Returns value of assets that would be exchanged for the amount of
/// shares owned by the `account`.
/// @param account Owner of shares.
Expand Down
8 changes: 3 additions & 5 deletions core/contracts/test/upgrades/stBTCV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
pragma solidity ^0.8.21;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";

import "../../Dispatcher.sol";
import "../../PausableOwnable.sol";
import "../../lib/ERC4626Fees.sol";
import {ZeroAddress} from "../../utils/Errors.sol";

/// @title stBTCV2
/// @dev This is a contract used to test stBTC upgradeability. It is a copy of
/// stBTC contract with some differences marked with `TEST:` comments.
contract stBTCV2 is ERC4626Fees, Ownable2StepUpgradeable {
contract stBTCV2 is ERC4626Fees, PausableOwnable {
using SafeERC20 for IERC20;

/// Dispatcher contract that routes tBTC from stBTC to a given vault and back.
Expand Down Expand Up @@ -64,9 +65,6 @@ contract stBTCV2 is ERC4626Fees, Ownable2StepUpgradeable {
/// @param min Minimum amount to check 'amount' against.
error LessThanMinDeposit(uint256 amount, uint256 min);

/// Reverts if the address is zero.
error ZeroAddress();

/// Reverts if the address is disallowed.
error DisallowedAddress();

Expand Down
5 changes: 5 additions & 0 deletions core/contracts/utils/Errors.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// SPDX-License-Identifier: GPL-3.0-only

pragma solidity ^0.8.21;

error ZeroAddress();
22 changes: 22 additions & 0 deletions core/deploy/14_update_pause_admin_stbtc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { HardhatRuntimeEnvironment } from "hardhat/types"
import type { DeployFunction } from "hardhat-deploy/types"

const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const { getNamedAccounts, deployments } = hre
const { deployer, pauseAdmin } = await getNamedAccounts()
const { log } = deployments

log(`updating pause admin account of stBTC contract to ${pauseAdmin}`)

await deployments.execute(
"stBTC",
{ from: deployer, log: true, waitConfirmations: 1 },
"updatePauseAdmin",
pauseAdmin,
)
}

export default func

func.tags = ["UpdatePauseAdminStBTC"]
func.dependencies = ["stBTC"]
1 change: 1 addition & 0 deletions core/deploy/21_transfer_ownership_stbtc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ export default func

func.tags = ["TransferOwnershipStBTC"]
func.dependencies = ["stBTC"]
func.runAtTheEnd = true
1 change: 1 addition & 0 deletions core/deploy/22_transfer_ownership_dispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ export default func

func.tags = ["TransferOwnershipDispatcher"]
func.dependencies = ["Dispatcher"]
func.runAtTheEnd = true
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ export default func

func.tags = ["TransferOwnershipAcreBitcoinDepositor"]
func.dependencies = ["AcreBitcoinDepositor"]
func.runAtTheEnd = true
5 changes: 5 additions & 0 deletions core/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ const config: HardhatUserConfig = {
sepolia: 0, // TODO: updated to the actual address once available
mainnet: "", // TODO: updated to the actual address once available
},
pauseAdmin: {
default: 5,
sepolia: 0, // TODO: updated to the actual address once available
mainnet: "", // TODO: updated to the actual address once available
},
},

contractSizer: {
Expand Down
Loading
Loading