Skip to content
Closed
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
5 changes: 3 additions & 2 deletions script/v2/DeployOracles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {Vm} from "forge-std/Vm.sol";
import {CappedOneDollarOracle} from "@level/src/v2/oracles/CappedOneDollarOracle.sol";
import {IERC4626Oracle} from "@level/src/v2/interfaces/level/IERC4626Oracle.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {AaveUmbrellaOracle} from "@level/src/v2/oracles/AaveUmbrellaOracle.sol";

import {DeploymentUtils} from "@level/script/v2/DeploymentUtils.s.sol";
import {Configurable} from "@level/config/Configurable.sol";
Expand Down Expand Up @@ -66,8 +67,8 @@ contract DeployOracles is Configurable, DeploymentUtils, Script {
console2.log("sUsdcOracle deployed to: %s", address(config.sparkVaults.sUsdc.oracle));

// Deploy waUsdcStakeTokenOracle
config.umbrellaVaults.waUsdcStakeToken.oracle =
deployERC4626Oracle(config.umbrellaVaults.waUsdcStakeToken.vault);
AaveUmbrellaOracle oracle = new AaveUmbrellaOracle(config.umbrellaVaults.waUsdcStakeToken.vault);
config.umbrellaVaults.waUsdcStakeToken.oracle = IERC4626Oracle(address(oracle));
console2.log("waUsdcStakeTokenOracle deployed to: %s", address(config.umbrellaVaults.waUsdcStakeToken.oracle));

vm.stopBroadcast();
Expand Down
48 changes: 29 additions & 19 deletions src/v2/common/libraries/VaultLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -543,25 +543,27 @@ library VaultLib {
internal
returns (uint256 staked)
{
// config.baseCollateral is the USDC/USDT token
// config.depositContract is the Aave Umbrella contract (stwaToken)
// config.receiptToken is also stwaToken
IERC4626StakeToken stakeToken = IERC4626StakeToken(_config.depositContract);
IERC4626 stataToken = IERC4626(stakeToken.asset());

// Wrap the aTokens
IERC4626StataToken stataToken = IERC4626StataToken(stakeToken.asset());
// Convert Token to waToken
vault.setTokenAllowance(address(_config.baseCollateral), address(stataToken), amount);
bytes memory sharesRaw = vault.manage(
address(stataToken), abi.encodeWithSignature("depositATokens(uint256,address)", amount, address(vault)), 0
address(stataToken), abi.encodeWithSignature("deposit(uint256,address)", amount, address(vault)), 0
);
uint256 shares = abi.decode(sharesRaw, (uint256));

uint256 waTokenBalance = abi.decode(sharesRaw, (uint256)); // waToken

// Stake waTokens with Aave Umbrella
vault.setTokenAllowance(address(stataToken), address(_config.depositContract), shares);
vault.setTokenAllowance(address(stataToken), address(stakeToken), waTokenBalance);
bytes memory stakedRaw = vault.manage(
address(_config.depositContract),
abi.encodeWithSignature("deposit(uint256,address)", shares, address(vault)),
0
address(stakeToken), abi.encodeWithSignature("deposit(uint256,address)", waTokenBalance, address(vault)), 0
);

uint256 staked_ = abi.decode(stakedRaw, (uint256));
uint256 staked_ = abi.decode(stakedRaw, (uint256)); // st-waToken

emit StakeToAaveUmbrella(address(vault), address(_config.baseCollateral), amount, staked_);

Expand All @@ -571,47 +573,55 @@ library VaultLib {
/// @notice Unstakes waTokens from Aave Umbrella
/// @param vault The vault address
/// @param _config The strategy config
/// @param amount The amount of assets to unstake
/// @param amount The amount of assets to unstake (USDC/USDT)
/// @return unstaked The amount of assets unstaked
function _unstakeFromAaveUmbrella(BoringVault vault, StrategyConfig memory _config, uint256 amount)
internal
returns (uint256 unstaked)
{
// Get cooldown snapshot
IERC4626StakeToken stakeToken = IERC4626StakeToken(_config.depositContract);
IERC4626StataToken stataToken = IERC4626StataToken(stakeToken.asset());
IERC4626 stataToken = IERC4626(stakeToken.asset()); // waToken
// cooldownSnapshot.amount is st-waToken
IERC4626StakeToken.CooldownSnapshot memory cooldownSnapshot = stakeToken.getStakerCooldown(address(vault));

if (
block.timestamp > cooldownSnapshot.endOfCooldown
&& block.timestamp - cooldownSnapshot.endOfCooldown <= cooldownSnapshot.withdrawalWindow
) {
if (amount > cooldownSnapshot.amount) {
amount = cooldownSnapshot.amount;
// How much waToken needs to be withdrawn to get the given amount of USDC/USDT
uint256 waTokenAmount = stataToken.previewWithdraw(amount);

// Check cooldown limits
uint256 maxWithdrawal = stakeToken.maxWithdraw(address(vault));
if (waTokenAmount > maxWithdrawal) {
waTokenAmount = maxWithdrawal;
}

// We're in the withdrawal window
bytes memory unstakedRaw = vault.manage(
address(stakeToken),
abi.encodeWithSignature("redeem(uint256,address,address)", amount, address(vault), address(vault)),
abi.encodeWithSignature(
"withdraw(uint256,address,address)", waTokenAmount, address(vault), address(vault)
),
0
);

uint256 wrappedATokens = abi.decode(unstakedRaw, (uint256));

bytes memory aTokensRaw = vault.manage(
bytes memory tokensRaw = vault.manage(
address(stataToken),
abi.encodeWithSignature(
"redeemATokens(uint256,address,address)", wrappedATokens, address(vault), address(vault)
"redeem(uint256,address,address)", wrappedATokens, address(vault), address(vault)
),
0
);

uint256 aTokens = abi.decode(aTokensRaw, (uint256));
uint256 tokens = abi.decode(tokensRaw, (uint256));

emit UnstakeFromAaveUmbrella(address(vault), address(_config.baseCollateral), amount, aTokens);
emit UnstakeFromAaveUmbrella(address(vault), address(_config.baseCollateral), amount, tokens);

return aTokens;
return tokens;
} else {
// We're not in the withdrawal window, need to call cooldown
revert("VaultManager: not in withdrawal window, call cooldown first");
Expand Down
63 changes: 63 additions & 0 deletions src/v2/oracles/AaveUmbrellaOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;

import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC4626Oracle} from "@level/src/v2/interfaces/level/IERC4626Oracle.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/**
* .-==+=======+:
* :---=-::-==:
* .-:-==-:-==:
* .:::--::::::. .--:-=--:--. .:--:::--..
* .=++=++:::::.. .:::---::--. ....::...:::.
* :::-::..::.. .::::-:::::. ...::...:::.
* ...::..::::.. .::::--::-:. ....::...:::..
* ............ ....:::..::. ------:......
* ........... ........:.... .....::..:.. ======-...... ...........
* :------:.:... ...:+***++*#+ .------:---. ...::::.:::... .....:-----::.
* .::::::::-:.. .::--..:-::.. .-=+===++=-==: ...:::..:--:.. .:==+=++++++*:
*
* @title AaveUmbrellaOracle
* @author Level (https://level.money)
* @notice Oracle contract for Aave Umbrella aToken vaults.
* @notice Returns the price of a staked-wrapped-aToken in terms of the underlying token
*/
contract AaveUmbrellaOracle is IERC4626Oracle {
IERC4626 public immutable stakedWrappedVault;

uint8 public immutable decimals_;
uint256 public immutable oneShare;

constructor(IERC4626 _stakedWrappedVault) {
stakedWrappedVault = _stakedWrappedVault;
IERC4626 wrappedAaveToken = IERC4626(stakedWrappedVault.asset()); // waToken

decimals_ = IERC20Metadata(wrappedAaveToken.asset()).decimals(); // decimals of underlying Token
oneShare = 10 ** stakedWrappedVault.decimals();
}

function update() external {}

function decimals() external view returns (uint8) {
return decimals_;
}

function description() external pure returns (string memory) {
return "Chainlink-compliant Aave Umbrella Oracle";
}

function version() external pure returns (uint256) {
return 1;
}

function getRoundData(uint80 /*_roundId */ ) external view returns (uint80, int256, uint256, uint256, uint80) {
return this.latestRoundData();
}

function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80) {
uint256 amountOfWrappedATokens = stakedWrappedVault.convertToAssets(oneShare); // 1 st-waToken to waToken
uint256 amountOfUnderlyingTokens = IERC4626(stakedWrappedVault.asset()).convertToAssets(amountOfWrappedATokens); // waToken to underlying Token
return (0, int256(amountOfUnderlyingTokens), block.timestamp, block.timestamp, 0);
}
}
Loading