Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
6370b91
fix: add oracle report freshness check
avsetsin Jan 12, 2026
df8bcb2
fix: update GGVParamsSupply
loga4 Jan 13, 2026
e6f83fb
fix: Redundant Initializable Inheritance
loga4 Jan 13, 2026
0826367
test: rebalance exceeding during bad debt
avsetsin Jan 13, 2026
034c903
fix: assets of
avsetsin Jan 13, 2026
5700fbd
fix: update checkpoint hint logic to handle NOT_FOUND case and add te…
avsetsin Jan 13, 2026
8b1b7f2
fix: ggvstrategy wsteth validation
loga4 Jan 14, 2026
c2235b6
fix: ggvstrategy wsteth validation
loga4 Jan 14, 2026
6f5fb49
fix: more robust ordering of checks/effects for requestWithdrawal (I-33)
arwer13 Jan 14, 2026
4d5ae0c
fix(Distributor): restrict setMerkleRoot be called twice in same block
arwer13 Jan 14, 2026
d9bf70b
fix(Factory): check executor and proposer are non-zero
arwer13 Jan 14, 2026
fca259a
fix(WQ): non-zero address check for _finalizer on initialize
arwer13 Jan 15, 2026
0c82585
Merge pull request #81 from lidofinance/fix/transfer-with-liability
arwer13 Jan 15, 2026
b60f413
upd: add test
loga4 Jan 15, 2026
2de575d
Merge pull request #83 from lidofinance/fix/i6-redundant-initialize
arwer13 Jan 15, 2026
d5ada4d
Merge pull request #84 from lidofinance/test/rebalance-exceeding-bad-…
arwer13 Jan 15, 2026
01ffc92
Merge pull request #85 from lidofinance/fix/assets-of
arwer13 Jan 15, 2026
0fa0219
Merge pull request #86 from lidofinance/fix/checkpoint-hint-check
arwer13 Jan 15, 2026
7df0e0d
Merge pull request #87 from lidofinance/fix/ggv-wsteth-validation
arwer13 Jan 15, 2026
62b1aba
Merge pull request #88 from lidofinance/fix/wq-reentrency
arwer13 Jan 15, 2026
59aec63
Remove lastProcessedBlock zero initialization in constructor
arwer13 Jan 15, 2026
e55eef7
Merge pull request #91 from lidofinance/fix/more-factory-checks
arwer13 Jan 15, 2026
c4b0464
Merge pull request #93 from lidofinance/fix/non-zero-finalizer
arwer13 Jan 15, 2026
1827e5d
Merge pull request #90 from lidofinance/fix/set-root-twice
arwer13 Jan 15, 2026
6b9c3df
test(GGV): add test for minMint type size
arwer13 Jan 15, 2026
358df9e
fix: test
loga4 Jan 15, 2026
4f51928
Merge pull request #82 from lidofinance/fix/ggv-supply
arwer13 Jan 15, 2026
3a7cb00
chore: update pool-factory-latest.json for testnet-8
arwer13 Jan 15, 2026
16a5b19
fix(StvStETHPool): remove ineffective RR check in ctor
arwer13 Jan 21, 2026
0deca5f
test: improve rounding test for wsteth burning
avsetsin Jan 21, 2026
92435f6
Merge pull request #96 from lidofinance/fix/rounding-tests
loga4 Jan 22, 2026
60fd887
Merge pull request #95 from lidofinance/fix/rr-ctor-check
loga4 Jan 22, 2026
a8fdbeb
remove GGV from factory
loga4 Jan 22, 2026
bc75776
fix: ggv
loga4 Jan 22, 2026
72653ad
Update justfile
loga4 Jan 23, 2026
16b3b10
Merge pull request #97 from lidofinance/fix/remove-ggv
loga4 Jan 23, 2026
1f9c7fb
fix: internal review
loga4 Jan 25, 2026
3c903b7
Merge pull request #98 from lidofinance/fix/nipicks
loga4 Jan 26, 2026
d0b04c2
✅ add wake fuzz
meditationduck Jan 12, 2026
48c4f7c
🎨 format code
meditationduck Jan 14, 2026
c0ceaee
✅ add wake prerelease version description
meditationduck Jan 14, 2026
dc1ae9e
update wake fuzz for fix
meditationduck Jan 27, 2026
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
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,11 @@ docs/
# MacOS
.DS_Store
artifacts/

