Skip to content

Functions

DISCLAIMER // NFA // DYOR

This analysis is based on observations of the contract behavior. We are not smart contract security experts. This document aims to explain what the contract appears to do based on the code. It should not be considered a comprehensive security audit or financial advice. Always verify critical information independently and consult with blockchain security professionals for important decisions.

⊙ generated by robots | curated by humans

METADATA
Contract Address 0x00000000...768E22 (etherscan)
Network Ethereum Mainnet
Analysis Date 2026-04-13

Function Selectors

SELECTOR FUNCTION SIGNATURE CATEGORY
0xb6b55f25 deposit(uint256) User
0xce5c7f61 harvest(bytes) User
receive() (fallback) User
0xa694fc3a stake(uint256) Admin
0x9483e91a withdraw(address,uint256,bytes,uint256) Admin
0xc9f78f5a withdrawStETH(address,uint256) Admin
0x776d1a01 setTarget(address) Admin
0xab37fbb7 setSlippage(uint16) Admin
0xbcc79256 setCondition(address,address) Admin
0xf2fde38b transferOwnership(address) Admin
0x0b76619b staked() View
0xf393a5f1 slipBps() View
0x8da5cb5b owner() View
0xd4b83992 target() View
0x38d52e0f asset() View
0xe534155d holder() View

Summary

CATEGORY COUNT
Total Functions 15 (including receive())
User Functions 3
Admin Functions 6
View Functions 6 (auto-generated getters)

User Functions

Function: receive()

Payable fallback. Any raw ETH transfer to the contract is staked into Lido as stETH, and staked is incremented by the actual stETH balance delta. During an active harvest(), this fallback short-circuits via a transient storage flag so ETH returned by the swap target does not get re-staked.

ATTRIBUTE VALUE
Selector — (fallback)
Parameters none
Access external, payable, permissionless
FLAG OBSERVATION
Uses transient storage (tload(0)) to detect re-entry from harvest() and early-return — prevents double-accounting
Delta is computed from actual balance, so it tolerates Lido's 1-wei rounding
Any address can push ETH in and inflate the principal; the sender has no claim on it
CONDITION REQUIREMENT
Lido submit must succeed (bool ok,) = STETH.call{value: msg.value}("") returns true
STEP ACTION
1 If transient slot 0 is non-zero (harvest active), return 0 bytes
2 Read current stETH balance
3 Forward msg.value to the Lido stETH contract
4 Increment staked by the balance delta
VARIABLE CHANGE
staked += balanceOf(this) - stethBal
CONDITION REVERT MESSAGE
Lido call returns false — (revert with empty data)
receive() external payable {
    assembly ("memory-safe") { if tload(0) { return(0, 0) } }
    uint256 stethBal = IERC20(STETH).balanceOf(address(this));
    (bool ok,) = STETH.call{value: msg.value}("");
    require(ok);
    unchecked { staked += IERC20(STETH).balanceOf(address(this)) - stethBal; }
}

Function: deposit(uint256 amt)

Deposit existing stETH. Pulls amt stETH from msg.sender and increments staked by the actual balance delta. Allows anyone to contribute principal; no accounting is produced to track who deposited what.

ATTRIBUTE VALUE
Selector 0xb6b55f25
Parameters amt — stETH amount (in wei) to pull
Access external, payable, permissionless
FLAG OBSERVATION
Uses balance-delta rather than amt to update staked, handling Lido's rounding
No claim or share token is issued — depositors have no way to withdraw
CONDITION REQUIREMENT
Caller has approved this contract for amt stETH IERC20(STETH).transferFrom(msg.sender, address(this), amt) succeeds
VARIABLE CHANGE
staked += balanceOf(this) - stethBal
CONDITION REVERT MESSAGE
transferFrom returns false — (revert with empty data)
function deposit(uint256 amt) public payable {
    uint256 stethBal = IERC20(STETH).balanceOf(address(this));
    require(IERC20(STETH).transferFrom(msg.sender, address(this), amt));
    unchecked { staked += IERC20(STETH).balanceOf(address(this)) - stethBal; }
}

Function: harvest(bytes calldata data)

Convert accrued yield stETH (balance above staked) to ETH by calling the configured target with caller-supplied data. Permissionless so that keeper bots can trigger it, but gated by two on-chain invariants that bound how much can be lost to slippage or misdirection in a single call.

