h33
0xa7036c8c28f96e8ce242d0af45d1b395b69daf38
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
6,216 bytes
ABI entries
68
25 read · 12 write
License
none
Contract information
- Address
- 0xa7036c8c28f96e8ce242d0af45d1b395b69daf38
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0xe49C113adF…48af1fadfb
- Creation tx
- 0x4ef275beaf…b72b5ae6ba
Token
- Name
- Hood Liquid Staking Token
- Symbol
- h33
- Decimals
- 18
- Holders
- 61
- Market
- View token page →
Read contract (25)
allowance(address, address) → uint256
asset() → address
balanceOf(address) → uint256
convertToAssets(uint256) → uint256
convertToShares(uint256) → uint256
decimals() → uint8
hood() → address
isCooldownActive() → bool
isLocked() → bool
isUnlocked() → bool
maxDeposit(address) → uint256
maxMint(address) → uint256
maxRedeem(address) → uint256
maxWithdraw(address) → uint256
name() → string
operator() → address
previewDeposit(uint256) → uint256
previewMint(uint256) → uint256
previewRedeem(uint256) → uint256
previewWithdraw(uint256) → uint256
ratio() → uint256
symbol() → string
totalAssets() → uint256
totalSupply() → uint256
xHood() → address
Events (11)
ApprovalCompoundedDepositEnteredExitedLockedNewOperatorTransferUnlockedUpdatedIndexWithdraw
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "_operator",
"type": "address"
},
{
"internalType": "address",
"name": "_xHood",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "allowance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "ERC20InsufficientAllowance",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "ERC20InsufficientBalance",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "approver",
"type": "address"
}
],
"name": "ERC20InvalidApprover",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "receiver",
"type": "address"
}
],
"name": "ERC20InvalidReceiver",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "ERC20InvalidSender",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "ERC20InvalidSpender",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "uint256",
"name": "assets",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "max",
"type": "uint256"
}
],
"name": "ERC4626ExceededMaxDeposit",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "uint256",
"name": "shares",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "max",
"type": "uint256"
}
],
"name": "ERC4626ExceededMaxMint",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "shares",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "max",
"type": "uint256"
}
],
"name": "ERC4626ExceededMaxRedeem",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "assets",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "max",
"type": "uint256"
}
],
"name": "ERC4626ExceededMaxWithdraw",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "FORBIDDEN_TOKEN",
"type": "error"
},
{
"inputs": [],
"name": "LOCKED",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "NOT_AUTHORIZED",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "NOT_CONFORMED_TO_SCALE",
"type": "error"
},
{
"inputs": [],
"name": "NOT_ENOUGH",
"type": "error"
},
{
"inputs": [],
"name": "REBASE_IN_PROGRESS",
"type": "error"
},
{
"inputs": [],
"name": "ReentrancyGuardReentrantCall",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"inputs": [],
"name": "ZERO",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "oldRatio",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "newRatio",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "Compounded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "assets",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "shares",
"type": "uint256"
}
],
"name": "Deposit",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "user",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ratioAtDeposit",
"type": "uint256"
}
],
"name": "Entered",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "user",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "_outAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ratioAtWithdrawal",
"type": "uint256"
}
],
"name": "Exited",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "_ts",
"type": "uint256"
}
],
"name": "Locked",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "_oldOperator",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "_newOperator",
"type": "address"
}
],
"name": "NewOperator",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "_ts",
"type": "uint256"
}
],
"name": "Unlocked",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "_index",
"type": "uint256"
}
],
"name": "UpdatedIndex",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "assets",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "shares",
"type": "uint256"
}
],
"name": "Withdraw",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "asset",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "compound",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "shares",
"type": "uint256"
}
],
"name": "convertToAssets",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "assets",
"type": "uint256"
}
],
"name": "convertToShares",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "assets",
"type": "uint256"
},
{
"internalType": "address",
"name": "receiver",
"type": "address"
}
],
"name": "deposit",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "hood",
"outputs": [
{
"internalType": "contract IERC20",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "isCooldownActive",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "isLocked",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "isUnlocked",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lock",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "maxDeposit",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "maxMint",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "maxRedeem",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "maxWithdraw",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "shares",
"type": "uint256"
},
{
"internalType": "address",
"name": "receiver",
"type": "address"
}
],
"name": "mint",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "operator",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "assets",
"type": "uint256"
}
],
"name": "previewDeposit",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "shares",
"type": "uint256"
}
],
"name": "previewMint",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "shares",
"type": "uint256"
}
],
"name": "previewRedeem",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "assets",
"type": "uint256"
}
],
"name": "previewWithdraw",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "ratio",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "shares",
"type": "uint256"
},
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "redeem",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_token",
"type": "address"
},
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "rescue",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalAssets",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_newOperator",
"type": "address"
}
],
"name": "transferOperator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "unlock",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "assets",
"type": "uint256"
},
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "withdraw",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "xHood",
"outputs": [
{
"internalType": "contract IXHood",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IXHood} from "./interface/IXHood.sol";
import {IH33} from "./interface/IH33.sol";
/// @title Canonical xHood Wrapper for Hood Exchange on Sonic
/// @dev Autocompounding shares token voting optimally each epoch
contract h33 is ERC4626, IH33, ReentrancyGuard {
using SafeERC20 for ERC20;
/// @inheritdoc IH33
address public operator;
IERC20 public immutable hood;
IXHood public immutable xHood;
bool public isLocked;
modifier whileNotLocked() {
require(isUnlocked(), LOCKED());
_;
}
modifier onlyOperator() {
require(msg.sender == operator, NOT_AUTHORIZED(msg.sender));
_;
}
constructor(
address _operator,
address _xHood
) ERC20("Hood Liquid Staking Token", "h33") ERC4626(IERC20(_xHood)) {
operator = _operator;
xHood = IXHood(_xHood);
hood = IERC20(xHood.HOOD());
/// @dev pre-approve hood and xHood
hood.approve(address(xHood), type(uint256).max);
}
/// @inheritdoc IH33
function compound() external onlyOperator {
/// @dev fetch the current ratio prior to compounding
uint256 currentRatio = ratio();
/// @dev cache the current hood balance
uint256 currentHoodBalance;
/// @dev fetch from simple IERC20 call to the underlying HOOD
currentHoodBalance = hood.balanceOf(address(this));
/// @dev convert to xHood
xHood.convertEmissionsToken(currentHoodBalance);
/// @dev fetch new ratio
uint256 newRatio = ratio();
emit Compounded(currentRatio, newRatio, currentHoodBalance);
}
/// @inheritdoc IH33
function rescue(
address _token,
uint256 _amount
) external nonReentrant onlyOperator {
uint256 snapshotxHoodBalance = totalAssets();
/// @dev transfer to the caller
IERC20(_token).transfer(msg.sender, _amount);
/// @dev _token could be any malicious contract someone sent to the h33 module
/// @dev extra security check to ensure xHood balance or allowance doesn't change when rescued
require(
xHood.allowance(_token, address(this)) == 0,
FORBIDDEN_TOKEN(address(xHood))
);
require(
totalAssets() == snapshotxHoodBalance,
FORBIDDEN_TOKEN(address(xHood))
);
}
function lock() external onlyOperator {
isLocked = true;
emit Locked(block.timestamp);
}
/// @inheritdoc IH33
function unlock() external onlyOperator {
isLocked = false;
emit Unlocked(block.timestamp);
}
/// @inheritdoc IH33
function transferOperator(address _newOperator) external onlyOperator {
address currentOperator = operator;
/// @dev set the new operator
operator = _newOperator;
emit NewOperator(currentOperator, operator);
}
/**
* Read Functions
*/
/// @inheritdoc ERC4626
function totalAssets() public view override returns (uint256) {
/// @dev simple call to the xHood balance of this contract
return xHood.balanceOf(address(this));
}
/// @inheritdoc IH33
function ratio() public view returns (uint256) {
if (totalSupply() == 0) return 1e18;
return (totalAssets() * 1e18) / totalSupply();
}
/// @inheritdoc IH33
function isUnlocked() public view returns (bool) {
return isLocked == false;
}
/// @inheritdoc IH33
function isCooldownActive() public view returns (bool) {
return isLocked == true;
}
/**
* ERC4626 internal overrides
*/
function _deposit(
address caller,
address receiver,
uint256 assets,
uint256 shares
) internal virtual override whileNotLocked {
SafeERC20.safeTransferFrom(xHood, caller, address(this), assets);
_mint(receiver, shares);
emit Deposit(caller, receiver, assets, shares);
}
function _withdraw(
address caller,
address receiver,
address owner,
uint256 assets,
uint256 shares
) internal virtual override whileNotLocked {
if (caller != owner) {
_spendAllowance(owner, caller, shares);
}
_burn(owner, shares);
SafeERC20.safeTransfer(xHood, receiver, assets);
emit Withdraw(caller, receiver, owner, assets, shares);
}
}
Chain explorer3398msChain node84ms