Artifacts
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 |
Runtime Bytecode
The deployed contract bytecode fetched from the blockchain.
Source: Etherscan — Contract Code
Command:
Artifact: 5,327-byte runtime bytecode. First 60 bytes shown below; full bytecode available from Etherscan or via cast code.
Creation Bytecode
The full bytecode used to deploy the contract, including the constructor prologue.
Source: Etherscan — Creation TX
Command:
export ETH_RPC_URL=https://eth.llamarpc.com
cast tx 0xa49ef903cb5be90b4e1d2bdfec294ffe729c095ff400cc0980d9ede30c54d046 input
Artifact: Returned by the Etherscan getcontractcreation endpoint (the CREATE2 factory's init-code). First 60 bytes shown below.
The constructor prologue stores tx.origin as owner at slot 1 (shifted left by 16 bits, packed with slipBps = 0) and emits an OwnershipTransferred(address(0), tx.origin) event via topic 0x8be0079c...6457e0.
Verified Source Code
Source code verified on Etherscan (Exact Match).
Source: Etherscan — Contract Source
Compiler: Solidity 0.8.34 (optimizer enabled, 9,999,999 runs, Cancun EVM)
Artifact:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.34;
interface IERC20 {
function approve(address, uint256) external returns (bool);
function balanceOf(address) external view returns (uint256);
function transfer(address, uint256) external returns (bool);
function transferFrom(address, address, uint256) external returns (bool);
}
/// @notice Simple Lido stETH harvesting contract to utilize ETH yield.
/// @dev Accepts raw ETH (converts to stETH) or stETH deposits -
/// which increment basis counter to track yield - which can reconvert
/// to ETH - which may then be used in withdraw() - optional condition
/// can be attached which ensures some sort of balance increase in ETH
/// or ERC20 asset occurs as result of spending such ETH on withdraw().
contract LidoHarvester {
event OwnershipTransferred(address indexed from, address indexed to);
address constant STETH = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
uint256 public staked;
uint16 public slipBps;
address public owner;
address public target;
address public asset;
address public holder;
error ConditionUnmet();
error Unauthorized();
modifier onlyOwner() {
require(msg.sender == owner, Unauthorized());
_;
}
function transferOwnership(address to) public payable onlyOwner {
emit OwnershipTransferred(msg.sender, owner = to);
}
function setSlippage(uint16 _slipBps) public payable onlyOwner {
require(_slipBps <= 10000);
slipBps = _slipBps;
}
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 setCondition(address _asset, address _holder) public payable onlyOwner {
(asset, holder) = (_asset, _holder);
}
constructor() payable {
emit OwnershipTransferred(address(0), owner = tx.origin);
}
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) 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 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 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 withdrawStETH(address to, uint256 amt) public payable onlyOwner {
staked -= amt;
require(IERC20(STETH).transfer(to, amt));
}
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);
}
}
}
Additional Artifacts
ABI (from Etherscan)
- Source: Etherscan v2 API (
action=getsourcecode) - Command:
OwnershipTransferred event topic
- Topic 0:
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0 - Signature:
OwnershipTransferred(address,address) - Explorer: events filter
Observed operational transactions
| KIND | TX HASH | NOTES |
|---|---|---|
| Deployment | 0xa49ef903...54d046 (tx) |
CREATE2 via factory 0x5c2271fd...d21822 |
setSlippage(50) |
0x4905519b...4b8185 (tx) |
Sets slippage to 0.5% |
setTarget(Curve stETH/ETH) |
0x774cdaa4...368be4 (tx) |
Grants infinite stETH approval to Curve pool |
| Initial funding | 0x42b7b6ab...2ecdc8 (tx) |
Raw ETH → staked via receive() |
deposit(…) |
0x7ebef8fe...998704 (tx) |
stETH in via transferFrom |
harvest(swapData) |
0x9b2aa0df...52cbfb (tx) |
Converted ~6.3e14 wei stETH yield → ETH via Curve; gasUsed 146,327 |
withdraw(to=zRouter, …) |
0x2bd2dd1b...d943cf (tx) |
Forwarded harvested ETH to zRouter with ZORG-gain condition |