@@ -7,12 +7,19 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
77import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol " ;
88import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol " ;
99import {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+ */
1117contract 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 " );
0 commit comments