Skip to content

Commit

Permalink
Merge branch 'v0.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
josemariasosa committed Jun 19, 2023
2 parents 88828dd + e6fdc2e commit 818bcd1
Show file tree
Hide file tree
Showing 56 changed files with 4,384 additions and 1,913 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ forge-cache/

# Sec-Ops
.env

# mac
.DS_Store
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ Finally, the `stAUR` 🪐 token is in the wild, live on AURORA `mainnet`.

The `stAUR` 🪐 token is the **Liquid Staking** token that represents a proportion of the total AURORA tokens staked in the [**Aurora Plus**](https://aurora.plus/) staking service. The staking is done through an additional smart contracts called **Depositors**. A good number of depositors to start with is two.

The **Depositors** are independent smart contracts that deposit the delegated AURORA tokens into the Aurora Plus staking service. The objective of spliting the deposits into multiple depositors is to allow deposits from one depositor, keeping the others of them without the redeem penalization.
The **Depositors** are independent smart contracts that deposit the delegated AURORA tokens into the Aurora Plus staking service. The objective of splitting the deposits into multiple depositors is to allow deposits from one depositor, keeping the others of them without the redeem penalization.

Three different contracts are needed to be deployed.

- The stAUR fungible token: ERC20, ERC4626.
- The staking manager is the contract than contains all the logic to stake, unstake and the stAUR-AURORA liquidity pool.
- The depositors are separated smart contracts that have the logic to deposit and withdraw from the Aurora plus staking service.

Using the Aurora SDK, after the liquidity pool is developed, the stAUR token could be used directy in Meta Yield.
Using the Aurora SDK, after the liquidity pool is developed, the stAUR token could be used directly in Meta Yield.

![Architecture](media/stakingAurora.png)

Expand Down Expand Up @@ -123,7 +123,7 @@ forge remappings > remappings.txt
```

```sh
## Runing the test in verbose mode.
## Running the test in verbose mode.
$ forge test -vvv
[⠆] Compiling...
No files changed, compilation skipped
Expand Down
Binary file not shown.
21 changes: 16 additions & 5 deletions contracts/Depositor.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

/// @title Meta Pool stAUR 🪐 <> AURORA depositor contract.

import "./interfaces/IAuroraStaking.sol";
import "./interfaces/IDepositor.sol";
import "./interfaces/IStakingManager.sol";
Expand All @@ -22,20 +24,20 @@ contract Depositor is AccessControl, IDepositor {
address immutable public auroraStaking;

modifier onlyManager() {
require(msg.sender == stakingManager, "ONLY_FOR_STAUR_MANAGER");
if (msg.sender != stakingManager) { revert Unauthorized(); }
_;
}

modifier onlyStAurVault() {
require(msg.sender == stAurVault, "ONLY_FOR_STAUR_VAULT");
if (msg.sender != stAurVault) { revert Unauthorized(); }
_;
}

constructor(
address _stakingManager,
address _collectRewardsRole
) {
require(_stakingManager != address(0), "INVALID_ZERO_ADDRESS");
if (_stakingManager == address(0)) { revert InvalidZeroAddress(); }
stakingManager = _stakingManager;

IStakingManager manager = IStakingManager(_stakingManager);
Expand All @@ -54,7 +56,7 @@ contract Depositor is AccessControl, IDepositor {
function updateStakingManager(
address _stakingManager
) external onlyRole(ADMIN_ROLE) {
require(_stakingManager != address(0), "INVALID_ZERO_ADDRESS");
if (_stakingManager == address(0)) { revert InvalidZeroAddress(); }
stakingManager = _stakingManager;

emit NewManagerUpdate(_stakingManager, msg.sender);
Expand Down Expand Up @@ -121,12 +123,21 @@ contract Depositor is AccessControl, IDepositor {
emit MoveRewardsToPending(address(this), _streamId);
}

/// @dev New function for release note `v.2.0`.
/// @notice Manually collect depositor Stream Rewards from Aurora Plus.
function moveAllRewardsToPending() external onlyRole(COLLECT_REWARDS_ROLE) {
IAuroraStaking(auroraStaking).moveAllRewardsToPending();

emit MoveAllRewardsToPending(address(this));
}

/// @notice Manually withdraw depositor Stream Rewards from Aurora Plus.
function withdrawRewards(
uint256 _streamId,
address _spender
) external onlyRole(COLLECT_REWARDS_ROLE) {
require(_streamId > 0, "WITHDRAW_ONLY_FOR_REWARDS");
// Withdraw only for the Rewards, the streamId 0 is reserved for AURORA.
if (_streamId == 0) { revert InvalidStreamId(); }
IAuroraStaking staking = IAuroraStaking(auroraStaking);
(,address rewardToken,,,,,,,,,) = staking.getStream(_streamId);
uint256 _amount = staking.getPending(_streamId, address(this));
Expand Down
196 changes: 196 additions & 0 deletions contracts/ERC4626Router.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.18;

/// @title Meta Pool implementation of a ERC4626 Router ☎️

/// @notice The Router was developed using the following repository as reference:
/// https://github.com/fei-protocol/ERC4626

import "@openzeppelin/contracts/interfaces/IERC4626.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC4626Router} from "./interfaces/IERC4626Router.sol";

contract ERC4626Router is IERC4626Router {
using SafeERC20 for IERC20;
using SafeERC20 for IERC4626;

constructor() {}

/// @dev tested
/// @inheritdoc IERC4626Router
function depositToVault(
IERC4626 _vault,
address _to,
uint256 _amount,
uint256 _minSharesOut
) external returns (uint256 _sharesOut) {
IERC20 asset = IERC20(_vault.asset());
_pullToken(asset, _amount, address(this));
asset.safeIncreaseAllowance(address(_vault), _amount);
return _deposit(_vault, _to, _amount, _minSharesOut);
}

/// @dev tested
/// @inheritdoc IERC4626Router
function mintToVault(
IERC4626 _vault,
address _to,
uint256 _shares,
uint256 _maxAmountIn
) external returns (uint256 _amountIn) {
IERC20 asset = IERC20(_vault.asset());
uint256 _assets = _vault.previewMint(_shares);
_pullToken(asset, _assets, address(this));
asset.safeIncreaseAllowance(address(_vault), _assets);
return _mint(_vault, _to, _shares, _maxAmountIn);
}

/// @dev tested
/// @inheritdoc IERC4626Router
function redeemFromVault(
IERC4626 _vault,
address _to,
uint256 _shares,
uint256 _minAmountOut
) external returns (uint256 _amountOut) {
// Using the vault as a safe IER20.
IERC20 vault = IERC20(_vault);
_pullToken(vault, _shares, address(this));
vault.safeIncreaseAllowance(address(_vault), _shares);
return _redeem(_vault, _to, _shares, _minAmountOut);
}

/// @dev tested
/// @inheritdoc IERC4626Router
function withdrawFromVault(
IERC4626 _vault,
address _to,
uint256 _amount,
uint256 _maxSharesOut
) external returns (uint256 _sharesOut) {
// Using the vault as a safe IER20.
IERC20 vault = IERC20(_vault);
uint256 _shares = _vault.previewWithdraw(_amount);
_pullToken(vault, _shares, address(this));
vault.safeIncreaseAllowance(address(_vault), _shares);
return _withdraw(_vault, _to, _amount, _maxSharesOut);
}

/// @notice Not for release v0.2.0.
// /// @inheritdoc IERC4626Router
// function depositMax(
// IERC4626 _vault,
// address _to,
// uint256 _minSharesOut
// ) external returns (uint256 _sharesOut) {
// IERC20 asset = IERC20(_vault.asset());
// uint256 assetBalance = asset.balanceOf(msg.sender);
// uint256 maxDeposit = _vault.maxDeposit(_to);
// uint256 amount = maxDeposit < assetBalance ? maxDeposit : assetBalance;
// _pullToken(asset, amount, address(this));
// return _deposit(_vault, _to, amount, _minSharesOut);
// }

/// @notice Not for release v0.2.0.
// /// @inheritdoc IERC4626Router
// function redeemMax(
// IERC4626 _vault,
// address _to,
// uint256 _minAmountOut
// ) external returns (uint256 _amountOut) {
// uint256 shareBalance = _vault.balanceOf(msg.sender);
// uint256 maxRedeem = _vault.maxRedeem(msg.sender);
// uint256 amountShares = maxRedeem < shareBalance ? maxRedeem : shareBalance;
// return _redeem(_vault, _to, amountShares, _minAmountOut);
// }

/// ************************
/// * Private 🦡 functions *
/// ************************

/************************** Mint **************************/

/// @notice mint `shares` from an ERC4626 vault.
/// @param _vault The ERC4626 vault to mint shares from.
/// @param _to The destination of ownership shares.
/// @param _shares The amount of shares to mint from `vault`.
/// @param _maxAmountIn The max amount of assets used to mint.
/// @return _amountIn the amount of assets used to mint by `to`.
/// @dev throws MaxAmountError
function _mint(
IERC4626 _vault,
address _to,
uint256 _shares,
uint256 _maxAmountIn
) private returns (uint256 _amountIn) {
if ((_amountIn = _vault.mint(_shares, _to)) > _maxAmountIn) {
revert MaxAmountError();
}
}

/************************** Deposit **************************/

/// @notice deposit `amount` to an ERC4626 vault.
/// @param _vault The ERC4626 vault to deposit assets to.
/// @param _to The destination of ownership shares.
/// @param _amount The amount of assets to deposit to `vault`.
/// @param _minSharesOut The min amount of `vault` shares received by `to`.
/// @return _sharesOut the amount of shares received by `to`.
/// @dev throws MinSharesError
function _deposit(
IERC4626 _vault,
address _to,
uint256 _amount,
uint256 _minSharesOut
) private returns (uint256 _sharesOut) {
if ((_sharesOut = _vault.deposit(_amount, _to)) < _minSharesOut) {
revert MinSharesError();
}
}

/************************** Withdraw **************************/

/// @notice withdraw `amount` from an ERC4626 vault.
/// @param _vault The ERC4626 vault to withdraw assets from.
/// @param _to The destination of assets.
/// @param _amount The amount of assets to withdraw from vault.
/// @param _maxSharesOut The max amount of shares to pay for assets.
/// @return _sharesOut the amount of shares received by `to`.
/// @dev throws MaxSharesError
function _withdraw(
IERC4626 _vault,
address _to,
uint256 _amount,
uint256 _maxSharesOut
) private returns (uint256 _sharesOut) {
if ((_sharesOut = _vault.withdraw(_amount, _to, address(this))) > _maxSharesOut) {
revert MaxSharesError();
}
}

/************************** Redeem **************************/

/// @notice redeem `shares` shares from an ERC4626 vault.
/// @param _vault The ERC4626 vault to redeem shares from.
/// @param _to The destination of assets.
/// @param _shares The amount of shares to redeem from vault.
/// @param _minAmountOut The min amount of assets received by `to`.
/// @return _amountOut the amount of assets received by `to`.
/// @dev throws MinAmountError
function _redeem(
IERC4626 _vault,
address _to,
uint256 _shares,
uint256 _minAmountOut
) private returns (uint256 _amountOut) {
if ((_amountOut = _vault.redeem(_shares, _to, address(this))) < _minAmountOut) {
revert MinAmountError();
}
}

/// @dev Safe Transfer funds from sender to recipient.
function _pullToken(IERC20 _token, uint256 _amount, address _recipient) private {
_token.safeTransferFrom(msg.sender, _recipient, _amount);
}
}
Loading

0 comments on commit 818bcd1

Please sign in to comment.