Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 15 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "forge fmt $CLAUDE_FILE_PATHS 2>/dev/null || true"
}
]
}
]
}
}
7 changes: 4 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
branch = v1.14.0
[submodule "lib/aave-v3-origin"]
path = lib/aave-v3-origin
url = https://github.com/aave-dao/aave-v3-origin
branch = v3.5.0
branch = v3.6.0
[submodule "lib/composable-cow"]
path = lib/composable-cow
url = https://github.com/cowprotocol/composable-cow
branch = main
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
branch = v5.4.0
branch = v5.5.0
[submodule "lib/v3-core"]
path = lib/v3-core
url = https://github.com/stakewise/v3-core
branch = v4.0.0
branch = main
81 changes: 81 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Build Commands

```bash
forge build # Compile contracts
forge test # Run all tests
forge test --mt test_deposit # Run specific test by name
forge test -vvv # Run tests with verbose output
forge fmt # Format code
forge lint --severity high # Lint for high-severity issues
```

## Deployment

Scripts in `script/` follow pattern `Deploy<ContractName>.s.sol`. Run with:
```bash
forge script script/DeployX.s.sol --rpc-url $MAINNET_RPC_URL --broadcast
```

Available RPC endpoints: `mainnet`, `hoodi`, `chiado`, `gnosis` (configured in foundry.toml)

## Testing Notes

- Tests use mainnet fork (requires `MAINNET_RPC_URL` env variable)
- Fork block numbers are specified in test files (e.g., `forkBlockNumber = 23_117_000`)
- Tests often use `vm.prank()`, `vm.warp()`, and `vm.startPrank()`/`vm.stopPrank()` for impersonation and time manipulation

## Architecture Overview

This is the StakeWise v3 periphery contracts repository containing supplementary contracts that interact with the core v3-core protocol.

### Key Components

**StrategiesRegistry** (`src/StrategiesRegistry.sol`): Central registry managing strategies and their proxies. Tracks enabled strategies, proxy addresses, and strategy-specific configurations.

**StrategyProxy** (`src/StrategyProxy.sol`): Minimal proxy contract that executes transactions on behalf of leverage strategies. Each user-vault-strategy combination gets its own proxy deployed via `Clones.cloneDeterministic()`.

**LeverageStrategy** (`src/leverage/LeverageStrategy.sol`): Abstract base for leverage strategies. Uses osToken flash loans to create leveraged positions by:
1. Depositing osToken to lending protocol (e.g., Aave)
2. Borrowing asset tokens (WETH/GNO)
3. Depositing borrowed assets to vault and minting more osToken
4. Repeating via flash loan for maximum leverage

Chain-specific implementations:
- `EthAaveLeverageStrategy` - Ethereum mainnet with Aave V3
- `GnoAaveLeverageStrategy` - Gnosis chain with Aave V3

**TokensConverter** (`src/converters/`): Converts reward tokens to vault asset tokens via CoW Protocol swaps. Uses composable conditional orders.

**Helper Contracts** (`src/helpers/`):
- `BoostHelpers` - Calculates boost position details and LTV ratios
- `StakeHelpers` - Facilitates staking operations
- `VaultUserLtvTracker` - Tracks user LTV positions across vaults

**MerkleDistributor** (`src/MerkleDistributor.sol`): Distributes incentives using merkle proofs with oracle-signed root updates.

### Key Patterns

- Strategy proxies are deterministically addressed: `keccak256(abi.encode(strategyId, vault, user))`
- Flash loan pattern for leverage: borrow osToken, convert to assets, deposit to vault, mint more osToken, repay flash loan
- Strategy configs stored in StrategiesRegistry as `bytes` under `keccak256(strategyId, configName)`

## Dependencies

- `v3-core`: StakeWise core protocol (vaults, osToken, keeper)
- `aave-v3-origin`: Aave V3 for borrowing/lending
- `composable-cow`: CoW Protocol for token swaps
- `openzeppelin-contracts-upgradeable`: Upgradeable contract patterns

## Solidity Version

Solidity 0.8.26 with Cancun EVM, optimizer enabled (200 runs), via-IR compilation.

## Code Style

