From e383ec1600452389d27acc3df14488971bea24a5 Mon Sep 17 00:00:00 2001 From: "John McClure (pickleback)" Date: Thu, 19 Dec 2024 01:52:15 -0600 Subject: [PATCH] enforce that when `isWrapped=true`, `asBase=false` --- contracts/EverlongStrategy.sol | 14 ++++++++++---- contracts/interfaces/IEverlongStrategy.sol | 4 ++++ test/everlong/units/Everlong.t.sol | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/contracts/EverlongStrategy.sol b/contracts/EverlongStrategy.sol index 687f874..54aeb6e 100644 --- a/contracts/EverlongStrategy.sol +++ b/contracts/EverlongStrategy.sol @@ -120,6 +120,7 @@ contract EverlongStrategy is BaseStrategy { /// @notice Whether to use Hyperdrive's base token to purchase bonds. /// If false, use the Hyperdrive's `vaultSharesToken`. + /// @dev When `isWrapped=true`, `asBase` must be set to false. bool public immutable asBase; /// @notice Whether the strategy asset is a wrapped version of hyperdrive's @@ -172,12 +173,21 @@ contract EverlongStrategy is BaseStrategy { bool _asBase, bool _isWrapped ) BaseStrategy(_asset, __name) { + // When the asset is wrapped, `_asBase` must be false. + if (_isWrapped && _asBase) { + revert IEverlongStrategy.WrappedBaseMismatch(); + } + // Store the hyperdrive instance's address. hyperdrive = _hyperdrive; // Store whether to interact with hyperdrive using its base token. asBase = _asBase; + // Store whether `asset` should be treated as a wrapped hyperdrive + // token. + isWrapped = _isWrapped; + // Store the hyperdrive's PoolConfig since it's static. _poolConfig = IHyperdrive(_hyperdrive).getPoolConfig(); @@ -185,10 +195,6 @@ contract EverlongStrategy is BaseStrategy { executionToken = address( _asBase ? _poolConfig.baseToken : _poolConfig.vaultSharesToken ); - - // Store whether `asset` should be treated as a wrapped hyperdrive - // token. - isWrapped = _isWrapped; } // ╭───────────────────────────────────────────────────────────────────────╮ diff --git a/contracts/interfaces/IEverlongStrategy.sol b/contracts/interfaces/IEverlongStrategy.sol index 88d154b..c3bbb5c 100644 --- a/contracts/interfaces/IEverlongStrategy.sol +++ b/contracts/interfaces/IEverlongStrategy.sol @@ -40,6 +40,10 @@ interface IEverlongStrategy is IPermissionedStrategy, IEverlongEvents { /// a non-wrapped asset. error AssetNotWrapped(); + /// @notice Thrown when creating a strategy with both `isWrapped == true` + /// and `isBase == true`. + error WrappedBaseMismatch(); + // ╭───────────────────────────────────────────────────────────────────────╮ // │ SETTERS │ // ╰───────────────────────────────────────────────────────────────────────╯ diff --git a/test/everlong/units/Everlong.t.sol b/test/everlong/units/Everlong.t.sol index 8749c88..52f5a26 100644 --- a/test/everlong/units/Everlong.t.sol +++ b/test/everlong/units/Everlong.t.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.20; +import { console2 as console } from "forge-std/console2.sol"; import { IEverlongStrategy } from "../../../contracts/interfaces/IEverlongStrategy.sol"; +import { EverlongStrategy } from "../../../contracts/EverlongStrategy.sol"; import { EVERLONG_STRATEGY_KIND, EVERLONG_VERSION } from "../../../contracts/libraries/Constants.sol"; import { EverlongTest } from "../EverlongTest.sol"; @@ -33,4 +35,18 @@ contract TestEverlong is EverlongTest { "version does not match" ); } + + /// @dev Tests that the error `IEverlongStrategy.WrappedBaseMismatch()` is + /// thrown when creating a strategy with `isWrapped=true` and + /// `asBase=true`. + function test_wrapped_isbase_true_failure() external { + vm.expectRevert(); + new EverlongStrategy( + address(address(asset)), + "EverlongTest", + address(hyperdrive), + true, + true + ); + } }