ATTRIBUTE VALUE
Selector 0xce5c7f61
Parameters data — arbitrary calldata forwarded to target
Access external, payable, permissionless
Returns yield — stETH yield at call time (before the swap)
FLAG OBSERVATION
Sets transient flag tstore(0, 1) before the call so receive() does not re-stake returned ETH, then clears it
Post-call invariant: ETH balance must grow by at least yield * (1 - slipBps/10000)
Post-call invariant: stETH must remain within 2 wei of staked (protects principal)
Invariant compares self balances only — does not consult an external price oracle
target already holds unlimited stETH approval, so the swap does not need a per-call allowance
CONDITION REQUIREMENT
stETH balance > staked Otherwise returns 0 without calling target
STEP ACTION
1 Read target, staked, ETH balance, stETH balance
2 If no yield, return 0
3 Set transient slot 0 to 1
4 (bool ok,) = target.call(data)
5 Clear transient slot 0
6 Require address(this).balance >= ethBal + yield * (10000 - slipBps) / 10000
7 Require IERC20(STETH).balanceOf(this) + 2 >= staked
VARIABLE CHANGE
ETH balance (implicit) grows by at least the slippage-adjusted yield
stETH balance (implicit) drops by approximately yield
staked unchanged
CONDITION REVERT MESSAGE
target.call(data) returns false — (revert with empty data)
ETH gain below slippage floor — (revert with empty data)
stETH balance + 2 < staked — (revert with empty data)
function harvest(bytes calldata data) public payable returns (uint256 yield) {
    address _target = target;
    uint256 _staked = staked;
    uint256 ethBal = address(this).balance;
    uint256 stethBal = IERC20(STETH).balanceOf(address(this));
    if (stethBal <= _staked) return 0;
    unchecked { yield = stethBal - _staked; }
    assembly ("memory-safe") { tstore(0, 1) }
    (bool ok,) = _target.call(data);
    assembly ("memory-safe") { tstore(0, 0) }
    require(ok);
    unchecked {
        require(address(this).balance >= ethBal + yield * (10000 - slipBps) / 10000);
        require(IERC20(STETH).balanceOf(address(this)) + 2 >= _staked);
    }
}
sequenceDiagram
    participant Keeper
    participant H as LidoHarvester
    participant T as target (Curve)
    participant S as stETH

    Keeper->>H: harvest(data)
    H->>S: balanceOf(this)
    H->>H: yield = stethBal - staked
    H->>H: tstore(0, 1)
    H->>T: call(data)
    T->>S: transferFrom(this, …, yield)
    T-->>H: ETH return (via call)
    Note over H: receive() short-circuits<br/>because tload(0) = 1
    H->>H: tstore(0, 0)
    H->>H: require ETH gain ≥ yield × (1 - slipBps)
    H->>H: require stETH + 2 ≥ staked

Admin Functions

Function: stake(uint256 amt)

Stake the contract's ETH into Lido. If amt == 0, stakes the entire ETH balance; otherwise stakes amt. Useful when ETH has accumulated in the contract (e.g., post-harvest) and the owner wants to roll it back into the principal.

ATTRIBUTE VALUE
Selector 0xa694fc3a
Parameters amt — ETH to stake (0 = full balance)
Access onlyOwner, payable
CONDITION REQUIREMENT
Caller is owner msg.sender == owner else Unauthorized()
Lido submit must succeed (bool ok,) = STETH.call{value: amt}("") returns true
VARIABLE CHANGE
staked += balanceOf(this) - stethBal
function stake(uint256 amt) public payable onlyOwner {
    if (amt == 0) amt = address(this).balance;
    uint256 stethBal = IERC20(STETH).balanceOf(address(this));
    (bool ok,) = STETH.call{value: amt}("");
    require(ok);
    unchecked { staked += IERC20(STETH).balanceOf(address(this)) - stethBal; }
}

Function: withdraw(address to, uint256 val, bytes calldata data, uint256 minGain)

Call an arbitrary address with arbitrary calldata and any ETH value, optionally gated by a minimum increase in a configured asset/holder balance. Intended for the owner to push harvested ETH into downstream destinations (e.g., calling a DEX router that delivers tokens to a multisig).

ATTRIBUTE VALUE
Selector 0x9483e91a
Parameters to — call target; val — ETH to attach; data — calldata; minGain — required increase in asset held by holder
Access onlyOwner, payable
FLAG OBSERVATION
to, val, and data are entirely owner-controlled — this is an unrestricted external call
The minGain guard is optional: only enforced when holder != address(0)
Condition supports native ETH (asset == address(0)) or any ERC-20 token
The current to observed on-chain is the zRouter (0x000000...F600e4), buying ZORG for the zOrg Safe
CONDITION REQUIREMENT
Caller is owner msg.sender == owner else Unauthorized()
STEP ACTION
1 Load holder and asset into memory
2 If holder != 0, record balBefore = holder's asset balance (native ETH if asset == 0)
3 (bool ok,) = to.call{value: val}(data)
4 If conditioned, require balAfter >= balBefore + minGain else ConditionUnmet()
VARIABLE CHANGE
ETH balance -= val (plus any ETH the target pulls via the call)
Storage unchanged
CONDITION REVERT MESSAGE
Caller is not owner Unauthorized()
to.call returns false — (revert with empty data)
Holder's asset gain below minGain ConditionUnmet()
function withdraw(address to, uint256 val, bytes calldata data, uint256 minGain)
    public payable onlyOwner
{
    address _holder = holder;
    address _asset = asset;
    uint256 balBefore;
    bool conditioned = _holder != address(0);
    bool ethCondition = _asset == address(0);

    if (conditioned) balBefore = ethCondition ? _holder.balance : IERC20(_asset).balanceOf(_holder);

    (bool ok,) = to.call{value: val}(data);
    require(ok);

    if (conditioned) {
        uint256 balAfter = ethCondition ? _holder.balance : IERC20(_asset).balanceOf(_holder);
        require(balAfter >= balBefore + minGain, ConditionUnmet());
    }
}

