Skip to content
This repository has been archived by the owner on Nov 30, 2024. It is now read-only.

Commit

Permalink
WIP: Eduena endowment fund contract
Browse files Browse the repository at this point in the history
  • Loading branch information
fiando committed Nov 15, 2024
1 parent 2992897 commit 4b30c34
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 159 deletions.
19 changes: 0 additions & 19 deletions script/Counter.s.sol

This file was deleted.

115 changes: 115 additions & 0 deletions src/Eduena.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./interfaces/IUSDe.sol";
import "forge-std/console.sol"; //NOTE: testing only

error InsufficientBalance();
error OnlyOwner();
error DepositAmountZero();
error InsufficientYield();
error ExceedsUnclaimedYield();
error NotEligibleForScholarship();

contract Eduena is ERC20, ReentrancyGuard {
using SafeERC20 for IERC20;

address public owner;
ISUSDe public sUSDe;
IERC20 public USDe;
uint256 public lastAssetValueInUSDe;
uint256 public totalUnclaimedYield;

event Deposit(address indexed donor, uint256 amount);
event Stake(uint256 amount);
event Withdraw(address indexed recipient, uint256 amount);
event YieldUpdated(uint256 newAssetValueInUSDe, uint256 yield);

constructor(address _USDe, address _sUSDe) ERC20("Eduena", "EDN") {
owner = msg.sender;
USDe = IERC20(_USDe);
sUSDe = ISUSDe(_sUSDe);
}

function deposit(uint256 amount) external nonReentrant {
if (amount == 0) revert DepositAmountZero();

USDe.safeTransferFrom(msg.sender, address(this), amount);
uint256 shares = _calculateShares(amount);
_mint(msg.sender, shares);
emit Deposit(msg.sender, amount);
_stake(amount);
updateYield();
}

function _stake(uint256 amount) internal {
USDe.approve(address(sUSDe), amount);
sUSDe.deposit(amount, address(this));
emit Stake(amount);
}

function withdraw(uint256 amount) external nonReentrant {
_withdraw(msg.sender, amount);
}

function _withdraw(address recipient, uint256 amount) private {
uint256 shares = _calculateShares(amount);
uint256 previewAmount = sUSDe.previewRedeem(shares);

_burn(msg.sender, shares);
sUSDe.transfer(recipient, amount);
emit Withdraw(recipient, previewAmount);
updateYield();
}

// This function is a simulation to demonstrate the distribution of scholarships to students.
function distribute(
address recipient,
uint256 amount
) external nonReentrant {
bool isEligible = checkEligibility(recipient);
if (!isEligible) revert NotEligibleForScholarship();

if (amount > totalUnclaimedYield) revert ExceedsUnclaimedYield();

uint256 sUSDeBalance = sUSDe.balanceOf(address(this));
if (amount > sUSDeBalance) revert InsufficientBalance();

_withdraw(recipient, amount);
}

function checkEligibility(address student) internal pure returns (bool) {
return true;
}

//FIXME: Fix the logic of this function
function updateYield() public {
uint256 sUSDeBalance = sUSDe.balanceOf(address(this));
uint256 assetValueInUSDe = sUSDe.previewMint(sUSDeBalance);

if (lastAssetValueInUSDe == 0) {
lastAssetValueInUSDe = sUSDe.balanceOf(address(this));
}

if (lastAssetValueInUSDe > 0) {
uint256 yield = assetValueInUSDe - lastAssetValueInUSDe;
uint256 shares = _calculateShares(yield);
_mint(address(this), shares);

totalUnclaimedYield += yield;
emit YieldUpdated(assetValueInUSDe, yield);
}
}

function _calculateShares(uint256 amount) internal view returns (uint256) {
if (totalSupply() == 0) {
return amount;
} else {
return (amount * totalSupply()) / lastAssetValueInUSDe;
}
}
}
114 changes: 0 additions & 114 deletions src/EduenaEndowmentFund.sol

This file was deleted.

3 changes: 2 additions & 1 deletion src/interfaces/IUSDe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ interface ISUSDe {
) external returns (uint256);

function previewRedeem(uint256 shares) external view returns (uint256);
function previewDeposit(uint256 assets) external view returns (uint256);

function previewMint(uint256 shares) external view returns (uint256);

function balanceOf(address account) external view returns (uint256);
}
6 changes: 5 additions & 1 deletion src/mocks/MockSUSDe.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ contract MockSUSDe is ERC4626 {
constructor(
ERC20 _usdeToken
) ERC4626(_usdeToken) ERC20("Mock Staked USDe", "MSUSDe") {}
}

