-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #101 from DyadStablecoin/feat/apxeth-vault
Add apxETH vault
- Loading branch information
Showing
6 changed files
with
226 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule v3-periphery
added at
80f26c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import "forge-std/Script.sol"; | ||
|
||
import {Parameters} from "../../src/params/Parameters.sol"; | ||
import {VaultWeETH} from "../../src/core/Vault.weETH.sol"; | ||
import {VaultApxETH} from "../../src/core/Vault.apxETH.sol"; | ||
import {VaultManager} from "../../src/core/VaultManager.sol"; | ||
import {IAggregatorV3} from "../../src/interfaces/IAggregatorV3.sol"; | ||
import {IVault} from "../../src/interfaces/IVault.sol"; | ||
import {DNft} from "../../src/core/DNft.sol"; | ||
|
||
import {ERC20} from "@solmate/src/tokens/ERC20.sol"; | ||
|
||
contract DeployVault is Script, Parameters { | ||
function run() public { | ||
vm.startBroadcast(); // ---------------------- | ||
|
||
new VaultApxETH( | ||
MAINNET_FEE_RECIPIENT, | ||
VaultManager (MAINNET_V2_VAULT_MANAGER), | ||
ERC20 (MAINNET_APXETH), | ||
IAggregatorV3(MAINNET_APXETH_ORACLE), | ||
IVault(MAINNET_V2_WETH_VAULT), | ||
DNft(MAINNET_DNFT) | ||
); | ||
|
||
vm.stopBroadcast(); // ---------------------------- | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import {IVaultManager} from "../interfaces/IVaultManager.sol"; | ||
import {IVault} from "../interfaces/IVault.sol"; | ||
import {IAggregatorV3} from "../interfaces/IAggregatorV3.sol"; | ||
import {DNft} from "./DNft.sol"; | ||
|
||
import {Owned} from "@solmate/src/auth/Owned.sol"; | ||
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; | ||
import {SafeTransferLib} from "@solmate/src/utils/SafeTransferLib.sol"; | ||
import {FixedPointMathLib} from "@solmate/src/utils/FixedPointMathLib.sol"; | ||
import {ERC20} from "@solmate/src/tokens/ERC20.sol"; | ||
|
||
contract VaultApxETH is Owned, IVault { | ||
using SafeTransferLib for ERC20; | ||
using SafeCast for int; | ||
using FixedPointMathLib for uint; | ||
|
||
error ExceedsDepositCap(); | ||
|
||
uint public constant STALE_DATA_TIMEOUT = 36 hours; | ||
|
||
IVaultManager public immutable vaultManager; | ||
ERC20 public immutable asset; | ||
IAggregatorV3 public immutable oracle; | ||
IVault public immutable wethVault; | ||
DNft public immutable dNft; | ||
|
||
uint256 public depositCap; | ||
|
||
mapping(uint => uint) public id2asset; | ||
|
||
modifier onlyVaultManager() { | ||
if (msg.sender != address(vaultManager)) revert NotVaultManager(); | ||
_; | ||
} | ||
|
||
constructor( | ||
address owner, | ||
IVaultManager _vaultManager, | ||
ERC20 _asset, | ||
IAggregatorV3 _oracle, | ||
IVault _wethVault, | ||
DNft _dNft | ||
) Owned(owner) { | ||
vaultManager = _vaultManager; | ||
asset = _asset; | ||
oracle = _oracle; | ||
wethVault = _wethVault; | ||
dNft = _dNft; | ||
|
||
depositCap = type(uint256).max; | ||
} | ||
|
||
function deposit(uint id, uint amount) external onlyVaultManager { | ||
if (asset.balanceOf(address(this)) > depositCap) { | ||
revert ExceedsDepositCap(); | ||
} | ||
id2asset[id] += amount; | ||
emit Deposit(id, amount); | ||
} | ||
|
||
function withdraw( | ||
uint id, | ||
address to, | ||
uint amount | ||
) external onlyVaultManager { | ||
id2asset[id] -= amount; | ||
asset.safeTransfer(to, amount); | ||
emit Withdraw(id, to, amount); | ||
} | ||
|
||
function move(uint from, uint to, uint amount) external onlyVaultManager { | ||
id2asset[from] -= amount; | ||
id2asset[to] += amount; | ||
emit Move(from, to, amount); | ||
} | ||
|
||
function getUsdValue(uint id) external view returns (uint) { | ||
return | ||
(id2asset[id] * assetPrice() * 1e18) / | ||
10 ** oracle.decimals() / | ||
10 ** asset.decimals(); | ||
} | ||
|
||
function balanceOf( | ||
address account | ||
) external view returns (uint256 assetBalance) { | ||
uint256 dnftBalance = dNft.balanceOf(account); | ||
for (uint256 i; i < dnftBalance; ++i) { | ||
uint256 id = dNft.tokenOfOwnerByIndex(account, i); | ||
assetBalance += id2asset[id]; | ||
} | ||
} | ||
|
||
function setDepositCap(uint _depositCap) external onlyOwner { | ||
depositCap = _depositCap; | ||
} | ||
|
||
function assetPrice() public view returns (uint) { | ||
(, int256 answer, , uint256 updatedAt, ) = oracle.latestRoundData(); | ||
if (block.timestamp > updatedAt + STALE_DATA_TIMEOUT) | ||
revert StaleData(); | ||
return answer.toUint256().mulDivDown(wethVault.assetPrice(), 10 ** wethVault.oracle().decimals()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import "forge-std/Test.sol"; | ||
import "forge-std/console.sol"; | ||
import {DNft} from "../src/core/DNft.sol"; | ||
import {Parameters} from "../src/params/Parameters.sol"; | ||
import {VaultApxETH} from "../src/core/Vault.apxETH.sol"; | ||
import {VaultManager} from "../src/core/VaultManager.sol"; | ||
import {ERC20} from "@solmate/src/tokens/ERC20.sol"; | ||
import {IAggregatorV3} from "../src/interfaces/IAggregatorV3.sol"; | ||
import {IVault} from "../src/interfaces/IVault.sol"; | ||
|
||
contract VaultApxETHTest is Test, Parameters { | ||
VaultApxETH vault; | ||
|
||
uint256 depositCap = 100 ether; | ||
|
||
function setUp() public { | ||
vault = new VaultApxETH( | ||
address(MAINNET_FEE_RECIPIENT), // owner | ||
VaultManager(MAINNET_V2_VAULT_MANAGER), | ||
ERC20(MAINNET_APXETH), | ||
IAggregatorV3(MAINNET_APXETH_ORACLE), | ||
IVault(MAINNET_V2_WETH_VAULT), | ||
DNft(MAINNET_DNFT) | ||
); | ||
|
||
vm.prank(MAINNET_FEE_RECIPIENT); | ||
vault.setDepositCap(depositCap); | ||
} | ||
|
||
function test_assetPrice() public view { | ||
uint256 price = vault.assetPrice(); | ||
console.log("price: %s", price); | ||
} | ||
|
||
function testFuzz_setDepositCapReverts(address sender) public { | ||
vm.assume(sender != address(MAINNET_FEE_RECIPIENT)); | ||
uint256 cap = 1000e18; | ||
vm.prank(sender); | ||
vm.expectRevert("UNAUTHORIZED"); | ||
vault.setDepositCap(cap); | ||
} | ||
|
||
function testFuzz_setDepositCapAsOwner(uint256 cap) public { | ||
vm.prank(MAINNET_FEE_RECIPIENT); | ||
vault.setDepositCap(cap); | ||
uint256 newCap = vault.depositCap(); | ||
assertEq(newCap, cap); | ||
} | ||
|
||
function testFuzzDepositCap( | ||
uint256 currentDeposit, | ||
uint256 depositAmount | ||
) public { | ||
vm.assume(depositAmount < type(uint128).max); | ||
vm.assume(currentDeposit < depositCap); | ||
|
||
vm.mockCall( | ||
MAINNET_APXETH, | ||
abi.encodeWithSignature("balanceOf(address)", address(vault)), | ||
abi.encode(currentDeposit + depositAmount) | ||
); | ||
|
||
vm.prank(MAINNET_V2_VAULT_MANAGER); | ||
if (currentDeposit + depositAmount > depositCap) { | ||
vm.expectRevert(VaultApxETH.ExceedsDepositCap.selector); | ||
} | ||
vault.deposit(1, depositAmount); | ||
} | ||
} |