feat(token-aggregation): reports, PMM quotes, config; Engine X flash vaults
- Expand token-aggregation API (report routes), canonical tokens, pools - Add flash vault contracts + tests (indexed, DODO cwUSDC, XAUT borrow) - PMM pools JSON, deploy/export scripts, metamask verified list Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
179
test/flash/DBISEngineXSingleSidedDodoCwusdcVault.t.sol
Normal file
179
test/flash/DBISEngineXSingleSidedDodoCwusdcVault.t.sol
Normal file
@@ -0,0 +1,179 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import {Test} from "forge-std/Test.sol";
|
||||
import {DBISEngineXSingleSidedDodoCwusdcVault} from
|
||||
"../../contracts/flash/DBISEngineXSingleSidedDodoCwusdcVault.sol";
|
||||
import {MockMintableToken} from "../dbis/MockMintableToken.sol";
|
||||
|
||||
contract MockDodoPool {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
address public immutable base;
|
||||
address public immutable quote;
|
||||
uint256 public baseReserve;
|
||||
uint256 public quoteReserve;
|
||||
|
||||
constructor(address base_, address quote_) {
|
||||
base = base_;
|
||||
quote = quote_;
|
||||
}
|
||||
|
||||
function _BASE_TOKEN_() external view returns (address) {
|
||||
return base;
|
||||
}
|
||||
|
||||
function _QUOTE_TOKEN_() external view returns (address) {
|
||||
return quote;
|
||||
}
|
||||
|
||||
function querySellBase(address, uint256 payBaseAmount) external pure returns (uint256 receiveQuoteAmount, uint256 mtFee) {
|
||||
return (payBaseAmount, 0);
|
||||
}
|
||||
|
||||
function querySellQuote(address, uint256 payQuoteAmount) external pure returns (uint256 receiveBaseAmount, uint256 mtFee) {
|
||||
return (payQuoteAmount, 0);
|
||||
}
|
||||
|
||||
function getVaultReserve() external view returns (uint256, uint256) {
|
||||
return (baseReserve, quoteReserve);
|
||||
}
|
||||
|
||||
function buyShares(address) external returns (uint256 baseShare, uint256 quoteShare, uint256 lpShare) {
|
||||
uint256 baseBalance = IERC20(base).balanceOf(address(this));
|
||||
uint256 quoteBalance = IERC20(quote).balanceOf(address(this));
|
||||
baseShare = baseBalance - baseReserve;
|
||||
quoteShare = quoteBalance - quoteReserve;
|
||||
baseReserve = baseBalance;
|
||||
quoteReserve = quoteBalance;
|
||||
lpShare = baseShare < quoteShare ? baseShare : quoteShare;
|
||||
}
|
||||
}
|
||||
|
||||
contract MockDodoIntegration {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
function addLiquidity(address pool, uint256 baseAmount, uint256 quoteAmount)
|
||||
external
|
||||
returns (uint256 baseShare, uint256 quoteShare, uint256 lpShare)
|
||||
{
|
||||
require(baseAmount > 0 && quoteAmount > 0, "zero amount");
|
||||
address base = MockDodoPool(pool)._BASE_TOKEN_();
|
||||
address quote = MockDodoPool(pool)._QUOTE_TOKEN_();
|
||||
IERC20(base).safeTransferFrom(msg.sender, pool, baseAmount);
|
||||
IERC20(quote).safeTransferFrom(msg.sender, pool, quoteAmount);
|
||||
return MockDodoPool(pool).buyShares(msg.sender);
|
||||
}
|
||||
}
|
||||
|
||||
contract DBISEngineXSingleSidedDodoCwusdcVaultTest is Test {
|
||||
MockMintableToken internal cwusdc;
|
||||
MockMintableToken internal weth;
|
||||
MockDodoIntegration internal integration;
|
||||
MockDodoPool internal pool;
|
||||
DBISEngineXSingleSidedDodoCwusdcVault internal vault;
|
||||
|
||||
address internal constant FUNDER = address(0xF00D);
|
||||
address internal constant OWNER = address(0xA11CE);
|
||||
|
||||
function setUp() public {
|
||||
cwusdc = new MockMintableToken("Wrapped cWUSDC", "cWUSDC", 6, address(this));
|
||||
weth = new MockMintableToken("Wrapped Ether", "WETH", 18, address(this));
|
||||
integration = new MockDodoIntegration();
|
||||
pool = new MockDodoPool(address(cwusdc), address(weth));
|
||||
vault = new DBISEngineXSingleSidedDodoCwusdcVault(address(cwusdc), address(weth), address(integration), OWNER);
|
||||
|
||||
cwusdc.mint(FUNDER, 100_000_000);
|
||||
weth.mint(FUNDER, 1 ether);
|
||||
vm.startPrank(FUNDER);
|
||||
cwusdc.approve(address(vault), type(uint256).max);
|
||||
weth.approve(address(vault), type(uint256).max);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testAcceptsSingleSidedCwusdcAsInventoryButNotExecutable() public {
|
||||
vm.prank(FUNDER);
|
||||
vault.depositCwusdc(10_000_000);
|
||||
|
||||
(
|
||||
uint256 cwusdcBalance,
|
||||
uint256 quoteBalance,
|
||||
uint256 cwusdcInventory,
|
||||
uint256 quoteInventory,
|
||||
bool solvent,
|
||||
bool executable
|
||||
) = vault.solvencyState();
|
||||
|
||||
assertEq(cwusdcBalance, 10_000_000, "cw balance");
|
||||
assertEq(quoteBalance, 0, "quote balance");
|
||||
assertEq(cwusdcInventory, 10_000_000, "cw inventory");
|
||||
assertEq(quoteInventory, 0, "quote inventory");
|
||||
assertTrue(solvent, "single-sided inventory is solvent");
|
||||
assertFalse(executable, "single-sided inventory is not executable DODO liquidity");
|
||||
}
|
||||
|
||||
function testPromoteRequiresTwoSidedInventory() public {
|
||||
vm.prank(OWNER);
|
||||
vault.setDodoPool(address(pool));
|
||||
vm.prank(OWNER);
|
||||
vault.setCanary(1_000, 1_000, 1_000, 1_000);
|
||||
|
||||
vm.prank(FUNDER);
|
||||
vault.depositCwusdc(10_000_000);
|
||||
|
||||
vm.expectRevert(bytes("two-sided required"));
|
||||
vm.prank(OWNER);
|
||||
vault.promoteToDodo(1_000_000, 0, 0, 0, 0);
|
||||
|
||||
vm.expectRevert(bytes("insufficient quote inventory"));
|
||||
vm.prank(OWNER);
|
||||
vault.promoteToDodo(1_000_000, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
function testPromotesTwoSidedInventoryAndPassesCanary() public {
|
||||
vm.prank(OWNER);
|
||||
vault.setDodoPool(address(pool));
|
||||
vm.prank(OWNER);
|
||||
vault.setCanary(1_000, 1_000, 1_000, 1_000);
|
||||
|
||||
vm.startPrank(FUNDER);
|
||||
vault.depositCwusdc(10_000_000);
|
||||
vault.depositQuote(1 ether);
|
||||
vm.stopPrank();
|
||||
|
||||
vm.prank(OWNER);
|
||||
(uint256 baseShare, uint256 quoteShare, uint256 lpShare) =
|
||||
vault.promoteToDodo(2_000_000, 2_000_000, 2_000_000, 2_000_000, 2_000_000);
|
||||
|
||||
assertEq(baseShare, 2_000_000, "base share");
|
||||
assertEq(quoteShare, 2_000_000, "quote share");
|
||||
assertEq(lpShare, 2_000_000, "lp share");
|
||||
assertEq(vault.accountedCwusdcInventory(), 8_000_000, "remaining cw inventory");
|
||||
assertEq(vault.accountedQuoteInventory(), 1 ether - 2_000_000, "remaining quote inventory");
|
||||
assertEq(cwusdc.balanceOf(address(pool)), 2_000_000, "pool cw balance");
|
||||
assertEq(weth.balanceOf(address(pool)), 2_000_000, "pool quote balance");
|
||||
assertTrue(vault.canaryPasses(), "canary passes");
|
||||
}
|
||||
|
||||
function testCannotRescueAccountedInventory() public {
|
||||
vm.prank(FUNDER);
|
||||
vault.depositCwusdc(10_000_000);
|
||||
|
||||
vm.expectRevert(bytes("cwusdc insolvent"));
|
||||
vm.prank(OWNER);
|
||||
vault.rescueUnaccountedToken(address(cwusdc), OWNER, 1);
|
||||
}
|
||||
|
||||
function testOwnerCanWithdrawAccountedInventory() public {
|
||||
vm.prank(FUNDER);
|
||||
vault.depositCwusdc(10_000_000);
|
||||
|
||||
vm.prank(OWNER);
|
||||
vault.withdrawCwusdcInventory(OWNER, 4_000_000);
|
||||
|
||||
assertEq(vault.accountedCwusdcInventory(), 6_000_000, "inventory decremented");
|
||||
assertEq(cwusdc.balanceOf(OWNER), 4_000_000, "owner received");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user