- Single quotes for strings (`'string'` not `"string"`)
- Number underscores for thousands (`1_000_000`)
- Multi-line function headers with params first
14 changes: 14 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ optimizer = true
optimizer_runs = 200
via_ir = true
bytecode_hash = 'none'
remappings = [
# v3-core uses its own OpenZeppelin
"lib/v3-core:@openzeppelin/contracts-upgradeable/=lib/v3-core/lib/openzeppelin-contracts-upgradeable/contracts/",
"lib/v3-core:@openzeppelin/contracts/=lib/v3-core/lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
# Root project OpenZeppelin
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
"@stakewise-core/=lib/v3-core/contracts/",
"@stakewise-test/=lib/v3-core/test/",
"@aave-core/=lib/aave-v3-origin/src/contracts/",
"safe/=lib/composable-cow/lib/safe/contracts/",
"@composable-cow/=lib/composable-cow/src/",
"@cowprotocol/=lib/composable-cow/lib/cowprotocol/src/",
]

[lint]
lint_on_build = false
Expand Down
2 changes: 1 addition & 1 deletion lib/aave-v3-origin
Submodule aave-v3-origin updated 179 files
2 changes: 1 addition & 1 deletion lib/forge-std
Submodule forge-std updated 61 files
+1 −1 .github/CODEOWNERS
+6 −0 .github/dependabot.yml
+60 −17 .github/workflows/ci.yml
+6 −1 .github/workflows/sync.yml
+2 −2 CONTRIBUTING.md
+11 −9 README.md
+12 −0 RELEASE_CHECKLIST.md
+9 −14 foundry.toml
+2 −2 package.json
+2 −12 scripts/vm.py
+8 −2 src/Base.sol
+60 −0 src/Config.sol
+477 −0 src/LibVariable.sol
+2 −2 src/Script.sol
+28 −13 src/StdAssertions.sol
+13 −4 src/StdChains.sol
+9 −13 src/StdCheats.sol
+632 −0 src/StdConfig.sol
+2 −2 src/StdConstants.sol
+2 −2 src/StdError.sol
+2 −4 src/StdInvariant.sol
+4 −12 src/StdJson.sol
+6 −2 src/StdMath.sol
+11 −9 src/StdStorage.sol
+2 −2 src/StdStyle.sol
+4 −12 src/StdToml.sol
+5 −13 src/StdUtils.sol
+2 −4 src/Test.sol
+85 −53 src/Vm.sol
+10 −19 src/console.sol
+2 −2 src/console2.sol
+2 −2 src/interfaces/IERC1155.sol
+2 −2 src/interfaces/IERC165.sol
+2 −2 src/interfaces/IERC20.sol
+2 −2 src/interfaces/IERC4626.sol
+2 −2 src/interfaces/IERC6909.sol
+2 −2 src/interfaces/IERC721.sol
+4 −10 src/interfaces/IERC7540.sol
+2 −2 src/interfaces/IERC7575.sol
+3 −8 src/interfaces/IMulticall3.sol
+691 −1,380 src/safeconsole.sol
+2 −2 test/CommonBase.t.sol
+381 −0 test/Config.t.sol
+452 −0 test/LibVariable.t.sol
+2 −2 test/StdAssertions.t.sol
+25 −25 test/StdChains.t.sol
+19 −20 test/StdCheats.t.sol
+2 −2 test/StdConstants.t.sol
+3 −4 test/StdError.t.sol
+2 −2 test/StdJson.t.sol
+8 −8 test/StdMath.t.sol
+24 −27 test/StdStorage.t.sol
+2 −2 test/StdStyle.t.sol
+2 −2 test/StdToml.t.sol
+13 −13 test/StdUtils.t.sol
+3 −3 test/Vm.t.sol
+2 −4 test/compilation/CompilationScript.sol
+2 −4 test/compilation/CompilationScriptBase.sol
+2 −4 test/compilation/CompilationTest.sol
+2 −4 test/compilation/CompilationTestBase.sol
+81 −0 test/fixtures/config.toml
2 changes: 1 addition & 1 deletion lib/openzeppelin-contracts-upgradeable
2 changes: 1 addition & 1 deletion lib/v3-core
Submodule v3-core updated 157 files
9 changes: 0 additions & 9 deletions remappings.txt

This file was deleted.

2 changes: 1 addition & 1 deletion script/DeployAaveBoostHelpers.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.26;

import {Script} from 'forge-std/Script.sol';
import {console} from 'forge-std/console.sol';
import {BoostHelpers} from '../src/BoostHelpers.sol';
import {BoostHelpers} from '../src/helpers/BoostHelpers.sol';

