diff --git a/chains/avalanche/AvalancheConstantsLib.sol b/chains/avalanche/AvalancheConstantsLib.sol index 0b7ba974..96772214 100644 --- a/chains/avalanche/AvalancheConstantsLib.sol +++ b/chains/avalanche/AvalancheConstantsLib.sol @@ -21,7 +21,11 @@ library AvalancheConstantsLib { address public constant TOKEN_AUSD = 0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a; address public constant TOKEN_xBTC = 0x6eAf19b2FC24552925dB245F9Ff613157a7dbb4C; address public constant TOKEN_savBTS = 0x649342c6bff544d82DF1B2bA3C93e0C22cDeBa84; - address public constant TOKEN_savUSDC = 0x06d47F3fb376649c3A9Dafe069B3D6E35572219E; + address public constant TOKEN_savUSD = 0x06d47F3fb376649c3A9Dafe069B3D6E35572219E; + address public constant TOKEN_avUSD = 0x24dE8771bC5DdB3362Db529Fc3358F2df3A0E346; + address public constant TOKEN_sUSDp = 0x9D92c21205383651610f90722131655A5B8ED3E0; + address public constant TOKEN_USDp = 0x9eE1963f05553eF838604Dd39403be21ceF26AA4; + // Oracles address public constant ORACLE_CHAINLINK_USDC_USD = 0xF096872672F44d6EBA71458D74fe67F9a77a23B9; @@ -52,18 +56,43 @@ library AvalancheConstantsLib { address public constant AAVE_V3_POOL = 0x794a61358D6845594F94dc1DB02A252b5b4814aD; address public constant AAVE_aAvaUSDC = 0x625E7708f30cA75bfd92586e17077590C60eb4cD; + // Balancer + address public constant BEETS_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; + address public constant BEETS_VAULT_V3 = 0xbA1333333333a1BA1108E8412f11850A5C319bA9; + address public constant BEETS_V3_ROUTER = 0xF39CA6ede9BF7820a952b52f3c94af526bAB9015; + + address public constant BEETS_STABLE_POOL_USDp_GAMI_USDC = 0x1fED8401C145f64dA567881d272D0DF233118dCa; + + // DEX + address public constant POOL_PHARAOH_V3_AVUSD_USDC = 0xc9bFC3cDA285a75B32d5B787618128dB963dE675; + address public constant POOL_PHARAOH_V3_USDT_USDC = 0x9bFE3108Cc16D17a9Ec65545a0f50B2CA1C970c0; + address public constant POOL_ALGEBRA_SAVUSD_USDC = 0x86364634157b4247c1dD54c5945832eAF691Af79; + // Silo + address public constant SILO_LENS = 0x319f7155cC65F693E84689535aFb1343C704C0B8; address public constant SILO_VAULT_USDC_125 = 0xE0345f66318F482aCCcd67244A921C7FDC410957; address public constant SILO_VAULT_BTCb_130 = 0x7437ac81457Fa98fFB2d0C8f9943ecfE4813e2f1; address public constant SILO_VAULT_BTCb_121 = 0xD8a26DFEba3E9B558Bd79d10722636Dfb4dc2bF4; - address public constant SILO_VAULT_USDC_142 = 0x606fe9a70338e798a292CA22C1F28C829F24048E; - address public constant SILO_VAULT_USDC_129 = 0x672b77f0538b53Dc117C9dDfEb7377A678d321a6; + address public constant SILO_VAULT_142_USDC = 0x606fe9a70338e798a292CA22C1F28C829F24048E; + address public constant SILO_VAULT_142_SAVUSDC = 0xDcFFACBed111BA9d59b5e96A85934Db91117BDD9; + address public constant SILO_VAULT_129_USDC = 0x672b77f0538b53Dc117C9dDfEb7377A678d321a6; + address public constant SILO_VAULT_153_USDC = 0x2775631eA39d8d6e9A89C299387Ea55CfFC18b0b; + address public constant SILO_VAULT_153_SUSDP = 0x69F3B55E4001214EA54319cDCdD4e2Ae904440Af; + + address public constant SILO_MANAGED_VAULT_GAMI_USDC = 0x1F0570a081FeE0e4dF6eAC470f9d2D53CDEDa1c5; address public constant SILO_MANAGED_VAULT_USDC_MEV = 0x4dc1ce9b9f9EF00c144BfAD305f16c62293dC0E8; address public constant SILO_MANAGED_VAULT_BTCb_MEV = 0x1f8E769B5B6010B2C2BBCd68629EA1a0a0Eda7E3; address public constant SILO_MANAGED_VAULT_AUSD_VARLAMOURE = 0x3d7B0c3997E48fA3FC96cd057d1fb4E5F891835B; address public constant SILO_MANAGED_VAULT_USDt_VARLAMOURE = 0x6c09bfdc1df45D6c4Ff78Dc9F1C13aF29eB335d4; + // Liquidity book - https://github.com/lfj-gg + // Router and quoter are taken for the savUSDC-pool: 0x7a5b4e301fc2b148cefe57257a236eb845082797 + address public constant LB_LFJ_ROUTER_V2 = 0x18556DA13313f3532c54711497A8FedAC273220E; + address public constant LB_LFJ_QUOTER_V2 = 0x9A550a522BBaDFB69019b0432800Ed17855A51C3; + address public constant POOL_LB_LFJ_LBPAIR_savUSDC_avUSD = 0xb768091A8e6FFCdc215767937Bd9fb039CB06577; + address public constant POOL_LB_LFJ_LBPAIR_avUSDC_USDC = 0xBe36F03F83D773dd05CaA41EA0891Fd92ba3Ed6c; + // ---------------------------------- LayerZero-v2 https://docs.layerzero.network/v2/deployments/chains/avalanche uint32 public constant LAYER_ZERO_V2_ENDPOINT_ID = 30106; address public constant LAYER_ZERO_V2_ENDPOINT = 0x1a44076050125825900e736c501f859c50fE728c; diff --git a/chains/avalanche/AvalancheLib.sol b/chains/avalanche/AvalancheLib.sol index adadd556..8daaf945 100644 --- a/chains/avalanche/AvalancheLib.sol +++ b/chains/avalanche/AvalancheLib.sol @@ -11,6 +11,7 @@ import {ChainlinkAdapter} from "../../src/adapters/ChainlinkAdapter.sol"; import {DeployAdapterLib} from "../../script/libs/DeployAdapterLib.sol"; import {EulerMerklFarmStrategy} from "../../src/strategies/EulerMerklFarmStrategy.sol"; import {EulerStrategy} from "../../src/strategies/EulerStrategy.sol"; +import {IBalancerAdapter} from "../../src/interfaces/IBalancerAdapter.sol"; import {IFactory} from "../../src/interfaces/IFactory.sol"; import {IPlatformDeployer} from "../../src/interfaces/IPlatformDeployer.sol"; import {IPlatform} from "../../src/interfaces/IPlatform.sol"; @@ -23,6 +24,7 @@ import {SiloStrategy} from "../../src/strategies/SiloStrategy.sol"; import {StrategyDeveloperLib} from "../../src/strategies/libs/StrategyDeveloperLib.sol"; import {StrategyIdLib} from "../../src/strategies/libs/StrategyIdLib.sol"; import {VaultTypeLib} from "../../src/core/libs/VaultTypeLib.sol"; +import {SiloAdvancedLeverageStrategy} from "../../src/strategies/SiloAdvancedLeverageStrategy.sol"; /// @dev Avalanche network [chainId: 43114] deploy library // ,---, ,--, ,---, @@ -82,6 +84,11 @@ library AvalancheLib { //region ----- Deploy AMM adapters ----- DeployAdapterLib.deployAmmAdapter(platform, AmmAdapterIdLib.UNISWAPV3); DeployAdapterLib.deployAmmAdapter(platform, AmmAdapterIdLib.ALGEBRA_V4); + DeployAdapterLib.deployAmmAdapter(platform, AmmAdapterIdLib.ALGEBRA); + DeployAdapterLib.deployAmmAdapter(platform, AmmAdapterIdLib.ERC_4626); + DeployAdapterLib.deployAmmAdapter(platform, AmmAdapterIdLib.BALANCER_V3_STABLE); + IBalancerAdapter(IPlatform(platform).ammAdapter(keccak256(bytes(AmmAdapterIdLib.BALANCER_V3_STABLE))).proxy).setupHelpers(AvalancheConstantsLib.BEETS_V3_ROUTER); + DeployAdapterLib.deployAmmAdapter(platform, AmmAdapterIdLib.LBLFJ_V2); //endregion -- Deploy AMM adapters ---- //region ----- Setup Swapper ----- @@ -129,6 +136,7 @@ library AvalancheLib { factory.setStrategyImplementation(StrategyIdLib.EULER_MERKL_FARM, address(new EulerMerklFarmStrategy())); factory.setStrategyImplementation(StrategyIdLib.SILO_MANAGED_MERKL_FARM, address(new SiloManagedMerklFarmStrategy())); factory.setStrategyImplementation(StrategyIdLib.SILO_MERKL_FARM, address(new SiloMerklFarmStrategy())); + factory.setStrategyImplementation(StrategyIdLib.SILO_ADVANCED_LEVERAGE, address(new SiloAdvancedLeverageStrategy())); //endregion //region ----- Add DeX aggregators ----- @@ -196,9 +204,9 @@ library AvalancheLib { _farms[i++] = AvalancheFarmMakerLib._makeSiloManagedMerklFarm(AvalancheConstantsLib.SILO_MANAGED_VAULT_USDt_VARLAMOURE); // 7 _farms[i++] = AvalancheFarmMakerLib._makeSiloMerklFarm(AvalancheConstantsLib.SILO_VAULT_BTCb_130, AvalancheConstantsLib.TOKEN_WAVAX, address(0), true); // 8 - _farms[i++] = AvalancheFarmMakerLib._makeSiloMerklFarm(AvalancheConstantsLib.SILO_VAULT_USDC_142, AvalancheConstantsLib.TOKEN_WAVAX, address(0), true); // 9 + _farms[i++] = AvalancheFarmMakerLib._makeSiloMerklFarm(AvalancheConstantsLib.SILO_VAULT_142_USDC, AvalancheConstantsLib.TOKEN_WAVAX, address(0), true); // 9 _farms[i++] = AvalancheFarmMakerLib._makeSiloMerklFarm(AvalancheConstantsLib.SILO_VAULT_BTCb_121, AvalancheConstantsLib.TOKEN_WAVAX, address(0), true); // 10 - _farms[i++] = AvalancheFarmMakerLib._makeSiloMerklFarm(AvalancheConstantsLib.SILO_VAULT_USDC_129, AvalancheConstantsLib.TOKEN_WAVAX, address(0), true); // 11 + _farms[i++] = AvalancheFarmMakerLib._makeSiloMerklFarm(AvalancheConstantsLib.SILO_VAULT_129_USDC, AvalancheConstantsLib.TOKEN_WAVAX, address(0), true); // 11 } function _makePoolData( diff --git a/script/libs/DeployAdapterLib.sol b/script/libs/DeployAdapterLib.sol index cf9fa1f2..1d84bb70 100644 --- a/script/libs/DeployAdapterLib.sol +++ b/script/libs/DeployAdapterLib.sol @@ -1,23 +1,25 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; -import {Proxy} from "../../src/core/proxy/Proxy.sol"; -import {AmmAdapterIdLib} from "../../src/adapters/libs/AmmAdapterIdLib.sol"; -import {UniswapV3Adapter} from "../../src/adapters/UniswapV3Adapter.sol"; +import "../../src/adapters/LbAdapterLFJV2.sol"; import {AlgebraAdapter} from "../../src/adapters/AlgebraAdapter.sol"; -import {KyberAdapter} from "../../src/adapters/KyberAdapter.sol"; -import {CurveAdapter} from "../../src/adapters/CurveAdapter.sol"; +import {AlgebraV4Adapter} from "../../src/adapters/AlgebraV4Adapter.sol"; +import {AmmAdapterIdLib} from "../../src/adapters/libs/AmmAdapterIdLib.sol"; import {BalancerComposableStableAdapter} from "../../src/adapters/BalancerComposableStableAdapter.sol"; +import {BalancerV3ReClammAdapter} from "../../src/adapters/BalancerV3ReClammAdapter.sol"; +import {BalancerV3StableAdapter} from "../../src/adapters/BalancerV3StableAdapter.sol"; import {BalancerWeightedAdapter} from "../../src/adapters/BalancerWeightedAdapter.sol"; -import {SolidlyAdapter} from "../../src/adapters/SolidlyAdapter.sol"; -import {AlgebraV4Adapter} from "../../src/adapters/AlgebraV4Adapter.sol"; +import {CurveAdapter} from "../../src/adapters/CurveAdapter.sol"; import {ERC4626Adapter} from "../../src/adapters/ERC4626Adapter.sol"; -import {BalancerV3StableAdapter} from "../../src/adapters/BalancerV3StableAdapter.sol"; -import {PendleAdapter} from "../../src/adapters/PendleAdapter.sol"; -import {MetaVaultAdapter} from "../../src/adapters/MetaVaultAdapter.sol"; -import {IPlatform} from "../../src/interfaces/IPlatform.sol"; import {IAmmAdapter} from "../../src/interfaces/IAmmAdapter.sol"; -import {BalancerV3ReClammAdapter} from "../../src/adapters/BalancerV3ReClammAdapter.sol"; +import {IPlatform} from "../../src/interfaces/IPlatform.sol"; +import {KyberAdapter} from "../../src/adapters/KyberAdapter.sol"; +import {MetaVaultAdapter} from "../../src/adapters/MetaVaultAdapter.sol"; +import {PendleAdapter} from "../../src/adapters/PendleAdapter.sol"; +import {Proxy} from "../../src/core/proxy/Proxy.sol"; +import {SolidlyAdapter} from "../../src/adapters/SolidlyAdapter.sol"; +import {UniswapV3Adapter} from "../../src/adapters/UniswapV3Adapter.sol"; +import {LbAdapterLFJV2} from "../../src/adapters/LbAdapterLFJV2.sol"; library DeployAdapterLib { function deployAmmAdapter(address platform, string memory id) internal returns (address) { @@ -80,6 +82,10 @@ library DeployAdapterLib { proxy.initProxy(address(new BalancerV3ReClammAdapter())); } + if (eq(id, AmmAdapterIdLib.LBLFJ_V2)) { + proxy.initProxy(address(new LbAdapterLFJV2())); + } + require(proxy.implementation() != address(0), string.concat("Unknown AmmAdapter:", id)); IAmmAdapter(address(proxy)).init(platform); IPlatform(platform).addAmmAdapter(id, address(proxy)); diff --git a/src/adapters/LbAdapterLFJV2.sol b/src/adapters/LbAdapterLFJV2.sol new file mode 100644 index 00000000..0767426c --- /dev/null +++ b/src/adapters/LbAdapterLFJV2.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {AmmAdapterIdLib} from "./libs/AmmAdapterIdLib.sol"; +import {ConstantsLib} from "../core/libs/ConstantsLib.sol"; +import {Controllable, IControllable, IERC165} from "../core/base/Controllable.sol"; +import {IAmmAdapter} from "../interfaces/IAmmAdapter.sol"; +import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {ILbPairV2} from "../integrations/lblfgv2/ILbPairV2.sol"; +import {IERC20Metadata} from "../../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +/// @title Adapter for Liquidity Book (DLMM) as implemented by LFJ, see https://github.com/lfj-gg +/// @author omriss (https://github.com/omriss) +contract LbAdapterLFJV2 is Controllable, IAmmAdapter { + using SafeERC20 for IERC20; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CONSTANTS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @inheritdoc IControllable + string public constant VERSION = "1.0.0"; + + error IncorrectTokens(); + error InsufficientOutputAmount(uint amountInLeft); + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* INITIALIZATION */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @inheritdoc IAmmAdapter + function init(address platform_) external initializer { + __Controllable_init(platform_); + } + + //region ------------------------------------ User actions + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* USER ACTIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @inheritdoc IAmmAdapter + //slither-disable-next-line reentrancy-events + function swap( + address pool, + address tokenIn, + address tokenOut, + address recipient, + uint priceImpactTolerance + ) external { + ILbPairV2 pair = ILbPairV2(pool); + + // --------------- token-out balance before swap (to calculate actual amountOut after swap) + uint balanceBefore = IERC20(tokenOut).balanceOf(recipient); + + // --------------- verify that provided tokens are correct + address tokenX = pair.getTokenX(); + require(_isValid(pair, tokenIn, tokenOut, tokenX), IncorrectTokens()); + + // --------------- input amount should be sent on balance of the pair contract before swap call + uint amount = IERC20(tokenIn).balanceOf(address(this)); + IERC20(tokenIn).safeTransfer(pool, amount); + + // --------------- ensure that there is enough output amount in the pair + (uint128 amountInLeft, uint128 price,) = pair.getSwapOut(uint128(amount), tokenIn == tokenX); + require(amountInLeft == 0, InsufficientOutputAmount(amountInLeft)); + + // --------------- perform the swap + pair.swap(tokenX == tokenIn, recipient); + + // --------------- actual amount out received by the recipient + uint balanceAfter = IERC20(tokenOut).balanceOf(recipient); + uint amountOut = balanceAfter > balanceBefore ? balanceAfter - balanceBefore : 0; + + // --------------- verify that price impact is within the tolerance limit + uint priceImpact = (price - amountOut) * ConstantsLib.DENOMINATOR / price; + if (priceImpact >= priceImpactTolerance) { + revert(string(abi.encodePacked("!PRICE ", Strings.toString(priceImpact)))); + } + + emit SwapInPool(pool, tokenIn, tokenOut, recipient, priceImpactTolerance, amount, amountOut); + } + + //endregion ---------------------------------- User actions + + //region ------------------------------------ View functions + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* VIEW FUNCTIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @inheritdoc IAmmAdapter + function ammAdapterId() external pure returns (string memory) { + return AmmAdapterIdLib.LBLFJ_V2; + } + + /// @inheritdoc IAmmAdapter + function poolTokens(address pool) public view returns (address[] memory tokens) { + tokens = new address[](2); + tokens[0] = ILbPairV2(pool).getTokenX(); + tokens[1] = ILbPairV2(pool).getTokenY(); + } + + /// @inheritdoc IAmmAdapter + function getLiquidityForAmounts(address, uint[] memory) external pure returns (uint, uint[] memory) { + revert("Not supported"); + } + + /// @inheritdoc IAmmAdapter + function getProportions(address) external pure returns (uint[] memory) { + revert("Not supported"); + } + + /// @inheritdoc IAmmAdapter + function getPrice(address pool, address tokenIn, address tokenOut, uint amount) public view returns (uint price) { + address tokenX = ILbPairV2(pool).getTokenX(); + if (_isValid(ILbPairV2(pool), tokenIn, tokenOut, tokenX)) { + if (amount == 0) { + amount = 10 ** IERC20Metadata(tokenIn).decimals(); + } + (uint128 amountInLeft, uint128 amountOut,) = ILbPairV2(pool).getSwapOut(uint128(amount), tokenIn == tokenX); + + // todo how to handle following case without reverting? + require(amountInLeft == 0, InsufficientOutputAmount(amountInLeft)); + + price = amountOut; + } + + return price; + } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) public view override(Controllable, IERC165) returns (bool) { + return interfaceId == type(IAmmAdapter).interfaceId || super.supportsInterface(interfaceId); + } + + //endregion -------------------------------- View functions + + /// @inheritdoc IAmmAdapter + function getTwaPrice( + address, + /*pool*/ + address, + /*tokenIn*/ + address, + /*tokenOut*/ + uint, + /*amount*/ + uint32 /*period*/ + ) external pure returns (uint) { + revert("Not supported"); + } + + function _isValid(ILbPairV2 pair, address tokenIn, address tokenOut, address tokenX) internal view returns (bool) { + address tokenY = pair.getTokenY(); + return (tokenX == tokenIn && tokenY == tokenOut) || (tokenY == tokenIn && tokenX == tokenOut); + } +} diff --git a/src/adapters/libs/AmmAdapterIdLib.sol b/src/adapters/libs/AmmAdapterIdLib.sol index b201c6ac..24b95b38 100644 --- a/src/adapters/libs/AmmAdapterIdLib.sol +++ b/src/adapters/libs/AmmAdapterIdLib.sol @@ -17,4 +17,5 @@ library AmmAdapterIdLib { string public constant BALANCER_V3_RECLAMM = "BalancerV3ReCLAMM"; string public constant BRUNCH = "Brunch"; string public constant AAVE_V3 = "AaveV3"; + string public constant LBLFJ_V2 = "LbLFJV2"; } diff --git a/src/integrations/lblfgv2/ILbPairV2.sol b/src/integrations/lblfgv2/ILbPairV2.sol new file mode 100644 index 00000000..269b421b --- /dev/null +++ b/src/integrations/lblfgv2/ILbPairV2.sol @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +interface ILbPairV2 { + function approveForAll(address spender, bool approved) external; + + function balanceOf(address account, uint256 id) + external + view + returns (uint256); + + function balanceOfBatch(address[] memory accounts, uint256[] memory ids) + external + view + returns (uint256[] memory batchBalances); + + function batchTransferFrom( + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts + ) external; + + function burn( + address from, + address to, + uint256[] memory ids, + uint256[] memory amountsToBurn + ) external returns (bytes32[] memory amounts); + + function collectProtocolFees() + external + returns (bytes32 collectedProtocolFees); + + function flashLoan( + address receiver, + bytes32 amounts, + bytes memory data + ) external; + + function forceDecay() external; + + function getActiveId() external view returns (uint24 activeId); + + function getBin(uint24 id) + external + view + returns (uint128 binReserveX, uint128 binReserveY); + + function getBinStep() external pure returns (uint16); + + function getFactory() external view returns (address factory); + + function getIdFromPrice(uint256 price) external pure returns (uint24 id); + + function getLBHooksParameters() external view returns (bytes32); + + function getNextNonEmptyBin(bool swapForY, uint24 id) + external + view + returns (uint24 nextId); + + function getOracleParameters() + external + view + returns ( + uint8 sampleLifetime, + uint16 size, + uint16 activeSize, + uint40 lastUpdated, + uint40 firstTimestamp + ); + + function getOracleSampleAt(uint40 lookupTimestamp) + external + view + returns ( + uint64 cumulativeId, + uint64 cumulativeVolatility, + uint64 cumulativeBinCrossed + ); + + function getPriceFromId(uint24 id) external pure returns (uint256 price); + + function getProtocolFees() + external + view + returns (uint128 protocolFeeX, uint128 protocolFeeY); + + function getReserves() + external + view + returns (uint128 reserveX, uint128 reserveY); + + function getStaticFeeParameters() + external + view + returns ( + uint16 baseFactor, + uint16 filterPeriod, + uint16 decayPeriod, + uint16 reductionFactor, + uint24 variableFeeControl, + uint16 protocolShare, + uint24 maxVolatilityAccumulator + ); + + function getSwapIn(uint128 amountOut, bool swapForY) + external + view + returns ( + uint128 amountIn, + uint128 amountOutLeft, + uint128 fee + ); + + function getSwapOut(uint128 amountIn, bool swapForY) + external + view + returns ( + uint128 amountInLeft, + uint128 amountOut, + uint128 fee + ); + + function getTokenX() external view returns (address tokenX); + + function getTokenY() external view returns (address tokenY); + + function getVariableFeeParameters() + external + view + returns ( + uint24 volatilityAccumulator, + uint24 volatilityReference, + uint24 idReference, + uint40 timeOfLastUpdate + ); + + function implementation() external view returns (address); + + function increaseOracleLength(uint16 newLength) external; + + function initialize( + uint16 baseFactor, + uint16 filterPeriod, + uint16 decayPeriod, + uint16 reductionFactor, + uint24 variableFeeControl, + uint16 protocolShare, + uint24 maxVolatilityAccumulator, + uint24 activeId + ) external; + + function isApprovedForAll(address owner, address spender) + external + view + returns (bool); + + function mint( + address to, + bytes32[] memory liquidityConfigs, + address refundTo + ) + external + returns ( + bytes32 amountsReceived, + bytes32 amountsLeft, + uint256[] memory liquidityMinted + ); + + function name() external view returns (string memory); + + function setHooksParameters( + bytes32 hooksParameters, + bytes memory onHooksSetData + ) external; + + function setStaticFeeParameters( + uint16 baseFactor, + uint16 filterPeriod, + uint16 decayPeriod, + uint16 reductionFactor, + uint24 variableFeeControl, + uint16 protocolShare, + uint24 maxVolatilityAccumulator + ) external; + + function swap(bool swapForY, address to) + external + returns (bytes32 amountsOut); + + function symbol() external view returns (string memory); + + function totalSupply(uint256 id) external view returns (uint256); +} diff --git a/src/integrations/lblfgv2/ILbQuoterV2.sol b/src/integrations/lblfgv2/ILbQuoterV2.sol new file mode 100644 index 00000000..4c53e1b5 --- /dev/null +++ b/src/integrations/lblfgv2/ILbQuoterV2.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +interface ILbQuoterV2 { + struct Quote { + address[] route; + address[] pairs; + uint256[] binSteps; + uint8[] versions; + uint128[] amounts; + uint128[] virtualAmountsWithoutSlippage; + uint128[] fees; + } + + function findBestPathFromAmountIn(address[] memory route, uint128 amountIn) external view + returns (Quote memory quote); + + function findBestPathFromAmountOut( + address[] memory route, + uint128 amountOut + ) external view returns (Quote memory quote); + + function getFactoryV1() external view returns (address factoryV1); + + function getFactoryV2_1() external view returns (address factoryV2_1); + + function getFactoryV2_2() external view returns (address factoryV2_2); + + function getLegacyFactoryV2() external view returns (address legacyFactoryV2); + + function getLegacyRouterV2() external view returns (address legacyRouterV2); + + function getRouterV2_1() external view returns (address routerV2_1); + + function getRouterV2_2() external view returns (address routerV2_2); + + +} diff --git a/src/integrations/lblfgv2/ILbRouterV2.sol b/src/integrations/lblfgv2/ILbRouterV2.sol new file mode 100644 index 00000000..2ddf023f --- /dev/null +++ b/src/integrations/lblfgv2/ILbRouterV2.sol @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +interface ILbRouterV2 { + function addLiquidity( + ILbRouterV2.LiquidityParameters memory liquidityParameters + ) + external + returns ( + uint256 amountXAdded, + uint256 amountYAdded, + uint256 amountXLeft, + uint256 amountYLeft, + uint256[] memory depositIds, + uint256[] memory liquidityMinted + ); + + function addLiquidityNATIVE( + ILbRouterV2.LiquidityParameters memory liquidityParameters + ) + external + payable + returns ( + uint256 amountXAdded, + uint256 amountYAdded, + uint256 amountXLeft, + uint256 amountYLeft, + uint256[] memory depositIds, + uint256[] memory liquidityMinted + ); + + function createLBPair( + address tokenX, + address tokenY, + uint24 activeId, + uint16 binStep + ) external returns (address pair); + + function getFactory() external view returns (address lbFactory); + + function getFactoryV2_1() external view returns (address lbFactory); + + function getIdFromPrice(address pair, uint256 price) + external + view + returns (uint24); + + function getLegacyFactory() external view returns (address legacyLBfactory); + + function getLegacyRouter() external view returns (address legacyRouter); + + function getPriceFromId(address pair, uint24 id) + external + view + returns (uint256); + + function getSwapIn( + address pair, + uint128 amountOut, + bool swapForY + ) + external + view + returns ( + uint128 amountIn, + uint128 amountOutLeft, + uint128 fee + ); + + function getSwapOut( + address pair, + uint128 amountIn, + bool swapForY + ) + external + view + returns ( + uint128 amountInLeft, + uint128 amountOut, + uint128 fee + ); + + function getV1Factory() external view returns (address factoryV1); + + function getWNATIVE() external view returns (address wnative); + + function removeLiquidity( + address tokenX, + address tokenY, + uint16 binStep, + uint256 amountXMin, + uint256 amountYMin, + uint256[] memory ids, + uint256[] memory amounts, + address to, + uint256 deadline + ) external returns (uint256 amountX, uint256 amountY); + + function removeLiquidityNATIVE( + address token, + uint16 binStep, + uint256 amountTokenMin, + uint256 amountNATIVEMin, + uint256[] memory ids, + uint256[] memory amounts, + address to, + uint256 deadline + ) external returns (uint256 amountToken, uint256 amountNATIVE); + + function swapExactNATIVEForTokens( + uint256 amountOutMin, + ILbRouterV2.Path memory path, + address to, + uint256 deadline + ) external payable returns (uint256 amountOut); + + function swapExactNATIVEForTokensSupportingFeeOnTransferTokens( + uint256 amountOutMin, + ILbRouterV2.Path memory path, + address to, + uint256 deadline + ) external payable returns (uint256 amountOut); + + function swapExactTokensForNATIVE( + uint256 amountIn, + uint256 amountOutMinNATIVE, + ILbRouterV2.Path memory path, + address to, + uint256 deadline + ) external returns (uint256 amountOut); + + function swapExactTokensForNATIVESupportingFeeOnTransferTokens( + uint256 amountIn, + uint256 amountOutMinNATIVE, + ILbRouterV2.Path memory path, + address to, + uint256 deadline + ) external returns (uint256 amountOut); + + function swapExactTokensForTokens( + uint256 amountIn, + uint256 amountOutMin, + ILbRouterV2.Path memory path, + address to, + uint256 deadline + ) external returns (uint256 amountOut); + + function swapExactTokensForTokensSupportingFeeOnTransferTokens( + uint256 amountIn, + uint256 amountOutMin, + ILbRouterV2.Path memory path, + address to, + uint256 deadline + ) external returns (uint256 amountOut); + + function swapNATIVEForExactTokens( + uint256 amountOut, + ILbRouterV2.Path memory path, + address to, + uint256 deadline + ) external payable returns (uint256[] memory amountsIn); + + function swapTokensForExactNATIVE( + uint256 amountNATIVEOut, + uint256 amountInMax, + ILbRouterV2.Path memory path, + address to, + uint256 deadline + ) external returns (uint256[] memory amountsIn); + + function swapTokensForExactTokens( + uint256 amountOut, + uint256 amountInMax, + ILbRouterV2.Path memory path, + address to, + uint256 deadline + ) external returns (uint256[] memory amountsIn); + + function sweep( + address token, + address to, + uint256 amount + ) external; + + function sweepLBToken( + address lbToken, + address to, + uint256[] memory ids, + uint256[] memory amounts + ) external; + + receive() external payable; + + struct LiquidityParameters { + address tokenX; + address tokenY; + uint256 binStep; + uint256 amountX; + uint256 amountY; + uint256 amountXMin; + uint256 amountYMin; + uint256 activeIdDesired; + uint256 idSlippage; + int256[] deltaIds; + uint256[] distributionX; + uint256[] distributionY; + address to; + address refundTo; + uint256 deadline; + } + + struct Path { + uint256[] pairBinSteps; + uint8[] versions; + address[] tokenPath; + } +} + + + diff --git a/test/adapters/LbAdapterLFJV2.Avalanche.t.sol b/test/adapters/LbAdapterLFJV2.Avalanche.t.sol new file mode 100644 index 00000000..336ec0ca --- /dev/null +++ b/test/adapters/LbAdapterLFJV2.Avalanche.t.sol @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +import {console} from "forge-std/console.sol"; +import {AmmAdapterIdLib} from "../../src/adapters/libs/AmmAdapterIdLib.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IAmmAdapter} from "../../src/interfaces/IAmmAdapter.sol"; +import {IPlatform} from "../../src/interfaces/IPlatform.sol"; +import {IPriceReader} from "../../src/interfaces/IPriceReader.sol"; +import {Proxy} from "../../src/core/proxy/Proxy.sol"; +import {LbAdapterLFJV2} from "../../src/adapters/LbAdapterLFJV2.sol"; +import {AvalancheSetup} from "../base/chains/AvalancheSetup.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {AvalancheConstantsLib} from "../../chains/avalanche/AvalancheConstantsLib.sol"; + +contract LbAdapterLFJV2Test is AvalancheSetup { + using SafeERC20 for IERC20; + + uint public constant FORK_BLOCK_C_CHAIN = 75107760; // Jan-05-2026 11:44:52 AM +UTC + + address public constant PLATFORM = AvalancheConstantsLib.PLATFORM; + + bytes32 public _hash; + LbAdapterLFJV2 public adapter; + address public multisig; + + struct State { + uint adapterBalanceAvUSD; + uint adapterBalanceStakedAvUSD; + uint userBalanceAvUSD; + uint userBalanceStakedAvUSD; + } + + constructor() { + vm.selectFork(vm.createFork(vm.envString("AVALANCHE_RPC_URL"), FORK_BLOCK_C_CHAIN)); + _hash = keccak256(bytes(AmmAdapterIdLib.LBLFJ_V2)); + + _addAdapter(); + } + + //region ------------------------------------ Tests for view functions + function testAmmAdapterId() public view { + assertEq(keccak256(bytes(adapter.ammAdapterId())), _hash); + } + + function testPoolTokens() public view { + address[] memory poolTokens = adapter.poolTokens(AvalancheConstantsLib.POOL_LB_LFJ_LBPAIR_savUSDC_avUSD); + assertEq(poolTokens.length, 2); + assertEq(poolTokens[0], AvalancheConstantsLib.TOKEN_savUSD); + assertEq(poolTokens[1], AvalancheConstantsLib.TOKEN_avUSD); + } + + function testNotSupportedMethods() public { + vm.expectRevert("Not supported"); + adapter.getLiquidityForAmounts(address(0), new uint[](2)); + + vm.expectRevert("Not supported"); + adapter.getProportions(address(0)); + } + + function testIERC165() public view { + assertEq(adapter.supportsInterface(type(IAmmAdapter).interfaceId), true); + assertEq(adapter.supportsInterface(type(IERC165).interfaceId), true); + } + + function testGetPriceDirectBadPaths() public view { + address pool = AvalancheConstantsLib.POOL_LB_LFJ_LBPAIR_savUSDC_avUSD; + + // --------------------- Incorrect tokens + assertEq( + adapter.getPrice(pool, AvalancheConstantsLib.TOKEN_savUSD, AvalancheConstantsLib.TOKEN_USDC, 0), + 0, + "wrong token out" + ); + assertEq( + adapter.getPrice(pool, AvalancheConstantsLib.TOKEN_USDC, AvalancheConstantsLib.TOKEN_savUSD, 0), + 0, + "wrong token in" + ); + + // --------------------- InsufficientOutputAmount + try adapter.getPrice( + pool, AvalancheConstantsLib.TOKEN_savUSD, AvalancheConstantsLib.TOKEN_avUSD, 1e30 + ) returns ( + uint + ) { + assertTrue(false, "getPrice didn't revert with InsufficientOutputAmount"); + } catch (bytes memory reason) { + assertTrue(reason.length >= 4, "revert reason too short"); + bytes4 receivedSelector; + assembly { + receivedSelector := mload(add(reason, 32)) + } + assertEq(receivedSelector, LbAdapterLFJV2.InsufficientOutputAmount.selector); + } + } + + function testGetTwaPrice() public { + vm.expectRevert("Not supported"); + adapter.getTwaPrice(address(0), address(0), address(0), 0, 0); + } + + //endregion ------------------------------------ Tests for view functions + + //region ------------------------------------ Tests for swaps + function testStakeAvUsd107() public { + _testStakeAvUSD(107e18); + } + + function testStakeAvUsd0() public { + _testStakeAvUSD(0); + } + + function testSwapBadPaths() public { + deal(AvalancheConstantsLib.TOKEN_savUSD, address(address(adapter)), 1e18); + deal(AvalancheConstantsLib.TOKEN_avUSD, address(address(adapter)), 1e18); + + // --------------------- Incorrect tokens + vm.expectRevert(LbAdapterLFJV2.IncorrectTokens.selector); + adapter.swap( + AvalancheConstantsLib.POOL_LB_LFJ_LBPAIR_savUSDC_avUSD, + AvalancheConstantsLib.TOKEN_savUSD, + AvalancheConstantsLib.TOKEN_USDC, + address(this), + 1_000 + ); + + vm.expectRevert(LbAdapterLFJV2.IncorrectTokens.selector); + adapter.swap( + AvalancheConstantsLib.POOL_LB_LFJ_LBPAIR_savUSDC_avUSD, + AvalancheConstantsLib.TOKEN_USDC, + AvalancheConstantsLib.TOKEN_savUSD, + address(this), + 1_000 + ); + + // --------------------- InsufficientOutputAmount + deal(AvalancheConstantsLib.TOKEN_avUSD, address(address(adapter)), 1e30); + + try adapter.swap( + AvalancheConstantsLib.POOL_LB_LFJ_LBPAIR_savUSDC_avUSD, + AvalancheConstantsLib.TOKEN_avUSD, + AvalancheConstantsLib.TOKEN_savUSD, + address(this), + 1e30 + ) { + assertTrue(false, "swap didn't revert with InsufficientOutputAmount"); + } catch (bytes memory reason) { + assertTrue(reason.length >= 4, "revert reason too short"); + bytes4 receivedSelector; + assembly { + receivedSelector := mload(add(reason, 32)) + } + assertEq(receivedSelector, LbAdapterLFJV2.InsufficientOutputAmount.selector); + } + } + + //endregion ------------------------------------ Tests for swaps + + //region ------------------------------------ Tests implementation + function _testStakeAvUSD(uint amount) internal { + deal(AvalancheConstantsLib.TOKEN_avUSD, address(address(this)), amount == 0 ? 1e18 : amount); + + // --------------------- swap avUSD to savUSD + uint expectedAmount = adapter.getPrice( + AvalancheConstantsLib.POOL_LB_LFJ_LBPAIR_savUSDC_avUSD, + AvalancheConstantsLib.TOKEN_avUSD, + AvalancheConstantsLib.TOKEN_savUSD, + amount + ); + State memory state0 = getState(); + + vm.prank(address(this)); + IERC20(AvalancheConstantsLib.TOKEN_avUSD).safeTransfer(address(adapter), amount == 0 ? 1e18 : amount); + + adapter.swap( + AvalancheConstantsLib.POOL_LB_LFJ_LBPAIR_savUSDC_avUSD, + AvalancheConstantsLib.TOKEN_avUSD, + AvalancheConstantsLib.TOKEN_savUSD, + address(this), + 1_000 + ); + State memory state1 = getState(); + + assertApproxEqAbs( + state1.userBalanceStakedAvUSD - state0.userBalanceStakedAvUSD, + expectedAmount, + expectedAmount / 100_000, + "expected staked amount" + ); + assertEq(state1.userBalanceAvUSD, 0, "all avUSD spent"); + assertEq(state1.adapterBalanceAvUSD, 0, "adapter avUSD balance"); + assertEq(state1.adapterBalanceStakedAvUSD, 0, "adapter savUSD balance"); + + // --------------------- swap savUSD to avUSD + expectedAmount = adapter.getPrice( + AvalancheConstantsLib.POOL_LB_LFJ_LBPAIR_savUSDC_avUSD, + AvalancheConstantsLib.TOKEN_savUSD, + AvalancheConstantsLib.TOKEN_avUSD, + state1.userBalanceStakedAvUSD + ); + + IERC20(AvalancheConstantsLib.TOKEN_savUSD).safeTransfer(address(adapter), state1.userBalanceStakedAvUSD); + adapter.swap( + AvalancheConstantsLib.POOL_LB_LFJ_LBPAIR_savUSDC_avUSD, + AvalancheConstantsLib.TOKEN_savUSD, + AvalancheConstantsLib.TOKEN_avUSD, + address(this), + 1_000 + ); + + State memory state2 = getState(); + assertApproxEqAbs( + state2.userBalanceAvUSD - state1.userBalanceAvUSD, + expectedAmount, + expectedAmount / 100_000, + "expected amount of BUSD" + ); + assertEq(state2.userBalanceStakedAvUSD, 0, "all Staked avUSD spent"); + assertEq(state2.adapterBalanceAvUSD, 0, "adapter avUSD balance"); + assertEq(state2.adapterBalanceStakedAvUSD, 0, "adapter savUSD balance"); + + assertApproxEqRel( + state2.userBalanceAvUSD, state0.userBalanceAvUSD, 1e18 / 1000, "initial amount without fees back" + ); + } + + //endregion ------------------------------------ Tests implementation + + //region ------------------------------------ Internal logic + function _swap( + address pool, + address tokenIn, + address tokenOut, + uint amount, + uint priceImpact + ) internal returns (uint) { + /// forge-lint: disable-next-line + IERC20(tokenIn).transfer(address(adapter), amount); + vm.roll(block.number + 6); + + uint balanceWas = IERC20(tokenOut).balanceOf(address(this)); + adapter.swap(pool, tokenIn, tokenOut, address(this), priceImpact); + return IERC20(tokenOut).balanceOf(address(this)) - balanceWas; + } + + //endregion ------------------------------------ Internal logic + + //region ------------------------------------ Helper functions + function getState() internal view returns (State memory state) { + state.adapterBalanceAvUSD = IERC20(AvalancheConstantsLib.TOKEN_avUSD).balanceOf(address(adapter)); + state.adapterBalanceStakedAvUSD = IERC20(AvalancheConstantsLib.TOKEN_savUSD).balanceOf(address(adapter)); + state.userBalanceAvUSD = IERC20(AvalancheConstantsLib.TOKEN_avUSD).balanceOf(address(this)); + state.userBalanceStakedAvUSD = IERC20(AvalancheConstantsLib.TOKEN_savUSD).balanceOf(address(this)); + return state; + } + + function _addAdapter() internal { + Proxy proxy = new Proxy(); + proxy.initProxy(address(new LbAdapterLFJV2())); + LbAdapterLFJV2(address(proxy)).init(PLATFORM); + IPriceReader priceReader = IPriceReader(IPlatform(PLATFORM).priceReader()); + multisig = IPlatform(PLATFORM).multisig(); + + vm.prank(multisig); + priceReader.addAdapter(address(proxy)); + + adapter = LbAdapterLFJV2(address(proxy)); + } + + function _dealAndApprove(address user, address spender, address[] memory assets, uint[] memory amounts) internal { + for (uint j; j < assets.length; ++j) { + deal(assets[j], user, amounts[j]); + vm.prank(user); + IERC20(assets[j]).approve(spender, amounts[j]); + } + } + //endregion ------------------------------------ Helper functions +} diff --git a/test/base/UniversalTest.sol b/test/base/UniversalTest.sol index fb2938df..013f0f30 100644 --- a/test/base/UniversalTest.sol +++ b/test/base/UniversalTest.sol @@ -775,8 +775,11 @@ abstract contract UniversalTest is Test, ChainSetup, Utils { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INIT VARIANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - (string[] memory variants,,,) = strategy.initVariants(address(platform)); - assertGt(variants.length, 0, "initVariants returns empty arrays"); + + // initVariants aren't used anymore + + // (string[] memory variants,,,) = strategy.initVariants(address(platform)); + // assertGt(variants.length, 0, "initVariants returns empty arrays"); } } } diff --git a/test/strategies/SiAL.Avalanche.sol b/test/strategies/SiAL.Avalanche.sol new file mode 100755 index 00000000..ccf6830d --- /dev/null +++ b/test/strategies/SiAL.Avalanche.sol @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import {AvalancheSetup} from "../base/chains/AvalancheSetup.sol"; +import {AvalancheConstantsLib} from "../../chains/avalanche/AvalancheConstantsLib.sol"; +import {UniversalTest} from "../base/UniversalTest.sol"; +import {StrategyIdLib} from "../../src/strategies/libs/StrategyIdLib.sol"; +import {IStrategy} from "../../src/interfaces/IStrategy.sol"; +import {ISwapper} from "../../src/interfaces/ISwapper.sol"; +import {IPlatform} from "../../src/interfaces/IPlatform.sol"; +import {ILeverageLendingStrategy} from "../../src/interfaces/ILeverageLendingStrategy.sol"; +import {IVault} from "../../src/interfaces/IVault.sol"; +import {AmmAdapterIdLib} from "../../src/adapters/libs/AmmAdapterIdLib.sol"; +import {SiloAdvancedLeverageStrategy} from "../../src/strategies/SiloAdvancedLeverageStrategy.sol"; + +contract SiALStrategyAvalancheTest is AvalancheSetup, UniversalTest { + // uint public constant FORK_BLOCK_C_CHAIN = 73957229; // Dec-18-2025 06:27:32 AM +UTC + // uint public constant FORK_BLOCK_C_CHAIN = 74277251; // Dec-23-2025 09:53:48 AM +UTC + uint public constant FORK_BLOCK_C_CHAIN = 75107760; // Jan-05-2026 11:44:52 AM +UTC + + constructor() { + vm.selectFork(vm.createFork(vm.envString("AVALANCHE_RPC_URL"), FORK_BLOCK_C_CHAIN)); + + allowZeroApr = true; + duration1 = 0.1 hours; + duration2 = 0.1 hours; + duration3 = 0.1 hours; + + // _upgradePlatform(); + } + + function testSiALAvalanche() public universalTest { + // Let's test same strategy twice: with and without whitelisting + // max ltv = 87%, liquidation threshold = 90% => max leverage = 1/(1-0.9) = 10 + _addStrategy(AvalancheConstantsLib.SILO_VAULT_142_SAVUSDC, AvalancheConstantsLib.SILO_VAULT_142_USDC, 85_00); + // _addStrategy( + // AvalancheConstantsLib.SILO_VAULT_153_SUSDP, + // AvalancheConstantsLib.SILO_VAULT_153_USDC, + // 85_00 + // ); + } + + function _preDeposit() internal override { + // _addRoutesSUSDP(); + _addRoutesSAVUSD(); + _setFlashLoanVault( + SiloAdvancedLeverageStrategy(payable(currentStrategy)), + AvalancheConstantsLib.POOL_PHARAOH_V3_USDT_USDC, + address(0), + uint(ILeverageLendingStrategy.FlashLoanKind.UniswapV3_2) + ); + // _setFlashLoanVault(SiloAdvancedLeverageStrategy(payable(currentStrategy)), AvalancheConstantsLib.BEETS_VAULT_V3, address(0), uint(ILeverageLendingStrategy.FlashLoanKind.BalancerV3_1)); + } + + function _addStrategy( + address strategyInitAddress0, + address strategyInitAddress1, + uint targetLeveragePercent + ) internal { + address[] memory initStrategyAddresses = new address[](4); + initStrategyAddresses[0] = strategyInitAddress0; + initStrategyAddresses[1] = strategyInitAddress1; + initStrategyAddresses[2] = AvalancheConstantsLib.BEETS_VAULT; + initStrategyAddresses[3] = AvalancheConstantsLib.SILO_LENS; + uint[] memory strategyInitNums = new uint[](1); + strategyInitNums[0] = targetLeveragePercent; + strategies.push( + Strategy({ + id: StrategyIdLib.SILO_ADVANCED_LEVERAGE, + pool: address(0), + farmId: type(uint).max, + strategyInitAddresses: initStrategyAddresses, + strategyInitNums: strategyInitNums + }) + ); + } + + function _addRoutesSUSDP() internal { + // add routes + ISwapper swapper = ISwapper(IPlatform(platform).swapper()); + ISwapper.PoolData[] memory pools = new ISwapper.PoolData[](3); + pools[0] = ISwapper.PoolData({ + pool: AvalancheConstantsLib.TOKEN_sUSDp, + ammAdapter: (IPlatform(platform).ammAdapter(keccak256(bytes(AmmAdapterIdLib.ERC_4626)))).proxy, + tokenIn: AvalancheConstantsLib.TOKEN_sUSDp, + tokenOut: AvalancheConstantsLib.TOKEN_USDp + }); + pools[1] = ISwapper.PoolData({ + pool: AvalancheConstantsLib.BEETS_STABLE_POOL_USDp_GAMI_USDC, + ammAdapter: (IPlatform(platform).ammAdapter(keccak256(bytes(AmmAdapterIdLib.BALANCER_V3_STABLE)))).proxy, + tokenIn: AvalancheConstantsLib.TOKEN_USDp, + tokenOut: AvalancheConstantsLib.SILO_MANAGED_VAULT_GAMI_USDC + }); + pools[2] = ISwapper.PoolData({ + pool: AvalancheConstantsLib.SILO_MANAGED_VAULT_GAMI_USDC, + ammAdapter: (IPlatform(platform).ammAdapter(keccak256(bytes(AmmAdapterIdLib.ERC_4626)))).proxy, + tokenIn: AvalancheConstantsLib.SILO_MANAGED_VAULT_GAMI_USDC, + tokenOut: AvalancheConstantsLib.TOKEN_USDC + }); + swapper.addPools(pools, false); + } + + function _addRoutesSAVUSD() internal { + // add routes + ISwapper swapper = ISwapper(IPlatform(platform).swapper()); + ISwapper.PoolData[] memory pools = new ISwapper.PoolData[](2); + pools[0] = ISwapper.PoolData({ + pool: AvalancheConstantsLib.POOL_LB_LFJ_LBPAIR_savUSDC_avUSD, + ammAdapter: (IPlatform(platform).ammAdapter(keccak256(bytes(AmmAdapterIdLib.LBLFJ_V2)))).proxy, + tokenIn: AvalancheConstantsLib.TOKEN_savUSD, + tokenOut: AvalancheConstantsLib.TOKEN_avUSD + }); + pools[1] = ISwapper.PoolData({ + pool: AvalancheConstantsLib.POOL_LB_LFJ_LBPAIR_avUSDC_USDC, + ammAdapter: (IPlatform(platform).ammAdapter(keccak256(bytes(AmmAdapterIdLib.LBLFJ_V2)))).proxy, + tokenIn: AvalancheConstantsLib.TOKEN_avUSD, + tokenOut: AvalancheConstantsLib.TOKEN_USDC + }); + swapper.addPools(pools, false); + } + + function _upgradePlatform() internal { + // we need to skip 1 day to update the swapper + // but we cannot simply skip 1 day, because the silo oracle will start to revert with InvalidPrice + // vm.warp(block.timestamp - 86400); + rewind(86400); + + IPlatform platform = IPlatform(AvalancheConstantsLib.PLATFORM); + + address[] memory proxies = new address[](1); + address[] memory implementations = new address[](1); + + // proxies[0] = platform.ammAdapter(keccak256(bytes(AmmAdapterIdLib.PHARAOH_V3))).proxy; + // implementations[0] = address(new PharaohV3Adapter()); + + vm.startPrank(AvalancheConstantsLib.MULTISIG); + platform.announcePlatformUpgrade("2025.12.2-alpha", proxies, implementations); + + skip(1 days); + platform.upgrade(); + vm.stopPrank(); + } + + function _setFlashLoanVault( + SiloAdvancedLeverageStrategy strategy, + address vaultC, + address vaultB, + uint kind + ) internal { + (uint[] memory params, address[] memory addresses) = strategy.getUniversalParams(); + params[10] = kind; + addresses[0] = vaultC; + addresses[1] = vaultB; + + vm.prank(AvalancheConstantsLib.MULTISIG); + strategy.setUniversalParams(params, addresses); + } +}