diff --git a/README.md b/README.md index d4155c6..79a88db 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Here’s the exact assignment: ## What I Implemented -![Architecture](https://github.com/0xfabb/veda-vault/tree/main/src/images/architecture.png) +![Architecture](https://raw.githubusercontent.com/0xfabb/veda-vault/refs/heads/main/src/images/architecture.png) ### Vault Strategy (On-Chain) diff --git a/src/contracts/Sanitizers/sanitizer.sol b/src/contracts/Sanitizers/sanitizer.sol index 7b13100..ce0c290 100644 --- a/src/contracts/Sanitizers/sanitizer.sol +++ b/src/contracts/Sanitizers/sanitizer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.20; abstract contract SanitizerGuards { mapping(address => bool) public Whitelist; @@ -13,4 +13,9 @@ abstract contract SanitizerGuards { require(Whitelist[user], "User not whitelisted"); _; } + + modifier checkAmount(uint256 _amount) { + require(_amount > 0, "Cannot deposit 0"); + _; + } } diff --git a/src/contracts/interfaces/IOverseer.sol b/src/contracts/interfaces/IOverseer.sol index 9dfaa41..c3f3edf 100644 --- a/src/contracts/interfaces/IOverseer.sol +++ b/src/contracts/interfaces/IOverseer.sol @@ -1,24 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -interface IOverseer { - /// @notice Stake wHYPE into stHYPE - /// @param to Address receiving the stHYPE - /// @param communityCode Likely used to track strategy logic (you can pass a string like "LOOPING") - function mint(address to, string memory communityCode) external payable; - - /// @notice Burn stHYPE and attempt to redeem wHYPE - /// @param from Address of the stHYPE holder - /// @param amount Amount of stHYPE to burn - /// @param communityCode Same strategy code used in mint - /// @return burnId ID for the redeem request (used to track status) - function burnAndRedeemIfPossible( - address from, - uint256 amount, - string memory communityCode - ) external returns (uint256 burnId); - /// @notice Redeem previously burned stHYPE (if available) - /// @param burnId ID returned from `burnAndRedeemIfPossible` +interface IOverseer { + function mint(address _user, string calldata communityCode) external; + function balanceOf(address _user) external view returns (uint256); + function burnAndRedeemIfPossible(address _user, uint256 _amount, string calldata communityCode) external returns (uint256); function redeem(uint256 burnId) external; } diff --git a/src/contracts/interfaces/IStHype.sol b/src/contracts/interfaces/IStHype.sol new file mode 100644 index 0000000..6baec13 --- /dev/null +++ b/src/contracts/interfaces/IStHype.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + + + +interface IStHYPE { + function stake(uint256 _amount) external; + function unstake(uint256 _amount) external; +} \ No newline at end of file diff --git a/src/contracts/vedaVault.sol b/src/contracts/vedaVault.sol index fed7736..81e1efb 100644 --- a/src/contracts/vedaVault.sol +++ b/src/contracts/vedaVault.sol @@ -1,62 +1,80 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.30; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; +import "./interfaces/IOverseer.sol"; +import "./interfaces/IStHype.sol"; import "./Sanitizers/sanitizer.sol"; -interface IOverseer { - function mint(address _user, string calldata communityCode) external; - function balanceOf(address _user) external view returns (uint256); -} - contract VedaVault is ERC20, Ownable, SanitizerGuards { IERC20 public immutable wHYPE; + IStHYPE public immutable stHYPE; // Changed from IERC20 to IStHYPE IOverseer public immutable overseer; string public communityCode; + mapping(address => uint256[]) public userBurnIds; + event Deposited(address indexed user, uint256 amount); event Withdrawn(address indexed user, uint256 amount); + event BurnInitiated(address indexed user, uint256 amount, uint256 burnId); + event Redeemed(address indexed user, uint256 burnId); constructor( address _wHYPE, + address _stHYPE, address _overseer, string memory _communityCode ) ERC20("Veda Vault HYPE", "vvHYPE") Ownable(msg.sender) { require(_wHYPE != address(0), "Invalid wHYPE address"); + require(_stHYPE != address(0), "Invalid stHYPE address"); require(_overseer != address(0), "Invalid overseer address"); wHYPE = IERC20(_wHYPE); + stHYPE = IStHYPE(_stHYPE); // Changed from IERC20 to IStHYPE overseer = IOverseer(_overseer); communityCode = _communityCode; } function deposit(uint256 _amount) external onlyEOA { - require(_amount > 0, "Cannot deposit 0"); - - // Transfer wHYPE from user to vault - require(wHYPE.transferFrom(msg.sender, address(this), _amount), "Transfer failed"); + checkAmount(_amount); + require(wHYPE.transferFrom(msg.sender, address(this), _amount), "wHYPE transfer failed"); + require(wHYPE.approve(address(stHYPE), _amount), "Could'nt approve for wHype"); // Fixed typo - // Mint stHYPE to user via overseer - overseer.mint(msg.sender, communityCode); - - // Mint vvHYPE to user + stHYPE.stake(_amount); // Now you can call directly without casting _mint(msg.sender, _amount); emit Deposited(msg.sender, _amount); } function withdraw(uint256 _amount) external onlyEOA { - require(_amount > 0, "Cannot withdraw 0"); + checkAmount(_amount); require(balanceOf(msg.sender) >= _amount, "Insufficient vvHYPE"); _burn(msg.sender, _amount); - require(wHYPE.transfer(msg.sender, _amount), "Transfer failed"); + stHYPE.unstake(_amount); // Now you can call directly without casting + + require(wHYPE.transfer(msg.sender, _amount), "wHYPE transfer back failed"); emit Withdrawn(msg.sender, _amount); } + function burnAndInitiateRedeem(uint256 _amount) external onlyEOA { + require(_amount > 0, "Zero amount"); + + _burn(msg.sender, _amount); + uint256 burnId = overseer.burnAndRedeemIfPossible(msg.sender, _amount, communityCode); + userBurnIds[msg.sender].push(burnId); + + emit BurnInitiated(msg.sender, _amount, burnId); + } + + function redeem(uint256 burnId) external onlyEOA { + overseer.redeem(burnId); + emit Redeemed(msg.sender, burnId); + } + function totalVaultAssets() external view returns (uint256) { return wHYPE.balanceOf(address(this)); } @@ -65,7 +83,6 @@ contract VedaVault is ERC20, Ownable, SanitizerGuards { return overseer.balanceOf(user); } - // Admin-only function to whitelist an address function whitelistAddress(address user) external onlyOwner { Whitelist[user] = true; } diff --git a/src/images/Architecture.png b/src/images/Architecture.png new file mode 100644 index 0000000..4e9f999 Binary files /dev/null and b/src/images/Architecture.png differ diff --git a/src/images/architecture.png b/src/images/architecture.png deleted file mode 100644 index 99ac47a..0000000 Binary files a/src/images/architecture.png and /dev/null differ