Skip to content

Commit

Permalink
[PDE-684] add support for buying/selling into erc4626 vaults. (#158)
Browse files Browse the repository at this point in the history
* add support for buying/selling into erc4626 vaults.

* add Truefi / Mansa tests

* erc4626/erc7540 redeem functions are same, no need to check interface

---------

Co-authored-by: Alp Guneysel <alp@Alps-MacBook-Pro.local>
  • Loading branch information
ungaro and Alp Guneysel authored Jan 23, 2025
1 parent add6b56 commit dcd30cd
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 2 deletions.
17 changes: 15 additions & 2 deletions nest/src/AggregateToken.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { IERC7540 } from "./interfaces/IERC7540Base.sol";
import { ERC4626Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";

import { ERC1155Holder } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import { ComponentToken } from "./ComponentToken.sol";
import { IAggregateToken } from "./interfaces/IAggregateToken.sol";
Expand All @@ -16,11 +19,13 @@ import { ITeller } from "./interfaces/ITeller.sol";

/**
* @title AggregateToken
* @author Eugene Y. Q. Shen
* @author Eugene Y. Q. Shen, Alp Guneysel
* @notice Implementation of the abstract ComponentToken that represents a basket of ComponentTokens
*/
contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder {

bytes4 private constant IERC7540_INTERFACE_ID = type(IERC7540).interfaceId;

// Storage

/// @custom:storage-location erc7201:plume.storage.AggregateToken
Expand Down Expand Up @@ -457,7 +462,15 @@ contract AggregateToken is ComponentToken, IAggregateToken, ERC1155Holder {
emit ComponentTokenListed(componentToken);
}

uint256 componentTokenAmount = componentToken.deposit(assets, address(this), address(this));
uint256 componentTokenAmount;
if (IERC165(address(componentToken)).supportsInterface(IERC7540_INTERFACE_ID)) {
// ERC7540 deposit
componentTokenAmount = IERC7540(address(componentToken)).deposit(assets, address(this), address(this));
} else {
// ERC4626 deposit
componentTokenAmount = IERC4626(address(componentToken)).deposit(assets, address(this));
}

emit ComponentTokenBought(msg.sender, componentToken, componentTokenAmount, assets);
}

Expand Down
21 changes: 21 additions & 0 deletions nest/src/interfaces/IERC7540Base.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/**
* @title IERC7540
* @notice Interface for ERC7540 standard which extends ERC4626 with controller functionality
*/
interface IERC7540 is IERC4626, IERC165 {

function deposit(uint256 assets, address receiver, address controller) external returns (uint256 shares);

function mint(uint256 shares, address receiver, address controller) external returns (uint256 assets);

function withdraw(uint256 assets, address receiver, address controller) external returns (uint256 shares);

function redeem(uint256 shares, address receiver, address controller) external returns (uint256 assets);

}
89 changes: 89 additions & 0 deletions nest/test/integrations/Mansa.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { AggregateToken } from "../../src/AggregateToken.sol";
import { ComponentToken } from "../../src/ComponentToken.sol";

import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { Test } from "forge-std/Test.sol";
import { console } from "forge-std/console.sol";
import { console2 } from "forge-std/console2.sol";

import { IComponentToken } from "../../src/interfaces/IComponentToken.sol";

contract MansaTest is Test {

AggregateToken public aggregateToken;
ComponentToken public componentToken;

address public constant MANAGER = 0xC0A7a3AD0e5A53cEF42AB622381D0b27969c4ab5;

address public constant MANSA_TOKEN = 0xa4ba0D2fbE9E1635348746bc3D30eD00c3E91E55;
address public constant AGGREGATE_TOKEN = 0x81537d879ACc8a290a1846635a0cAA908f8ca3a6;

address private constant NEST_ADMIN_ADDRESS = 0xC0A7a3AD0e5A53cEF42AB622381D0b27969c4ab5;
address private constant BORING_VAULT_ADDRESS = 0xe644F07B1316f28a7F134998e021eA9f7135F351;

UUPSUpgradeable private constant AGGREGATE_TOKEN_PROXY =
UUPSUpgradeable(payable(0x81537d879ACc8a290a1846635a0cAA908f8ca3a6));

// Add the component token addresses
address private constant ASSET_TOKEN = 0x2DEc3B6AdFCCC094C31a2DCc83a43b5042220Ea2;

// LiquidContinuousMultiTokenVault - Credbull
address private constant COMPONENT_TOKEN = 0x4B1fC984F324D2A0fDD5cD83925124b61175f5C6;

function setUp() public {
// Fork mainnet
vm.createSelectFork(vm.envString("PLUME_RPC_URL"));

// Get contract instances
//aggregateToken = AggregateToken(AGGREGATE_TOKEN);

vm.startBroadcast(NEST_ADMIN_ADDRESS);
// Deploy new implementation
AggregateToken newAggregateTokenImpl = new AggregateToken();

// Upgrade to new implementation
AGGREGATE_TOKEN_PROXY.upgradeToAndCall(address(newAggregateTokenImpl), "");

// Get the upgraded contract instance
aggregateToken = AggregateToken(address(AGGREGATE_TOKEN_PROXY));

componentToken = ComponentToken(MANSA_TOKEN);
//aggregateToken.addComponentToken(IComponentToken(MANSA_TOKEN));
//console2.log("Added MANSA_TOKEN to component list");

vm.stopBroadcast();
}

function testBuyComponentToken() public {
uint256 amountToBuy = 1 * 1e6; // 1 USDT (assuming 6 decimals)

// Get the asset address from the component token
address assetAddress = componentToken.asset();
console.log("assetAddress", assetAddress);
// Deal some asset tokens to the aggregate token
deal(assetAddress, address(aggregateToken), amountToBuy);

vm.startPrank(MANAGER);

aggregateToken.approveComponentToken(componentToken, amountToBuy);
aggregateToken.approveAssetToken(IERC20(assetAddress), address(componentToken), amountToBuy);

IERC20(assetAddress).approve(address(MANSA_TOKEN), amountToBuy);

// 3. Finally execute the buy
aggregateToken.buyComponentToken(ComponentToken(MANSA_TOKEN), amountToBuy);
vm.stopPrank();

// Verify the purchase
//assertEq(componentToken.balanceOf(address(aggregateToken)), amountToBuy);

// Verify request state is cleared
//assertEq(componentToken.pendingDepositRequest(0, address(aggregateToken)), 0);
//assertEq(componentToken.claimableDepositRequest(0, address(aggregateToken)), 0);
}

}

0 comments on commit dcd30cd

Please sign in to comment.