Skip to content

Commit

Permalink
Merge branch 'tbtc-depositor' into acre-sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
r-czajkowski committed Jan 22, 2024
2 parents 419b962 + 5bd8a40 commit 6eeba61
Show file tree
Hide file tree
Showing 112 changed files with 4,734 additions and 503 deletions.
33 changes: 20 additions & 13 deletions .github/workflows/core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defaults:
working-directory: ./core

jobs:
core-format:
core-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -30,10 +30,20 @@ jobs:
- name: Install Dependencies
run: pnpm install --prefer-offline --frozen-lockfile

- name: Format
run: pnpm run format
- name: Build
run: pnpm run build

core-build:
- name: Upload Build Artifacts
uses: actions/upload-artifact@v3
with:
name: core-build
path: |
core/build/
core/typechain/
if-no-files-found: error

core-format:
needs: [core-build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -50,17 +60,14 @@ jobs:
- name: Install Dependencies
run: pnpm install --prefer-offline --frozen-lockfile

- name: Build
run: pnpm run build

- name: Upload Build Artifacts
uses: actions/upload-artifact@v3
- name: Download Build Artifacts
uses: actions/download-artifact@v3
with:
name: core-build
path: |
core/build/
core/typechain/
if-no-files-found: error
path: core/

- name: Format
run: pnpm run format

core-slither:
needs: [core-build]
Expand Down
16 changes: 15 additions & 1 deletion core/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,19 @@
]
}
]
}
},
"overrides": [
{
"files": ["deploy/*.ts"],
"rules": {
"@typescript-eslint/unbound-method": "off"
}
},
{
"files": ["*.test.ts"],
"rules": {
"@typescript-eslint/no-unused-expressions": "off"
}
}
]
}
1 change: 1 addition & 0 deletions core/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ build/
cache/
export.json
export/
gen/
typechain/
196 changes: 193 additions & 3 deletions core/contracts/Acre.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
pragma solidity ^0.8.21;

import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./Dispatcher.sol";

