Contract Name:
ZenjiViewHelper
Contract Source Code:
<i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
import { ILoanManager } from "./interfaces/ILoanManager.sol";
import { IYieldStrategy } from "./interfaces/IYieldStrategy.sol";
import { IERC20 } from "./interfaces/IERC20.sol";
interface IZenjiView {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function getTotalCollateral() external view returns (uint256);
function loanManager() external view returns (ILoanManager);
function collateralAsset() external view returns (IERC20);
function debtAsset() external view returns (IERC20);
function idle() external view returns (bool);
function targetLtv() external view returns (uint256);
function DEADBAND_SPREAD() external view returns (uint256);
function feeRate() external view returns (uint256);
function PRECISION() external view returns (uint256);
function accumulatedFees() external view returns (uint256);
function lastStrategyBalance() external view returns (uint256);
function yieldStrategy() external view returns (IYieldStrategy);
function VIRTUAL_SHARE_OFFSET() external view returns (uint256);
}
/// @title ZenjiViewHelper
/// @notice External helper for view utilities to reduce Zenji bytecode size
contract ZenjiViewHelper {
function getUserValue(address vault, address user)
external
view
returns (uint256 collateralValue)
{
IZenjiView v = IZenjiView(vault);
uint256 supply = v.totalSupply();
if (supply == 0) return 0;
uint256 userShares = v.balanceOf(user);
uint256 offset = v.VIRTUAL_SHARE_OFFSET();
return (userShares * (v.getTotalCollateral() + offset)) / (supply + offset);
}
function getHealth(address vault) external view returns (int256 health) {
return IZenjiView(vault).loanManager().getHealth();
}
function isRebalanceNeeded(address vault) external view returns (bool needed) {
ILoanManager lm = IZenjiView(vault).loanManager();
if (!lm.loanExists()) return false;
uint256 ltv = lm.getCurrentLTV();
uint256 target = IZenjiView(vault).targetLtv();
uint256 deadband = IZenjiView(vault).DEADBAND_SPREAD();
return ltv < (target - deadband) || ltv > (target + deadband);
}
function getLtvBounds(address vault)
external
view
returns (uint256 lowerBand, uint256 upperBand)
{
IZenjiView v = IZenjiView(vault);
uint256 target = v.targetLtv();
uint256 deadband = v.DEADBAND_SPREAD();
lowerBand = target - deadband;
upperBand = target + deadband;
}
function getPendingFees(address vault)
external
view
returns (uint256 totalFees, uint256 pendingFees)
{
IZenjiView v = IZenjiView(vault);
IYieldStrategy strategy = v.yieldStrategy();
uint256 strategyBalance = strategy.balanceOf();
uint256 lastBalance = v.lastStrategyBalance();
if (strategyBalance > lastBalance) {
uint256 delta = strategyBalance - lastBalance;
pendingFees = (delta * v.feeRate()) / v.PRECISION();
}
totalFees = v.accumulatedFees() + pendingFees;
}
/// @notice Get total value in collateral asset units (e.g., WBTC satoshis)
/// @dev Computes all values natively in collateral terms to avoid double conversion errors
function getTotalCollateralValue(address vault) external view returns (uint256 totalValue) {
IZenjiView v = IZenjiView(vault);
ILoanManager lm = v.loanManager();
if (v.idle()) {
return v.collateralAsset().balanceOf(vault);
}
// Idle collateral in vault (native units)
totalValue = v.collateralAsset().balanceOf(vault);
// Net collateral value in loan manager (collateral - debt in collateral terms)
totalValue += lm.getNetCollateralValue();
// Yield strategy balance converted to collateral (excluding fees)
IYieldStrategy strategy = v.yieldStrategy();
if (address(strategy) != address(0)) {
uint256 strategyBalance = strategy.balanceOf();
uint256 accFees = v.accumulatedFees();
if (strategyBalance > accFees) {
totalValue += lm.getDebtValue(strategyBalance - accFees);
}
}
// Idle debt asset in vault converted to collateral
uint256 idleDebt = v.debtAsset().balanceOf(vault);
if (idleDebt > 0) {
totalValue += lm.getDebtValue(idleDebt);
}
// Raw collateral in loan manager (not yet supplied)
totalValue += lm.getCollateralBalance();
// Raw debt asset in loan manager converted to collateral
uint256 lmDebt = lm.getDebtBalance();
if (lmDebt > 0) {
totalValue += lm.getDebtValue(lmDebt);
}
}
/// @notice Get total value in debt asset units (for diagnostics/logging)
/// @dev This is a diagnostic function - use getTotalCollateralValue for actual accounting
function getTotalDebtValue(address vault) external view returns (uint256 totalValue) {
IZenjiView v = IZenjiView(vault);
ILoanManager lm = v.loanManager();
if (v.idle()) {
return lm.getCollateralValue(v.collateralAsset().balanceOf(vault));
}
// Idle collateral converted to debt units
totalValue = lm.getCollateralValue(v.collateralAsset().balanceOf(vault));
// Position value in debt terms
if (lm.loanExists()) {
(uint256 collateral, uint256 debt) = lm.getPositionValues();
uint256 collateralInDebt = lm.getCollateralValue(collateral);
if (collateralInDebt > debt) {
totalValue += collateralInDebt - debt;
}
}
// Strategy balance (already in debt units, minus fees)
IYieldStrategy strategy = v.yieldStrategy();
if (address(strategy) != address(0)) {
uint256 strategyBalance = strategy.balanceOf();
uint256 accFees = v.accumulatedFees();
if (strategyBalance > accFees) {
totalValue += strategyBalance - accFees;
}
}
// Idle debt in vault
totalValue += v.debtAsset().balanceOf(vault);
// Raw collateral in loan manager converted to debt
uint256 lmCollateral = lm.getCollateralBalance();
if (lmCollateral > 0) {
totalValue += lm.getCollateralValue(lmCollateral);
}
// Raw debt in loan manager
totalValue += lm.getDebtBalance();
}
function yieldCostBasis(address vault) external view returns (uint256) {
return IZenjiView(vault).yieldStrategy().costBasis();
}
function getYieldStrategyStats(address vault)
external
view
returns (
string memory strategyName,
uint256 currentValue,
uint256 costBasis,
uint256 unrealizedProfit
)
{
IYieldStrategy strategy = IZenjiView(vault).yieldStrategy();
strategyName = strategy.name();
currentValue = strategy.balanceOf();
costBasis = strategy.costBasis();
unrealizedProfit = strategy.unrealizedProfit();
}
function getYieldVaultStats(address vault)
external
view
returns (
uint256 yieldShares,
uint256 currentValue,
uint256 costBasis,
uint256 unrealizedProfit
)
{
IYieldStrategy strategy = IZenjiView(vault).yieldStrategy();
return (0, strategy.balanceOf(), strategy.costBasis(), strategy.unrealizedProfit());
}
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @title ILoanManager
/// @notice Interface for loan managers that handle collateralized borrowing
interface ILoanManager {
// ============ Events ============
event LoanCreated(uint256 collateral, uint256 debt, uint256 bands);
event CollateralAdded(uint256 amount);
event LoanBorrowedMore(uint256 collateral, uint256 debt);
event LoanRepaid(uint256 amount);
event CollateralRemoved(uint256 amount);
// ============ Errors ============
error Unauthorized();
error InvalidAddress();
error InvalidPrice();
error StaleOracle();
error DebtNotFullyRepaid();
error ZeroAmount();
error TransferFailed();
// ============ Loan Management Functions ============
/// @notice Create a new loan with collateral
/// @param collateral Amount of collateral asset
/// @param debt Amount of debt asset to borrow
/// @param bands Number of bands for the loan (if applicable)
function createLoan(uint256 collateral, uint256 debt, uint256 bands) external;
/// @notice Add collateral to an existing loan
/// @param collateral Amount of collateral asset
function addCollateral(uint256 collateral) external;
/// @notice Borrow more against existing collateral
/// @param collateral Additional collateral (can be 0)
/// @param debt Additional debt to borrow
function borrowMore(uint256 collateral, uint256 debt) external;
/// @notice Repay debt
/// @param amount Amount of debt asset to repay
function repayDebt(uint256 amount) external;
/// @notice Remove collateral from the loan
/// @param amount Amount of collateral asset to remove
function removeCollateral(uint256 amount) external;
/// @notice Unified position unwind for both partial and full withdrawals
/// @dev Pass type(uint256).max for collateralNeeded to fully close the position.
/// @param collateralNeeded Amount of collateral to free, or type(uint256).max for full close
function unwindPosition(uint256 collateralNeeded) external;
// ============ View Functions ============
/// @notice Get current LTV ratio
/// @return ltv Current LTV (1e18 = 100%)
function getCurrentLTV() external view returns (uint256 ltv);
/// @notice Get current collateral amount
/// @return collateral Current collateral amount
function getCurrentCollateral() external view returns (uint256 collateral);
/// @notice Get current debt amount
/// @return debt Current debt amount
function getCurrentDebt() external view returns (uint256 debt);
/// @notice Collateral asset used by this loan manager
function collateralAsset() external view returns (address);
/// @notice Debt asset used by this loan manager
function debtAsset() external view returns (address);
/// @notice Get vault health factor
/// @return health Health factor (positive = healthy)
function getHealth() external view returns (int256 health);
/// @notice Check if loan exists
/// @return exists True if loan exists
function loanExists() external view returns (bool exists);
/// @notice Get collateral value in debt asset terms
/// @param collateralAmount Amount of collateral asset
/// @return value Value in debt asset (18 decimals unless specified by implementation)
function getCollateralValue(uint256 collateralAmount) external view returns (uint256 value);
/// @notice Get debt asset value in collateral terms
/// @param debtAmount Amount of debt asset
/// @return value Value in collateral asset (collateral decimals)
function getDebtValue(uint256 debtAmount) external view returns (uint256 value);
/// @notice Calculate borrow amount for given collateral and target LTV
/// @param collateral Amount of collateral asset
/// @param targetLtv Target LTV (1e18 = 100%)
/// @return borrowAmount Amount of debt asset to borrow
function calculateBorrowAmount(uint256 collateral, uint256 targetLtv)
external
view
returns (uint256 borrowAmount);
/// @notice Calculate health after hypothetical changes
/// @param dCollateral Change in collateral
/// @param dDebt Change in debt
/// @return health Projected health factor
function healthCalculator(int256 dCollateral, int256 dDebt)
external
view
returns (int256 health);
/// @notice Get minimum collateral required for a debt amount
/// @param debt_ Desired debt amount
/// @param bands Number of bands
/// @return Minimum collateral required
function minCollateral(uint256 debt_, uint256 bands) external view returns (uint256);
/// @notice Get position values (collateral and debt)
/// @return collateralValue Current collateral value
/// @return debtValue Current debt value
function getPositionValues()
external
view
returns (uint256 collateralValue, uint256 debtValue);
/// @notice Get net collateral value (collateral - debt in collateral terms)
/// @return value Net value in collateral asset
function getNetCollateralValue() external view returns (uint256 value);
/// @notice Check oracle freshness
function checkOracleFreshness() external view;
// ============ Token Management ============
/// @notice Transfer collateral from this contract
/// @param to Recipient address
/// @param amount Amount to transfer
function transferCollateral(address to, uint256 amount) external;
/// @notice Transfer debt asset from this contract
/// @param to Recipient address
/// @param amount Amount to transfer
function transferDebt(address to, uint256 amount) external;
/// @notice Get collateral balance of this contract
/// @return balance Collateral balance
function getCollateralBalance() external view returns (uint256 balance);
/// @notice Get debt balance of this contract
/// @return balance Debt balance
function getDebtBalance() external view returns (uint256 balance);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @title IYieldStrategy
/// @notice Interface for yield strategies used by Zenji
/// @dev All strategies accept the debt asset from the vault and deploy to yield-generating protocols
interface IYieldStrategy {
// ============ Events ============
event Deposited(uint256 debtAssetAmount, uint256 underlyingDeposited);
event Withdrawn(uint256 debtAssetAmount, uint256 debtAssetReceived);
event Harvested(uint256 rewardsValue);
event EmergencyWithdrawn(uint256 debtAssetReceived);
event StrategyPauseToggled(bool paused, uint256 debtAssetReceived);
// ============ Errors ============
error Unauthorized();
error ZeroAmount();
error StrategyPaused();
error SlippageExceeded();
error TransferFailed();
error InvalidAddress();
// ============ Core Functions ============
/// @notice Deposit debt asset into the yield strategy
/// @param debtAssetAmount Amount of debt asset to deposit
/// @return underlyingDeposited Amount deposited into the underlying protocol
function deposit(uint256 debtAssetAmount) external returns (uint256 underlyingDeposited);
/// @notice Withdraw debt asset from the yield strategy
/// @param debtAssetAmount Amount of debt asset to withdraw
/// @return debtAssetReceived Actual debt asset received (may differ due to slippage)
function withdraw(uint256 debtAssetAmount) external returns (uint256 debtAssetReceived);
/// @notice Withdraw all assets from the yield strategy
/// @return debtAssetReceived Total debt asset received
function withdrawAll() external returns (uint256 debtAssetReceived);
/// @notice Harvest rewards and compound them back into the strategy
/// @return rewardsValue Value of rewards harvested (in debt asset terms)
function harvest() external returns (uint256 rewardsValue);
/// @notice Emergency withdraw all assets, bypassing slippage checks
/// @return debtAssetReceived Total debt asset received
function emergencyWithdraw() external returns (uint256 debtAssetReceived);
/// @notice Toggle pause state for the strategy
/// @dev When pausing, should unwind all deployed assets back to the vault
/// @return debtAssetReceived Total debt asset received if unwound (0 when unpausing)
function pauseStrategy() external returns (uint256 debtAssetReceived);
// ============ View Functions ============
/// @notice Returns the asset accepted by the strategy (debt asset)
function asset() external view returns (address);
/// @notice Returns the underlying asset of the yield protocol (debt asset or swapped asset)
function underlyingAsset() external view returns (address);
/// @notice Returns the current value of the strategy holdings in debt asset terms
function balanceOf() external view returns (uint256);
/// @notice Returns the total debt asset deposited (cost basis)
function costBasis() external view returns (uint256);
/// @notice Returns unrealized profit (current value - cost basis)
function unrealizedProfit() external view returns (uint256);
/// @notice Returns pending rewards value in debt asset terms
function pendingRewards() external view returns (uint256);
/// @notice Returns whether the strategy is paused
function paused() external view returns (bool);
/// @notice Returns the strategy name
function name() external view returns (string memory);
/// @notice Returns the vault address
function vault() external view returns (address);
} <i class='far fa-question-circle text-muted ms-2' data-bs-trigger='hover' data-bs-toggle='tooltip' data-bs-html='true' data-bs-title='Click on the check box to select individual contract to compare. Only 1 contract can be selected from each side.'></i>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.33;
/// @title IERC20
/// @notice Standard ERC20 interface
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
function decimals() external view returns (uint8);
function symbol() external view returns (string memory);
function name() external view returns (string memory);
}