Skip to content

Commit

Permalink
feat: created erc4626 vault and updated exchange contract
Browse files Browse the repository at this point in the history
  • Loading branch information
dannweeeee committed Oct 19, 2024
1 parent 6ffdb56 commit b3ccc78
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 10 deletions.
5 changes: 3 additions & 2 deletions script/deploy/Exchange.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ contract DeployExchange is Script {
function run() external {
vm.startBroadcast();

address registryAddress = 0x9d69b9b2907Bba74E65Aa6c87B2284fF0F0931e0;
address registryAddress = 0x2b4836d81370e37030727E4DCbd9cC5a772cf43A;
address usdcAddress = 0x036CbD53842c5426634e7929541eC2318f3dCF7e;
address vaultAddress = 0xd580248163CDD5AE3225A700E9f4e7CD525b27b0;

Exchange exchange = new Exchange(registryAddress, usdcAddress);
Exchange exchange = new Exchange(registryAddress, usdcAddress, vaultAddress);

console.log("Exchange deployed at:", address(exchange));

Expand Down
96 changes: 95 additions & 1 deletion src/Exchange.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Registry} from "./Registry.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";

/**
* @title Cube Exchange
* @author Dann Wee
* @notice Cube Exchange is a contract that allows users to transfer USDC to merchants.
*/
contract Exchange is ReentrancyGuard, Ownable {
using SafeERC20 for IERC20;

Registry public immutable registry;
IERC20 public immutable usdcToken;
IERC4626 public immutable vault;

uint256 public fee = 100; // 100 basis points fee (1%)
address public feeCollector;
Expand All @@ -21,15 +28,28 @@ contract Exchange is ReentrancyGuard, Ownable {
event FeeUpdated(uint256 newFee);
event FeeCollectorUpdated(address newFeeCollector);
event FeesWithdrawn(address indexed to, uint256 amount);
event VaultDeposit(address indexed merchant, uint256 assets, uint256 shares);
event VaultWithdraw(address indexed merchant, uint256 assets, uint256 shares);

constructor(address _registryAddress, address _usdcAddress) Ownable(msg.sender) {
constructor(address _registryAddress, address _usdcAddress, address _vaultAddress) Ownable(msg.sender) {
require(_registryAddress != address(0), "Invalid registry address");
require(_usdcAddress != address(0), "Invalid USDC address");
require(_vaultAddress != address(0), "Invalid vault address");
registry = Registry(_registryAddress);
usdcToken = IERC20(_usdcAddress);
feeCollector = address(this);
vault = IERC4626(_vaultAddress);
}

/////////////////////////
/////// FUNCTIONS ///////
/////////////////////////

/**
* @notice Transfer USDC to merchant.
* @param _uen Merchant's UEN.
* @param _amount Amount of USDC to transfer to merchant.
*/
function transferToMerchant(string memory _uen, uint256 _amount) external nonReentrant {
require(_amount > 0, "Amount must be greater than zero");

Expand All @@ -47,18 +67,92 @@ contract Exchange is ReentrancyGuard, Ownable {
emit Transfer(msg.sender, merchantWalletAddress, merchantAmount, _uen);
}

/**
* @notice Transfer USDC to vault.
* @param _uen Merchant's UEN.
* @param _amount Amount of USDC to transfer to vault.
*/
function transferToVault(string memory _uen, uint256 _amount) external nonReentrant {
require(_amount > 0, "Amount must be greater than zero");

address merchantWalletAddress = registry.getMerchantByUEN(_uen).wallet_address;
require(merchantWalletAddress != address(0), "Invalid merchant wallet address");

uint256 feeAmount = (_amount * fee) / 10000;
uint256 vaultAmount = _amount - feeAmount;

// Transfer USDC from sender to this contract
usdcToken.safeTransferFrom(msg.sender, address(this), _amount);

// Transfer fee to fee collector
if (feeAmount > 0) {
usdcToken.safeTransfer(feeCollector, feeAmount);
}

// Approve vault to spend USDC
usdcToken.approve(address(vault), vaultAmount);

// Deposit USDC into vault and mint shares to merchant
uint256 shares = vault.deposit(vaultAmount, merchantWalletAddress);

emit Transfer(msg.sender, merchantWalletAddress, vaultAmount, _uen);
emit VaultDeposit(merchantWalletAddress, vaultAmount, shares);
}

/**
* @notice Withdraw USDC from vault to merchant's wallet.
* @param _shares Amount of shares to withdraw.
*/
function withdrawToWallet(uint256 _shares) external nonReentrant {
address merchantWalletAddress = msg.sender;

// Check if the caller has enough shares
uint256 userShares = vault.balanceOf(merchantWalletAddress);
require(userShares >= _shares, "Insufficient shares");

// Check if the vault has enough assets to cover the withdrawal
uint256 assets = vault.convertToAssets(_shares);
require(vault.totalAssets() >= assets, "Insufficient vault assets");

// Approve the vault to spend shares on behalf of the user
vault.approve(address(this), _shares);

// Withdraw USDC from vault and burn shares
try vault.redeem(_shares, merchantWalletAddress, merchantWalletAddress) returns (uint256 redeemedAssets) {
require(redeemedAssets == assets, "Unexpected amount of assets redeemed");
emit VaultWithdraw(merchantWalletAddress, redeemedAssets, _shares);
} catch Error(string memory reason) {
revert(string(abi.encodePacked("Vault redeem failed: ", reason)));
} catch (bytes memory) /* lowLevelData */ {
revert("Vault redeem failed with unknown error");
}
}

/**
* @notice Set the fee.
* @param _newFee New fee.
*/
function setFee(uint256 _newFee) external onlyOwner {
require(_newFee <= 1000, "Fee cannot exceed 10%");
fee = _newFee;
emit FeeUpdated(_newFee);
}

/**
* @notice Set the fee collector.
* @param _newFeeCollector New fee collector.
*/
function setFeeCollector(address _newFeeCollector) external onlyOwner {
require(_newFeeCollector != address(0), "Invalid fee collector address");
feeCollector = _newFeeCollector;
emit FeeCollectorUpdated(_newFeeCollector);
}

/**
* @notice Withdraw fees.
* @param _to Withdrawal address.
* @param _amount Amount of USDC to withdraw.
*/
function withdrawFees(address _to, uint256 _amount) external onlyOwner {
require(_to != address(0), "Invalid withdrawal address");
require(_amount > 0, "Withdrawal amount must be greater than zero");
Expand Down
16 changes: 9 additions & 7 deletions src/Vault.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

pragma solidity ^0.8.21;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ERC4626Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

Expand All @@ -23,7 +25,7 @@ contract Vault is IERC4626Vault, ERC4626Upgradeable, OwnableUpgradeable {
/* ========== STATE VARIABLES ========== */

address public usdc;
address public aEthUSDC;
address public aBaseUsdc;
address public aaveV3Pool;

/* ========== CONSTRUCTOR ========== */
Expand All @@ -37,16 +39,16 @@ contract Vault is IERC4626Vault, ERC4626Upgradeable, OwnableUpgradeable {
* @dev Initialize the ERC4626AaveV3UsdcVault.
* @param _usdc USDC contract address.
* @param _aaveV3Pool Aave V3 Pool contract address.
* @param _aEthUSDC Aave USDC address.
* @param _aBaseUsdc Aave USDC address.
*/
function initialize(IERC20 _usdc, address _aaveV3Pool, address _aEthUSDC) external initializer {
function initialize(IERC20 _usdc, address _aaveV3Pool, address _aBaseUsdc) external initializer {
__Ownable_init(_msgSender());
__ERC4626_init(_usdc);
__ERC20_init("Wrapped Aave USDC V3", "wAEthUSDC");
__ERC20_init("Wrapped Aave USDC V3", "wABaseUSDC");

usdc = address(_usdc);
aaveV3Pool = _aaveV3Pool;
aEthUSDC = _aEthUSDC;
aBaseUsdc = _aBaseUsdc;
}

/* ========== VIEWS ========== */
Expand All @@ -55,7 +57,7 @@ contract Vault is IERC4626Vault, ERC4626Upgradeable, OwnableUpgradeable {
* @dev See {IERC4626-totalAssets}.
*/
function totalAssets() public view override returns (uint256) {
return IERC20(aEthUSDC).balanceOf(address(this));
return IERC20(aBaseUsdc).balanceOf(address(this));
}

/* ========== MUTATIVE FUNCTIONS ========== */
Expand Down

0 comments on commit b3ccc78

Please sign in to comment.