/// @title Acre
/// @notice This contract implements the ERC-4626 tokenized vault standard. By
Expand All @@ -14,12 +17,160 @@ import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.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 Acre is ERC4626 {
contract Acre is ERC4626, Ownable {
using SafeERC20 for IERC20;

/// Dispatcher contract that routes tBTC from Acre to a given vault and back.
Dispatcher public dispatcher;

/// Address of the treasury wallet, where fees should be transferred to.
address public treasury;

/// Minimum amount for a single deposit operation. The value should be set
/// low enough so the deposits routed through TbtcDepositor contract won't
/// be rejected. It means that minimumDepositAmount should be lower than
/// tBTC protocol's depositDustThreshold reduced by all the minting fees taken
/// before depositing in the Acre contract.
uint256 public minimumDepositAmount;

/// Maximum total amount of tBTC token held by Acre.
uint256 public maximumTotalAssets;

/// Emitted when a referral is used.
/// @param referral Used for referral program.
/// @param assets Amount of tBTC tokens staked.
event StakeReferral(uint16 indexed referral, uint256 assets);

/// Emitted when the treasury wallet address is updated.
/// @param treasury New treasury wallet address.
event TreasuryUpdated(address treasury);

/// Emitted when deposit parameters are updated.
/// @param minimumDepositAmount New value of the minimum deposit amount.
/// @param maximumTotalAssets New value of the maximum total assets amount.
event DepositParametersUpdated(
uint256 minimumDepositAmount,
uint256 maximumTotalAssets
);

/// Emitted when the dispatcher contract is updated.
/// @param oldDispatcher Address of the old dispatcher contract.
/// @param newDispatcher Address of the new dispatcher contract.
event DispatcherUpdated(address oldDispatcher, address newDispatcher);

/// Reverts if the amount is less than the minimum deposit amount.
/// @param amount Amount to check.
/// @param min Minimum amount to check 'amount' against.
error DepositAmountLessThanMin(uint256 amount, uint256 min);

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

constructor(
IERC20 tbtc
) ERC4626(tbtc) ERC20("Acre Staked Bitcoin", "stBTC") {}
IERC20 _tbtc,
address _treasury
) ERC4626(_tbtc) ERC20("Acre Staked Bitcoin", "stBTC") Ownable(msg.sender) {
treasury = _treasury;
// TODO: Revisit the exact values closer to the launch.
minimumDepositAmount = 0.001 * 1e18; // 0.001 tBTC
maximumTotalAssets = 25 * 1e18; // 25 tBTC
}

/// @notice Updates treasury wallet address.
/// @param newTreasury New treasury wallet address.
function updateTreasury(address newTreasury) external onlyOwner {
// TODO: Introduce a parameters update process.
treasury = newTreasury;

emit TreasuryUpdated(newTreasury);
}

/// @notice Updates deposit parameters.
/// @dev To disable the limit for deposits, set the maximum total assets to
/// maximum (`type(uint256).max`).
/// @param _minimumDepositAmount New value of the minimum deposit amount. It
/// is the minimum amount for a single deposit operation.
/// @param _maximumTotalAssets New value of the maximum total assets amount.
/// It is the maximum amount of the tBTC token that the Acre can
/// hold.
function updateDepositParameters(
uint256 _minimumDepositAmount,
uint256 _maximumTotalAssets
) external onlyOwner {
// TODO: Introduce a parameters update process.
minimumDepositAmount = _minimumDepositAmount;
maximumTotalAssets = _maximumTotalAssets;

emit DepositParametersUpdated(
_minimumDepositAmount,
_maximumTotalAssets
);
}

// TODO: Implement a governed upgrade process that initiates an update and
// then finalizes it after a delay.
/// @notice Updates the dispatcher contract and gives it an unlimited
/// allowance to transfer staked tBTC.
/// @param newDispatcher Address of the new dispatcher contract.
function updateDispatcher(Dispatcher newDispatcher) external onlyOwner {
if (address(newDispatcher) == address(0)) {
revert ZeroAddress();
}

address oldDispatcher = address(dispatcher);

emit DispatcherUpdated(oldDispatcher, address(newDispatcher));
dispatcher = newDispatcher;

// TODO: Once withdrawal/rebalancing is implemented, we need to revoke the
// approval of the vaults share tokens from the old dispatcher and approve
// a new dispatcher to manage the share tokens.

if (oldDispatcher != address(0)) {
// Setting allowance to zero for the old dispatcher
IERC20(asset()).forceApprove(oldDispatcher, 0);
}

// Setting allowance to max for the new dispatcher
IERC20(asset()).forceApprove(address(dispatcher), type(uint256).max);
}

/// @notice Mints shares to receiver by depositing exactly amount of
/// tBTC tokens.
/// @dev Takes into account a deposit parameter, minimum deposit amount,
/// which determines the minimum amount for a single deposit operation.
/// The amount of the assets has to be pre-approved in the tBTC
/// contract.
/// @param assets Approved amount of tBTC tokens to deposit.
/// @param receiver The address to which the shares will be minted.
/// @return Minted shares.
function deposit(
uint256 assets,
address receiver
) public override returns (uint256) {
if (assets < minimumDepositAmount) {
revert DepositAmountLessThanMin(assets, minimumDepositAmount);
}

return super.deposit(assets, receiver);
}

/// @notice Mints shares to receiver by depositing tBTC tokens.
/// @dev Takes into account a deposit parameter, minimum deposit amount,
/// which determines the minimum amount for a single deposit operation.
/// The amount of the assets has to be pre-approved in the tBTC
/// contract.
/// @param shares Amount of shares to mint. To get the amount of share use
/// `previewMint`.
/// @param receiver The address to which the shares will be minted.
function mint(
uint256 shares,
address receiver
) public override returns (uint256 assets) {
if ((assets = super.mint(shares, receiver)) < minimumDepositAmount) {
revert DepositAmountLessThanMin(assets, minimumDepositAmount);
}
}

/// @notice Stakes a given amount of tBTC token and mints shares to a
/// receiver.
Expand All @@ -43,4 +194,43 @@ contract Acre is ERC4626 {

return shares;
}

/// @notice Returns the maximum amount of the tBTC token that can be
/// deposited into the vault for the receiver, through a deposit
/// call. It takes into account the deposit parameter, maximum total
/// assets, which determines the total amount of tBTC token held by
/// Acre.
/// @return The maximum amount of the tBTC token.
function maxDeposit(address) public view override returns (uint256) {
if (maximumTotalAssets == type(uint256).max) {
return type(uint256).max;
}

uint256 _totalAssets = totalAssets();

return
_totalAssets >= maximumTotalAssets
? 0
: maximumTotalAssets - _totalAssets;
}

/// @notice Returns the maximum amount of the vault shares that can be
/// minted for the receiver, through a mint call.
/// @dev Since the Acre contract limits the maximum total tBTC tokens this
/// function converts the maximum deposit amount to shares.
/// @return The maximum amount of the vault shares.
function maxMint(address receiver) public view override returns (uint256) {
uint256 _maxDeposit = maxDeposit(receiver);

// slither-disable-next-line incorrect-equality
return
_maxDeposit == type(uint256).max
? type(uint256).max
: convertToShares(_maxDeposit);
}

/// @return Returns deposit parameters.
function depositParameters() public view returns (uint256, uint256) {
return (minimumDepositAmount, maximumTotalAssets);
}
}
Loading

0 comments on commit 6eeba61

Please sign in to comment.