contract DeployAaveBoostHelpers is Script {
struct ConfigParams {
Expand Down
7 changes: 4 additions & 3 deletions script/DeployStakeHelpers.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.26;

import {Script} from 'forge-std/Script.sol';
import {console} from 'forge-std/console.sol';
import {StakeHelpers} from '../src/StakeHelpers.sol';
import {StakeHelpers} from '../src/helpers/StakeHelpers.sol';

contract DeployStakeHelpers is Script {
struct ConfigParams {
Expand All @@ -30,8 +30,9 @@ contract DeployStakeHelpers is Script {
ConfigParams memory params = _readEnvVariables();

// Deploy StakeHelpers.
StakeHelpers stakeHelpers =
new StakeHelpers(params.keeper, params.osTokenConfigV1, params.osTokenConfig, params.osTokenVaultController);
StakeHelpers stakeHelpers = new StakeHelpers(
params.keeper, params.osTokenConfigV1, params.osTokenConfig, params.osTokenVaultController
);
console.log('StakeHelpers deployed at: ', address(stakeHelpers));

vm.stopBroadcast();
Expand Down
2 changes: 1 addition & 1 deletion script/DeployVaultUserLtvTracker.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.26;

import {Script} from 'forge-std/Script.sol';
import {console} from 'forge-std/console.sol';
import {VaultUserLtvTracker} from '../src/VaultUserLtvTracker.sol';
import {VaultUserLtvTracker} from '../src/helpers/VaultUserLtvTracker.sol';

contract DeployVaultUserLtvTracker is Script {
struct ConfigParams {
Expand Down
28 changes: 14 additions & 14 deletions snapshots/EthAaveLeverageStrategyTest.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"EthAaveLeverageStrategyTest_test_claimExitedAssets1": "538464",
"EthAaveLeverageStrategyTest_test_claimExitedAssets2": "1023328",
"EthAaveLeverageStrategyTest_test_claimExitedAssets3": "1037672",
"EthAaveLeverageStrategyTest_test_deposit_HasPosition": "744165",
"EthAaveLeverageStrategyTest_test_deposit_NoPosition": "873750",
"EthAaveLeverageStrategyTest_test_enterExitQueue": "230058",
"EthAaveLeverageStrategyTest_test_forceEnterExitQueue_borrowForceExitLtvPercent": "300885",
"EthAaveLeverageStrategyTest_test_forceEnterExitQueue_vaultForceExitLtvPercent": "251937",
"EthAaveLeverageStrategyTest_test_permit": "374456",
"EthAaveLeverageStrategyTest_test_rescueLendingAssets1": "668006",
"EthAaveLeverageStrategyTest_test_rescueLendingAssets2": "570201",
"EthAaveLeverageStrategyTest_test_rescueVaultAssets": "656361",
"EthAaveLeverageStrategyTest_test_upgradeProxy_withExitingPosition": "86598",
"EthAaveLeverageStrategyTest_test_upgradeProxy_withoutExitingPosition": "57794"
"EthAaveLeverageStrategyTest_test_claimExitedAssets1": "357019",
"EthAaveLeverageStrategyTest_test_claimExitedAssets2": "548475",
"EthAaveLeverageStrategyTest_test_claimExitedAssets3": "546487",
"EthAaveLeverageStrategyTest_test_deposit_HasPosition": "577168",
"EthAaveLeverageStrategyTest_test_deposit_NoPosition": "949853",
"EthAaveLeverageStrategyTest_test_enterExitQueue": "154826",
"EthAaveLeverageStrategyTest_test_forceEnterExitQueue_borrowForceExitLtvPercent": "191585",
"EthAaveLeverageStrategyTest_test_forceEnterExitQueue_vaultForceExitLtvPercent": "175637",
"EthAaveLeverageStrategyTest_test_permit": "328984",
"EthAaveLeverageStrategyTest_test_rescueLendingAssets1": "407148",
"EthAaveLeverageStrategyTest_test_rescueLendingAssets2": "311344",
"EthAaveLeverageStrategyTest_test_rescueVaultAssets": "629898",
"EthAaveLeverageStrategyTest_test_upgradeProxy_withExitingPosition": "34366",
"EthAaveLeverageStrategyTest_test_upgradeProxy_withoutExitingPosition": "12062"
}
18 changes: 9 additions & 9 deletions snapshots/MerkleDistributorTest.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"MerkleDistributorTest_test_addDistributor": "51298",
"MerkleDistributorTest_test_claim": "145456",
"MerkleDistributorTest_test_constructor": "1417841",
"MerkleDistributorTest_test_distributeOneTime": "76503",
"MerkleDistributorTest_test_distributePeriodically": "74627",
"MerkleDistributorTest_test_removeDistributor": "26886",
"MerkleDistributorTest_test_setRewardsDelay": "30905",
"MerkleDistributorTest_test_setRewardsMinOracles": "36951",
"MerkleDistributorTest_test_setRewardsRoot": "52046"
"MerkleDistributorTest_test_addDistributor": "29954",
"MerkleDistributorTest_test_claim": "108341",
"MerkleDistributorTest_test_constructor": "1253137",
"MerkleDistributorTest_test_distributeOneTime": "40639",
"MerkleDistributorTest_test_distributePeriodically": "39199",
"MerkleDistributorTest_test_removeDistributor": "3554",
"MerkleDistributorTest_test_setRewardsDelay": "9689",
"MerkleDistributorTest_test_setRewardsMinOracles": "11247",
"MerkleDistributorTest_test_setRewardsRoot": "14434"
}
6 changes: 3 additions & 3 deletions snapshots/StrategiesRegistryTest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"StrategiesRegistryTest_test_addStrategyProxy": "71090",
"StrategiesRegistryTest_test_setStrategy": "48814",
"StrategiesRegistryTest_test_setStrategyConfig": "73969"
"StrategiesRegistryTest_test_addStrategyProxy": "47650",
"StrategiesRegistryTest_test_setStrategy": "27458",
"StrategiesRegistryTest_test_setStrategyConfig": "51697"
}
17 changes: 11 additions & 6 deletions src/MerkleDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ contract MerkleDistributor is Ownable2Step, EIP712, IMerkleDistributor {
}

/// @inheritdoc IMerkleDistributor
function setDistributor(address distributor, bool isEnabled) external onlyOwner {
function setDistributor(
address distributor,
bool isEnabled
) external onlyOwner {
distributors[distributor] = isEnabled;
emit DistributorUpdated(msg.sender, distributor, isEnabled);
}
Expand Down Expand Up @@ -180,13 +183,11 @@ contract MerkleDistributor is Ownable2Step, EIP712, IMerkleDistributor {
bytes32 merkleRoot = rewardsRoot;

// verify the merkle proof
if (
!MerkleProof.verifyCalldata(
if (!MerkleProof.verifyCalldata(
merkleProof,
merkleRoot,
keccak256(bytes.concat(keccak256(abi.encode(tokens, account, cumulativeAmounts))))
)
) {
)) {
revert Errors.InvalidProof();
}

Expand Down Expand Up @@ -231,7 +232,11 @@ contract MerkleDistributor is Ownable2Step, EIP712, IMerkleDistributor {
* @param message The message that was signed
* @param signatures The concatenation of the oracles' signatures
*/
function _verifySignatures(uint256 requiredSignatures, bytes32 message, bytes calldata signatures) private view {
function _verifySignatures(
uint256 requiredSignatures,
bytes32 message,
bytes calldata signatures
) private view {
if (requiredSignatures == 0) revert Errors.InvalidOracles();

// check whether enough signatures
Expand Down
12 changes: 10 additions & 2 deletions src/OsTokenVaultEscrowAuth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,21 @@ contract OsTokenVaultEscrowAuth is IOsTokenVaultEscrowAuth {
* @param vaultsRegistry The address of the VaultsRegistry contract
* @param strategiesRegistry The address of the StrategiesRegistry contract
*/
constructor(address vaultsRegistry, address strategiesRegistry) {
constructor(
address vaultsRegistry,
address strategiesRegistry
) {
_vaultsRegistry = IVaultsRegistry(vaultsRegistry);
_strategiesRegistry = IStrategiesRegistry(strategiesRegistry);
}

/// @inheritdoc IOsTokenVaultEscrowAuth
function canRegister(address vault, address owner, uint256, uint256) external view returns (bool) {
function canRegister(
address vault,
address owner,
uint256,
uint256
) external view returns (bool) {
return _vaultsRegistry.vaults(vault) && IVaultVersion(vault).version() > 2
&& _strategiesRegistry.strategyProxies(owner);
}
Expand Down
10 changes: 8 additions & 2 deletions src/StrategiesRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ contract StrategiesRegistry is Ownable2Step, IStrategiesRegistry {
}

/// @inheritdoc IStrategiesRegistry
function setStrategy(address strategy, bool enabled) external onlyOwner {
function setStrategy(
address strategy,
bool enabled
) external onlyOwner {
if (strategy == address(0)) revert Errors.ZeroAddress();
if (strategies[strategy] == enabled) revert Errors.ValueNotChanged();
// update strategy
Expand All @@ -48,7 +51,10 @@ contract StrategiesRegistry is Ownable2Step, IStrategiesRegistry {
}

/// @inheritdoc IStrategiesRegistry
function addStrategyProxy(bytes32 strategyProxyId, address proxy) external {
function addStrategyProxy(
bytes32 strategyProxyId,
address proxy
) external {
if (strategyProxyId == bytes32(0)) revert InvalidStrategyProxyId();
if (proxy == address(0)) revert Errors.ZeroAddress();

Expand Down
Loading
Loading