function mint(address to, uint256 amount) public {
_mint(to, amount);
}
}
77 changes: 53 additions & 24 deletions test/EduEna.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,94 @@
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "../src/EduEna.sol";
import "../src/Eduena.sol";
import "../src/mocks/MockUSDe.sol";
import "../src/mocks/MockSUSDe.sol";

contract EduEnaTest is Test {
EduEna eduEna;
contract EduenaTest is Test {
Eduena eduena;
MockUSDe USDe;
MockSUSDe sUSDe;
address owner;
address donor;
address donor2;

function setUp() public {
owner = address(this);
donor = address(0x123);
donor2 = address(0x456);

USDe = new MockUSDe();
sUSDe = new MockSUSDe(USDe);
eduEna = new EduEna(address(USDe), address(sUSDe));
eduena = new Eduena(address(USDe), address(sUSDe));

USDe.mint(donor, 1000 ether);
USDe.mint(donor2, 1000 ether);
}

function testDeposit() public {
uint256 amount = 100 ether;

vm.startPrank(donor);
USDe.approve(address(eduEna), amount);
eduEna.deposit(amount);
USDe.approve(address(eduena), amount);
eduena.deposit(amount);
vm.stopPrank();

assertEq(eduEna.donorShares(donor), amount);
assertEq(USDe.balanceOf(address(eduEna)), 0);
assertEq(sUSDe.balanceOf(address(eduEna)), amount);
assertEq(USDe.balanceOf(address(eduena)), 0);
assertEq(sUSDe.balanceOf(address(eduena)), amount);
assertEq(sUSDe.totalSupply(), amount);

//TODO: test yield, increasing the value of the asset and donor2 depositing

vm.startPrank(donor2);
USDe.approve(address(eduena), amount);
eduena.deposit(amount);
vm.stopPrank();

// assertEq(USDe.balanceOf(address(eduena)), 0);
// assertEq(sUSDe.balanceOf(address(eduena)), amount * 2);
// assertEq(sUSDe.totalSupply(), amount * 2);

// console.log(eduena.totalSupply());
// console.log(eduena.lastAssetValueInUSDe());
// console.log(eduena.totalUnclaimedYield());
}

function testWithdraw() public {
uint256 depositAmount = 100 ether;
uint256 withdrawAmount = 50 ether;
uint256 withdrawAmount = 100 ether;

vm.startPrank(donor);
USDe.approve(address(eduEna), depositAmount);
eduEna.deposit(depositAmount);
USDe.approve(address(eduena), depositAmount);
eduena.deposit(depositAmount);
vm.stopPrank();

vm.startPrank(donor);
uint256 donorBalance = USDe.balanceOf(donor);
eduEna.withdraw(withdrawAmount);
eduena.withdraw(withdrawAmount);
vm.stopPrank();

assertEq(eduEna.donorShares(donor), depositAmount - withdrawAmount);
//FIXME: assert sUSDe balance of donor, instead of USDe balance of donor
assertEq(USDe.balanceOf(donor), withdrawAmount + donorBalance);
assertEq(sUSDe.balanceOf(address(eduEna)), depositAmount - withdrawAmount);

console.log("sUSDe balance of donor: %s", sUSDe.balanceOf(donor));
console.log("USDe balance of donor: %s", USDe.balanceOf(donor));
console.log(
"sUSDe balance of eduEna contract: %s",
sUSDe.balanceOf(address(eduEna))
assertEq(USDe.balanceOf(donor), 900 ether);
assertEq(
sUSDe.balanceOf(address(eduena)),
depositAmount - withdrawAmount
);
assertEq(sUSDe.balanceOf(donor), 100 ether);
assertEq(eduena.totalUnclaimedYield(), 0);
assertEq(eduena.lastAssetValueInUSDe(), 0 ether);
}

function testDistributeScholarship() public {
// uint256 depositAmount = 100 ether;
// address student = address(0x123);
// vm.startPrank(donor);
// USDe.approve(address(eduena), depositAmount);
// eduena.deposit(depositAmount);
// vm.stopPrank();
// vm.startPrank(donor);
// eduena.distributeScholarship(student, depositAmount);
// vm.stopPrank();
// assertEq(USDe.balanceOf(student), depositAmount);
// assertEq(sUSDe.balanceOf(address(eduena)), 0);
// assertEq(sUSDe.totalSupply(), 0);
}
}

0 comments on commit 4b30c34

Please sign in to comment.