Skip to content

Commit b3ccc78

Browse files
committed
feat: created erc4626 vault and updated exchange contract
1 parent 6ffdb56 commit b3ccc78

File tree

3 files changed

+107
-10
lines changed

3 files changed

+107
-10
lines changed

script/deploy/Exchange.s.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ contract DeployExchange is Script {
88
function run() external {
99
vm.startBroadcast();
1010

11-
address registryAddress = 0x9d69b9b2907Bba74E65Aa6c87B2284fF0F0931e0;
11+
address registryAddress = 0x2b4836d81370e37030727E4DCbd9cC5a772cf43A;
1212
address usdcAddress = 0x036CbD53842c5426634e7929541eC2318f3dCF7e;
13+
address vaultAddress = 0xd580248163CDD5AE3225A700E9f4e7CD525b27b0;
1314

14-
Exchange exchange = new Exchange(registryAddress, usdcAddress);
15+
Exchange exchange = new Exchange(registryAddress, usdcAddress, vaultAddress);
1516

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

src/Exchange.sol

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
77
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
88
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
99
import {Registry} from "./Registry.sol";
10+
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
1011

12+
/**
13+
* @title Cube Exchange
14+
* @author Dann Wee
15+
* @notice Cube Exchange is a contract that allows users to transfer USDC to merchants.
16+
*/
1117
contract Exchange is ReentrancyGuard, Ownable {
1218
using SafeERC20 for IERC20;
1319

1420
Registry public immutable registry;
1521
IERC20 public immutable usdcToken;
22+
IERC4626 public immutable vault;
1623

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

25-
constructor(address _registryAddress, address _usdcAddress) Ownable(msg.sender) {
34+
constructor(address _registryAddress, address _usdcAddress, address _vaultAddress) Ownable(msg.sender) {
2635
require(_registryAddress != address(0), "Invalid registry address");
2736
require(_usdcAddress != address(0), "Invalid USDC address");
37+
require(_vaultAddress != address(0), "Invalid vault address");
2838
registry = Registry(_registryAddress);
2939
usdcToken = IERC20(_usdcAddress);
3040
feeCollector = address(this);
41+
vault = IERC4626(_vaultAddress);
3142
}
3243

44+
/////////////////////////
45+
/////// FUNCTIONS ///////
46+
/////////////////////////
47+
48+
/**
49+
* @notice Transfer USDC to merchant.
50+
* @param _uen Merchant's UEN.
51+
* @param _amount Amount of USDC to transfer to merchant.
52+
*/
3353
function transferToMerchant(string memory _uen, uint256 _amount) external nonReentrant {
3454
require(_amount > 0, "Amount must be greater than zero");
3555

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

70+
/**
71+
* @notice Transfer USDC to vault.
72+
* @param _uen Merchant's UEN.
73+
* @param _amount Amount of USDC to transfer to vault.
74+
*/
75+
function transferToVault(string memory _uen, uint256 _amount) external nonReentrant {
76+
require(_amount > 0, "Amount must be greater than zero");
77+
78+
address merchantWalletAddress = registry.getMerchantByUEN(_uen).wallet_address;
79+
require(merchantWalletAddress != address(0), "Invalid merchant wallet address");
80+
81+
uint256 feeAmount = (_amount * fee) / 10000;
82+
uint256 vaultAmount = _amount - feeAmount;
83+
84+
// Transfer USDC from sender to this contract
85+
usdcToken.safeTransferFrom(msg.sender, address(this), _amount);
86+
87+
// Transfer fee to fee collector
88+
if (feeAmount > 0) {
89+
usdcToken.safeTransfer(feeCollector, feeAmount);
90+
}
91+
92+
// Approve vault to spend USDC
93+
usdcToken.approve(address(vault), vaultAmount);
94+
95+
// Deposit USDC into vault and mint shares to merchant
96+
uint256 shares = vault.deposit(vaultAmount, merchantWalletAddress);
97+
98+
emit Transfer(msg.sender, merchantWalletAddress, vaultAmount, _uen);
99+
emit VaultDeposit(merchantWalletAddress, vaultAmount, shares);
100+
}
101+
102+
/**
103+
* @notice Withdraw USDC from vault to merchant's wallet.
104+
* @param _shares Amount of shares to withdraw.
105+
*/
106+
function withdrawToWallet(uint256 _shares) external nonReentrant {
107+
address merchantWalletAddress = msg.sender;
108+
109+
// Check if the caller has enough shares
110+
uint256 userShares = vault.balanceOf(merchantWalletAddress);
111+
require(userShares >= _shares, "Insufficient shares");
112+
113+
// Check if the vault has enough assets to cover the withdrawal
114+
uint256 assets = vault.convertToAssets(_shares);
115+
require(vault.totalAssets() >= assets, "Insufficient vault assets");
116+
117+
// Approve the vault to spend shares on behalf of the user
118+
vault.approve(address(this), _shares);
119+
120+
// Withdraw USDC from vault and burn shares
121+
try vault.redeem(_shares, merchantWalletAddress, merchantWalletAddress) returns (uint256 redeemedAssets) {
122+
require(redeemedAssets == assets, "Unexpected amount of assets redeemed");
123+
emit VaultWithdraw(merchantWalletAddress, redeemedAssets, _shares);
124+
} catch Error(string memory reason) {
125+
revert(string(abi.encodePacked("Vault redeem failed: ", reason)));
126+
} catch (bytes memory) /* lowLevelData */ {
127+
revert("Vault redeem failed with unknown error");
128+
}
129+
}
130+
131+
/**
132+
* @notice Set the fee.
133+
* @param _newFee New fee.
134+
*/
50135
function setFee(uint256 _newFee) external onlyOwner {
51136
require(_newFee <= 1000, "Fee cannot exceed 10%");
52137
fee = _newFee;
53138
emit FeeUpdated(_newFee);
54139
}
55140

141+
/**
142+
* @notice Set the fee collector.
143+
* @param _newFeeCollector New fee collector.
144+
*/
56145
function setFeeCollector(address _newFeeCollector) external onlyOwner {
57146
require(_newFeeCollector != address(0), "Invalid fee collector address");
58147
feeCollector = _newFeeCollector;
59148
emit FeeCollectorUpdated(_newFeeCollector);
60149
}
61150

151+
/**
152+
* @notice Withdraw fees.
153+
* @param _to Withdrawal address.
154+
* @param _amount Amount of USDC to withdraw.
155+
*/
62156
function withdrawFees(address _to, uint256 _amount) external onlyOwner {
63157
require(_to != address(0), "Invalid withdrawal address");
64158
require(_amount > 0, "Withdrawal amount must be greater than zero");

src/Vault.sol

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// SPDX-License-Identifier: MIT
2-
pragma solidity 0.8.21;
2+
3+
pragma solidity ^0.8.21;
34

45
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
56
import {ERC4626Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
67
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
8+
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
79
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
810
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
911

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

2527
address public usdc;
26-
address public aEthUSDC;
28+
address public aBaseUsdc;
2729
address public aaveV3Pool;
2830

2931
/* ========== CONSTRUCTOR ========== */
@@ -37,16 +39,16 @@ contract Vault is IERC4626Vault, ERC4626Upgradeable, OwnableUpgradeable {
3739
* @dev Initialize the ERC4626AaveV3UsdcVault.
3840
* @param _usdc USDC contract address.
3941
* @param _aaveV3Pool Aave V3 Pool contract address.
40-
* @param _aEthUSDC Aave USDC address.
42+
* @param _aBaseUsdc Aave USDC address.
4143
*/
42-
function initialize(IERC20 _usdc, address _aaveV3Pool, address _aEthUSDC) external initializer {
44+
function initialize(IERC20 _usdc, address _aaveV3Pool, address _aBaseUsdc) external initializer {
4345
__Ownable_init(_msgSender());
4446
__ERC4626_init(_usdc);
45-
__ERC20_init("Wrapped Aave USDC V3", "wAEthUSDC");
47+
__ERC20_init("Wrapped Aave USDC V3", "wABaseUSDC");
4648

4749
usdc = address(_usdc);
4850
aaveV3Pool = _aaveV3Pool;
49-
aEthUSDC = _aEthUSDC;
51+
aBaseUsdc = _aBaseUsdc;
5052
}
5153

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

6163
/* ========== MUTATIVE FUNCTIONS ========== */

0 commit comments

Comments
 (0)