Function: withdrawStETH(address to, uint256 amt)

Send amt stETH from the contract to to, decrementing staked by the same amount. The primary principal exit for the owner.

ATTRIBUTE VALUE
Selector 0xc9f78f5a
Parameters to — recipient; amt — stETH amount
Access onlyOwner, payable
FLAG OBSERVATION
Decrements staked by amt before the transfer — if amt > staked, this underflows in unchecked-arithmetic context. The surrounding block is checked (Solidity 0.8.x), so it would revert on underflow
Owner can withdraw all principal at any time with no delay
CONDITION REQUIREMENT
Caller is owner msg.sender == owner else Unauthorized()
amt <= staked Otherwise subtraction reverts
stETH transfer succeeds IERC20(STETH).transfer(to, amt) returns true
VARIABLE CHANGE
staked -= amt
function withdrawStETH(address to, uint256 amt) public payable onlyOwner {
    staked -= amt;
    require(IERC20(STETH).transfer(to, amt));
}

Function: setTarget(address _target)

Set the swap target used by harvest(). Revokes the stETH approval granted to the previous target, then grants type(uint256).max stETH approval to the new target.

ATTRIBUTE VALUE
Selector 0x776d1a01
Parameters _target — new swap target (zero address disables)
Access onlyOwner, payable
FLAG OBSERVATION
Grants unlimited stETH allowance to _target — any bug or compromise of the target risks the entire principal
Revokes old target's approval before setting the new one
Setting _target = address(0) disables future harvests (receive/transferFrom would fail on a zero-call without allowance logic)
VARIABLE CHANGE
target = _target
stETH allowance[this][oldTarget] = 0 (if old was non-zero)
stETH allowance[this][_target] = type(uint256).max (if new is non-zero)
function setTarget(address _target) public payable onlyOwner {
    address old = target;
    if (old != address(0)) IERC20(STETH).approve(old, 0);
    target = _target;
    if (_target != address(0)) IERC20(STETH).approve(_target, type(uint256).max);
}

Function: setSlippage(uint16 _slipBps)

Set the slippage tolerance (in basis points) applied to the harvest() ETH-gain invariant.

ATTRIBUTE VALUE
Selector 0xab37fbb7
Parameters _slipBps — 0–10000 bps
Access onlyOwner, payable
CONDITION REQUIREMENT
_slipBps <= 10000 Otherwise reverts
VARIABLE CHANGE
slipBps = _slipBps
function setSlippage(uint16 _slipBps) public payable onlyOwner {
    require(_slipBps <= 10000);
    slipBps = _slipBps;
}

Function: setCondition(address _asset, address _holder)

Set the optional withdraw() post-call condition. When _holder is non-zero, each withdraw() must grow the holder's balance of _asset (or native ETH, if _asset == 0) by at least minGain.

ATTRIBUTE VALUE
Selector 0xbcc79256
Parameters _asset — ERC-20 (or 0 for ETH); _holder — address to measure
Access onlyOwner, payable
VARIABLE CHANGE
asset = _asset
holder = _holder
function setCondition(address _asset, address _holder) public payable onlyOwner {
    (asset, holder) = (_asset, _holder);
}

Function: transferOwnership(address to)

Single-step ownership transfer. Emits OwnershipTransferred(from, to).

ATTRIBUTE VALUE
Selector 0xf2fde38b
Parameters to — new owner
Access onlyOwner, payable
FLAG OBSERVATION
Single-step — no pending-owner pattern; transferring to an unreachable address permanently locks admin functions
Accepts to = address(0) — would effectively renounce ownership, also permanently disabling withdraw, withdrawStETH, setTarget, etc.
VARIABLE CHANGE
owner = to
function transferOwnership(address to) public payable onlyOwner {
    emit OwnershipTransferred(msg.sender, owner = to);
}

View Functions

All six view functions are Solidity-generated public getters with no custom logic. They return the current value of the like-named storage variable.

Function: staked()

Returns the principal basis — cumulative stETH that has been deposited or staked, net of withdrawStETH.

ATTRIBUTE VALUE
Selector 0x0b76619b
Returns uint256

Function: slipBps()

Returns the slippage tolerance (basis points) applied to harvest().

ATTRIBUTE VALUE
Selector 0xf393a5f1
Returns uint16

Function: owner()

Returns the owner address.

ATTRIBUTE VALUE
Selector 0x8da5cb5b
Returns address

Function: target()

Returns the current swap target (holder of unlimited stETH approval).

ATTRIBUTE VALUE
Selector 0xd4b83992
Returns address

Function: asset()

Returns the asset tracked by the optional withdraw() condition. address(0) means native ETH.

ATTRIBUTE VALUE
Selector 0x38d52e0f
Returns address

Function: holder()

Returns the account whose balance of asset must grow during withdraw(). address(0) disables the condition.

ATTRIBUTE VALUE
Selector 0xe534155d
Returns address