Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
7 changes: 6 additions & 1 deletion src/contracts/Sanitizers/sanitizer.sol
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -13,4 +13,9 @@ abstract contract SanitizerGuards {
require(Whitelist[user], "User not whitelisted");
_;
}

modifier checkAmount(uint256 _amount) {
require(_amount > 0, "Cannot deposit 0");
_;
}
}
22 changes: 4 additions & 18 deletions src/contracts/interfaces/IOverseer.sol
Original file line number Diff line number Diff line change
@@ -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;
}
9 changes: 9 additions & 0 deletions src/contracts/interfaces/IStHype.sol
Original file line number Diff line number Diff line change
@@ -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;
}
51 changes: 34 additions & 17 deletions src/contracts/vedaVault.sol
Original file line number Diff line number Diff line change
@@ -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));
}
Expand All @@ -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;
}
Expand Down
Binary file added src/images/Architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed src/images/architecture.png
Binary file not shown.
Loading