# Wake files
.wake
pytypes
__pycache__/
*.py[cod]
.hypothesis/
wake-coverage.cov
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ new Factory(locatorAddress, SubFactories({
stvStETHPoolFactory,
withdrawalQueueFactory,
distributorFactory,
ggvStrategyFactory,
timelockFactory
}))
```
Expand All @@ -98,7 +97,6 @@ new Factory(locatorAddress, SubFactories({
// Simplified wrappers
createPoolStvStart(vaultConfig, timelockConfig, commonPoolConfig, allowlistEnabled)
createPoolStvStETHStart(vaultConfig, timelockConfig, commonPoolConfig, allowlistEnabled, reserveRatioGapBP)
createPoolGGVStart(vaultConfig, timelockConfig, commonPoolConfig, reserveRatioGapBP)

// Generic (used by wrappers above)
createPoolStart(vaultConfig, timelockConfig, commonPoolConfig, auxiliaryConfig, strategyFactory, strategyDeployBytes)
Expand Down Expand Up @@ -218,7 +216,6 @@ just -E .env.xxx deploy-factory # Deploy factory
just -E .env.xxx deploy-pool-start ... # Start pool deployment
just -E .env.xxx deploy-pool-finish ... # Finish pool deployment
just -E .env.xxx deploy-all <env> # Deploy factory + all pool types
just -E .env.xxx deploy-ggv-mocks # Deploy GGV mock contracts

# Testing
just test-unit # Run unit tests (no env needed)
Expand Down
14 changes: 7 additions & 7 deletions deployments/pool-factory-latest.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"deployment": {
"factory": "0x66eb579EA49573F042d694200f0a4cA667B0D9B9",
"factory": "0xE9Fe2017febEcb60F9245d72c9D3F45B44e1Bb8C",
"network": "560048"
},
"factories": {
"distributorFactory": "0x1ea2E6B3C3A723042A32A565227D5AbBf2359df7",
"ggvStrategyFactory": "0xD2d7eA0baacd77aE117Ab1d9D4317830884F2935",
"stvPoolFactory": "0x783DBAA9165E4d78453dAB85c64CD19684c12C71",
"stvStETHPoolFactory": "0xe95b68E74747dB78e253331Dd121664779465436",
"timelockFactory": "0x08ba7d7Ba6a3B99268e0698ae4746FAC4ed93951",
"withdrawalQueueFactory": "0xeeb5356160517fd1ca1148540570469eFF430d19"
"distributorFactory": "0xD34AD5a37296316E05a4324cd05A92507d407Ca5",
"ggvStrategyFactory": "0xd0c45b07B55B8dF7c9631085817e15A7d2602a9B",
"stvPoolFactory": "0x910Ad23fC0281B6cD38e004458582Fe0038a5174",
"stvStETHPoolFactory": "0xD65D28B54b9BF09bDD500Beee0DE3127c5CaddF1",
"timelockFactory": "0x25bd8A5A5dC0AF788DC52d16Bad4E12c0E460a97",
"withdrawalQueueFactory": "0xE6A9dfbe4a9f3003BD459ac614F9458d6C5eaaA9"
}
}
21 changes: 3 additions & 18 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,8 @@ deploy-all env_file:
just deploy-pool-start $FACTORY "config/${POOL_CONFIG_NAME}"
just deploy-pool-finish $FACTORY $INTERMEDIATE_JSON

export POOL_CONFIG_NAME="hoodi-stv-ggv.json"
export INTERMEDIATE_JSON="deployments/intermediate-${NOW}-${POOL_CONFIG_NAME}"
just deploy-pool-start $FACTORY "config/${POOL_CONFIG_NAME}"
just deploy-pool-finish $FACTORY $INTERMEDIATE_JSON

deploy-ggv-mocks:
forge script script/DeployGGVMocks.s.sol:DeployGGVMocks $(just _script-flags) --gas-limit {{fusaka_tx_gas_limit}} --sig 'run()'

publish-sources address contract_path constructor_args:
forge verify-contract {{address}} {{contract_path}} \
--verifier etherscan \
Expand Down Expand Up @@ -113,16 +107,13 @@ verify-all:
FACTORY_CONFIG="config/hoodi-factory.json"

FACTORY=$(jq -r '.deployment.factory' "$DEPLOYMENT_JSON")
GGV_STRATEGY_FACTORY=$(jq -r '.factories.ggvStrategyFactory' "$DEPLOYMENT_JSON")
STV_POOL_FACTORY=$(jq -r '.factories.stvPoolFactory' "$DEPLOYMENT_JSON")
STV_STETH_POOL_FACTORY=$(jq -r '.factories.stvStETHPoolFactory' "$DEPLOYMENT_JSON")
WITHDRAWAL_QUEUE_FACTORY=$(jq -r '.factories.withdrawalQueueFactory' "$DEPLOYMENT_JSON")
TIMELOCK_FACTORY=$(jq -r '.factories.timelockFactory' "$DEPLOYMENT_JSON")
DISTRIBUTOR_FACTORY=$(jq -r '.factories.distributorFactory' "$DEPLOYMENT_JSON")

LOCATOR=$(jq -r '.lidoLocator' "$FACTORY_CONFIG")
TELLER=$(jq -r '.strategies.ggv.teller' "$FACTORY_CONFIG")
BORING_QUEUE=$(jq -r '.strategies.ggv.boringOnChainQueue' "$FACTORY_CONFIG")

echo "Verifying contracts..."
echo "Factory: $FACTORY"
Expand Down Expand Up @@ -153,18 +144,12 @@ verify-all:
--verifier etherscan --rpc-url {{env('RPC_URL')}} --watch -vvvv || echo "Failed to verify DistributorFactory"

echo ""
echo "6. Verifying GGVStrategyFactory..."
GGV_ARGS=$(cast abi-encode "constructor(address,address)" "$TELLER" "$BORING_QUEUE")
forge verify-contract "$GGV_STRATEGY_FACTORY" src/factories/GGVStrategyFactory.sol:GGVStrategyFactory \
--verifier etherscan --rpc-url {{env('RPC_URL')}} --constructor-args "$GGV_ARGS" --watch -vvvv || echo "Failed to verify GGVStrategyFactory"

echo ""
echo "7. Verifying Factory..."
echo "6. Verifying Factory..."

FACTORY_ARGS=$(cast abi-encode \
"constructor(address,(address,address,address,address,address,address))" \
"constructor(address,(address,address,address,address,address))" \
"$LOCATOR" \
"($STV_POOL_FACTORY,$STV_STETH_POOL_FACTORY,$WITHDRAWAL_QUEUE_FACTORY,$DISTRIBUTOR_FACTORY,$GGV_STRATEGY_FACTORY,$TIMELOCK_FACTORY)")
"($STV_POOL_FACTORY,$STV_STETH_POOL_FACTORY,$WITHDRAWAL_QUEUE_FACTORY,$DISTRIBUTOR_FACTORY,$TIMELOCK_FACTORY)")
forge verify-contract "$FACTORY" src/Factory.sol:Factory \
--verifier etherscan --rpc-url {{env('RPC_URL')}} --constructor-args "$FACTORY_ARGS" --watch -vvvv || echo "Failed to verify Factory"

Expand Down
22 changes: 4 additions & 18 deletions script/DeployFactory.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,17 @@ import {console2} from "forge-std/console2.sol";

import {Factory} from "src/Factory.sol";
import {DistributorFactory} from "src/factories/DistributorFactory.sol";
import {GGVStrategyFactory} from "src/factories/GGVStrategyFactory.sol";
import {StvPoolFactory} from "src/factories/StvPoolFactory.sol";
import {StvStETHPoolFactory} from "src/factories/StvStETHPoolFactory.sol";
import {TimelockFactory} from "src/factories/TimelockFactory.sol";
import {WithdrawalQueueFactory} from "src/factories/WithdrawalQueueFactory.sol";

contract DeployFactory is Script {
function _deployImplFactories(address _ggvTeller, address _ggvBoringQueue)
internal
returns (Factory.SubFactories memory f)
{
function _deployImplFactories() internal returns (Factory.SubFactories memory f) {
f.stvPoolFactory = address(new StvPoolFactory());
f.stvStETHPoolFactory = address(new StvStETHPoolFactory());
f.withdrawalQueueFactory = address(new WithdrawalQueueFactory());
f.distributorFactory = address(new DistributorFactory());
f.ggvStrategyFactory = address(new GGVStrategyFactory(_ggvTeller, _ggvBoringQueue));
f.timelockFactory = address(new TimelockFactory());
}

Expand All @@ -36,7 +31,6 @@ contract DeployFactory is Script {
factoriesSection =
vm.serializeAddress("factories", "withdrawalQueueFactory", _subFactories.withdrawalQueueFactory);
factoriesSection = vm.serializeAddress("factories", "distributorFactory", _subFactories.distributorFactory);
factoriesSection = vm.serializeAddress("factories", "ggvStrategyFactory", _subFactories.ggvStrategyFactory);
factoriesSection = vm.serializeAddress("factories", "timelockFactory", _subFactories.timelockFactory);

string memory out = "";
Expand All @@ -50,24 +44,16 @@ contract DeployFactory is Script {
vm.writeJson(json, "deployments/pool-factory-latest.json");
}

function _readFactoryConfig(string memory _paramsPath)
internal
view
returns (address locator, address teller, address boringQueue)
{
function _readFactoryConfig(string memory _paramsPath) internal view returns (address locator) {
require(
vm.isFile(_paramsPath),
string(abi.encodePacked("FACTORY_PARAMS_JSON file does not exist at: ", _paramsPath))
);
string memory json = vm.readFile(_paramsPath);

locator = vm.parseJsonAddress(json, "$.lidoLocator");
teller = vm.parseJsonAddress(json, "$.strategies.ggv.teller");
boringQueue = vm.parseJsonAddress(json, "$.strategies.ggv.boringOnChainQueue");

require(locator != address(0), "lidoLocator missing");
require(teller != address(0), "strategies.ggv.teller missing");
require(boringQueue != address(0), "strategies.ggv.boringOnChainQueue missing");
}

function run() external {
Expand All @@ -83,12 +69,12 @@ contract DeployFactory is Script {
);

// Read all factory configuration from JSON file
(address locatorAddress, address ggvTeller, address ggvBoringQueue) = _readFactoryConfig(paramsJsonPath);
(address locatorAddress) = _readFactoryConfig(paramsJsonPath);

vm.startBroadcast();

// Deploy implementation factories and proxy stub
Factory.SubFactories memory subFactories = _deployImplFactories(ggvTeller, ggvBoringQueue);
Factory.SubFactories memory subFactories = _deployImplFactories();

Factory factory = new Factory(locatorAddress, subFactories);

Expand Down
13 changes: 3 additions & 10 deletions script/DeployPool.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ contract DeployPool is Script {
json = vm.serializeBytes("_ctorBytecode", "withdrawalQueueProxy", withdrawalCtorBytecode);
}

function _readPoolParams(string memory _path, address _ggvFactory) internal view returns (PoolParams memory p) {
function _readPoolParams(string memory _path) internal view returns (PoolParams memory p) {
string memory json = vm.readFile(_path);
p.vaultConfig = Factory.VaultConfig({
nodeOperator: vm.parseJsonAddress(json, "$.vaultConfig.nodeOperator"),
Expand Down Expand Up @@ -184,14 +184,7 @@ contract DeployPool is Script {

try vm.parseJsonAddress(json, "$.strategyFactory") returns (address addr) {
p.strategyFactory = addr;
} catch {
string memory strategyFactoryString = vm.parseJsonString(json, "$.strategyFactory");
if (keccak256(bytes(strategyFactoryString)) == keccak256(bytes("GGV"))) {
p.strategyFactory = _ggvFactory;
} else {
revert("Invalid strategy factory: must be either address or 'GGV'");
}
}
} catch {}
}

function _loadIntermediate(string memory _path) internal view returns (Factory.PoolIntermediate memory) {
Expand Down Expand Up @@ -270,7 +263,7 @@ contract DeployPool is Script {

require(msg.sender.balance > 1 ether, "msg.sender balance must be above 1 ether");

PoolParams memory p = _readPoolParams(paramsJsonPath, address(_factory.GGV_STRATEGY_FACTORY()));
PoolParams memory p = _readPoolParams(paramsJsonPath);

require(bytes(p.commonPoolConfig.name).length != 0, "commonPoolConfig.name missing");
require(bytes(p.commonPoolConfig.symbol).length != 0, "commonPoolConfig.symbol missing");
Expand Down
4 changes: 2 additions & 2 deletions src/Distributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ contract Distributor is AccessControlEnumerable {

// ==================== Errors ====================
error AlreadyProcessed();
error AlreadySetInThisBlock();
error InvalidProof();
error ClaimableTooLow();
error RootNotSet();
Expand All @@ -50,8 +51,6 @@ contract Distributor is AccessControlEnumerable {
* @param _manager The address of the manager (MANAGER_ROLE)
*/
constructor(address _owner, address _manager) {
lastProcessedBlock = block.number;

_grantRole(DEFAULT_ADMIN_ROLE, _owner);
_grantRole(MANAGER_ROLE, _manager);
}
Expand Down Expand Up @@ -86,6 +85,7 @@ contract Distributor is AccessControlEnumerable {
function setMerkleRoot(bytes32 _root, string calldata _cid) external {
_checkRole(MANAGER_ROLE, msg.sender);
if (_root == root && keccak256(bytes(_cid)) == keccak256(bytes(cid))) revert AlreadyProcessed();
if (block.number == lastProcessedBlock) revert AlreadySetInThisBlock();

emit MerkleRootUpdated(root, _root, cid, _cid, lastProcessedBlock, block.number);

Expand Down
54 changes: 12 additions & 42 deletions src/Factory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {StvPool} from "./StvPool.sol";
import {StvStETHPool} from "./StvStETHPool.sol";
import {WithdrawalQueue} from "./WithdrawalQueue.sol";
import {DistributorFactory} from "./factories/DistributorFactory.sol";
import {GGVStrategyFactory} from "./factories/GGVStrategyFactory.sol";
import {StvPoolFactory} from "./factories/StvPoolFactory.sol";
import {StvStETHPoolFactory} from "./factories/StvStETHPoolFactory.sol";
import {TimelockFactory} from "./factories/TimelockFactory.sol";
Expand Down Expand Up @@ -38,15 +37,13 @@ contract Factory {
* @param stvStETHPoolFactory Factory for deploying StvStETHPool implementations
* @param withdrawalQueueFactory Factory for deploying WithdrawalQueue implementations
* @param distributorFactory Factory for deploying Distributor implementations
* @param ggvStrategyFactory Factory for deploying GGV strategy implementations
* @param timelockFactory Factory for deploying Timelock controllers
*/
struct SubFactories {
address stvPoolFactory;
address stvStETHPoolFactory;
address withdrawalQueueFactory;
address distributorFactory;
address ggvStrategyFactory;
address timelockFactory;
}

Expand Down Expand Up @@ -81,6 +78,7 @@ contract Factory {
* @param minWithdrawalDelayTime Minimum delay time for processing withdrawals
* @param name ERC20 token name for the pool shares
* @param symbol ERC20 token symbol for the pool shares
* @param emergencyCommittee Address of the emergency committee
*/
struct CommonPoolConfig {
uint256 minWithdrawalDelayTime;
Expand Down Expand Up @@ -117,7 +115,9 @@ contract Factory {
* @notice Intermediate state returned by deployment start functions
* @param dashboard Address of the deployed dashboard
* @param poolProxy Address of the deployed pool proxy (not yet initialized)
* @param poolImpl Address of the deployed pool implementation (not yet initialized)
* @param withdrawalQueueProxy Address of the deployed withdrawal queue proxy (not yet initialized)
* @param wqImpl Address of the deployed withdrawal queue implementation (not yet initialized)
* @param timelock Address of the deployed timelock controller
*/
struct PoolIntermediate {
Expand Down Expand Up @@ -280,11 +280,6 @@ contract Factory {
*/
DistributorFactory public immutable DISTRIBUTOR_FACTORY;

/**
* @notice Factory for deploying GGV strategy implementations
*/
GGVStrategyFactory public immutable GGV_STRATEGY_FACTORY;

/**
* @notice Factory for deploying Timelock controllers
*/
Expand All @@ -300,11 +295,6 @@ contract Factory {
*/
bytes32 public immutable DEFAULT_ADMIN_ROLE = 0x00;

/**
* @notice Total basis points constant (100.00%)
*/
uint256 public constant TOTAL_BASIS_POINTS = 100_00;

/**
* @notice Maximum time allowed between start and finish deployment phases
*/
Expand Down Expand Up @@ -342,7 +332,6 @@ contract Factory {
STV_STETH_POOL_FACTORY = StvStETHPoolFactory(_subFactories.stvStETHPoolFactory);
WITHDRAWAL_QUEUE_FACTORY = WithdrawalQueueFactory(_subFactories.withdrawalQueueFactory);
DISTRIBUTOR_FACTORY = DistributorFactory(_subFactories.distributorFactory);
GGV_STRATEGY_FACTORY = GGVStrategyFactory(_subFactories.ggvStrategyFactory);
TIMELOCK_FACTORY = TimelockFactory(_subFactories.timelockFactory);

DUMMY_IMPLEMENTATION = address(new DummyImplementation());
Expand Down Expand Up @@ -409,33 +398,6 @@ contract Factory {
createPoolStart(_vaultConfig, _timelockConfig, _commonPoolConfig, _auxiliaryPoolConfig, address(0), "");
}

/**
* @notice Initiates deployment of a GGV strategy pool (first phase)
* @param _vaultConfig Configuration for the vault
* @param _timelockConfig Configuration for the timelock controller
* @param _commonPoolConfig Common pool parameters (name, symbol, withdrawal delay)
* @param _reserveRatioGapBP Maximum allowed reserve ratio gap in basis points
* @return intermediate Deployment state needed for finish phase
* @dev ETH for vault connection deposit should be sent in createPoolFinish
* @dev Automatically enables allowlist and minting for GGV pools
*/
function createPoolGGVStart(
VaultConfig memory _vaultConfig,
TimelockConfig memory _timelockConfig,
CommonPoolConfig memory _commonPoolConfig,
uint256 _reserveRatioGapBP
) external returns (PoolIntermediate memory intermediate) {
AuxiliaryPoolConfig memory auxiliaryConfig = AuxiliaryPoolConfig({
allowListEnabled: true,
allowListManager: address(0),
mintingEnabled: true,
reserveRatioGapBP: _reserveRatioGapBP
});
intermediate = createPoolStart(
_vaultConfig, _timelockConfig, _commonPoolConfig, auxiliaryConfig, address(GGV_STRATEGY_FACTORY), ""
);
}

/**
* @notice Generic pool deployment start function (first phase)
* @param _vaultConfig Configuration for the vault
Expand Down Expand Up @@ -472,6 +434,14 @@ contract Factory {
}
}

// Validate proposer and executor addresses
if (_timelockConfig.proposer == address(0)) {
revert InvalidConfiguration("proposer must not be zero address");
}
if (_timelockConfig.executor == address(0)) {
revert InvalidConfiguration("executor must not be zero address");
}

address timelock = TIMELOCK_FACTORY.deploy(
_timelockConfig.minDelaySeconds, _timelockConfig.proposer, _timelockConfig.executor
);
Expand Down Expand Up @@ -753,7 +723,7 @@ contract Factory {
address _strategyFactory,
bytes memory _strategyDeployBytes,
PoolIntermediate memory _intermediate
) public pure returns (bytes32 result) {
) internal pure returns (bytes32 result) {
result = keccak256(
abi.encode(
_sender,
Expand Down
Loading